Merge "[Unfold transition] Update latency tracker to listen for animation start" into tm-qpr-dev
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index 0b04fb4..6063ce2 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 @@
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 @@
}
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 79b42b8..9269df3 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 @@
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 @@
}
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 @@
* 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 @@
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 4e2736c7..c7dc0ab 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 @@
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.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 @@
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,8 +83,11 @@
}
@Test
- fun unfold_eventPropagated() {
+ fun unfold_startedFolded_animationsDisabled_eventPropagatedOnScreenTurnedOnEvent() {
+ setAnimationsEnabled(false)
+ sendFoldEvent(folded = true)
sendFoldEvent(folded = false)
+
sendScreenTurnedOnEvent()
verify(latencyTracker).onActionStart(any())
@@ -85,14 +95,77 @@
}
@Test
- fun fold_eventNotPropagated() {
+ 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_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 @@
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 ecc029d..074b1e1 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 @@
}
private fun onStartTransition() {
+ Trace.beginSection( "$TAG#onStartTransition")
listeners.forEach { it.onTransitionStarted() }
+ Trace.endSection()
+
isTransitionRunning = true
if (DEBUG) {