Loading packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +12 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -39,14 +40,22 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL } override fun onScreenTurnedOn() { traceSection("$TRACE_TAG#onScreenTurnedOn") { listeners.forEach(ScreenListener::onScreenTurnedOn) } } override fun onScreenTurningOff() { traceSection("$TRACE_TAG#onScreenTurningOff") { listeners.forEach(ScreenListener::onScreenTurningOff) } } override fun onScreenTurningOn() { traceSection("$TRACE_TAG#onScreenTurningOn") { listeners.forEach(ScreenListener::onScreenTurningOn) } } } private const val TRACE_TAG = "LifecycleScreenStatusProvider" No newline at end of file packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +78 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -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) } } /** Loading @@ -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" ) } } } } Loading @@ -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) packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt +91 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -76,22 +83,88 @@ 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() Loading @@ -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 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -157,7 +158,10 @@ class PhysicsBasedUnfoldTransitionProgressProvider( } private fun onStartTransition() { Trace.beginSection( "$TAG#onStartTransition") listeners.forEach { it.onTransitionStarted() } Trace.endSection() isTransitionRunning = true if (DEBUG) { Loading Loading
packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +12 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -39,14 +40,22 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL } override fun onScreenTurnedOn() { traceSection("$TRACE_TAG#onScreenTurnedOn") { listeners.forEach(ScreenListener::onScreenTurnedOn) } } override fun onScreenTurningOff() { traceSection("$TRACE_TAG#onScreenTurningOff") { listeners.forEach(ScreenListener::onScreenTurningOff) } } override fun onScreenTurningOn() { traceSection("$TRACE_TAG#onScreenTurningOn") { listeners.forEach(ScreenListener::onScreenTurningOn) } } } private const val TRACE_TAG = "LifecycleScreenStatusProvider" No newline at end of file
packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +78 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -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) } } /** Loading @@ -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" ) } } } } Loading @@ -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)
packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt +91 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -76,22 +83,88 @@ 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() Loading @@ -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
packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -157,7 +158,10 @@ class PhysicsBasedUnfoldTransitionProgressProvider( } private fun onStartTransition() { Trace.beginSection( "$TAG#onStartTransition") listeners.forEach { it.onTransitionStarted() } Trace.endSection() isTransitionRunning = true if (DEBUG) { Loading