Merge "Start fold animation only when the large screen is visible" into tm-qpr-dev

This commit is contained in:
Nicolò Mazzucato
2023-03-22 14:51:24 +00:00
committed by Android (Google) Code Review
3 changed files with 97 additions and 27 deletions

View File

@@ -16,6 +16,9 @@
package com.android.systemui.unfold.updates 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.os.Handler
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.core.util.Consumer 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.unfold.util.UnfoldKeyguardVisibilityProvider
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
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
@@ -49,20 +53,19 @@ import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
class DeviceFoldStateProviderTest : SysuiTestCase() { class DeviceFoldStateProviderTest : SysuiTestCase() {
@Mock @Mock private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider
private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider
@Mock @Mock private lateinit var handler: Handler
private lateinit var handler: Handler
@Mock @Mock private lateinit var rotationChangeProvider: RotationChangeProvider
private lateinit var rotationChangeProvider: RotationChangeProvider
@Mock @Mock private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider
private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider
@Captor @Mock private lateinit var resources: Resources
private lateinit var rotationListener: ArgumentCaptor<RotationListener>
@Mock private lateinit var context: Context
@Captor private lateinit var rotationListener: ArgumentCaptor<RotationListener>
private val foldProvider = TestFoldProvider() private val foldProvider = TestFoldProvider()
private val screenOnStatusProvider = TestScreenOnStatusProvider() private val screenOnStatusProvider = TestScreenOnStatusProvider()
@@ -81,10 +84,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { val config =
object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() {
override val halfFoldedTimeoutMillis: Int override val halfFoldedTimeoutMillis: Int
get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() get() = HALF_OPENED_TIMEOUT_MILLIS.toInt()
} }
whenever(context.resources).thenReturn(resources)
whenever(context.mainExecutor).thenReturn(mContext.mainExecutor)
foldStateProvider = foldStateProvider =
DeviceFoldStateProvider( DeviceFoldStateProvider(
@@ -95,6 +101,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
activityTypeProvider, activityTypeProvider,
unfoldKeyguardVisibilityProvider, unfoldKeyguardVisibilityProvider,
rotationChangeProvider, rotationChangeProvider,
context,
context.mainExecutor, context.mainExecutor,
handler handler
) )
@@ -112,7 +119,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
override fun onUnfoldedScreenAvailable() { override fun onUnfoldedScreenAvailable() {
unfoldedScreenAvailabilityUpdates.add(Unit) unfoldedScreenAvailabilityUpdates.add(Unit)
} }
}) }
)
foldStateProvider.start() foldStateProvider.start()
verify(rotationChangeProvider).addCallback(capture(rotationListener)) verify(rotationChangeProvider).addCallback(capture(rotationListener))
@@ -134,6 +142,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
// By default, we're on launcher. // By default, we're on launcher.
setupForegroundActivityType(isHomeActivity = true) setupForegroundActivityType(isHomeActivity = true)
setIsLargeScreen(true)
} }
@Test @Test
@@ -387,7 +396,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent( sendHingeAngleEvent(
START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 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) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
} }
@@ -430,7 +441,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent( sendHingeAngleEvent(
START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 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) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
} }
@@ -531,8 +544,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
rotationListener.value.onRotationChanged(1) rotationListener.value.onRotationChanged(1)
assertThat(foldUpdates).containsExactly( assertThat(foldUpdates)
FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN) .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN)
} }
@Test @Test
@@ -545,6 +558,45 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED) 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?) { private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity) whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
} }
@@ -566,6 +618,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
foldProvider.notifyFolded(folded) 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() { private fun fireScreenOnEvent() {
screenOnStatusProvider.notifyScreenTurnedOn() screenOnStatusProvider.notifyScreenTurnedOn()
} }

View File

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

View File

@@ -15,12 +15,14 @@
*/ */
package com.android.systemui.unfold.updates package com.android.systemui.unfold.updates
import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Trace import android.os.Trace
import android.util.Log import android.util.Log
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.core.util.Consumer 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.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
@@ -45,6 +47,7 @@ constructor(
private val activityTypeProvider: CurrentActivityTypeProvider, private val activityTypeProvider: CurrentActivityTypeProvider,
private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider, private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider,
private val rotationChangeProvider: RotationChangeProvider, private val rotationChangeProvider: RotationChangeProvider,
private val context: Context,
@UnfoldMain private val mainExecutor: Executor, @UnfoldMain private val mainExecutor: Executor,
@UnfoldMain private val handler: Handler @UnfoldMain private val handler: Handler
) : FoldStateProvider { ) : FoldStateProvider {
@@ -136,6 +139,7 @@ constructor(
val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate
val screenAvailableEventSent = isUnfoldHandled val screenAvailableEventSent = isUnfoldHandled
val isOnLargeScreen = isOnLargeScreen()
if ( if (
angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle
@@ -144,7 +148,9 @@ constructor(
// angle range as closing threshold could overlap this range // angle range as closing threshold could overlap this range
screenAvailableEventSent && // do not send transition event if we are still in the screenAvailableEventSent && // do not send transition event if we are still in the
// process of turning on the inner display // 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) notifyFoldUpdate(transitionUpdate, lastHingeAngle)
} }
@@ -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. */ /** While the screen is off or the device is folded, hinge angle updates are not needed. */
private fun updateHingeAngleProviderState() { private fun updateHingeAngleProviderState() {
if (isScreenOn && !isFolded) { if (isScreenOn && !isFolded) {