From 6a496023ae081521d46ca10b23f6f536d691c701 Mon Sep 17 00:00:00 2001 From: Uwais Ashraf Date: Thu, 23 Feb 2023 11:16:27 +0000 Subject: [PATCH] Add a threshold to the trigger fold animation Test: Unit tests added and manual testing Bug: 256792419 Change-Id: If414fd3b30bff705243a36b1e3e4c32cf2601609 --- .../unfold/FoldStateLoggingProviderImpl.kt | 6 +- ...sedUnfoldTransitionProgressProviderTest.kt | 11 +- .../updates/DeviceFoldStateProviderTest.kt | 124 +++++++++++++----- .../unfold/util/TestFoldStateProvider.kt | 4 + .../FixedTimingTransitionProgressProvider.kt | 12 +- ...csBasedUnfoldTransitionProgressProvider.kt | 23 ++-- .../unfold/updates/DeviceFoldStateProvider.kt | 62 ++++++--- .../unfold/updates/FoldStateProvider.kt | 13 +- 8 files changed, 174 insertions(+), 81 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt index 2683971f852c0..981f429d1f8f4 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt @@ -61,8 +61,6 @@ class FoldStateLoggingProviderImpl( foldStateProvider.stop() } - override fun onHingeAngleUpdate(angle: Float) {} - override fun onFoldUpdate(@FoldUpdate update: Int) { val now = clock.elapsedRealtime() when (update) { @@ -77,6 +75,10 @@ class FoldStateLoggingProviderImpl( } } + override fun onUnfoldedScreenAvailable() { + Log.d(TAG, "Unfolded screen available") + } + private fun dispatchState(@LoggedFoldedStates current: Int) { val now = clock.elapsedRealtime() val previous = lastState diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt index 5288608a202d1..0413d92b6abb3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt @@ -25,7 +25,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.util.TestFoldStateProvider import org.junit.Before import org.junit.Test @@ -50,7 +49,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -67,7 +66,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -84,7 +83,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, ) with(listener.ensureTransitionFinished()) { @@ -113,7 +112,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) }, @@ -129,7 +128,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testFoldImmediatelyAfterUnfold_runsFoldAnimation() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { 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 6086e16fb49a4..8476d0d456035 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 @@ -26,6 +26,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener +import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener @@ -71,6 +72,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { private val foldUpdates: MutableList = arrayListOf() private val hingeAngleUpdates: MutableList = arrayListOf() + private val unfoldedScreenAvailabilityUpdates: MutableList = arrayListOf() private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @@ -106,6 +108,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onFoldUpdate(update: Int) { foldUpdates.add(update) } + + override fun onUnfoldedScreenAvailable() { + unfoldedScreenAvailabilityUpdates.add(Unit) + } }) foldStateProvider.start() @@ -156,8 +162,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(10) screenOnStatusProvider.notifyScreenTurnedOn() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -174,8 +180,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(40) sendHingeAngleEvent(10) - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_START_CLOSING) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -223,7 +230,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fireScreenOnEvent() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -277,7 +284,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_afterTimeout_finishHalfOpenEventEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) @@ -288,7 +295,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_beforeTimeout_abortNotEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -298,7 +305,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -309,7 +316,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_timeoutAfterTimeoutRescheduled_finishHalfOpenStateEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) // The timeout should not trigger here. @@ -323,7 +330,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) sendHingeAngleEvent(80) @@ -334,20 +341,19 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() { val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt() - for (i in 1..maxAngle) { - foldUpdates.clear() - - simulateFolding(startAngle = i) + val minAngle = Math.ceil(HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toDouble()).toInt() + 1 + for (startAngle in minAngle..maxAngle) { + setInitialHingeAngle(startAngle) + sendHingeAngleEvent(startAngle - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) - simulateTimeout() // Timeout to set the state to aborted. } } @Test fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -357,7 +363,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -367,7 +373,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -377,9 +383,11 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -388,7 +396,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun startClosingEvent_whileNotOnKeyguardAndNotOnLauncher_doesNotTriggerBeforeThreshold() { setKeyguardVisibility(visible = false) setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -398,7 +406,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileKeyguardStateNotAvailable_triggerBeforeThreshold() { setKeyguardVisibility(visible = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -408,7 +416,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileonKeyguard_doesTriggerBeforeThreshold() { setKeyguardVisibility(visible = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -418,9 +426,59 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnKeyguard_triggersAfterThreshold() { setKeyguardVisibility(visible = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_doesNotTriggerBelowThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + + assertThat(foldUpdates).isEmpty() + } + + @Test + fun startClosingEvent_triggersAfterThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + sendHingeAngleEvent(thresholdAngle - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_triggersAfterThreshold_fromHalfOpen() { + setInitialHingeAngle(120) + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES + 1).toInt()) + assertThat(foldUpdates).isEmpty() + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES - 1).toInt()) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startOpeningAndClosingEvents_triggerWithOpenAndClose() { + setInitialHingeAngle(120) + sendHingeAngleEvent(130) + sendHingeAngleEvent(120) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_notInterrupted_whenAngleIsSlightlyIncreased() { + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(111) + sendHingeAngleEvent(100) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -504,11 +562,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } } - private fun simulateFolding(startAngle: Int) { - sendHingeAngleEvent(startAngle) - sendHingeAngleEvent(startAngle - 1) - } - private fun setFoldState(folded: Boolean) { foldProvider.notifyFolded(folded) } @@ -521,6 +574,17 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { testHingeAngleProvider.notifyAngle(angle.toFloat()) } + private fun setInitialHingeAngle(angle: Int) { + setFoldState(angle == 0) + sendHingeAngleEvent(angle) + if (scheduledRunnableDelay != null) { + simulateTimeout() + } + hingeAngleUpdates.clear() + foldUpdates.clear() + unfoldedScreenAvailabilityUpdates.clear() + } + private class TestFoldProvider : FoldProvider { private val callbacks = arrayListOf() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt index a064e8c810762..fbb0e5a72cd1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt @@ -57,4 +57,8 @@ class TestFoldStateProvider : FoldStateProvider { fun sendHingeAngleUpdate(angle: Float) { listeners.forEach { it.onHingeAngleUpdate(angle) } } + + fun sendUnfoldedScreenAvailable() { + listeners.forEach { it.onUnfoldedScreenAvailable() } + } } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index 4622464b204db..c437e5c23d1b7 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt @@ -21,7 +21,6 @@ import android.util.FloatProperty import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import javax.inject.Inject @@ -59,12 +58,15 @@ constructor(private val foldStateProvider: FoldStateProvider) : } override fun onFoldUpdate(@FoldUpdate update: Int) { - when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> animator.start() - FOLD_UPDATE_FINISH_CLOSED -> animator.cancel() + if (update == FOLD_UPDATE_FINISH_CLOSED) { + animator.cancel() } } + override fun onUnfoldedScreenAvailable() { + animator.start() + } + override fun addCallback(listener: TransitionProgressListener) { listeners.add(listener) } @@ -73,8 +75,6 @@ constructor(private val foldStateProvider: FoldStateProvider) : listeners.remove(listener) } - override fun onHingeAngleUpdate(angle: Float) {} - private object AnimationProgressProperty : FloatProperty("animation_progress") { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 6ffbe5aa25c06..d19b414cb9639 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -28,7 +28,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener @@ -78,21 +77,11 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> { - startTransition(startValue = 0f) - - // Stop the animation if the device has already opened by the time when - // the display is available as we won't receive the full open event anymore - if (foldStateProvider.isFinishedOpening) { - cancelTransition(endValue = 1f, animate = true) - } - } FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> { // Do not cancel if we haven't started the transition yet. // This could happen when we fully unfolded the device before the screen // became available. In this case we start and immediately cancel the animation - // in FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE event handler, so we don't need to - // cancel it here. + // in onUnfoldedScreenAvailable event handler, so we don't need to cancel it here. if (isTransitionRunning) { cancelTransition(endValue = 1f, animate = true) } @@ -125,6 +114,16 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( } } + override fun onUnfoldedScreenAvailable() { + startTransition(startValue = 0f) + + // Stop the animation if the device has already opened by the time when + // the display is available as we won't receive the full open event anymore + if (foldStateProvider.isFinishedOpening) { + cancelTransition(endValue = 1f, animate = true) + } + } + private fun cancelTransition(endValue: Float, animate: Boolean) { if (isTransitionRunning && animate) { if (endValue == 1.0f && !isAnimatedCancelRunning) { 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 97c9ba99f096d..82fd2258120a4 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 @@ -54,6 +54,7 @@ constructor( @FoldUpdate private var lastFoldUpdate: Int? = null @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f + @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngleBeforeTransition: Float = 0f private val hingeAngleListener = HingeAngleListener() private val screenListener = ScreenStatusListener() @@ -112,29 +113,45 @@ constructor( private fun onHingeAngle(angle: Float) { if (DEBUG) { - Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") + Log.d( + TAG, + "Hinge angle: $angle, " + + "lastHingeAngle: $lastHingeAngle, " + + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" + ) Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt()) } - val isClosing = angle < lastHingeAngle + val currentDirection = + if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + if (isTransitionInProgress && currentDirection != lastFoldUpdate) { + lastHingeAngleBeforeTransition = lastHingeAngle + } + + val isClosing = angle < lastHingeAngleBeforeTransition + val transitionUpdate = + if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + val angleChangeSurpassedThreshold = + Math.abs(angle - lastHingeAngleBeforeTransition) > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES - val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING + val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val screenAvailableEventSent = isUnfoldHandled - if (isClosing // hinge angle should be decreasing since last update - && !closingEventDispatched // we haven't sent closing event already - && !isFullyOpened // do not send closing event if we are in fully opened hinge + if ( + angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle + eventNotAlreadyDispatched && // we haven't sent transition event already + !isFullyOpened && // do not send transition event if we are in fully opened hinge // angle range as closing threshold could overlap this range - && screenAvailableEventSent // do not send closing event if we are still in - // the process of turning on the inner display - && isClosingThresholdMet(angle) // hinge angle is below certain threshold. + 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. ) { - notifyFoldUpdate(FOLD_UPDATE_START_CLOSING) + notifyFoldUpdate(transitionUpdate, lastHingeAngle) } if (isTransitionInProgress) { if (isFullyOpened) { - notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) + notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN, angle) cancelTimeout() } else { // The timeout will trigger some constant time after the last angle update. @@ -146,7 +163,7 @@ constructor( outputListeners.forEach { it.onHingeAngleUpdate(angle) } } - private fun isClosingThresholdMet(currentAngle: Float) : Boolean { + private fun isClosingThresholdMet(currentAngle: Float): Boolean { val closingThreshold = getClosingThreshold() return closingThreshold == null || currentAngle < closingThreshold } @@ -179,23 +196,29 @@ constructor( if (isFolded) { hingeAngleProvider.stop() - notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED, lastHingeAngle) cancelTimeout() isUnfoldHandled = false } else { - notifyFoldUpdate(FOLD_UPDATE_START_OPENING) + notifyFoldUpdate(FOLD_UPDATE_START_OPENING, lastHingeAngle) rescheduleAbortAnimationTimeout() hingeAngleProvider.start() } } } - private fun notifyFoldUpdate(@FoldUpdate update: Int) { + private fun notifyFoldUpdate(@FoldUpdate update: Int, angle: Float) { if (DEBUG) { Log.d(TAG, update.name()) } + val previouslyTransitioning = isTransitionInProgress + outputListeners.forEach { it.onFoldUpdate(update) } lastFoldUpdate = update + + if (previouslyTransitioning != isTransitionInProgress) { + lastHingeAngleBeforeTransition = angle + } } private fun rescheduleAbortAnimationTimeout() { @@ -209,7 +232,8 @@ constructor( handler.removeCallbacks(timeoutRunnable) } - private fun cancelAnimation(): Unit = notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) + private fun cancelAnimation(): Unit = + notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener { @@ -221,7 +245,7 @@ constructor( // receive 'folded' event. If SystemUI started when device is already folded it will // still receive 'folded' event on startup. if (!isFolded && !isUnfoldHandled) { - outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) } + outputListeners.forEach { it.onUnfoldedScreenAvailable() } isUnfoldHandled = true } } @@ -257,7 +281,6 @@ fun @receiver:FoldUpdate Int.name() = when (this) { FOLD_UPDATE_START_OPENING -> "START_OPENING" FOLD_UPDATE_START_CLOSING -> "START_CLOSING" - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE" FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN" FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN" FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED" @@ -270,5 +293,8 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) /** Threshold after which we consider the device fully unfolded. */ @VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f +/** Threshold after which hinge angle updates are considered. This is to eliminate noise. */ +@VisibleForTesting const val HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES = 7.5f + /** Fold animation on top of apps only when the angle exceeds this threshold. */ @VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index c7a8bf3367774..0af372f9da247 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -31,8 +31,9 @@ interface FoldStateProvider : CallbackController { val isFinishedOpening: Boolean interface FoldUpdatesListener { - fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) - fun onFoldUpdate(@FoldUpdate update: Int) + @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {} + @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {} + @JvmDefault fun onUnfoldedScreenAvailable() {} } @IntDef( @@ -40,7 +41,6 @@ interface FoldStateProvider : CallbackController { [ FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_FINISH_HALF_OPEN, FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_CLOSED]) @@ -50,7 +50,6 @@ interface FoldStateProvider : CallbackController { const val FOLD_UPDATE_START_OPENING = 0 const val FOLD_UPDATE_START_CLOSING = 1 -const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2 -const val FOLD_UPDATE_FINISH_HALF_OPEN = 3 -const val FOLD_UPDATE_FINISH_FULL_OPEN = 4 -const val FOLD_UPDATE_FINISH_CLOSED = 5 +const val FOLD_UPDATE_FINISH_HALF_OPEN = 2 +const val FOLD_UPDATE_FINISH_FULL_OPEN = 3 +const val FOLD_UPDATE_FINISH_CLOSED = 4