Loading packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +82 −23 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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<RotationListener> @Mock private lateinit var resources: Resources @Mock private lateinit var context: Context @Captor private lateinit var rotationListener: ArgumentCaptor<RotationListener> private val foldProvider = TestFoldProvider() private val screenOnStatusProvider = TestScreenOnStatusProvider() Loading @@ -81,10 +84,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { 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( Loading @@ -95,6 +101,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { activityTypeProvider, unfoldKeyguardVisibilityProvider, rotationChangeProvider, context, context.mainExecutor, handler ) Loading @@ -112,7 +119,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onUnfoldedScreenAvailable() { unfoldedScreenAvailabilityUpdates.add(Unit) } }) } ) foldStateProvider.start() verify(rotationChangeProvider).addCallback(capture(rotationListener)) Loading @@ -134,6 +142,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { // By default, we're on launcher. setupForegroundActivityType(isHomeActivity = true) setIsLargeScreen(true) } @Test Loading Loading @@ -387,7 +396,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent( START_CLOSING_ON_APPS_THRESHOLD_DEGREES - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1 ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } Loading Loading @@ -430,7 +441,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent( START_CLOSING_ON_APPS_THRESHOLD_DEGREES - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1 ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } Loading Loading @@ -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 Loading @@ -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) } Loading @@ -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() } Loading packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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 packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +14 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading Loading @@ -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 Loading @@ -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) } Loading Loading @@ -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) { Loading Loading
packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +82 −23 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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<RotationListener> @Mock private lateinit var resources: Resources @Mock private lateinit var context: Context @Captor private lateinit var rotationListener: ArgumentCaptor<RotationListener> private val foldProvider = TestFoldProvider() private val screenOnStatusProvider = TestScreenOnStatusProvider() Loading @@ -81,10 +84,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { 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( Loading @@ -95,6 +101,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { activityTypeProvider, unfoldKeyguardVisibilityProvider, rotationChangeProvider, context, context.mainExecutor, handler ) Loading @@ -112,7 +119,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onUnfoldedScreenAvailable() { unfoldedScreenAvailabilityUpdates.add(Unit) } }) } ) foldStateProvider.start() verify(rotationChangeProvider).addCallback(capture(rotationListener)) Loading @@ -134,6 +142,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { // By default, we're on launcher. setupForegroundActivityType(isHomeActivity = true) setIsLargeScreen(true) } @Test Loading Loading @@ -387,7 +396,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent( START_CLOSING_ON_APPS_THRESHOLD_DEGREES - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1 ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } Loading Loading @@ -430,7 +441,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent( START_CLOSING_ON_APPS_THRESHOLD_DEGREES - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1 ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } Loading Loading @@ -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 Loading @@ -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) } Loading @@ -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() } Loading
packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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
packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +14 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading Loading @@ -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 Loading @@ -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) } Loading Loading @@ -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) { Loading