Merge "[Unfold transition] Update latency tracker to listen for animation start" into tm-qpr-dev am: 596e3f9025

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20637217

Change-Id: Ie5445ece5ea5d0c19d7f0e37306a6f8cb297ad99
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Nick Chameyev
2022-12-19 11:45:46 +00:00
committed by Automerger Merge Worker
4 changed files with 185 additions and 11 deletions

View File

@@ -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
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.util.traceSection
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -39,14 +40,22 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL
} }
override fun onScreenTurnedOn() { override fun onScreenTurnedOn() {
traceSection("$TRACE_TAG#onScreenTurnedOn") {
listeners.forEach(ScreenListener::onScreenTurnedOn) listeners.forEach(ScreenListener::onScreenTurnedOn)
} }
}
override fun onScreenTurningOff() { override fun onScreenTurningOff() {
traceSection("$TRACE_TAG#onScreenTurningOff") {
listeners.forEach(ScreenListener::onScreenTurningOff) listeners.forEach(ScreenListener::onScreenTurningOff)
} }
}
override fun onScreenTurningOn() { override fun onScreenTurningOn() {
traceSection("$TRACE_TAG#onScreenTurningOn") {
listeners.forEach(ScreenListener::onScreenTurningOn) listeners.forEach(ScreenListener::onScreenTurningOn)
} }
}
} }
private const val TRACE_TAG = "LifecycleScreenStatusProvider"

View File

@@ -16,12 +16,18 @@
package com.android.systemui.unfold package com.android.systemui.unfold
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.hardware.devicestate.DeviceStateManager import android.hardware.devicestate.DeviceStateManager
import android.util.Log
import com.android.internal.util.LatencyTracker import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.keyguard.ScreenLifecycle 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 java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
@@ -41,17 +47,19 @@ class UnfoldLatencyTracker
constructor( constructor(
private val latencyTracker: LatencyTracker, private val latencyTracker: LatencyTracker,
private val deviceStateManager: DeviceStateManager, private val deviceStateManager: DeviceStateManager,
private val transitionProgressProvider: Optional<UnfoldTransitionProgressProvider>,
@UiBackground private val uiBgExecutor: Executor, @UiBackground private val uiBgExecutor: Executor,
private val context: Context, private val context: Context,
private val contentResolver: ContentResolver,
private val screenLifecycle: ScreenLifecycle private val screenLifecycle: ScreenLifecycle
) : ScreenLifecycle.Observer { ) : ScreenLifecycle.Observer, TransitionProgressListener {
private var folded: Boolean? = null private var folded: Boolean? = null
private var isTransitionEnabled: Boolean? = null
private val foldStateListener = FoldStateListener(context) private val foldStateListener = FoldStateListener(context)
private val isFoldable: Boolean private val isFoldable: Boolean
get() = get() =
context context.resources
.resources
.getIntArray(com.android.internal.R.array.config_foldedDeviceStates) .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
.isNotEmpty() .isNotEmpty()
@@ -62,6 +70,11 @@ constructor(
} }
deviceStateManager.registerCallback(uiBgExecutor, foldStateListener) deviceStateManager.registerCallback(uiBgExecutor, foldStateListener)
screenLifecycle.addObserver(this) 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. * end action event only if we previously received a fold state.
*/ */
override fun onScreenTurnedOn() { 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) 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) { private fun onFoldEvent(folded: Boolean) {
if (this.folded != folded) { val oldFolded = this.folded
if (oldFolded != folded) {
this.folded = 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) 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) : private inner class FoldStateListener(context: Context) :
DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) }) DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) })
} }
private const val TAG = "UnfoldLatencyTracker"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)

View File

@@ -18,6 +18,7 @@ package com.android.systemui.unfold
import android.hardware.devicestate.DeviceStateManager import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.provider.Settings
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker import com.android.internal.util.LatencyTracker
@@ -32,9 +33,11 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.Captor import org.mockito.Captor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.util.Optional
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@SmallTest @SmallTest
@@ -59,14 +62,18 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker
private val transitionProgressProvider = TestUnfoldTransitionProvider()
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
unfoldLatencyTracker = UnfoldLatencyTracker( unfoldLatencyTracker = UnfoldLatencyTracker(
latencyTracker, latencyTracker,
deviceStateManager, deviceStateManager,
Optional.of(transitionProgressProvider),
context.mainExecutor, context.mainExecutor,
context, context,
context.contentResolver,
screenLifecycle screenLifecycle
).apply { init() } ).apply { init() }
deviceStates = FoldableTestUtils.findDeviceStates(context) deviceStates = FoldableTestUtils.findDeviceStates(context)
@@ -76,8 +83,11 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
} }
@Test @Test
fun unfold_eventPropagated() { fun unfold_startedFolded_animationsDisabled_eventPropagatedOnScreenTurnedOnEvent() {
setAnimationsEnabled(false)
sendFoldEvent(folded = true)
sendFoldEvent(folded = false) sendFoldEvent(folded = false)
sendScreenTurnedOnEvent() sendScreenTurnedOnEvent()
verify(latencyTracker).onActionStart(any()) verify(latencyTracker).onActionStart(any())
@@ -85,13 +95,76 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
} }
@Test @Test
fun fold_eventNotPropagated() { fun unfold_startedFolded_animationsEnabledOnScreenTurnedOn_eventNotFinished() {
setAnimationsEnabled(true)
sendFoldEvent(folded = 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. sendScreenTurnedOnEvent() // outer display on.
verifyNoMoreInteractions(latencyTracker) verifyNoMoreInteractions(latencyTracker)
} }
@Test
fun fold_animationsEnabled_screenTurnedOn_eventNotPropagated() {
setAnimationsEnabled(true)
sendFoldEvent(folded = true)
sendScreenTurnedOnEvent() // outer display on.
transitionProgressProvider.onTransitionStarted()
verifyNoMoreInteractions(latencyTracker)
}
@Test @Test
fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() { fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() {
sendScreenTurnedOnEvent() sendScreenTurnedOnEvent()
@@ -107,4 +180,20 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
private fun sendScreenTurnedOnEvent() { private fun sendScreenTurnedOnEvent() {
screenLifecycleCaptor.value.onScreenTurnedOn() 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()
)
}
} }

View File

@@ -16,6 +16,7 @@
package com.android.systemui.unfold.progress package com.android.systemui.unfold.progress
import android.os.Trace import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.util.Log import android.util.Log
import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -157,7 +158,10 @@ class PhysicsBasedUnfoldTransitionProgressProvider(
} }
private fun onStartTransition() { private fun onStartTransition() {
Trace.beginSection( "$TAG#onStartTransition")
listeners.forEach { it.onTransitionStarted() } listeners.forEach { it.onTransitionStarted() }
Trace.endSection()
isTransitionRunning = true isTransitionRunning = true
if (DEBUG) { if (DEBUG) {