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
This commit is contained in:
@@ -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<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()
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user