[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
This commit is contained in:
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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,8 +83,11 @@ 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())
|
||||
@@ -85,13 +95,76 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@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 @@ 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user