Merge changes from topic "cancel_unfold_anim_on_rotation" into tm-qpr-dev
* changes: Cancel unfold animation when rotation changes Create RotationChangeProvider in sysui
This commit is contained in:
committed by
Android (Google) Code Review
commit
aea723c854
@@ -17,6 +17,7 @@ package com.android.systemui.shared.animation
|
|||||||
|
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
|
import android.view.Surface.Rotation
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
||||||
@@ -58,14 +59,14 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
|
|||||||
* Updates display properties in order to calculate the initial position for the views
|
* Updates display properties in order to calculate the initial position for the views
|
||||||
* Must be called before [registerViewForAnimation]
|
* Must be called before [registerViewForAnimation]
|
||||||
*/
|
*/
|
||||||
fun updateDisplayProperties() {
|
@JvmOverloads
|
||||||
|
fun updateDisplayProperties(@Rotation rotation: Int = windowManager.defaultDisplay.rotation) {
|
||||||
windowManager.defaultDisplay.getSize(screenSize)
|
windowManager.defaultDisplay.getSize(screenSize)
|
||||||
|
|
||||||
// Simple implementation to get current fold orientation,
|
// Simple implementation to get current fold orientation,
|
||||||
// this might not be correct on all devices
|
// this might not be correct on all devices
|
||||||
// TODO: use JetPack WindowManager library to get the fold orientation
|
// TODO: use JetPack WindowManager library to get the fold orientation
|
||||||
isVerticalFold = windowManager.defaultDisplay.rotation == Surface.ROTATION_0 ||
|
isVerticalFold = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
||||||
windowManager.defaultDisplay.rotation == Surface.ROTATION_180
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,12 +15,11 @@
|
|||||||
package com.android.systemui.unfold.util
|
package com.android.systemui.unfold.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.RemoteException
|
|
||||||
import android.view.IRotationWatcher
|
|
||||||
import android.view.IWindowManager
|
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
|
* [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
|
||||||
@@ -29,27 +28,21 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
|
|||||||
*/
|
*/
|
||||||
class NaturalRotationUnfoldProgressProvider(
|
class NaturalRotationUnfoldProgressProvider(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val windowManagerInterface: IWindowManager,
|
private val rotationChangeProvider: RotationChangeProvider,
|
||||||
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
|
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
|
||||||
) : UnfoldTransitionProgressProvider {
|
) : UnfoldTransitionProgressProvider {
|
||||||
|
|
||||||
private val scopedUnfoldTransitionProgressProvider =
|
private val scopedUnfoldTransitionProgressProvider =
|
||||||
ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
|
ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
|
||||||
private val rotationWatcher = RotationWatcher()
|
|
||||||
|
|
||||||
private var isNaturalRotation: Boolean = false
|
private var isNaturalRotation: Boolean = false
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
try {
|
rotationChangeProvider.addCallback(rotationListener)
|
||||||
windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
|
rotationListener.onRotationChanged(context.display.rotation)
|
||||||
} catch (e: RemoteException) {
|
|
||||||
throw e.rethrowFromSystemServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
onRotationChanged(context.display.rotation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRotationChanged(rotation: Int) {
|
private val rotationListener = RotationListener { rotation ->
|
||||||
val isNewRotationNatural =
|
val isNewRotationNatural =
|
||||||
rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
||||||
|
|
||||||
@@ -60,12 +53,7 @@ class NaturalRotationUnfoldProgressProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
try {
|
rotationChangeProvider.removeCallback(rotationListener)
|
||||||
windowManagerInterface.removeRotationWatcher(rotationWatcher)
|
|
||||||
} catch (e: RemoteException) {
|
|
||||||
e.rethrowFromSystemServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
scopedUnfoldTransitionProgressProvider.destroy()
|
scopedUnfoldTransitionProgressProvider.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +64,4 @@ class NaturalRotationUnfoldProgressProvider(
|
|||||||
override fun removeCallback(listener: TransitionProgressListener) {
|
override fun removeCallback(listener: TransitionProgressListener) {
|
||||||
scopedUnfoldTransitionProgressProvider.removeCallback(listener)
|
scopedUnfoldTransitionProgressProvider.removeCallback(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class RotationWatcher : IRotationWatcher.Stub() {
|
|
||||||
override fun onRotationChanged(rotation: Int) {
|
|
||||||
this@NaturalRotationUnfoldProgressProvider.onRotationChanged(rotation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ import android.os.Trace
|
|||||||
import android.view.Choreographer
|
import android.view.Choreographer
|
||||||
import android.view.Display
|
import android.view.Display
|
||||||
import android.view.DisplayInfo
|
import android.view.DisplayInfo
|
||||||
import android.view.IRotationWatcher
|
|
||||||
import android.view.IWindowManager
|
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceControl
|
import android.view.SurfaceControl
|
||||||
import android.view.SurfaceControlViewHost
|
import android.view.SurfaceControlViewHost
|
||||||
@@ -40,6 +38,7 @@ import com.android.systemui.statusbar.LightRevealEffect
|
|||||||
import com.android.systemui.statusbar.LightRevealScrim
|
import com.android.systemui.statusbar.LightRevealScrim
|
||||||
import com.android.systemui.statusbar.LinearLightRevealEffect
|
import com.android.systemui.statusbar.LinearLightRevealEffect
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||||
import com.android.systemui.util.traceSection
|
import com.android.systemui.util.traceSection
|
||||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
@@ -58,7 +57,7 @@ constructor(
|
|||||||
private val displayAreaHelper: Optional<DisplayAreaHelper>,
|
private val displayAreaHelper: Optional<DisplayAreaHelper>,
|
||||||
@Main private val executor: Executor,
|
@Main private val executor: Executor,
|
||||||
@UiBackground private val backgroundExecutor: Executor,
|
@UiBackground private val backgroundExecutor: Executor,
|
||||||
private val windowManagerInterface: IWindowManager
|
private val rotationChangeProvider: RotationChangeProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val transitionListener = TransitionListener()
|
private val transitionListener = TransitionListener()
|
||||||
@@ -78,7 +77,7 @@ constructor(
|
|||||||
fun init() {
|
fun init() {
|
||||||
deviceStateManager.registerCallback(executor, FoldListener())
|
deviceStateManager.registerCallback(executor, FoldListener())
|
||||||
unfoldTransitionProgressProvider.addCallback(transitionListener)
|
unfoldTransitionProgressProvider.addCallback(transitionListener)
|
||||||
windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
|
rotationChangeProvider.addCallback(rotationWatcher)
|
||||||
|
|
||||||
val containerBuilder =
|
val containerBuilder =
|
||||||
SurfaceControl.Builder(SurfaceSession())
|
SurfaceControl.Builder(SurfaceSession())
|
||||||
@@ -86,7 +85,9 @@ constructor(
|
|||||||
.setName("unfold-overlay-container")
|
.setName("unfold-overlay-container")
|
||||||
|
|
||||||
displayAreaHelper.get().attachToRootDisplayArea(
|
displayAreaHelper.get().attachToRootDisplayArea(
|
||||||
Display.DEFAULT_DISPLAY, containerBuilder) { builder ->
|
Display.DEFAULT_DISPLAY,
|
||||||
|
containerBuilder
|
||||||
|
) { builder ->
|
||||||
executor.execute {
|
executor.execute {
|
||||||
overlayContainer = builder.build()
|
overlayContainer = builder.build()
|
||||||
|
|
||||||
@@ -244,8 +245,8 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class RotationWatcher : IRotationWatcher.Stub() {
|
private inner class RotationWatcher : RotationChangeProvider.RotationListener {
|
||||||
override fun onRotationChanged(newRotation: Int) =
|
override fun onRotationChanged(newRotation: Int) {
|
||||||
traceSection("UnfoldLightRevealOverlayAnimation#onRotationChanged") {
|
traceSection("UnfoldLightRevealOverlayAnimation#onRotationChanged") {
|
||||||
if (currentRotation != newRotation) {
|
if (currentRotation != newRotation) {
|
||||||
currentRotation = newRotation
|
currentRotation = newRotation
|
||||||
@@ -253,6 +254,7 @@ constructor(
|
|||||||
root?.relayout(getLayoutParams())
|
root?.relayout(getLayoutParams())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FoldListener :
|
private inner class FoldListener :
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
package com.android.systemui.unfold
|
package com.android.systemui.unfold
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.IWindowManager
|
|
||||||
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
|
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||||
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
|
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider
|
import com.android.systemui.unfold.updates.FoldStateProvider
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
|
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
|
||||||
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
|
||||||
@@ -65,11 +65,11 @@ class UnfoldTransitionModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
fun provideNaturalRotationProgressProvider(
|
fun provideNaturalRotationProgressProvider(
|
||||||
context: Context,
|
context: Context,
|
||||||
windowManager: IWindowManager,
|
rotationChangeProvider: RotationChangeProvider,
|
||||||
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
|
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
|
||||||
): Optional<NaturalRotationUnfoldProgressProvider> =
|
): Optional<NaturalRotationUnfoldProgressProvider> =
|
||||||
unfoldTransitionProgressProvider.map { provider ->
|
unfoldTransitionProgressProvider.map { provider ->
|
||||||
NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
|
NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -25,16 +25,21 @@ import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
|
|||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||||
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider
|
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider
|
||||||
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
|
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
|
||||||
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.Mockito.`when` as whenever
|
import org.mockito.Mockito.`when` as whenever
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@@ -48,6 +53,12 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
private lateinit var handler: Handler
|
private lateinit var handler: Handler
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var rotationChangeProvider: RotationChangeProvider
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private lateinit var rotationListener: ArgumentCaptor<RotationListener>
|
||||||
|
|
||||||
private val foldProvider = TestFoldProvider()
|
private val foldProvider = TestFoldProvider()
|
||||||
private val screenOnStatusProvider = TestScreenOnStatusProvider()
|
private val screenOnStatusProvider = TestScreenOnStatusProvider()
|
||||||
private val testHingeAngleProvider = TestHingeAngleProvider()
|
private val testHingeAngleProvider = TestHingeAngleProvider()
|
||||||
@@ -76,6 +87,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
screenOnStatusProvider,
|
screenOnStatusProvider,
|
||||||
foldProvider,
|
foldProvider,
|
||||||
activityTypeProvider,
|
activityTypeProvider,
|
||||||
|
rotationChangeProvider,
|
||||||
context.mainExecutor,
|
context.mainExecutor,
|
||||||
handler
|
handler
|
||||||
)
|
)
|
||||||
@@ -92,6 +104,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
})
|
})
|
||||||
foldStateProvider.start()
|
foldStateProvider.start()
|
||||||
|
|
||||||
|
verify(rotationChangeProvider).addCallback(capture(rotationListener))
|
||||||
|
|
||||||
whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
|
whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
|
||||||
scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
|
scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
|
||||||
scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
|
scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
|
||||||
@@ -372,6 +386,27 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
assertThat(testHingeAngleProvider.isStarted).isFalse()
|
assertThat(testHingeAngleProvider.isStarted).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onRotationChanged_whileInProgress_cancelled() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING)
|
||||||
|
|
||||||
|
rotationListener.value.onRotationChanged(1)
|
||||||
|
|
||||||
|
assertThat(foldUpdates).containsExactly(
|
||||||
|
FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onRotationChanged_whileNotInProgress_noUpdates() {
|
||||||
|
setFoldState(folded = true)
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED)
|
||||||
|
|
||||||
|
rotationListener.value.onRotationChanged(1)
|
||||||
|
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
|
private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
|
||||||
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
|
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,18 +16,19 @@
|
|||||||
package com.android.systemui.unfold.util
|
package com.android.systemui.unfold.util
|
||||||
|
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.view.IRotationWatcher
|
|
||||||
import android.view.IWindowManager
|
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.unfold.TestUnfoldTransitionProvider
|
import com.android.systemui.unfold.TestUnfoldTransitionProvider
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.clearInvocations
|
import org.mockito.Mockito.clearInvocations
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
@@ -38,32 +39,26 @@ import org.mockito.MockitoAnnotations
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock
|
@Mock lateinit var rotationChangeProvider: RotationChangeProvider
|
||||||
lateinit var windowManager: IWindowManager
|
|
||||||
|
|
||||||
private val sourceProvider = TestUnfoldTransitionProvider()
|
private val sourceProvider = TestUnfoldTransitionProvider()
|
||||||
|
|
||||||
@Mock
|
@Mock lateinit var transitionListener: TransitionProgressListener
|
||||||
lateinit var transitionListener: TransitionProgressListener
|
|
||||||
|
@Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
|
||||||
|
|
||||||
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
|
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
|
||||||
|
|
||||||
private val rotationWatcherCaptor =
|
|
||||||
ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
progressProvider = NaturalRotationUnfoldProgressProvider(
|
progressProvider =
|
||||||
context,
|
NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, sourceProvider)
|
||||||
windowManager,
|
|
||||||
sourceProvider
|
|
||||||
)
|
|
||||||
|
|
||||||
progressProvider.init()
|
progressProvider.init()
|
||||||
|
|
||||||
verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
|
verify(rotationChangeProvider).addCallback(capture(rotationListenerCaptor))
|
||||||
|
|
||||||
progressProvider.addCallback(transitionListener)
|
progressProvider.addCallback(transitionListener)
|
||||||
}
|
}
|
||||||
@@ -127,6 +122,6 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onRotationChanged(rotation: Int) {
|
private fun onRotationChanged(rotation: Int) {
|
||||||
rotationWatcherCaptor.value.onRotationChanged(rotation)
|
rotationListenerCaptor.value.onRotationChanged(rotation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ import android.content.ContentResolver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.view.IWindowManager
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||||
import com.android.systemui.unfold.dagger.UnfoldBackground
|
import com.android.systemui.unfold.dagger.UnfoldBackground
|
||||||
import com.android.systemui.unfold.dagger.UnfoldMain
|
import com.android.systemui.unfold.dagger.UnfoldMain
|
||||||
import com.android.systemui.unfold.updates.FoldProvider
|
import com.android.systemui.unfold.updates.FoldProvider
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
||||||
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
|
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
|
||||||
@@ -39,11 +41,11 @@ import javax.inject.Singleton
|
|||||||
*
|
*
|
||||||
* This component is meant to be used for places that don't use dagger. By providing those
|
* This component is meant to be used for places that don't use dagger. By providing those
|
||||||
* parameters to the factory, all dagger objects are correctly instantiated. See
|
* parameters to the factory, all dagger objects are correctly instantiated. See
|
||||||
* [createUnfoldTransitionProgressProvider] for an example.
|
* [createUnfoldSharedComponent] for an example.
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
@Component(modules = [UnfoldSharedModule::class])
|
@Component(modules = [UnfoldSharedModule::class])
|
||||||
internal interface UnfoldSharedComponent {
|
interface UnfoldSharedComponent {
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
@@ -58,9 +60,11 @@ internal interface UnfoldSharedComponent {
|
|||||||
@BindsInstance @UnfoldMain executor: Executor,
|
@BindsInstance @UnfoldMain executor: Executor,
|
||||||
@BindsInstance @UnfoldBackground backgroundExecutor: Executor,
|
@BindsInstance @UnfoldBackground backgroundExecutor: Executor,
|
||||||
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
|
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
|
||||||
|
@BindsInstance windowManager: IWindowManager,
|
||||||
@BindsInstance contentResolver: ContentResolver = context.contentResolver
|
@BindsInstance contentResolver: ContentResolver = context.contentResolver
|
||||||
): UnfoldSharedComponent
|
): UnfoldSharedComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
|
val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
|
||||||
|
val rotationChangeProvider: RotationChangeProvider
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package com.android.systemui.unfold
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.view.IWindowManager
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||||
import com.android.systemui.unfold.updates.FoldProvider
|
import com.android.systemui.unfold.updates.FoldProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
@@ -27,14 +28,15 @@ import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
|||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for [UnfoldTransitionProgressProvider].
|
* Factory for [UnfoldSharedComponent].
|
||||||
*
|
*
|
||||||
* This is needed as Launcher has to create the object manually. If dagger is available, this object
|
* This wraps the autogenerated factory (for discoverability), and is needed as Launcher has to
|
||||||
* is provided in [UnfoldSharedModule].
|
* create the object manually. If dagger is available, this object is provided in
|
||||||
|
* [UnfoldSharedModule].
|
||||||
*
|
*
|
||||||
* This should **never** be called from sysui, as the object is already provided in that process.
|
* This should **never** be called from sysui, as the object is already provided in that process.
|
||||||
*/
|
*/
|
||||||
fun createUnfoldTransitionProgressProvider(
|
fun createUnfoldSharedComponent(
|
||||||
context: Context,
|
context: Context,
|
||||||
config: UnfoldTransitionConfig,
|
config: UnfoldTransitionConfig,
|
||||||
screenStatusProvider: ScreenStatusProvider,
|
screenStatusProvider: ScreenStatusProvider,
|
||||||
@@ -44,8 +46,9 @@ fun createUnfoldTransitionProgressProvider(
|
|||||||
mainHandler: Handler,
|
mainHandler: Handler,
|
||||||
mainExecutor: Executor,
|
mainExecutor: Executor,
|
||||||
backgroundExecutor: Executor,
|
backgroundExecutor: Executor,
|
||||||
tracingTagPrefix: String
|
tracingTagPrefix: String,
|
||||||
): UnfoldTransitionProgressProvider =
|
windowManager: IWindowManager,
|
||||||
|
): UnfoldSharedComponent =
|
||||||
DaggerUnfoldSharedComponent.factory()
|
DaggerUnfoldSharedComponent.factory()
|
||||||
.create(
|
.create(
|
||||||
context,
|
context,
|
||||||
@@ -57,9 +60,6 @@ fun createUnfoldTransitionProgressProvider(
|
|||||||
mainHandler,
|
mainHandler,
|
||||||
mainExecutor,
|
mainExecutor,
|
||||||
backgroundExecutor,
|
backgroundExecutor,
|
||||||
tracingTagPrefix)
|
tracingTagPrefix,
|
||||||
.unfoldTransitionProvider
|
windowManager,
|
||||||
.orElse(null)
|
)
|
||||||
?: throw IllegalStateException(
|
|
||||||
"Trying to create " +
|
|
||||||
"UnfoldTransitionProgressProvider when the transition is disabled")
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ import com.android.systemui.unfold.util.CallbackController
|
|||||||
*
|
*
|
||||||
* onTransitionProgress callback could be called on each frame.
|
* onTransitionProgress callback could be called on each frame.
|
||||||
*
|
*
|
||||||
* Use [createUnfoldTransitionProgressProvider] to create instances of this interface
|
* Use [createUnfoldSharedComponent] to create instances of this interface when dagger is not
|
||||||
|
* available.
|
||||||
*/
|
*/
|
||||||
interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgressListener> {
|
interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgressListener> {
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
|||||||
import com.android.systemui.unfold.dagger.UnfoldMain
|
import com.android.systemui.unfold.dagger.UnfoldMain
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
|
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
|
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
|
||||||
|
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
|
||||||
import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
|
import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
|
||||||
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
|
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
|
||||||
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
||||||
@@ -40,22 +41,24 @@ constructor(
|
|||||||
private val screenStatusProvider: ScreenStatusProvider,
|
private val screenStatusProvider: ScreenStatusProvider,
|
||||||
private val foldProvider: FoldProvider,
|
private val foldProvider: FoldProvider,
|
||||||
private val activityTypeProvider: CurrentActivityTypeProvider,
|
private val activityTypeProvider: CurrentActivityTypeProvider,
|
||||||
|
private val rotationChangeProvider: RotationChangeProvider,
|
||||||
@UnfoldMain private val mainExecutor: Executor,
|
@UnfoldMain private val mainExecutor: Executor,
|
||||||
@UnfoldMain private val handler: Handler
|
@UnfoldMain private val handler: Handler
|
||||||
) : FoldStateProvider {
|
) : FoldStateProvider {
|
||||||
|
|
||||||
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
|
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
|
||||||
|
|
||||||
@FoldUpdate
|
@FoldUpdate private var lastFoldUpdate: Int? = null
|
||||||
private var lastFoldUpdate: Int? = null
|
|
||||||
|
|
||||||
@FloatRange(from = 0.0, to = 180.0)
|
@FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
|
||||||
private var lastHingeAngle: Float = 0f
|
|
||||||
|
|
||||||
private val hingeAngleListener = HingeAngleListener()
|
private val hingeAngleListener = HingeAngleListener()
|
||||||
private val screenListener = ScreenStatusListener()
|
private val screenListener = ScreenStatusListener()
|
||||||
private val foldStateListener = FoldStateListener()
|
private val foldStateListener = FoldStateListener()
|
||||||
private val timeoutRunnable = TimeoutRunnable()
|
private val timeoutRunnable = Runnable { cancelAnimation() }
|
||||||
|
private val rotationListener = RotationListener {
|
||||||
|
if (isTransitionInProgress) cancelAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
|
* Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
|
||||||
@@ -72,6 +75,7 @@ constructor(
|
|||||||
foldProvider.registerCallback(foldStateListener, mainExecutor)
|
foldProvider.registerCallback(foldStateListener, mainExecutor)
|
||||||
screenStatusProvider.addCallback(screenListener)
|
screenStatusProvider.addCallback(screenListener)
|
||||||
hingeAngleProvider.addCallback(hingeAngleListener)
|
hingeAngleProvider.addCallback(hingeAngleListener)
|
||||||
|
rotationChangeProvider.addCallback(rotationListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
@@ -79,6 +83,7 @@ constructor(
|
|||||||
foldProvider.unregisterCallback(foldStateListener)
|
foldProvider.unregisterCallback(foldStateListener)
|
||||||
hingeAngleProvider.removeCallback(hingeAngleListener)
|
hingeAngleProvider.removeCallback(hingeAngleListener)
|
||||||
hingeAngleProvider.stop()
|
hingeAngleProvider.stop()
|
||||||
|
rotationChangeProvider.removeCallback(rotationListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addCallback(listener: FoldUpdatesListener) {
|
override fun addCallback(listener: FoldUpdatesListener) {
|
||||||
@@ -90,14 +95,15 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val isFinishedOpening: Boolean
|
override val isFinishedOpening: Boolean
|
||||||
get() = !isFolded &&
|
get() =
|
||||||
|
!isFolded &&
|
||||||
(lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN ||
|
(lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN ||
|
||||||
lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
|
lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
|
||||||
|
|
||||||
private val isTransitionInProgress: Boolean
|
private val isTransitionInProgress: Boolean
|
||||||
get() =
|
get() =
|
||||||
lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
|
lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
|
||||||
lastFoldUpdate == FOLD_UPDATE_START_CLOSING
|
lastFoldUpdate == FOLD_UPDATE_START_CLOSING
|
||||||
|
|
||||||
private fun onHingeAngle(angle: Float) {
|
private fun onHingeAngle(angle: Float) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -168,7 +174,7 @@ constructor(
|
|||||||
|
|
||||||
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
|
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, stateToString(update))
|
Log.d(TAG, update.name())
|
||||||
}
|
}
|
||||||
outputListeners.forEach { it.onFoldUpdate(update) }
|
outputListeners.forEach { it.onFoldUpdate(update) }
|
||||||
lastFoldUpdate = update
|
lastFoldUpdate = update
|
||||||
@@ -185,6 +191,8 @@ constructor(
|
|||||||
handler.removeCallbacks(timeoutRunnable)
|
handler.removeCallbacks(timeoutRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun cancelAnimation(): Unit = notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
|
||||||
|
|
||||||
private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
|
private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
|
||||||
|
|
||||||
override fun onScreenTurnedOn() {
|
override fun onScreenTurnedOn() {
|
||||||
@@ -225,16 +233,10 @@ constructor(
|
|||||||
onHingeAngle(angle)
|
onHingeAngle(angle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class TimeoutRunnable : Runnable {
|
|
||||||
override fun run() {
|
|
||||||
notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stateToString(@FoldUpdate update: Int): String {
|
fun @receiver:FoldUpdate Int.name() =
|
||||||
return when (update) {
|
when (this) {
|
||||||
FOLD_UPDATE_START_OPENING -> "START_OPENING"
|
FOLD_UPDATE_START_OPENING -> "START_OPENING"
|
||||||
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
|
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
|
||||||
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
|
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
|
||||||
@@ -243,15 +245,12 @@ private fun stateToString(@FoldUpdate update: Int): String {
|
|||||||
FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
|
FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
|
||||||
else -> "UNKNOWN"
|
else -> "UNKNOWN"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private const val TAG = "DeviceFoldProvider"
|
private const val TAG = "DeviceFoldProvider"
|
||||||
private const val DEBUG = false
|
private const val DEBUG = false
|
||||||
|
|
||||||
/** Threshold after which we consider the device fully unfolded. */
|
/** Threshold after which we consider the device fully unfolded. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
|
||||||
const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
|
|
||||||
|
|
||||||
/** Fold animation on top of apps only when the angle exceeds this threshold. */
|
/** Fold animation on top of apps only when the angle exceeds this threshold. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
|
||||||
const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
|
|
||||||
|
|||||||
@@ -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