• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.notification.stack;
18 
19 import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
20 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
21 
22 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
23 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
24 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
25 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
26 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
27 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
28 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
29 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
30 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
31 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
32 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
33 
34 import android.content.res.Configuration;
35 import android.content.res.Resources;
36 import android.graphics.Point;
37 import android.graphics.PointF;
38 import android.provider.Settings;
39 import android.service.notification.NotificationListenerService;
40 import android.service.notification.StatusBarNotification;
41 import android.util.Log;
42 import android.util.MathUtils;
43 import android.util.Pair;
44 import android.view.Display;
45 import android.view.LayoutInflater;
46 import android.view.MotionEvent;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.view.WindowInsets;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.colorextraction.ColorExtractor;
53 import com.android.internal.jank.InteractionJankMonitor;
54 import com.android.internal.logging.MetricsLogger;
55 import com.android.internal.logging.UiEvent;
56 import com.android.internal.logging.UiEventLogger;
57 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
58 import com.android.internal.statusbar.IStatusBarService;
59 import com.android.internal.statusbar.NotificationVisibility;
60 import com.android.systemui.ExpandHelper;
61 import com.android.systemui.Gefingerpoken;
62 import com.android.systemui.R;
63 import com.android.systemui.SwipeHelper;
64 import com.android.systemui.animation.Interpolators;
65 import com.android.systemui.classifier.Classifier;
66 import com.android.systemui.classifier.FalsingCollector;
67 import com.android.systemui.colorextraction.SysuiColorExtractor;
68 import com.android.systemui.dagger.qualifiers.Main;
69 import com.android.systemui.media.KeyguardMediaController;
70 import com.android.systemui.plugins.FalsingManager;
71 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
72 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
73 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
74 import com.android.systemui.plugins.statusbar.StatusBarStateController;
75 import com.android.systemui.statusbar.FeatureFlags;
76 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
77 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
78 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
79 import com.android.systemui.statusbar.NotificationRemoteInputManager;
80 import com.android.systemui.statusbar.NotificationShelfController;
81 import com.android.systemui.statusbar.RemoteInputController;
82 import com.android.systemui.statusbar.StatusBarState;
83 import com.android.systemui.statusbar.SysuiStatusBarStateController;
84 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
85 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
86 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
87 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
88 import com.android.systemui.statusbar.notification.NotificationEntryListener;
89 import com.android.systemui.statusbar.notification.NotificationEntryManager;
90 import com.android.systemui.statusbar.notification.collection.NotifCollection;
91 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
92 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
93 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
94 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
95 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
96 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
97 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
98 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
99 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
100 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
101 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
102 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
103 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
104 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
105 import com.android.systemui.statusbar.notification.row.ExpandableView;
106 import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
107 import com.android.systemui.statusbar.notification.row.NotificationGuts;
108 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
109 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
110 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
111 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
112 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
113 import com.android.systemui.statusbar.phone.KeyguardBypassController;
114 import com.android.systemui.statusbar.phone.ScrimController;
115 import com.android.systemui.statusbar.phone.ShadeController;
116 import com.android.systemui.statusbar.phone.StatusBar;
117 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
118 import com.android.systemui.statusbar.policy.ConfigurationController;
119 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
120 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
121 import com.android.systemui.statusbar.policy.ZenModeController;
122 import com.android.systemui.tuner.TunerService;
123 
124 import java.util.ArrayList;
125 import java.util.List;
126 import java.util.function.BiConsumer;
127 import java.util.function.Consumer;
128 
129 import javax.inject.Inject;
130 import javax.inject.Named;
131 
132 import kotlin.Unit;
133 
134 /**
135  * Controller for {@link NotificationStackScrollLayout}.
136  */
137 @StatusBarComponent.StatusBarScope
138 public class NotificationStackScrollLayoutController {
139     private static final String TAG = "StackScrollerController";
140     private static final boolean DEBUG = false;
141 
142     private final boolean mAllowLongPress;
143     private final NotificationGutsManager mNotificationGutsManager;
144     private final HeadsUpManagerPhone mHeadsUpManager;
145     private final NotificationRoundnessManager mNotificationRoundnessManager;
146     private final TunerService mTunerService;
147     private final DynamicPrivacyController mDynamicPrivacyController;
148     private final ConfigurationController mConfigurationController;
149     private final ZenModeController mZenModeController;
150     private final MetricsLogger mMetricsLogger;
151     private final FalsingCollector mFalsingCollector;
152     private final FalsingManager mFalsingManager;
153     private final Resources mResources;
154     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
155     private final ScrimController mScrimController;
156     private final FeatureFlags mFeatureFlags;
157     private final NotifPipeline mNotifPipeline;
158     private final NotifCollection mNotifCollection;
159     private final NotificationEntryManager mNotificationEntryManager;
160     private final IStatusBarService mIStatusBarService;
161     private final UiEventLogger mUiEventLogger;
162     private final ForegroundServiceDismissalFeatureController mFgFeatureController;
163     private final ForegroundServiceSectionController mFgServicesSectionController;
164     private final LayoutInflater mLayoutInflater;
165     private final NotificationRemoteInputManager mRemoteInputManager;
166     private final VisualStabilityManager mVisualStabilityManager;
167     private final ShadeController mShadeController;
168     private final KeyguardMediaController mKeyguardMediaController;
169     private final SysuiStatusBarStateController mStatusBarStateController;
170     private final KeyguardBypassController mKeyguardBypassController;
171     private final NotificationLockscreenUserManager mLockscreenUserManager;
172     // TODO: StatusBar should be encapsulated behind a Controller
173     private final StatusBar mStatusBar;
174     private final SectionHeaderController mSilentHeaderController;
175     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
176 
177     private NotificationStackScrollLayout mView;
178     private boolean mFadeNotificationsOnDismiss;
179     private NotificationSwipeHelper mSwipeHelper;
180     private boolean mShowEmptyShadeView;
181     private int mBarState;
182     private HeadsUpAppearanceController mHeadsUpAppearanceController;
183 
184     private final NotificationListContainerImpl mNotificationListContainer =
185             new NotificationListContainerImpl();
186 
187     private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
188 
189     /**
190      * The total distance in pixels that the full shade transition takes to transition entirely to
191      * the full shade.
192      */
193     private int mTotalDistanceForFullShadeTransition;
194 
195     /**
196      * The amount of movement the notifications do when transitioning to the full shade before
197      * reaching the overstrech
198      */
199     private int mNotificationDragDownMovement;
200 
201     @VisibleForTesting
202     final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
203             new View.OnAttachStateChangeListener() {
204                 @Override
205                 public void onViewAttachedToWindow(View v) {
206                     mConfigurationController.addCallback(mConfigurationListener);
207                     mZenModeController.addCallback(mZenModeControllerCallback);
208                     mBarState = mStatusBarStateController.getState();
209                     mStatusBarStateController.addCallback(
210                             mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
211                 }
212 
213                 @Override
214                 public void onViewDetachedFromWindow(View v) {
215                     mConfigurationController.removeCallback(mConfigurationListener);
216                     mZenModeController.removeCallback(mZenModeControllerCallback);
217                     mStatusBarStateController.removeCallback(mStateListener);
218                 }
219             };
220 
221     private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
222         if (mView.isExpanded()) {
223             // The bottom might change because we're using the final actual height of the view
224             mView.setAnimateBottomOnLayout(true);
225         }
226         // Let's update the footer once the notifications have been updated (in the next frame)
227         mView.post(() -> {
228             updateFooter();
229             updateSectionBoundaries("dynamic privacy changed");
230         });
231     };
232 
233     @VisibleForTesting
234     final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
235         @Override
236         public void onDensityOrFontScaleChanged() {
237             updateShowEmptyShadeView();
238             mView.reinflateViews();
239         }
240 
241         @Override
242         public void onOverlayChanged() {
243             updateShowEmptyShadeView();
244             mView.updateCornerRadius();
245             mView.updateBgColor();
246             mView.updateDecorViews();
247             mView.reinflateViews();
248         }
249 
250         @Override
251         public void onUiModeChanged() {
252             mView.updateBgColor();
253             mView.updateDecorViews();
254         }
255 
256         @Override
257         public void onThemeChanged() {
258             updateFooter();
259         }
260 
261         @Override
262         public void onConfigChanged(Configuration newConfig) {
263             updateResources();
264         }
265     };
266 
updateResources()267     private void updateResources() {
268         mNotificationDragDownMovement = mResources.getDimensionPixelSize(
269                 R.dimen.lockscreen_shade_notification_movement);
270         mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
271                 R.dimen.lockscreen_shade_qs_transition_distance);
272     }
273 
274     private final StatusBarStateController.StateListener mStateListener =
275             new StatusBarStateController.StateListener() {
276                 @Override
277                 public void onStatePreChange(int oldState, int newState) {
278                     if (oldState == StatusBarState.SHADE_LOCKED
279                             && newState == KEYGUARD) {
280                         mView.requestAnimateEverything();
281                     }
282                 }
283 
284                 @Override
285                 public void onStateChanged(int newState) {
286                     mBarState = newState;
287                     mView.setStatusBarState(mBarState);
288                 }
289 
290                 @Override
291                 public void onStatePostChange() {
292                     mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
293                             mLockscreenUserManager.isAnyProfilePublicMode());
294                     mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
295                     mNotificationEntryManager.updateNotifications("StatusBar state changed");
296                 }
297             };
298 
299     private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
300         @Override
301         public void onUserChanged(int userId) {
302             mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
303         }
304     };
305 
306     /**
307      * Set the overexpansion of the panel to be applied to the view.
308      */
setOverExpansion(float overExpansion)309     public void setOverExpansion(float overExpansion) {
310         mView.setOverExpansion(overExpansion);
311     }
312 
313     private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
314         @Override
315         public void onMenuClicked(
316                 View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) {
317             if (!mAllowLongPress) {
318                 return;
319             }
320             if (view instanceof ExpandableNotificationRow) {
321                 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
322                 mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
323                         .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
324                         .setType(MetricsEvent.TYPE_ACTION)
325                 );
326             }
327             mNotificationGutsManager.openGuts(view, x, y, item);
328         }
329 
330         @Override
331         public void onMenuReset(View row) {
332             View translatingParentView = mSwipeHelper.getTranslatingParentView();
333             if (translatingParentView != null && row == translatingParentView) {
334                 mSwipeHelper.clearExposedMenuView();
335                 mSwipeHelper.clearTranslatingParentView();
336                 if (row instanceof ExpandableNotificationRow) {
337                     mHeadsUpManager.setMenuShown(
338                             ((ExpandableNotificationRow) row).getEntry(), false);
339 
340                 }
341             }
342         }
343 
344         @Override
345         public void onMenuShown(View row) {
346             if (row instanceof ExpandableNotificationRow) {
347                 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
348                 mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
349                         .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
350                         .setType(MetricsEvent.TYPE_ACTION));
351                 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
352                 mSwipeHelper.onMenuShown(row);
353                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
354                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
355                         false /* resetMenu */);
356 
357                 // Check to see if we want to go directly to the notification guts
358                 NotificationMenuRowPlugin provider = notificationRow.getProvider();
359                 if (provider.shouldShowGutsOnSnapOpen()) {
360                     NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap();
361                     if (item != null) {
362                         Point origin = provider.getRevealAnimationOrigin();
363                         mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
364                     } else  {
365                         Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
366                                 + "menu item in menuItemtoExposeOnSnap. Skipping.");
367                     }
368 
369                     // Close the menu row since we went directly to the guts
370                     mSwipeHelper.resetExposedMenuView(false, true);
371                 }
372             }
373         }
374     };
375 
376     private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
377             new NotificationSwipeHelper.NotificationCallback() {
378 
379                 @Override
380                 public void onDismiss() {
381                     mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
382                             false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
383                             false /* resetMenu */);
384                 }
385 
386                 @Override
387                 public float getTotalTranslationLength(View animView) {
388                     return mView.getTotalTranslationLength(animView);
389                 }
390 
391                 @Override
392                 public void onSnooze(StatusBarNotification sbn,
393                         NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
394                     mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
395                 }
396 
397                 @Override
398                 public boolean shouldDismissQuickly() {
399                     return mView.isExpanded() && mView.isFullyAwake();
400                 }
401 
402                 @Override
403                 public void onDragCancelled(View v) {
404                     mFalsingCollector.onNotificationStopDismissing();
405                 }
406 
407                 /**
408                  * Handles cleanup after the given {@code view} has been fully swiped out (including
409                  * re-invoking dismiss logic in case the notification has not made its way out yet).
410                  */
411                 @Override
412                 public void onChildDismissed(View view) {
413                     if (!(view instanceof ActivatableNotificationView)) {
414                         return;
415                     }
416                     ActivatableNotificationView row = (ActivatableNotificationView) view;
417                     if (!row.isDismissed()) {
418                         handleChildViewDismissed(view);
419                     }
420                     ViewGroup transientContainer = row.getTransientContainer();
421                     if (transientContainer != null) {
422                         transientContainer.removeTransientView(view);
423                     }
424                 }
425 
426                 /**
427                  * Starts up notification dismiss and tells the notification, if any, to remove
428                  * itself from the layout.
429                  *
430                  * @param view view (e.g. notification) to dismiss from the layout
431                  */
432 
433                 public void handleChildViewDismissed(View view) {
434                     if (mView.getDismissAllInProgress()) {
435                         return;
436                     }
437                     mView.onSwipeEnd();
438                     if (view instanceof ExpandableNotificationRow) {
439                         ExpandableNotificationRow row = (ExpandableNotificationRow) view;
440                         if (row.isHeadsUp()) {
441                             mHeadsUpManager.addSwipedOutNotification(
442                                     row.getEntry().getSbn().getKey());
443                         }
444                         row.performDismiss(false /* fromAccessibility */);
445                     }
446 
447                     mView.addSwipedOutView(view);
448                     mFalsingCollector.onNotificationDismissed();
449                     if (mFalsingCollector.shouldEnforceBouncer()) {
450                         mStatusBar.executeRunnableDismissingKeyguard(
451                                 null,
452                                 null /* cancelAction */,
453                                 false /* dismissShade */,
454                                 true /* afterKeyguardGone */,
455                                 false /* deferred */);
456                     }
457                 }
458 
459                 @Override
460                 public boolean isAntiFalsingNeeded() {
461                     return mView.onKeyguard();
462                 }
463 
464                 @Override
465                 public View getChildAtPosition(MotionEvent ev) {
466                     View child = mView.getChildAtPosition(
467                             ev.getX(),
468                             ev.getY(),
469                             true /* requireMinHeight */,
470                             false /* ignoreDecors */);
471                     if (child instanceof ExpandableNotificationRow) {
472                         ExpandableNotificationRow row = (ExpandableNotificationRow) child;
473                         ExpandableNotificationRow parent = row.getNotificationParent();
474                         if (parent != null && parent.areChildrenExpanded()
475                                 && (parent.areGutsExposed()
476                                 || mSwipeHelper.getExposedMenuView() == parent
477                                 || (parent.getAttachedChildren().size() == 1
478                                 && parent.getEntry().isClearable()))) {
479                             // In this case the group is expanded and showing the menu for the
480                             // group, further interaction should apply to the group, not any
481                             // child notifications so we use the parent of the child. We also do the
482                             // same if we only have a single child.
483                             child = parent;
484                         }
485                     }
486                     return child;
487                 }
488 
489                 @Override
490                 public void onBeginDrag(View v) {
491                     mFalsingCollector.onNotificationStartDismissing();
492                     mView.onSwipeBegin(v);
493                 }
494 
495                 @Override
496                 public void onChildSnappedBack(View animView, float targetLeft) {
497                     mView.onSwipeEnd();
498                     if (animView instanceof ExpandableNotificationRow) {
499                         ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
500                         if (row.isPinned() && !canChildBeDismissed(row)
501                                 && row.getEntry().getSbn().getNotification().fullScreenIntent
502                                 == null) {
503                             mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
504                                     true /* removeImmediately */);
505                         }
506                     }
507                 }
508 
509                 @Override
510                 public boolean updateSwipeProgress(View animView, boolean dismissable,
511                         float swipeProgress) {
512                     // Returning true prevents alpha fading.
513                     return !mFadeNotificationsOnDismiss;
514                 }
515 
516                 @Override
517                 public float getFalsingThresholdFactor() {
518                     return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
519                 }
520 
521                 @Override
522                 public int getConstrainSwipeStartPosition() {
523                     NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
524                     if (menuRow != null) {
525                         return Math.abs(menuRow.getMenuSnapTarget());
526                     }
527                     return 0;
528                 }
529 
530                 @Override
531                 public boolean canChildBeDismissed(View v) {
532                     return NotificationStackScrollLayout.canChildBeDismissed(v);
533                 }
534 
535                 @Override
536                 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
537                     //TODO: b/131242807 for why this doesn't do anything with direction
538                     return canChildBeDismissed(v);
539                 }
540             };
541 
542     private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
543             new OnHeadsUpChangedListener() {
544                 @Override
545                 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
546                     mView.setInHeadsUpPinnedMode(inPinnedMode);
547                 }
548 
549                 @Override
550                 public void onHeadsUpPinned(NotificationEntry entry) {
551                     mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
552                 }
553 
554                 @Override
555                 public void onHeadsUpUnPinned(NotificationEntry entry) {
556                     ExpandableNotificationRow row = entry.getRow();
557                     // update the roundedness posted, because we might be animating away the
558                     // headsup soon, so no need to set the roundedness to 0 and then back to 1.
559                     row.post(() -> mNotificationRoundnessManager.updateView(row,
560                             true /* animate */));
561                 }
562 
563                 @Override
564                 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
565                     long numEntries = mHeadsUpManager.getAllEntries().count();
566                     NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
567                     mView.setNumHeadsUp(numEntries);
568                     mView.setTopHeadsUpEntry(topEntry);
569                     generateHeadsUpAnimation(entry, isHeadsUp);
570                     ExpandableNotificationRow row = entry.getRow();
571                     mNotificationRoundnessManager.updateView(row, true /* animate */);
572                 }
573             };
574 
575     private final ZenModeController.Callback mZenModeControllerCallback =
576             new ZenModeController.Callback() {
577                 @Override
578                 public void onZenChanged(int zen) {
579                     updateShowEmptyShadeView();
580                 }
581             };
582 
583     @Inject
NotificationStackScrollLayoutController( @amedALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationGutsManager notificationGutsManager, HeadsUpManagerPhone headsUpManager, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DynamicPrivacyController dynamicPrivacyController, ConfigurationController configurationController, SysuiStatusBarStateController statusBarStateController, KeyguardMediaController keyguardMediaController, KeyguardBypassController keyguardBypassController, ZenModeController zenModeController, SysuiColorExtractor colorExtractor, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, FalsingCollector falsingCollector, FalsingManager falsingManager, @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, StatusBar statusBar, ScrimController scrimController, NotificationGroupManagerLegacy legacyGroupManager, GroupExpansionManager groupManager, @SilentHeader SectionHeaderController silentHeaderController, FeatureFlags featureFlags, NotifPipeline notifPipeline, NotifCollection notifCollection, NotificationEntryManager notificationEntryManager, LockscreenShadeTransitionController lockscreenShadeTransitionController, IStatusBarService iStatusBarService, UiEventLogger uiEventLogger, ForegroundServiceDismissalFeatureController fgFeatureController, ForegroundServiceSectionController fgServicesSectionController, LayoutInflater layoutInflater, NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, ShadeController shadeController)584     public NotificationStackScrollLayoutController(
585             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
586             NotificationGutsManager notificationGutsManager,
587             HeadsUpManagerPhone headsUpManager,
588             NotificationRoundnessManager notificationRoundnessManager,
589             TunerService tunerService,
590             DynamicPrivacyController dynamicPrivacyController,
591             ConfigurationController configurationController,
592             SysuiStatusBarStateController statusBarStateController,
593             KeyguardMediaController keyguardMediaController,
594             KeyguardBypassController keyguardBypassController,
595             ZenModeController zenModeController,
596             SysuiColorExtractor colorExtractor,
597             NotificationLockscreenUserManager lockscreenUserManager,
598             MetricsLogger metricsLogger,
599             FalsingCollector falsingCollector,
600             FalsingManager falsingManager,
601             @Main Resources resources,
602             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
603             StatusBar statusBar,
604             ScrimController scrimController,
605             NotificationGroupManagerLegacy legacyGroupManager,
606             GroupExpansionManager groupManager,
607             @SilentHeader SectionHeaderController silentHeaderController,
608             FeatureFlags featureFlags,
609             NotifPipeline notifPipeline,
610             NotifCollection notifCollection,
611             NotificationEntryManager notificationEntryManager,
612             LockscreenShadeTransitionController lockscreenShadeTransitionController,
613             IStatusBarService iStatusBarService,
614             UiEventLogger uiEventLogger,
615             ForegroundServiceDismissalFeatureController fgFeatureController,
616             ForegroundServiceSectionController fgServicesSectionController,
617             LayoutInflater layoutInflater,
618             NotificationRemoteInputManager remoteInputManager,
619             VisualStabilityManager visualStabilityManager,
620             ShadeController shadeController) {
621         mAllowLongPress = allowLongPress;
622         mNotificationGutsManager = notificationGutsManager;
623         mHeadsUpManager = headsUpManager;
624         mNotificationRoundnessManager = notificationRoundnessManager;
625         mTunerService = tunerService;
626         mDynamicPrivacyController = dynamicPrivacyController;
627         mConfigurationController = configurationController;
628         mStatusBarStateController = statusBarStateController;
629         mKeyguardMediaController = keyguardMediaController;
630         mKeyguardBypassController = keyguardBypassController;
631         mZenModeController = zenModeController;
632         mLockscreenUserManager = lockscreenUserManager;
633         mMetricsLogger = metricsLogger;
634         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
635         mFalsingCollector = falsingCollector;
636         mFalsingManager = falsingManager;
637         mResources = resources;
638         mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
639         mStatusBar = statusBar;
640         mScrimController = scrimController;
641         groupManager.registerGroupExpansionChangeListener(
642                 (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
643         legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
644             @Override
645             public void onGroupCreatedFromChildren(NotificationGroup group) {
646                 mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
647             }
648 
649             @Override
650             public void onGroupsChanged() {
651                 mStatusBar.requestNotificationUpdate("onGroupsChanged");
652             }
653         });
654         mSilentHeaderController = silentHeaderController;
655         mFeatureFlags = featureFlags;
656         mNotifPipeline = notifPipeline;
657         mNotifCollection = notifCollection;
658         mNotificationEntryManager = notificationEntryManager;
659         mIStatusBarService = iStatusBarService;
660         mUiEventLogger = uiEventLogger;
661         mFgFeatureController = fgFeatureController;
662         mFgServicesSectionController = fgServicesSectionController;
663         mLayoutInflater = layoutInflater;
664         mRemoteInputManager = remoteInputManager;
665         mVisualStabilityManager = visualStabilityManager;
666         mShadeController = shadeController;
667         updateResources();
668     }
669 
attach(NotificationStackScrollLayout view)670     public void attach(NotificationStackScrollLayout view) {
671         mView = view;
672         mView.setController(this);
673         mView.setTouchHandler(new TouchHandler());
674         mView.setStatusBar(mStatusBar);
675         mView.setDismissAllAnimationListener(this::onAnimationEnd);
676         mView.setDismissListener((selection) -> mUiEventLogger.log(
677                 NotificationPanelEvent.fromSelection(selection)));
678         mView.setFooterDismissListener(() ->
679                 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
680         mView.setRemoteInputManager(mRemoteInputManager);
681         mView.setShadeController(mShadeController);
682 
683         if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
684             mView.initializeForegroundServiceSection(
685                     (ForegroundServiceDungeonView) mFgServicesSectionController.createView(
686                             mLayoutInflater));
687         }
688 
689         mSwipeHelper = mNotificationSwipeHelperBuilder
690                 .setSwipeDirection(SwipeHelper.X)
691                 .setNotificationCallback(mNotificationCallback)
692                 .setOnMenuEventListener(mMenuEventListener)
693                 .build();
694 
695         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
696             mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
697                 @Override
698                 public void onEntryUpdated(NotificationEntry entry) {
699                     mView.onEntryUpdated(entry);
700                 }
701             });
702         } else {
703             mNotificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
704                 @Override
705                 public void onPreEntryUpdated(NotificationEntry entry) {
706                     mView.onEntryUpdated(entry);
707                 }
708             });
709         }
710 
711         mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
712                 mSwipeHelper);
713 
714         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
715         mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
716         mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
717 
718         mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
719 
720         mLockscreenShadeTransitionController.setStackScroller(this);
721 
722         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
723 
724         mFadeNotificationsOnDismiss =  // TODO: this should probably be injected directly
725                 mResources.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
726 
727         mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
728         mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
729 
730         mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
731 
732         mTunerService.addTunable(
733                 (key, newValue) -> {
734                     switch (key) {
735                         case Settings.Secure.NOTIFICATION_DISMISS_RTL:
736                             mView.updateDismissRtlSetting("1".equals(newValue));
737                             break;
738                         case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
739                             updateFooter();
740                             break;
741                         case HIGH_PRIORITY:
742                             mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
743                             break;
744                     }
745                 },
746                 HIGH_PRIORITY,
747                 Settings.Secure.NOTIFICATION_DISMISS_RTL,
748                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
749 
750         mKeyguardMediaController.setVisibilityChangedListener(visible -> {
751             if (visible) {
752                 mView.generateAddAnimation(
753                         mKeyguardMediaController.getSinglePaneContainer(),
754                         false /*fromMoreCard */);
755             } else {
756                 mView.generateRemoveAnimation(mKeyguardMediaController.getSinglePaneContainer());
757             }
758             mView.requestChildrenUpdate();
759             return Unit.INSTANCE;
760         });
761 
762         if (mView.isAttachedToWindow()) {
763             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
764         }
765         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
766         mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
767     }
768 
isInVisibleLocation(NotificationEntry entry)769     private boolean isInVisibleLocation(NotificationEntry entry) {
770         ExpandableNotificationRow row = entry.getRow();
771         ExpandableViewState childViewState = row.getViewState();
772 
773         if (childViewState == null) {
774             return false;
775         }
776         if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
777             return false;
778         }
779         if (row.getVisibility() != View.VISIBLE) {
780             return false;
781         }
782         return true;
783     }
784 
isViewAffectedBySwipe(ExpandableView expandableView)785     public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
786         return mNotificationRoundnessManager.isViewAffectedBySwipe(expandableView);
787     }
788 
addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener)789     public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
790         mView.addOnExpandedHeightChangedListener(listener);
791     }
792 
removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener)793     public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
794         mView.removeOnExpandedHeightChangedListener(listener);
795     }
796 
addOnLayoutChangeListener(View.OnLayoutChangeListener listener)797     public void addOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
798         mView.addOnLayoutChangeListener(listener);
799     }
800 
removeOnLayoutChangeListener(View.OnLayoutChangeListener listener)801     public void removeOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
802         mView.removeOnLayoutChangeListener(listener);
803     }
804 
setHeadsUpAppearanceController(HeadsUpAppearanceController controller)805     public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) {
806         mHeadsUpAppearanceController = controller;
807         mView.setHeadsUpAppearanceController(controller);
808     }
809 
requestLayout()810     public void requestLayout() {
811         mView.requestLayout();
812     }
813 
getDisplay()814     public Display getDisplay() {
815         return mView.getDisplay();
816     }
817 
getRootWindowInsets()818     public WindowInsets getRootWindowInsets() {
819         return mView.getRootWindowInsets();
820     }
821 
getRight()822     public int getRight() {
823         return mView.getRight();
824     }
825 
isLayoutRtl()826     public boolean isLayoutRtl() {
827         return mView.isLayoutRtl();
828     }
829 
830     /**
831      * @return the left of the view.
832      */
getLeft()833     public int getLeft() {
834         return mView.getLeft();
835     }
836 
837     /**
838      * @return the top of the view.
839      */
getTop()840     public int getTop() {
841         return mView.getTop();
842     }
843 
getTranslationX()844     public float getTranslationX() {
845         return mView.getTranslationX();
846     }
847 
indexOfChild(View view)848     public int indexOfChild(View view) {
849         return mView.indexOfChild(view);
850     }
851 
setOnHeightChangedListener( ExpandableView.OnHeightChangedListener listener)852     public void setOnHeightChangedListener(
853             ExpandableView.OnHeightChangedListener listener) {
854         mView.setOnHeightChangedListener(listener);
855     }
856 
setOverscrollTopChangedListener( OnOverscrollTopChangedListener listener)857     public void setOverscrollTopChangedListener(
858             OnOverscrollTopChangedListener listener) {
859         mView.setOverscrollTopChangedListener(listener);
860     }
861 
setOnEmptySpaceClickListener( OnEmptySpaceClickListener listener)862     public void setOnEmptySpaceClickListener(
863             OnEmptySpaceClickListener listener) {
864         mView.setOnEmptySpaceClickListener(listener);
865     }
866 
setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow)867     public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
868         mView.setTrackingHeadsUp(expandableNotificationRow);
869         mNotificationRoundnessManager.setTrackingHeadsUp(expandableNotificationRow);
870     }
871 
wakeUpFromPulse()872     public void wakeUpFromPulse() {
873         mView.wakeUpFromPulse();
874     }
875 
isPulseExpanding()876     public boolean isPulseExpanding() {
877         return mView.isPulseExpanding();
878     }
879 
setOnPulseHeightChangedListener(Runnable listener)880     public void setOnPulseHeightChangedListener(Runnable listener) {
881         mView.setOnPulseHeightChangedListener(listener);
882     }
883 
setDozeAmount(float amount)884     public void setDozeAmount(float amount) {
885         mView.setDozeAmount(amount);
886     }
887 
getSpeedBumpIndex()888     public int getSpeedBumpIndex() {
889         return mView.getSpeedBumpIndex();
890     }
891 
setHideAmount(float linearAmount, float amount)892     public void setHideAmount(float linearAmount, float amount) {
893         mView.setHideAmount(linearAmount, amount);
894     }
895 
notifyHideAnimationStart(boolean hide)896     public void notifyHideAnimationStart(boolean hide) {
897         mView.notifyHideAnimationStart(hide);
898     }
899 
setPulseHeight(float height)900     public float setPulseHeight(float height) {
901         return mView.setPulseHeight(height);
902     }
903 
getLocationOnScreen(int[] outLocation)904     public void getLocationOnScreen(int[] outLocation) {
905         mView.getLocationOnScreen(outLocation);
906     }
907 
getChildAtRawPosition(float x, float y)908     public ExpandableView getChildAtRawPosition(float x, float y) {
909         return mView.getChildAtRawPosition(x, y);
910     }
911 
getLayoutParams()912     public ViewGroup.LayoutParams getLayoutParams() {
913         return mView.getLayoutParams();
914     }
915 
916     /**
917      * Updates layout parameters on the root view
918      */
setLayoutParams(ViewGroup.LayoutParams lp)919     public void setLayoutParams(ViewGroup.LayoutParams lp) {
920         mView.setLayoutParams(lp);
921     }
922 
setIsFullWidth(boolean isFullWidth)923     public void setIsFullWidth(boolean isFullWidth) {
924         mView.setIsFullWidth(isFullWidth);
925     }
926 
isAddOrRemoveAnimationPending()927     public boolean isAddOrRemoveAnimationPending() {
928         return mView.isAddOrRemoveAnimationPending();
929     }
930 
getVisibleNotificationCount()931     public int getVisibleNotificationCount() {
932         return mView.getVisibleNotificationCount();
933     }
934 
getIntrinsicContentHeight()935     public int getIntrinsicContentHeight() {
936         return mView.getIntrinsicContentHeight();
937     }
938 
setIntrinsicPadding(int intrinsicPadding)939     public void setIntrinsicPadding(int intrinsicPadding) {
940         mView.setIntrinsicPadding(intrinsicPadding);
941     }
942 
getHeight()943     public int getHeight() {
944         return mView.getHeight();
945     }
946 
getChildCount()947     public int getChildCount() {
948         return mView.getChildCount();
949     }
950 
getChildAt(int i)951     public ExpandableView getChildAt(int i) {
952         return (ExpandableView) mView.getChildAt(i);
953     }
954 
goToFullShade(long delay)955     public void goToFullShade(long delay) {
956         mView.goToFullShade(delay);
957     }
958 
setOverScrollAmount(float amount, boolean onTop, boolean animate, boolean cancelAnimators)959     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
960             boolean cancelAnimators) {
961         mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
962     }
963 
setOverScrollAmount(float amount, boolean onTop, boolean animate)964     public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
965         mView.setOverScrollAmount(amount, onTop, animate);
966     }
967 
resetScrollPosition()968     public void resetScrollPosition() {
969         mView.resetScrollPosition();
970     }
971 
setShouldShowShelfOnly(boolean shouldShowShelfOnly)972     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
973         mView.setShouldShowShelfOnly(shouldShowShelfOnly);
974     }
975 
cancelLongPress()976     public void cancelLongPress() {
977         mView.cancelLongPress();
978     }
979 
getX()980     public float getX() {
981         return mView.getX();
982     }
983 
isBelowLastNotification(float x, float y)984     public boolean isBelowLastNotification(float x, float y) {
985         return mView.isBelowLastNotification(x, y);
986     }
987 
getWidth()988     public float getWidth() {
989         return mView.getWidth();
990     }
991 
getOpeningHeight()992     public float getOpeningHeight() {
993         return mView.getOpeningHeight();
994     }
995 
getBottomMostNotificationBottom()996     public float getBottomMostNotificationBottom() {
997         return mView.getBottomMostNotificationBottom();
998     }
999 
checkSnoozeLeavebehind()1000     public void checkSnoozeLeavebehind() {
1001         if (mView.getCheckSnoozeLeaveBehind()) {
1002             mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
1003                     false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
1004                     false /* resetMenu */);
1005             mView.setCheckForLeaveBehind(false);
1006         }
1007     }
1008 
setQsExpanded(boolean expanded)1009     public void setQsExpanded(boolean expanded) {
1010         mView.setQsExpanded(expanded);
1011         updateShowEmptyShadeView();
1012     }
1013 
setScrollingEnabled(boolean enabled)1014     public void setScrollingEnabled(boolean enabled) {
1015         mView.setScrollingEnabled(enabled);
1016     }
1017 
setQsExpansionFraction(float expansionFraction)1018     public void setQsExpansionFraction(float expansionFraction) {
1019         mView.setQsExpansionFraction(expansionFraction);
1020     }
1021 
setOnStackYChanged(Consumer<Boolean> onStackYChanged)1022     public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
1023         mView.setOnStackYChanged(onStackYChanged);
1024     }
1025 
calculateAppearFractionBypass()1026     public float calculateAppearFractionBypass() {
1027         return mView.calculateAppearFractionBypass();
1028     }
1029 
updateTopPadding(float qsHeight, boolean animate)1030     public void updateTopPadding(float qsHeight, boolean animate) {
1031         mView.updateTopPadding(qsHeight, animate);
1032     }
1033 
isScrolledToBottom()1034     public boolean isScrolledToBottom() {
1035         return mView.isScrolledToBottom();
1036     }
1037 
getNotGoneChildCount()1038     public int getNotGoneChildCount() {
1039         return mView.getNotGoneChildCount();
1040     }
1041 
getIntrinsicPadding()1042     public float getIntrinsicPadding() {
1043         return mView.getIntrinsicPadding();
1044     }
1045 
getLayoutMinHeight()1046     public float getLayoutMinHeight() {
1047         return mView.getLayoutMinHeight();
1048     }
1049 
getEmptyBottomMargin()1050     public int getEmptyBottomMargin() {
1051         return mView.getEmptyBottomMargin();
1052     }
1053 
getTopPaddingOverflow()1054     public float getTopPaddingOverflow() {
1055         return mView.getTopPaddingOverflow();
1056     }
1057 
getTopPadding()1058     public int getTopPadding() {
1059         return mView.getTopPadding();
1060     }
1061 
getEmptyShadeViewHeight()1062     public float getEmptyShadeViewHeight() {
1063         return mView.getEmptyShadeViewHeight();
1064     }
1065 
setAlpha(float alpha)1066     public void setAlpha(float alpha) {
1067         mView.setAlpha(alpha);
1068     }
1069 
calculateAppearFraction(float height)1070     public float calculateAppearFraction(float height) {
1071         return mView.calculateAppearFraction(height);
1072     }
1073 
onExpansionStarted()1074     public void onExpansionStarted() {
1075         mView.onExpansionStarted();
1076         checkSnoozeLeavebehind();
1077     }
1078 
onExpansionStopped()1079     public void onExpansionStopped() {
1080         mView.setCheckForLeaveBehind(false);
1081         mView.onExpansionStopped();
1082     }
1083 
onPanelTrackingStarted()1084     public void onPanelTrackingStarted() {
1085         mView.onPanelTrackingStarted();
1086     }
1087 
onPanelTrackingStopped()1088     public void onPanelTrackingStopped() {
1089         mView.onPanelTrackingStopped();
1090     }
1091 
setHeadsUpBoundaries(int height, int bottomBarHeight)1092     public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
1093         mView.setHeadsUpBoundaries(height, bottomBarHeight);
1094     }
1095 
setUnlockHintRunning(boolean running)1096     public void setUnlockHintRunning(boolean running) {
1097         mView.setUnlockHintRunning(running);
1098     }
1099 
isFooterViewNotGone()1100     public boolean isFooterViewNotGone() {
1101         return mView.isFooterViewNotGone();
1102     }
1103 
isFooterViewContentVisible()1104     public boolean isFooterViewContentVisible() {
1105         return mView.isFooterViewContentVisible();
1106     }
1107 
getFooterViewHeightWithPadding()1108     public int getFooterViewHeightWithPadding() {
1109         return mView.getFooterViewHeightWithPadding();
1110     }
1111 
1112     /**
1113      * Update whether we should show the empty shade view (no notifications in the shade).
1114      * If so, send the update to our view.
1115      */
updateShowEmptyShadeView()1116     public void updateShowEmptyShadeView() {
1117         mShowEmptyShadeView = mBarState != KEYGUARD
1118                 && !mView.isQsExpanded()
1119                 && mView.getVisibleNotificationCount() == 0;
1120         mView.updateEmptyShadeView(
1121                 mShowEmptyShadeView,
1122                 mZenModeController.areNotificationsHiddenInShade());
1123     }
1124 
isShowingEmptyShadeView()1125     public boolean isShowingEmptyShadeView() {
1126         return mShowEmptyShadeView;
1127     }
1128 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)1129     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1130         mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
1131     }
1132 
getHeadsUpCallback()1133     public HeadsUpTouchHelper.Callback getHeadsUpCallback() {
1134         return mView.getHeadsUpCallback();
1135     }
1136 
forceNoOverlappingRendering(boolean force)1137     public void forceNoOverlappingRendering(boolean force) {
1138         mView.forceNoOverlappingRendering(force);
1139     }
1140 
setTranslationX(float translation)1141     public void setTranslationX(float translation) {
1142         mView.setTranslationX(translation);
1143     }
1144 
setExpandingVelocity(float velocity)1145     public void setExpandingVelocity(float velocity) {
1146         mView.setExpandingVelocity(velocity);
1147     }
1148 
setExpandedHeight(float expandedHeight)1149     public void setExpandedHeight(float expandedHeight) {
1150         mView.setExpandedHeight(expandedHeight);
1151     }
1152 
setQsContainer(ViewGroup view)1153     public void setQsContainer(ViewGroup view) {
1154         mView.setQsContainer(view);
1155     }
1156 
setAnimationsEnabled(boolean enabled)1157     public void setAnimationsEnabled(boolean enabled) {
1158         mView.setAnimationsEnabled(enabled);
1159     }
1160 
setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)1161     public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
1162         mView.setDozing(dozing, animate, wakeUpTouchLocation);
1163     }
1164 
setPulsing(boolean pulsing, boolean animatePulse)1165     public void setPulsing(boolean pulsing, boolean animatePulse) {
1166         mView.setPulsing(pulsing, animatePulse);
1167     }
1168 
1169     /**
1170      * Return whether there are any clearable notifications
1171      */
hasActiveClearableNotifications(@electedRows int selection)1172     public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
1173         if (mDynamicPrivacyController.isInLockedDownShade()) {
1174             return false;
1175         }
1176         int childCount = getChildCount();
1177         for (int i = 0; i < childCount; i++) {
1178             View child = getChildAt(i);
1179             if (!(child instanceof ExpandableNotificationRow)) {
1180                 continue;
1181             }
1182             final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1183             if (row.canViewBeDismissed() &&
1184                     NotificationStackScrollLayout.matchesSelection(row, selection)) {
1185                 return true;
1186             }
1187         }
1188         return false;
1189     }
1190 
1191     /**
1192      * Set the maximum number of notifications that can currently be displayed
1193      */
setMaxDisplayedNotifications(int maxNotifications)1194     public void setMaxDisplayedNotifications(int maxNotifications) {
1195         mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
1196     }
1197 
createDelegate()1198     public RemoteInputController.Delegate createDelegate() {
1199         return new RemoteInputController.Delegate() {
1200             public void setRemoteInputActive(NotificationEntry entry,
1201                     boolean remoteInputActive) {
1202                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
1203                 entry.notifyHeightChanged(true /* needsAnimation */);
1204                 updateFooter();
1205             }
1206 
1207             public void lockScrollTo(NotificationEntry entry) {
1208                 mView.lockScrollTo(entry.getRow());
1209             }
1210 
1211             public void requestDisallowLongPressAndDismiss() {
1212                 mView.requestDisallowLongPress();
1213                 mView.requestDisallowDismiss();
1214             }
1215         };
1216     }
1217 
1218     public void updateSectionBoundaries(String reason) {
1219         mView.updateSectionBoundaries(reason);
1220     }
1221 
1222     public void updateFooter() {
1223         mView.updateFooter();
1224     }
1225 
1226     public void onUpdateRowStates() {
1227         mView.onUpdateRowStates();
1228     }
1229 
1230     public ActivatableNotificationView getActivatedChild() {
1231         return mView.getActivatedChild();
1232     }
1233 
1234     public void setActivatedChild(ActivatableNotificationView view) {
1235         mView.setActivatedChild(view);
1236     }
1237 
1238     public void runAfterAnimationFinished(Runnable r) {
1239         mView.runAfterAnimationFinished(r);
1240     }
1241 
1242     public void setShelfController(NotificationShelfController notificationShelfController) {
1243         mView.setShelfController(notificationShelfController);
1244     }
1245 
1246     public ExpandableView getFirstChildNotGone() {
1247         return mView.getFirstChildNotGone();
1248     }
1249 
1250     private void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
1251         mView.generateHeadsUpAnimation(entry, isHeadsUp);
1252     }
1253 
1254     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
1255         mView.generateHeadsUpAnimation(row, isHeadsUp);
1256     }
1257 
1258     public void setMaxTopPadding(int padding) {
1259         mView.setMaxTopPadding(padding);
1260     }
1261 
1262     public int getTransientViewCount() {
1263         return mView.getTransientViewCount();
1264     }
1265 
1266     public View getTransientView(int i) {
1267         return mView.getTransientView(i);
1268     }
1269 
1270     public int getPositionInLinearLayout(ExpandableView row) {
1271         return mView.getPositionInLinearLayout(row);
1272     }
1273 
1274     public NotificationStackScrollLayout getView() {
1275         return mView;
1276     }
1277 
1278     public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) {
1279         return mView.calculateGapHeight(previousView, child, count);
1280     }
1281 
1282     NotificationRoundnessManager getNoticationRoundessManager() {
1283         return mNotificationRoundnessManager;
1284     }
1285 
1286     public NotificationListContainer getNotificationListContainer() {
1287         return mNotificationListContainer;
1288     }
1289 
1290     public void resetCheckSnoozeLeavebehind() {
1291         mView.resetCheckSnoozeLeavebehind();
1292     }
1293 
1294     private DismissedByUserStats getDismissedByUserStats(
1295             NotificationEntry entry,
1296             int numVisibleEntries
1297     ) {
1298         return new DismissedByUserStats(
1299                 DISMISSAL_SHADE,
1300                 DISMISS_SENTIMENT_NEUTRAL,
1301                 NotificationVisibility.obtain(
1302                         entry.getKey(),
1303                         entry.getRanking().getRank(),
1304                         numVisibleEntries,
1305                         true,
1306                         NotificationLogger.getNotificationLocation(entry)));
1307     }
1308 
1309     /**
1310      * @return if the shade has currently any active notifications.
1311      */
1312     public boolean hasActiveNotifications() {
1313         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
1314             return !mNotifPipeline.getShadeList().isEmpty();
1315         } else {
1316             return mNotificationEntryManager.hasActiveNotifications();
1317         }
1318     }
1319 
1320     public void closeControlsIfOutsideTouch(MotionEvent ev) {
1321         NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
1322         NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
1323         View translatingParentView = mSwipeHelper.getTranslatingParentView();
1324         View view = null;
1325         if (guts != null && !guts.getGutsContent().isLeavebehind()) {
1326             // Only close visible guts if they're not a leavebehind.
1327             view = guts;
1328         } else if (menuRow != null && menuRow.isMenuVisible()
1329                 && translatingParentView != null) {
1330             // Checking menu
1331             view = translatingParentView;
1332         }
1333         if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
1334             // Touch was outside visible guts / menu notification, close what's visible
1335             mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
1336                     false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
1337                     false /* resetMenu */);
1338             mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
1339         }
1340     }
1341 
1342     public void clearSilentNotifications() {
1343         // Leave the shade open if there will be other notifs left over to clear
1344         final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
1345         mView.clearNotifications(ROWS_GENTLE, closeShade);
1346     }
1347 
1348     private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
1349             @SelectedRows int selectedRows) {
1350         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
1351             if (selectedRows == ROWS_ALL) {
1352                 mNotifCollection.dismissAllNotifications(
1353                         mLockscreenUserManager.getCurrentUserId());
1354             } else {
1355                 final List<Pair<NotificationEntry, DismissedByUserStats>>
1356                         entriesWithRowsDismissedFromShade = new ArrayList<>();
1357                 final int numVisibleEntries = mNotifPipeline.getShadeListCount();
1358                 for (ExpandableNotificationRow row : viewsToRemove) {
1359                     final NotificationEntry entry = row.getEntry();
1360                     entriesWithRowsDismissedFromShade.add(
1361                             new Pair<>(
1362                                     entry,
1363                                     getDismissedByUserStats(entry, numVisibleEntries)));
1364                 }
1365                 mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
1366             }
1367         } else {
1368             for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
1369                 if (canChildBeDismissed(rowToRemove)) {
1370                     mNotificationEntryManager.performRemoveNotification(
1371                             rowToRemove.getEntry().getSbn(),
1372                             getDismissedByUserStats(
1373                                     rowToRemove.getEntry(),
1374                                     mNotificationEntryManager.getActiveNotificationsCount()),
1375                             NotificationListenerService.REASON_CANCEL_ALL);
1376                 } else {
1377                     rowToRemove.resetTranslation();
1378                 }
1379             }
1380             if (selectedRows == ROWS_ALL) {
1381                 try {
1382                     // TODO(b/169585328): Do not clear media player notifications
1383                     mIStatusBarService.onClearAllNotifications(
1384                             mLockscreenUserManager.getCurrentUserId());
1385                 } catch (Exception ignored) {
1386                 }
1387             }
1388         }
1389     }
1390 
1391     /**
1392      * @return the expand helper callback.
1393      */
1394     public ExpandHelper.Callback getExpandHelperCallback() {
1395         return mView.getExpandHelperCallback();
1396     }
1397 
1398     /**
1399      * @return If the shade is in the locked down shade.
1400      */
1401     public boolean isInLockedDownShade() {
1402         return mDynamicPrivacyController.isInLockedDownShade();
1403     }
1404 
1405     /**
1406      * Set the dimmed state for all of the notification views.
1407      */
1408     public void setDimmed(boolean dimmed, boolean animate) {
1409         mView.setDimmed(dimmed, animate);
1410     }
1411 
1412     /**
1413      * @return the inset during the full shade transition, that needs to be added to the position
1414      *         of the quick settings edge. This is relevant for media, that is transitioning
1415      *         from the keyguard host to the quick settings one.
1416      */
1417     public int getFullShadeTransitionInset() {
1418         MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
1419         if (view == null || view.getHeight() == 0
1420                 || mStatusBarStateController.getState() != KEYGUARD) {
1421             return 0;
1422         }
1423         return view.getHeight() + mView.getPaddingAfterMedia();
1424     }
1425 
1426     /**
1427      * Set the amount of pixels we have currently dragged down if we're transitioning to the full
1428      * shade. 0.0f means we're not transitioning yet.
1429      */
1430     public void setTransitionToFullShadeAmount(float amount) {
1431         float extraTopInset = 0.0f;
1432         if (mStatusBarStateController.getState() == KEYGUARD) {
1433             float overallProgress = MathUtils.saturate(amount / mView.getHeight());
1434             float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress,
1435                     0.6f,
1436                     (float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight());
1437             extraTopInset = transitionProgress * mNotificationDragDownMovement;
1438         }
1439         mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
1440     }
1441 
1442     /**
1443      * Set a listener to when scrolling changes.
1444      */
1445     public void setOnScrollListener(Consumer<Integer> listener) {
1446         mView.setOnScrollListener(listener);
1447     }
1448 
1449     /**
1450      * Set rounded rect clipping bounds on this view.
1451      */
1452     public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
1453             int bottomRadius) {
1454         mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
1455     }
1456 
1457     /**
1458      * Request an animation whenever the toppadding changes next
1459      */
1460     public void animateNextTopPaddingChange() {
1461         mView.animateNextTopPaddingChange();
1462     }
1463 
1464     /**
1465      * Enum for UiEvent logged from this class
1466      */
1467     enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
1468         INVALID(0),
1469         @UiEvent(doc = "User dismissed all notifications from notification panel.")
1470         DISMISS_ALL_NOTIFICATIONS_PANEL(312),
1471         @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
1472         DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
1473         private final int mId;
1474         NotificationPanelEvent(int id) {
1475             mId = id;
1476         }
1477         @Override public int getId() {
1478             return mId;
1479         }
1480 
1481         public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) {
1482             if (selection == ROWS_ALL) {
1483                 return DISMISS_ALL_NOTIFICATIONS_PANEL;
1484             }
1485             if (selection == NotificationStackScrollLayout.ROWS_GENTLE) {
1486                 return DISMISS_SILENT_NOTIFICATIONS_PANEL;
1487             }
1488             if (NotificationStackScrollLayoutController.DEBUG) {
1489                 throw new IllegalArgumentException("Unexpected selection" + selection);
1490             }
1491             return INVALID;
1492         }
1493     }
1494 
1495     private class NotificationListContainerImpl implements NotificationListContainer {
1496 
1497         @Override
1498         public void setChildTransferInProgress(boolean childTransferInProgress) {
1499             mView.setChildTransferInProgress(childTransferInProgress);
1500         }
1501 
1502         @Override
1503         public void changeViewPosition(ExpandableView child, int newIndex) {
1504             mView.changeViewPosition(child, newIndex);
1505         }
1506 
1507         @Override
1508         public void notifyGroupChildAdded(ExpandableView row) {
1509             mView.notifyGroupChildAdded(row);
1510         }
1511 
1512         @Override
1513         public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
1514             mView.notifyGroupChildRemoved(row, childrenContainer);
1515         }
1516 
1517         @Override
1518         public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
1519             mView.generateAddAnimation(child, fromMoreCard);
1520         }
1521 
1522         @Override
1523         public void generateChildOrderChangedEvent() {
1524             mView.generateChildOrderChangedEvent();
1525         }
1526 
1527         @Override
1528         public int getContainerChildCount() {
1529             return mView.getContainerChildCount();
1530         }
1531 
1532         @Override
1533         public void setNotificationActivityStarter(
1534                 NotificationActivityStarter notificationActivityStarter) {
1535             mView.setNotificationActivityStarter(notificationActivityStarter);
1536         }
1537 
1538         @Override
1539         public int getTopClippingStartLocation() {
1540             return mView.getTopClippingStartLocation();
1541         }
1542 
1543         @Override
1544         public View getContainerChildAt(int i) {
1545             return mView.getContainerChildAt(i);
1546         }
1547 
1548         @Override
1549         public void removeContainerView(View v) {
1550             mView.removeContainerView(v);
1551         }
1552 
1553         @Override
1554         public void addContainerView(View v) {
1555             mView.addContainerView(v);
1556         }
1557 
1558         @Override
1559         public void addContainerViewAt(View v, int index) {
1560             mView.addContainerViewAt(v, index);
1561         }
1562 
1563         @Override
1564         public void setMaxDisplayedNotifications(int maxNotifications) {
1565             mView.setMaxDisplayedNotifications(maxNotifications);
1566         }
1567 
1568         @Override
1569         public ViewGroup getViewParentForNotification(NotificationEntry entry) {
1570             return mView.getViewParentForNotification(entry);
1571         }
1572 
1573         @Override
1574         public void resetExposedMenuView(boolean animate, boolean force) {
1575             mSwipeHelper.resetExposedMenuView(animate, force);
1576         }
1577 
1578         @Override
1579         public NotificationSwipeActionHelper getSwipeActionHelper() {
1580             return mSwipeHelper;
1581         }
1582 
1583         @Override
1584         public void cleanUpViewStateForEntry(NotificationEntry entry) {
1585             mView.cleanUpViewStateForEntry(entry);
1586         }
1587 
1588         @Override
1589         public void setChildLocationsChangedListener(
1590                 NotificationLogger.OnChildLocationsChangedListener listener) {
1591             mView.setChildLocationsChangedListener(listener);
1592         }
1593 
1594         public boolean hasPulsingNotifications() {
1595             return mView.hasPulsingNotifications();
1596         }
1597 
1598         @Override
1599         public boolean isInVisibleLocation(NotificationEntry entry) {
1600             return NotificationStackScrollLayoutController.this.isInVisibleLocation(entry);
1601         }
1602 
1603         @Override
1604         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
1605             mView.onChildHeightChanged(view, needsAnimation);
1606         }
1607 
1608         @Override
1609         public void onReset(ExpandableView view) {
1610             mView.onChildHeightReset(view);
1611         }
1612 
1613         @Override
1614         public void bindRow(ExpandableNotificationRow row) {
1615             row.setHeadsUpAnimatingAwayListener(animatingAway -> {
1616                 mNotificationRoundnessManager.updateView(row, false);
1617                 mHeadsUpAppearanceController.updateHeader(row.getEntry());
1618             });
1619         }
1620 
1621         @Override
1622         public void applyExpandAnimationParams(ExpandAnimationParameters params) {
1623             mView.applyExpandAnimationParams(params);
1624         }
1625 
1626         @Override
1627         public void setExpandingNotification(ExpandableNotificationRow row) {
1628             mView.setExpandingNotification(row);
1629         }
1630 
1631         @Override
1632         public boolean containsView(View v) {
1633             return mView.containsView(v);
1634         }
1635 
1636         @Override
1637         public void setWillExpand(boolean willExpand) {
1638             mView.setWillExpand(willExpand);
1639         }
1640     }
1641 
1642     class TouchHandler implements Gefingerpoken {
1643         @Override
1644         public boolean onInterceptTouchEvent(MotionEvent ev) {
1645             mView.initDownStates(ev);
1646             mView.handleEmptySpaceClick(ev);
1647 
1648             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
1649             boolean expandWantsIt = false;
1650             if (!mSwipeHelper.isSwiping()
1651                     && !mView.getOnlyScrollingInThisMotion() && guts == null) {
1652                 expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
1653             }
1654             boolean scrollWantsIt = false;
1655             if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
1656                 scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
1657             }
1658             boolean swipeWantsIt = false;
1659             if (!mView.isBeingDragged()
1660                     && !mView.isExpandingNotification()
1661                     && !mView.getExpandedInThisMotion()
1662                     && !mView.getOnlyScrollingInThisMotion()
1663                     && !mView.getDisallowDismissInThisMotion()) {
1664                 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
1665             }
1666             // Check if we need to clear any snooze leavebehinds
1667             boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
1668             if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
1669                     !expandWantsIt && !scrollWantsIt) {
1670                 mView.setCheckForLeaveBehind(false);
1671                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
1672                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
1673                         false /* resetMenu */);
1674             }
1675             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1676                 mView.setCheckForLeaveBehind(true);
1677             }
1678 
1679             // When swiping directly on the NSSL, this would only get an onTouchEvent.
1680             // We log any touches other than down, which will be captured by onTouchEvent.
1681             // In the intercept we only start tracing when it's not a down (otherwise that down
1682             // would be duplicated when intercepted).
1683             if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
1684                 InteractionJankMonitor.getInstance().begin(mView,
1685                         CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
1686             }
1687             return swipeWantsIt || scrollWantsIt || expandWantsIt;
1688         }
1689 
1690         @Override
1691         public boolean onTouchEvent(MotionEvent ev) {
1692             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
1693             boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1694                     || ev.getActionMasked() == MotionEvent.ACTION_UP;
1695             mView.handleEmptySpaceClick(ev);
1696             boolean expandWantsIt = false;
1697             boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
1698             boolean expandingNotification = mView.isExpandingNotification();
1699             if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
1700                     && guts == null) {
1701                 ExpandHelper expandHelper = mView.getExpandHelper();
1702                 if (isCancelOrUp) {
1703                     expandHelper.onlyObserveMovements(false);
1704                 }
1705                 boolean wasExpandingBefore = expandingNotification;
1706                 expandWantsIt = expandHelper.onTouchEvent(ev);
1707                 expandingNotification = mView.isExpandingNotification();
1708                 if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
1709                         && !mView.getDisallowScrollingInThisMotion()) {
1710                     mView.dispatchDownEventToScroller(ev);
1711                 }
1712             }
1713             boolean scrollerWantsIt = false;
1714             if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
1715                     && !mView.getDisallowScrollingInThisMotion()) {
1716                 scrollerWantsIt = mView.onScrollTouch(ev);
1717             }
1718             boolean horizontalSwipeWantsIt = false;
1719             if (!mView.isBeingDragged()
1720                     && !expandingNotification
1721                     && !mView.getExpandedInThisMotion()
1722                     && !onlyScrollingInThisMotion
1723                     && !mView.getDisallowDismissInThisMotion()) {
1724                 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1725             }
1726 
1727             // Check if we need to clear any snooze leavebehinds
1728             if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
1729                     && guts.getGutsContent() instanceof NotificationSnooze) {
1730                 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1731                 if ((ns.isExpanded() && isCancelOrUp)
1732                         || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1733                     // If the leavebehind is expanded we clear it on the next up event, otherwise we
1734                     // clear it on the next non-horizontal swipe or expand event.
1735                     checkSnoozeLeavebehind();
1736                 }
1737             }
1738             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1739                 // Ensure the falsing manager records the touch. we don't do anything with it
1740                 // at the moment.
1741                 mFalsingManager.isFalseTouch(Classifier.SHADE_DRAG);
1742                 mView.setCheckForLeaveBehind(true);
1743             }
1744             traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
1745             return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
1746         }
1747 
1748         private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
1749             // Handle interaction jank monitor cases.
1750             switch (action) {
1751                 case MotionEvent.ACTION_DOWN:
1752                     if (scrollerWantsIt) {
1753                         InteractionJankMonitor.getInstance()
1754                                 .begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
1755                     }
1756                     break;
1757                 case MotionEvent.ACTION_UP:
1758                     if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
1759                         InteractionJankMonitor.getInstance()
1760                                 .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
1761                     }
1762                     break;
1763                 case MotionEvent.ACTION_CANCEL:
1764                     if (scrollerWantsIt) {
1765                         InteractionJankMonitor.getInstance()
1766                                 .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
1767                     }
1768                     break;
1769             }
1770         }
1771     }
1772 }
1773