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:
Nicolo' Mazzucato
2022-10-12 12:44:00 +00:00
parent 9a0ed1afe1
commit 683cfc81cc
2 changed files with 177 additions and 0 deletions

View File

@@ -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()
}
}

View File

@@ -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) } }
}
}
}