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