From d40289686c322f7217062658dcabad721d088528 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Mon, 20 Mar 2023 23:19:34 +0000 Subject: Start fold animation only when the large screen is visible Before, when there was a device state override, it was possible to trigger the fold animation on the smaller screen as well. Now, the fold (closing) animation is triggered only when we're on the large display. Bug: 268343352 Test: DeviceFoldStateProviderTest + manual test Change-Id: Ib573b308edfb1954b36718722ffe1eafefa568ce --- .../unfold/updates/DeviceFoldStateProviderTest.kt | 105 ++++++++++++++++----- .../unfold/compat/ScreenSizeFoldProvider.kt | 2 +- .../unfold/updates/DeviceFoldStateProvider.kt | 17 +++- 3 files changed, 97 insertions(+), 27 deletions(-) diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 8476d0d45603..bf54d4297ad8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -16,6 +16,9 @@ package com.android.systemui.unfold.updates +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources import android.os.Handler import android.testing.AndroidTestingRunner import androidx.core.util.Consumer @@ -33,6 +36,7 @@ import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenLis import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor import org.junit.Before @@ -49,20 +53,19 @@ import org.mockito.MockitoAnnotations @SmallTest class DeviceFoldStateProviderTest : SysuiTestCase() { - @Mock - private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider + @Mock private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider - @Mock - private lateinit var handler: Handler + @Mock private lateinit var handler: Handler - @Mock - private lateinit var rotationChangeProvider: RotationChangeProvider + @Mock private lateinit var rotationChangeProvider: RotationChangeProvider - @Mock - private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider + @Mock private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider - @Captor - private lateinit var rotationListener: ArgumentCaptor + @Mock private lateinit var resources: Resources + + @Mock private lateinit var context: Context + + @Captor private lateinit var rotationListener: ArgumentCaptor private val foldProvider = TestFoldProvider() private val screenOnStatusProvider = TestScreenOnStatusProvider() @@ -81,10 +84,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { - override val halfFoldedTimeoutMillis: Int - get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() - } + val config = + object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { + override val halfFoldedTimeoutMillis: Int + get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() + } + whenever(context.resources).thenReturn(resources) + whenever(context.mainExecutor).thenReturn(mContext.mainExecutor) foldStateProvider = DeviceFoldStateProvider( @@ -95,6 +101,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { activityTypeProvider, unfoldKeyguardVisibilityProvider, rotationChangeProvider, + context, context.mainExecutor, handler ) @@ -112,7 +119,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onUnfoldedScreenAvailable() { unfoldedScreenAvailabilityUpdates.add(Unit) } - }) + } + ) foldStateProvider.start() verify(rotationChangeProvider).addCallback(capture(rotationListener)) @@ -134,6 +142,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { // By default, we're on launcher. setupForegroundActivityType(isHomeActivity = true) + setIsLargeScreen(true) } @Test @@ -181,7 +190,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(10) assertThat(foldUpdates) - .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @@ -386,8 +395,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent( - START_CLOSING_ON_APPS_THRESHOLD_DEGREES - - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - + 1 + ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -429,8 +440,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent( - START_CLOSING_ON_APPS_THRESHOLD_DEGREES - - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - + 1 + ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -470,7 +483,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(130) sendHingeAngleEvent(120) assertThat(foldUpdates) - .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) } @Test @@ -531,8 +544,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { rotationListener.value.onRotationChanged(1) - assertThat(foldUpdates).containsExactly( - FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN) } @Test @@ -545,6 +558,45 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED) } + @Test + fun onFolding_onSmallScreen_tansitionDoesNotStart() { + setIsLargeScreen(false) + + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(100) + + assertThat(foldUpdates).isEmpty() + } + + @Test + fun onFolding_onLargeScreen_tansitionStarts() { + setIsLargeScreen(true) + + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(100) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun onUnfold_onSmallScreen_emitsStartOpening() { + // the new display state might arrive later, so it shouldn't be used to decide to send the + // start opening event, but only for the closing. + setFoldState(folded = true) + setIsLargeScreen(false) + foldUpdates.clear() + + setFoldState(folded = false) + screenOnStatusProvider.notifyScreenTurningOn() + sendHingeAngleEvent(10) + sendHingeAngleEvent(20) + screenOnStatusProvider.notifyScreenTurnedOn() + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING) + } + private fun setupForegroundActivityType(isHomeActivity: Boolean?) { whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity) } @@ -566,6 +618,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { foldProvider.notifyFolded(folded) } + private fun setIsLargeScreen(isLargeScreen: Boolean) { + val smallestScreenWidth = if (isLargeScreen) { 601 } else { 10 } + val configuration = Configuration() + configuration.smallestScreenWidthDp = smallestScreenWidth + whenever(resources.configuration).thenReturn(configuration) + } + private fun fireScreenOnEvent() { screenOnStatusProvider.notifyScreenTurnedOn() } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt index 2044f05664d0..380c1fcbf732 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt @@ -53,4 +53,4 @@ class ScreenSizeFoldProvider(private val context: Context) : FoldProvider { } } -private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 +internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index d653fc7beff2..a633a5e41882 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -15,12 +15,14 @@ */ package com.android.systemui.unfold.updates +import android.content.Context import android.os.Handler import android.os.Trace import android.util.Log import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.core.util.Consumer +import com.android.systemui.unfold.compat.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate @@ -45,6 +47,7 @@ constructor( private val activityTypeProvider: CurrentActivityTypeProvider, private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider, private val rotationChangeProvider: RotationChangeProvider, + private val context: Context, @UnfoldMain private val mainExecutor: Executor, @UnfoldMain private val handler: Handler ) : FoldStateProvider { @@ -119,7 +122,7 @@ constructor( "lastHingeAngle: $lastHingeAngle, " + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" ) - Trace.setCounter( "hinge_angle", angle.toLong()) + Trace.setCounter("hinge_angle", angle.toLong()) } val currentDirection = @@ -136,6 +139,7 @@ constructor( val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val screenAvailableEventSent = isUnfoldHandled + val isOnLargeScreen = isOnLargeScreen() if ( angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle @@ -144,7 +148,9 @@ constructor( // angle range as closing threshold could overlap this range screenAvailableEventSent && // do not send transition event if we are still in the // process of turning on the inner display - isClosingThresholdMet(angle) // hinge angle is below certain threshold. + isClosingThresholdMet(angle) && // hinge angle is below certain threshold. + isOnLargeScreen // Avoids sending closing event when on small screen. + // Start event is sent regardless due to hall sensor. ) { notifyFoldUpdate(transitionUpdate, lastHingeAngle) } @@ -233,7 +239,7 @@ constructor( } private fun cancelAnimation(): Unit = - notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) + notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener { @@ -261,6 +267,11 @@ constructor( } } + private fun isOnLargeScreen(): Boolean { + return context.resources.configuration.smallestScreenWidthDp > + INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + } + /** While the screen is off or the device is folded, hinge angle updates are not needed. */ private fun updateHingeAngleProviderState() { if (isScreenOn && !isFolded) { -- cgit v1.2.3-59-g8ed1b