From 64982b53145d229fa416cc76f6d3bb234eeea363 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Mon, 19 Dec 2022 16:59:26 -0800 Subject: Fix jank in CUJ_NOTIFICATION_HEADS_UP_APPEAR Use Choreographer to delay animation and mitigate jank in J Test: 1) setprop persist.debug.animator.trace_fraction true && device_config put interaction_jank_monitor enabled true. 2) Capture perfetto trace while posting test HUN. 3) Inspect the trace; verify that sysui's frames during the J CUJ are not in high latency state Bug: 262705829 Change-Id: I34df2978310a27859725ede28dd556c35d4bd785 --- .../row/ActivatableNotificationView.java | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index fbe88dff07f1..7addc8fe7a15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -24,6 +24,7 @@ import android.graphics.Canvas; import android.graphics.Point; import android.util.AttributeSet; import android.util.MathUtils; +import android.view.Choreographer; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -492,12 +493,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (animationListener != null) { mAppearAnimator.addListener(animationListener); } - if (delay > 0) { - // we need to apply the initial state already to avoid drawn frames in the wrong state - updateAppearAnimationAlpha(); - updateAppearRect(); - mAppearAnimator.setStartDelay(delay); - } + // we need to apply the initial state already to avoid drawn frames in the wrong state + updateAppearAnimationAlpha(); + updateAppearRect(); mAppearAnimator.addListener(new AnimatorListenerAdapter() { private boolean mWasCancelled; @@ -528,7 +526,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mWasCancelled = true; } }); - mAppearAnimator.start(); + + // Cache the original animator so we can check if the animation should be started in the + // Choreographer callback. It's possible that the original animator (mAppearAnimator) is + // replaced with a new value before the callback is called. + ValueAnimator cachedAnimator = mAppearAnimator; + // Even when delay=0, starting the animation on the next frame is necessary to avoid jank. + // Not doing so will increase the chances our Animator will be forced to skip a value of + // the animation's progression, causing stutter. + Choreographer.getInstance().postFrameCallbackDelayed( + frameTimeNanos -> { + if (mAppearAnimator == cachedAnimator) { + mAppearAnimator.start(); + } + }, delay); } private int getCujType(boolean isAppearing) { -- cgit v1.2.3-59-g8ed1b