Merge "Monitor fold to AOD latency" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
560e645f3a
@@ -142,6 +142,11 @@ public class LatencyTracker {
|
||||
*/
|
||||
public static final int ACTION_LOAD_SHARE_SHEET = 16;
|
||||
|
||||
/**
|
||||
* Time it takes to show AOD display after folding the device.
|
||||
*/
|
||||
public static final int ACTION_FOLD_TO_AOD = 17;
|
||||
|
||||
private static final int[] ACTIONS_ALL = {
|
||||
ACTION_EXPAND_PANEL,
|
||||
ACTION_TOGGLE_RECENTS,
|
||||
@@ -160,6 +165,7 @@ public class LatencyTracker {
|
||||
ACTION_UDFPS_ILLUMINATE,
|
||||
ACTION_SHOW_BACK_ARROW,
|
||||
ACTION_LOAD_SHARE_SHEET,
|
||||
ACTION_FOLD_TO_AOD,
|
||||
};
|
||||
|
||||
/** @hide */
|
||||
@@ -181,6 +187,7 @@ public class LatencyTracker {
|
||||
ACTION_UDFPS_ILLUMINATE,
|
||||
ACTION_SHOW_BACK_ARROW,
|
||||
ACTION_LOAD_SHARE_SHEET,
|
||||
ACTION_FOLD_TO_AOD
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Action {
|
||||
@@ -204,6 +211,7 @@ public class LatencyTracker {
|
||||
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
|
||||
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
|
||||
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
|
||||
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD
|
||||
};
|
||||
|
||||
private static LatencyTracker sLatencyTracker;
|
||||
@@ -297,6 +305,8 @@ public class LatencyTracker {
|
||||
return "ACTION_SHOW_BACK_ARROW";
|
||||
case 17:
|
||||
return "ACTION_LOAD_SHARE_SHEET";
|
||||
case 19:
|
||||
return "ACTION_FOLD_TO_AOD";
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid action");
|
||||
}
|
||||
|
||||
@@ -3881,29 +3881,37 @@ public final class NotificationPanelViewController extends PanelViewController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts fold to AOD animation
|
||||
* Starts fold to AOD animation.
|
||||
*
|
||||
* @param startAction invoked when the animation starts.
|
||||
* @param endAction invoked when the animation finishes, also if it was cancelled.
|
||||
* @param cancelAction invoked when the animation is cancelled, before endAction.
|
||||
*/
|
||||
public void startFoldToAodAnimation(Runnable endAction) {
|
||||
public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
|
||||
Runnable cancelAction) {
|
||||
mView.animate()
|
||||
.translationX(0)
|
||||
.alpha(1f)
|
||||
.setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
|
||||
.setInterpolator(EMPHASIZED_DECELERATE)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
endAction.run();
|
||||
}
|
||||
.translationX(0)
|
||||
.alpha(1f)
|
||||
.setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
|
||||
.setInterpolator(EMPHASIZED_DECELERATE)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
startAction.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
endAction.run();
|
||||
}
|
||||
})
|
||||
.setUpdateListener(anim -> {
|
||||
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
|
||||
})
|
||||
.start();
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
cancelAction.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
endAction.run();
|
||||
}
|
||||
}).setUpdateListener(anim -> {
|
||||
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,11 +22,12 @@ import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.core.view.OneShotPreDrawListener
|
||||
import com.android.internal.util.LatencyTracker
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||
import com.android.systemui.statusbar.LightRevealScrim
|
||||
import com.android.systemui.statusbar.phone.ScreenOffAnimation
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.statusbar.phone.ScreenOffAnimation
|
||||
import com.android.systemui.statusbar.policy.CallbackController
|
||||
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
@@ -47,7 +48,8 @@ constructor(
|
||||
private val context: Context,
|
||||
private val deviceStateManager: DeviceStateManager,
|
||||
private val wakefulnessLifecycle: WakefulnessLifecycle,
|
||||
private val globalSettings: GlobalSettings
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val latencyTracker: LatencyTracker,
|
||||
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
|
||||
|
||||
private lateinit var mCentralSurfaces: CentralSurfaces
|
||||
@@ -64,12 +66,14 @@ constructor(
|
||||
private var isAnimationPlaying = false
|
||||
|
||||
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
|
||||
private val foldToAodLatencyTracker = FoldToAodLatencyTracker()
|
||||
|
||||
private val startAnimationRunnable = Runnable {
|
||||
mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation {
|
||||
// End action
|
||||
setAnimationState(playing = false)
|
||||
}
|
||||
mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
|
||||
/* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
|
||||
/* endAction= */ { setAnimationState(playing = false) },
|
||||
/* cancelAction= */ { setAnimationState(playing = false) },
|
||||
)
|
||||
}
|
||||
|
||||
override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
|
||||
@@ -82,11 +86,13 @@ constructor(
|
||||
/** Returns true if we should run fold to AOD animation */
|
||||
override fun shouldPlayAnimation(): Boolean = shouldPlayAnimation
|
||||
|
||||
override fun startAnimation(): Boolean =
|
||||
if (alwaysOnEnabled &&
|
||||
private fun shouldStartAnimation(): Boolean =
|
||||
alwaysOnEnabled &&
|
||||
wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
|
||||
globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
|
||||
) {
|
||||
|
||||
override fun startAnimation(): Boolean =
|
||||
if (shouldStartAnimation()) {
|
||||
setAnimationState(playing = true)
|
||||
mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
|
||||
true
|
||||
@@ -97,6 +103,7 @@ constructor(
|
||||
|
||||
override fun onStartedWakingUp() {
|
||||
if (isAnimationPlaying) {
|
||||
foldToAodLatencyTracker.cancel()
|
||||
handler.removeCallbacks(startAnimationRunnable)
|
||||
mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
|
||||
}
|
||||
@@ -137,7 +144,8 @@ constructor(
|
||||
// but we should wait for the initial animation preparations to be drawn
|
||||
// (setting initial alpha/translation)
|
||||
OneShotPreDrawListener.add(
|
||||
mCentralSurfaces.notificationPanelViewController.view, onReady
|
||||
mCentralSurfaces.notificationPanelViewController.view,
|
||||
onReady
|
||||
)
|
||||
} else {
|
||||
// No animation, call ready callback immediately
|
||||
@@ -209,5 +217,41 @@ constructor(
|
||||
isFoldHandled = false
|
||||
}
|
||||
this.isFolded = isFolded
|
||||
})
|
||||
if (isFolded) {
|
||||
foldToAodLatencyTracker.onFolded()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Tracks the latency of fold to AOD using [LatencyTracker].
|
||||
*
|
||||
* Events that trigger start and end are:
|
||||
*
|
||||
* - Start: Once [DeviceStateManager] sends the folded signal [FoldToAodLatencyTracker.onFolded]
|
||||
* is called and latency tracking starts.
|
||||
* - End: Once the fold -> AOD animation starts, [FoldToAodLatencyTracker.onAnimationStarted] is
|
||||
* called, and latency tracking stops.
|
||||
*/
|
||||
private inner class FoldToAodLatencyTracker {
|
||||
|
||||
/** Triggers the latency logging, if needed. */
|
||||
fun onFolded() {
|
||||
if (shouldStartAnimation()) {
|
||||
latencyTracker.onActionStart(LatencyTracker.ACTION_FOLD_TO_AOD)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called once the Fold -> AOD animation is started.
|
||||
*
|
||||
* For latency tracking, this determines the end of the fold to aod action.
|
||||
*/
|
||||
fun onAnimationStarted() {
|
||||
latencyTracker.onActionEnd(LatencyTracker.ACTION_FOLD_TO_AOD)
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
latencyTracker.onActionCancel(LatencyTracker.ACTION_FOLD_TO_AOD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.unfold
|
||||
|
||||
import android.hardware.devicestate.DeviceStateManager
|
||||
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
|
||||
import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.util.LatencyTracker
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||
import com.android.systemui.shade.NotificationPanelViewController
|
||||
import com.android.systemui.statusbar.LightRevealScrim
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.unfold.util.FoldableDeviceStates
|
||||
import com.android.systemui.unfold.util.FoldableTestUtils
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
@RunWithLooper
|
||||
class FoldAodAnimationControllerTest : SysuiTestCase() {
|
||||
|
||||
@Mock lateinit var deviceStateManager: DeviceStateManager
|
||||
|
||||
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
|
||||
|
||||
@Mock lateinit var globalSettings: GlobalSettings
|
||||
|
||||
@Mock lateinit var latencyTracker: LatencyTracker
|
||||
|
||||
@Mock lateinit var centralSurfaces: CentralSurfaces
|
||||
|
||||
@Mock lateinit var lightRevealScrim: LightRevealScrim
|
||||
|
||||
@Mock lateinit var notificationPanelViewController: NotificationPanelViewController
|
||||
|
||||
@Mock lateinit var viewGroup: ViewGroup
|
||||
|
||||
@Mock lateinit var viewTreeObserver: ViewTreeObserver
|
||||
|
||||
@Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
|
||||
|
||||
private lateinit var deviceStates: FoldableDeviceStates
|
||||
|
||||
private lateinit var testableLooper: TestableLooper
|
||||
|
||||
lateinit var foldAodAnimationController: FoldAodAnimationController
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testableLooper = TestableLooper.get(this)
|
||||
|
||||
foldAodAnimationController =
|
||||
FoldAodAnimationController(
|
||||
Handler(testableLooper.looper),
|
||||
context.mainExecutor,
|
||||
context,
|
||||
deviceStateManager,
|
||||
wakefulnessLifecycle,
|
||||
globalSettings,
|
||||
latencyTracker,
|
||||
)
|
||||
.apply { initialize(centralSurfaces, lightRevealScrim) }
|
||||
deviceStates = FoldableTestUtils.findDeviceStates(context)
|
||||
|
||||
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
|
||||
whenever(viewGroup.viewTreeObserver).thenReturn(viewTreeObserver)
|
||||
whenever(wakefulnessLifecycle.lastSleepReason)
|
||||
.thenReturn(PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD)
|
||||
whenever(centralSurfaces.notificationPanelViewController)
|
||||
.thenReturn(notificationPanelViewController)
|
||||
whenever(notificationPanelViewController.startFoldToAodAnimation(any(), any(), any()))
|
||||
.then {
|
||||
val onActionStarted = it.arguments[0] as Runnable
|
||||
onActionStarted.run()
|
||||
}
|
||||
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
|
||||
|
||||
foldAodAnimationController.setIsDozing(dozing = true)
|
||||
setAodEnabled(enabled = true)
|
||||
sendFoldEvent(folded = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onFolded_aodDisabled_doesNotLogLatency() {
|
||||
setAodEnabled(enabled = false)
|
||||
|
||||
fold()
|
||||
simulateScreenTurningOn()
|
||||
|
||||
verifyNoMoreInteractions(latencyTracker)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onFolded_aodEnabled_logsLatency() {
|
||||
setAodEnabled(enabled = true)
|
||||
|
||||
fold()
|
||||
simulateScreenTurningOn()
|
||||
|
||||
verify(latencyTracker).onActionStart(any())
|
||||
verify(latencyTracker).onActionEnd(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onFolded_animationCancelled_doesNotLogLatency() {
|
||||
setAodEnabled(enabled = true)
|
||||
|
||||
fold()
|
||||
foldAodAnimationController.onScreenTurningOn({})
|
||||
foldAodAnimationController.onStartedWakingUp()
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
verify(latencyTracker).onActionStart(any())
|
||||
verify(latencyTracker).onActionCancel(any())
|
||||
}
|
||||
|
||||
private fun simulateScreenTurningOn() {
|
||||
foldAodAnimationController.onScreenTurningOn({})
|
||||
foldAodAnimationController.onScreenTurnedOn()
|
||||
testableLooper.processAllMessages()
|
||||
}
|
||||
|
||||
private fun fold() = sendFoldEvent(folded = true)
|
||||
|
||||
private fun setAodEnabled(enabled: Boolean) =
|
||||
foldAodAnimationController.onAlwaysOnChanged(alwaysOn = enabled)
|
||||
|
||||
private fun sendFoldEvent(folded: Boolean) {
|
||||
val state = if (folded) deviceStates.folded else deviceStates.unfolded
|
||||
foldStateListenerCaptor.value.onStateChanged(state)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user