• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.unfold.updates
18 
19 import android.content.Context
20 import android.hardware.display.DisplayManager
21 import android.os.Handler
22 import android.os.RemoteException
23 import android.os.Trace
24 import androidx.annotation.AnyThread
25 import com.android.systemui.unfold.dagger.UnfoldBg
26 import com.android.systemui.unfold.util.CallbackController
27 import dagger.assisted.Assisted
28 import dagger.assisted.AssistedFactory
29 import dagger.assisted.AssistedInject
30 import java.util.concurrent.CopyOnWriteArrayList
31 import java.util.concurrent.atomic.AtomicInteger
32 
33 private const val INVALID_ROTATION = -1
34 
35 /**
36  * Allows to subscribe to rotation changes. Updates are provided for the display associated to
37  * [context].
38  */
39 class RotationChangeProvider
40 @AssistedInject
41 constructor(
42     private val displayManager: DisplayManager,
43     private val context: Context,
44     @UnfoldBg private val bgHandler: Handler,
45     @Assisted private val callbackHandler: Handler,
46 ) : CallbackController<RotationChangeProvider.RotationListener> {
47 
48     private val listeners = CopyOnWriteArrayList<RotationListener>()
49 
50     private val displayListener = RotationDisplayListener()
51     private val lastRotation = AtomicInteger(INVALID_ROTATION)
52 
addCallbacknull53     override fun addCallback(listener: RotationListener) {
54         bgHandler.post {
55             if (listeners.isEmpty()) {
56                 subscribeToRotation()
57             }
58             listeners += listener
59         }
60     }
61 
removeCallbacknull62     override fun removeCallback(listener: RotationListener) {
63         bgHandler.post {
64             listeners -= listener
65             if (listeners.isEmpty()) {
66                 unsubscribeToRotation()
67                 lastRotation.set(INVALID_ROTATION)
68             }
69         }
70     }
71 
subscribeToRotationnull72     private fun subscribeToRotation() {
73         try {
74             displayManager.registerDisplayListener(displayListener, callbackHandler)
75         } catch (e: RemoteException) {
76             throw e.rethrowFromSystemServer()
77         }
78     }
79 
unsubscribeToRotationnull80     private fun unsubscribeToRotation() {
81         try {
82             displayManager.unregisterDisplayListener(displayListener)
83         } catch (e: RemoteException) {
84             throw e.rethrowFromSystemServer()
85         }
86     }
87 
88     /** Gets notified of rotation changes. */
interfacenull89     fun interface RotationListener {
90         /**
91          * Called once rotation changes. This callback is called on the handler provided to
92          * [RotationChangeProvider.Factory.create].
93          */
94         @AnyThread fun onRotationChanged(newRotation: Int)
95     }
96 
97     private inner class RotationDisplayListener : DisplayManager.DisplayListener {
98 
onDisplayChangednull99         override fun onDisplayChanged(displayId: Int) {
100             Trace.beginSection("RotationChangeProvider.RotationDisplayListener#onDisplayChanged")
101             try {
102                 val display = context.display ?: return
103 
104                 if (displayId == display.displayId) {
105                     val currentRotation = display.rotation
106                     if (lastRotation.compareAndSet(lastRotation.get(), currentRotation)) {
107                         listeners.forEach { it.onRotationChanged(currentRotation) }
108                     }
109                 }
110             } finally {
111                 Trace.endSection()
112             }
113         }
114 
onDisplayAddednull115         override fun onDisplayAdded(displayId: Int) {}
116 
onDisplayRemovednull117         override fun onDisplayRemoved(displayId: Int) {}
118     }
119 
120     @AssistedFactory
121     interface Factory {
122         /** Creates a new [RotationChangeProvider] that provides updated using [callbackHandler]. */
createnull123         fun create(callbackHandler: Handler): RotationChangeProvider
124     }
125 }
126