Use InteractionJankMonitor to instrument CUJs of NotificationShade
Instrument the CUJs of NotificationShade, including:
- Expand / collapse / scroll NotificationShade.
- Expand / collapse QS panel.
- Expand / collapse NotificationShade in lockscreen.
Will have a follow up cl for:
- Expand and swipe in NSSL.
- Headsup arriving / dismissing.
- Maybe clear animation?
Bug: 169220725
Bug: 169221093
Bug: 169220802
Bug: 169220995
Test: Manually
Change-Id: Ib5e003e1698132715820b539bbcf35012fe83547
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 500de2d..9320499 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
@@ -73,6 +74,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
@@ -97,7 +99,6 @@
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -260,6 +261,7 @@
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
+ private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -3789,6 +3791,13 @@
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
float currentOverScrollTop = getCurrentOverScrollAmount(true);
if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ mFlingAfterUpEvent = true;
+ setFinishScrollingCallback(() -> {
+ mFlingAfterUpEvent = false;
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ setFinishScrollingCallback(null);
+ });
fling(-initialVelocity);
} else {
onOverScrollFling(false, initialVelocity);
@@ -3840,6 +3849,10 @@
return true;
}
+ boolean isFlingAfterUpEvent() {
+ return mFlingAfterUpEvent;
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 703c214..6820819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -19,6 +19,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
@@ -47,6 +48,7 @@
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -1556,6 +1558,14 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+
+ // When swiping directly on the NSSL, this would only get an onTouchEvent.
+ // We log any touches other than down, which will be captured by onTouchEvent.
+ // In the intercept we only start tracing when it's not a down (otherwise that down
+ // would be duplicated when intercepted).
+ if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
return swipeWantsIt || scrollWantsIt || expandWantsIt;
}
@@ -1611,7 +1621,32 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+ traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
}
+
+ private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
+ // Handle interaction jank monitor cases.
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cd9cc07..3f636ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,8 @@
import static android.view.View.GONE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
@@ -62,6 +64,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
@@ -1139,6 +1142,7 @@
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mNotificationStackScrollLayoutController.cancelLongPress();
}
break;
@@ -1170,6 +1174,7 @@
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
onQsExpansionStarted();
notifyExpandingFinished();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1202,6 +1207,19 @@
&& x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
}
+ private void traceQsJank(boolean startTracing, boolean wasCancelled) {
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (startTracing) {
+ monitor.begin(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ if (wasCancelled) {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ }
+ }
+ }
+
private void initDownStates(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOnlyAffordanceInThisMotion = false;
@@ -1315,9 +1333,9 @@
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
-
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mConflictingQsExpansionGesture = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1405,6 +1423,7 @@
return;
}
mExpectingSynthesizedDown = true;
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
onTrackingStarted();
updatePanelExpanded();
}
@@ -1474,6 +1493,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mInitialTouchY = y;
mInitialTouchX = x;
onQsExpansionStarted();
@@ -1513,6 +1533,9 @@
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ } else {
+ traceQsJank(false /* startTracing */,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
}
if (mQsVelocityTracker != null) {
mQsVelocityTracker.recycle();
@@ -1893,7 +1916,7 @@
* @see #flingSettings(float, int, Runnable, boolean)
*/
public void flingSettings(float vel, int type) {
- flingSettings(vel, type, null, false /* isClick */);
+ flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
}
/**
@@ -1923,6 +1946,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
return;
}
@@ -1947,12 +1971,18 @@
setQsExpansion((Float) animation.getAnimatedValue());
});
animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
@Override
public void onAnimationStart(Animator animation) {
notifyExpandingStarted();
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
mAnimatingQS = false;
notifyExpandingFinished();
@@ -1961,6 +1991,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
}
});
// Let's note that we're animating QS. Moving the animator here will cancel it immediately,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index bc80a1a..a4fc3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -49,6 +49,7 @@
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
@@ -145,6 +146,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
+ InteractionJankMonitor.getInstance().init(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 6fa99ba..5a01f47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -40,6 +41,7 @@
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
@@ -109,6 +111,7 @@
private boolean mMotionAborted;
private boolean mUpwardsWhenThresholdReached;
private boolean mAnimatingOnDown;
+ private boolean mHandlingPointerUp;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -356,6 +359,9 @@
protected void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
+ if (!mHandlingPointerUp) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
@@ -571,6 +577,7 @@
target = getMaxPanelHeight() - getClearAllHeightWithPadding();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
return;
}
@@ -622,7 +629,12 @@
}
setAnimator(null);
if (!mCancelled) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
+ } else {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
notifyBarPanelExpansionChanged();
}
@@ -1272,7 +1284,9 @@
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
+ mHandlingPointerUp = true;
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+ mHandlingPointerUp = false;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -1330,6 +1344,12 @@
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ } else {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
break;
}
return !mGestureWaitForTouchSlop || mTracking;