diff options
author | 2022-12-06 17:47:27 +0000 | |
---|---|---|
committer | 2022-12-16 15:31:29 +0000 | |
commit | 8874ab952f8fc29b18bd202064413a977d080172 (patch) | |
tree | b26ecf7e6254681fb3a026351a5758e88347e326 | |
parent | c514109b3bc4794ea12a94bedf08edf3d5b27a48 (diff) |
[Unfold transition] Update latency tracker to listen for animation start
We are going to introduce potential delay before
starting the animation, so updating the latency
tracker to track the end of the unfold display switch
only when the animation is started.
Bug: 258214245
Test: atest com.android.systemui.unfold.UnfoldLatencyTrackerTest
Test: manual unfold with/without animations => check that metric is reported
Change-Id: If3017719631f115579f16493136cce0d0e5e2f80
4 files changed, 185 insertions, 11 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt index 0b04fb43c2d7..6063ce27ca16 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.util.traceSection import javax.inject.Inject import javax.inject.Singleton @@ -39,14 +40,22 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL } override fun onScreenTurnedOn() { - listeners.forEach(ScreenListener::onScreenTurnedOn) + traceSection("$TRACE_TAG#onScreenTurnedOn") { + listeners.forEach(ScreenListener::onScreenTurnedOn) + } } override fun onScreenTurningOff() { - listeners.forEach(ScreenListener::onScreenTurningOff) + traceSection("$TRACE_TAG#onScreenTurningOff") { + listeners.forEach(ScreenListener::onScreenTurningOff) + } } override fun onScreenTurningOn() { - listeners.forEach(ScreenListener::onScreenTurningOn) + traceSection("$TRACE_TAG#onScreenTurningOn") { + listeners.forEach(ScreenListener::onScreenTurningOn) + } } } + +private const val TRACE_TAG = "LifecycleScreenStatusProvider"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt index 79b42b8daab1..9269df31e37e 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt @@ -16,12 +16,18 @@ package com.android.systemui.unfold +import android.content.ContentResolver import android.content.Context import android.hardware.devicestate.DeviceStateManager +import android.util.Log import com.android.internal.util.LatencyTracker import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.keyguard.ScreenLifecycle +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled +import com.android.systemui.util.Compile +import java.util.Optional import java.util.concurrent.Executor import javax.inject.Inject @@ -41,17 +47,19 @@ class UnfoldLatencyTracker constructor( private val latencyTracker: LatencyTracker, private val deviceStateManager: DeviceStateManager, + private val transitionProgressProvider: Optional<UnfoldTransitionProgressProvider>, @UiBackground private val uiBgExecutor: Executor, private val context: Context, + private val contentResolver: ContentResolver, private val screenLifecycle: ScreenLifecycle -) : ScreenLifecycle.Observer { +) : ScreenLifecycle.Observer, TransitionProgressListener { private var folded: Boolean? = null + private var isTransitionEnabled: Boolean? = null private val foldStateListener = FoldStateListener(context) private val isFoldable: Boolean get() = - context - .resources + context.resources .getIntArray(com.android.internal.R.array.config_foldedDeviceStates) .isNotEmpty() @@ -62,6 +70,11 @@ constructor( } deviceStateManager.registerCallback(uiBgExecutor, foldStateListener) screenLifecycle.addObserver(this) + if (transitionProgressProvider.isPresent) { + // Might not be present if the device is not a foldable device or unfold transition + // is disabled in the device configuration + transitionProgressProvider.get().addCallback(this) + } } /** @@ -71,16 +84,72 @@ constructor( * end action event only if we previously received a fold state. */ override fun onScreenTurnedOn() { - if (folded == false) { + if (DEBUG) { + Log.d( + TAG, + "onScreenTurnedOn: folded = $folded, isTransitionEnabled = $isTransitionEnabled" + ) + } + + // We use onScreenTurnedOn event to finish tracking only if we are not playing + // the unfold animation (e.g. it could be disabled because of battery saver). + // When animation is enabled finishing of the tracking will be done in onTransitionStarted. + if (folded == false && isTransitionEnabled == false) { latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) + + if (DEBUG) { + Log.d(TAG, "onScreenTurnedOn: ending ACTION_SWITCH_DISPLAY_UNFOLD") + } + } + } + + /** + * This callback is used to end the metric when the unfold animation is enabled because it could + * add an additional delay to synchronize with launcher. + */ + override fun onTransitionStarted() { + if (DEBUG) { + Log.d( + TAG, + "onTransitionStarted: folded = $folded, isTransitionEnabled = $isTransitionEnabled" + ) + } + + if (folded == false && isTransitionEnabled == true) { + latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) + + if (DEBUG) { + Log.d(TAG, "onTransitionStarted: ending ACTION_SWITCH_DISPLAY_UNFOLD") + } } } private fun onFoldEvent(folded: Boolean) { - if (this.folded != folded) { + val oldFolded = this.folded + + if (oldFolded != folded) { this.folded = folded - if (!folded) { // unfolding started + + if (DEBUG) { + Log.d(TAG, "Received onFoldEvent = $folded") + } + + // Do not start tracking when oldFolded is null, this means that this is the first + // onFoldEvent after booting the device or starting SystemUI and not actual folding or + // unfolding the device. + if (oldFolded != null && !folded) { + // Unfolding started latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) + isTransitionEnabled = + transitionProgressProvider.isPresent && contentResolver.areAnimationsEnabled() + + if (DEBUG) { + Log.d( + TAG, + "Starting ACTION_SWITCH_DISPLAY_UNFOLD, " + + "isTransitionEnabled = $isTransitionEnabled" + ) + } } } } @@ -88,3 +157,6 @@ constructor( private inner class FoldStateListener(context: Context) : DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) }) } + +private const val TAG = "UnfoldLatencyTracker" +private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE) diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt index 4e2736c74007..c7dc0ab8f9cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.unfold import android.hardware.devicestate.DeviceStateManager import android.hardware.devicestate.DeviceStateManager.FoldStateListener +import android.provider.Settings import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker @@ -32,9 +33,11 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations +import java.util.Optional @RunWith(AndroidTestingRunner::class) @SmallTest @@ -59,14 +62,18 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker + private val transitionProgressProvider = TestUnfoldTransitionProvider() + @Before fun setUp() { MockitoAnnotations.initMocks(this) unfoldLatencyTracker = UnfoldLatencyTracker( latencyTracker, deviceStateManager, + Optional.of(transitionProgressProvider), context.mainExecutor, context, + context.contentResolver, screenLifecycle ).apply { init() } deviceStates = FoldableTestUtils.findDeviceStates(context) @@ -76,23 +83,89 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { } @Test - fun unfold_eventPropagated() { + fun unfold_startedFolded_animationsDisabled_eventPropagatedOnScreenTurnedOnEvent() { + setAnimationsEnabled(false) + sendFoldEvent(folded = true) + sendFoldEvent(folded = false) + + sendScreenTurnedOnEvent() + + verify(latencyTracker).onActionStart(any()) + verify(latencyTracker).onActionEnd(any()) + } + + @Test + fun unfold_startedFolded_animationsEnabledOnScreenTurnedOn_eventNotFinished() { + setAnimationsEnabled(true) + sendFoldEvent(folded = true) + sendFoldEvent(folded = false) + + sendScreenTurnedOnEvent() + + verify(latencyTracker).onActionStart(any()) + verify(latencyTracker, never()).onActionEnd(any()) + } + + @Test + fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() { + setAnimationsEnabled(true) + sendFoldEvent(folded = false) + + sendScreenTurnedOnEvent() + transitionProgressProvider.onTransitionStarted() + + verifyNoMoreInteractions(latencyTracker) + } + + @Test + fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { + setAnimationsEnabled(true) + sendFoldEvent(folded = true) + sendFoldEvent(folded = false) + + sendScreenTurnedOnEvent() + transitionProgressProvider.onTransitionStarted() + + verify(latencyTracker).onActionStart(any()) + verify(latencyTracker).onActionEnd(any()) + } + + @Test + fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { + setAnimationsEnabled(true) + sendFoldEvent(folded = false) + sendFoldEvent(folded = true) sendFoldEvent(folded = false) + sendScreenTurnedOnEvent() + transitionProgressProvider.onTransitionStarted() verify(latencyTracker).onActionStart(any()) verify(latencyTracker).onActionEnd(any()) } @Test - fun fold_eventNotPropagated() { + fun fold_animationsDisabled_screenTurnedOn_eventNotPropagated() { + setAnimationsEnabled(false) sendFoldEvent(folded = true) + sendScreenTurnedOnEvent() // outer display on. verifyNoMoreInteractions(latencyTracker) } @Test + fun fold_animationsEnabled_screenTurnedOn_eventNotPropagated() { + setAnimationsEnabled(true) + sendFoldEvent(folded = true) + + sendScreenTurnedOnEvent() // outer display on. + transitionProgressProvider.onTransitionStarted() + + verifyNoMoreInteractions(latencyTracker) + } + + @Test fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() { sendScreenTurnedOnEvent() @@ -107,4 +180,20 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { private fun sendScreenTurnedOnEvent() { screenLifecycleCaptor.value.onScreenTurnedOn() } + + private fun setAnimationsEnabled(enabled: Boolean) { + val durationScale = + if (enabled) { + 1f + } else { + 0f + } + + // It uses [TestableSettingsProvider] and it will be cleared after the test + Settings.Global.putString( + context.contentResolver, + Settings.Global.ANIMATOR_DURATION_SCALE, + durationScale.toString() + ) + } }
\ No newline at end of file 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 ecc029dc1a07..074b1e162fed 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 @@ -16,6 +16,7 @@ package com.android.systemui.unfold.progress import android.os.Trace +import android.os.Trace.TRACE_TAG_APP import android.util.Log import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat @@ -157,7 +158,10 @@ class PhysicsBasedUnfoldTransitionProgressProvider( } private fun onStartTransition() { + Trace.beginSection( "$TAG#onStartTransition") listeners.forEach { it.onTransitionStarted() } + Trace.endSection() + isTransitionRunning = true if (DEBUG) { |