Create RotationChangeProvider in sysui
This allows to subscribe to rotation changes in a specific executor. Raw usages of IRotationWatcher made the callback be received in a binder thread (not the main one). Usages in child cls to make each small. Bug: 241743859 Test: RotationChangeProviderTest Change-Id: I2de074a3d5330d93b3d4cc2be1e60864a34ce6a6
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.unfold.updates
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.IRotationWatcher
|
||||
import android.view.IWindowManager
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class RotationChangeProviderTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var rotationChangeProvider: RotationChangeProvider
|
||||
|
||||
@Mock lateinit var windowManagerInterface: IWindowManager
|
||||
@Mock lateinit var listener: RotationListener
|
||||
@Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher>
|
||||
private val fakeExecutor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
rotationChangeProvider =
|
||||
RotationChangeProvider(windowManagerInterface, context, fakeExecutor)
|
||||
rotationChangeProvider.addCallback(listener)
|
||||
fakeExecutor.runAllReady()
|
||||
verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onRotationChanged_rotationUpdated_listenerReceivesIt() {
|
||||
sendRotationUpdate(42)
|
||||
|
||||
verify(listener).onRotationChanged(42)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onRotationChanged_subscribersRemoved_noRotationChangeReceived() {
|
||||
sendRotationUpdate(42)
|
||||
verify(listener).onRotationChanged(42)
|
||||
|
||||
rotationChangeProvider.removeCallback(listener)
|
||||
fakeExecutor.runAllReady()
|
||||
sendRotationUpdate(43)
|
||||
|
||||
verify(windowManagerInterface).removeRotationWatcher(any())
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
private fun sendRotationUpdate(newRotation: Int) {
|
||||
rotationWatcher.value.onRotationChanged(newRotation)
|
||||
fakeExecutor.runAllReady()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.unfold.updates
|
||||
|
||||
import android.content.Context
|
||||
import android.os.RemoteException
|
||||
import android.view.IRotationWatcher
|
||||
import android.view.IWindowManager
|
||||
import android.view.Surface.Rotation
|
||||
import com.android.systemui.unfold.dagger.UnfoldMain
|
||||
import com.android.systemui.unfold.util.CallbackController
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Allows to subscribe to rotation changes.
|
||||
*
|
||||
* This is needed as rotation updates from [IWindowManager] are received in a binder thread, while
|
||||
* most of the times we want them in the main one. Updates are provided for the display associated
|
||||
* to [context].
|
||||
*/
|
||||
class RotationChangeProvider
|
||||
@Inject
|
||||
constructor(
|
||||
private val windowManagerInterface: IWindowManager,
|
||||
private val context: Context,
|
||||
@UnfoldMain private val mainExecutor: Executor,
|
||||
) : CallbackController<RotationChangeProvider.RotationListener> {
|
||||
|
||||
private val listeners = mutableListOf<RotationListener>()
|
||||
|
||||
private val rotationWatcher = RotationWatcher()
|
||||
|
||||
override fun addCallback(listener: RotationListener) {
|
||||
mainExecutor.execute {
|
||||
if (listeners.isEmpty()) {
|
||||
subscribeToRotation()
|
||||
}
|
||||
listeners += listener
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeCallback(listener: RotationListener) {
|
||||
mainExecutor.execute {
|
||||
listeners -= listener
|
||||
if (listeners.isEmpty()) {
|
||||
unsubscribeToRotation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToRotation() {
|
||||
try {
|
||||
windowManagerInterface.watchRotation(rotationWatcher, context.displayId)
|
||||
} catch (e: RemoteException) {
|
||||
throw e.rethrowFromSystemServer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun unsubscribeToRotation() {
|
||||
try {
|
||||
windowManagerInterface.removeRotationWatcher(rotationWatcher)
|
||||
} catch (e: RemoteException) {
|
||||
throw e.rethrowFromSystemServer()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets notified of rotation changes. */
|
||||
fun interface RotationListener {
|
||||
/** Called once rotation changes. */
|
||||
fun onRotationChanged(@Rotation newRotation: Int)
|
||||
}
|
||||
|
||||
private inner class RotationWatcher : IRotationWatcher.Stub() {
|
||||
override fun onRotationChanged(rotation: Int) {
|
||||
mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user