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.app.animation.Interpolators.STANDARD;
23 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
24 import static com.android.server.notification.Flags.screenshareNotificationHiding;
25 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
26 import static com.android.systemui.Flags.confineNotificationTouchToViewWidth;
27 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
28 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
29 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
30 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
31 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
32 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
33 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
34 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
35 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
36 
37 import android.animation.ObjectAnimator;
38 import android.content.res.Configuration;
39 import android.graphics.Point;
40 import android.os.Trace;
41 import android.os.UserHandle;
42 import android.provider.Settings;
43 import android.service.notification.StatusBarNotification;
44 import android.util.Log;
45 import android.util.Pair;
46 import android.util.Property;
47 import android.view.Display;
48 import android.view.MotionEvent;
49 import android.view.View;
50 import android.view.ViewGroup;
51 import android.view.WindowInsets;
52 
53 import androidx.annotation.NonNull;
54 import androidx.annotation.Nullable;
55 
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.jank.InteractionJankMonitor;
58 import com.android.internal.logging.MetricsLogger;
59 import com.android.internal.logging.UiEvent;
60 import com.android.internal.logging.UiEventLogger;
61 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
62 import com.android.internal.view.OneShotPreDrawListener;
63 import com.android.systemui.Dumpable;
64 import com.android.systemui.ExpandHelper;
65 import com.android.systemui.Gefingerpoken;
66 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
67 import com.android.systemui.classifier.Classifier;
68 import com.android.systemui.classifier.FalsingCollector;
69 import com.android.systemui.dagger.SysUISingleton;
70 import com.android.systemui.dump.DumpManager;
71 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
72 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
73 import com.android.systemui.keyguard.shared.model.KeyguardState;
74 import com.android.systemui.keyguard.shared.model.TransitionStep;
75 import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
76 import com.android.systemui.plugins.ActivityStarter;
77 import com.android.systemui.plugins.FalsingManager;
78 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
79 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
80 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
81 import com.android.systemui.plugins.statusbar.StatusBarStateController;
82 import com.android.systemui.power.domain.interactor.PowerInteractor;
83 import com.android.systemui.res.R;
84 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
85 import com.android.systemui.scene.ui.view.WindowRootView;
86 import com.android.systemui.shade.ShadeController;
87 import com.android.systemui.shade.ShadeViewController;
88 import com.android.systemui.statusbar.CommandQueue;
89 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
90 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
91 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
92 import com.android.systemui.statusbar.NotificationRemoteInputManager;
93 import com.android.systemui.statusbar.NotificationShelf;
94 import com.android.systemui.statusbar.RemoteInputController;
95 import com.android.systemui.statusbar.StatusBarState;
96 import com.android.systemui.statusbar.SysuiStatusBarStateController;
97 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
98 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
99 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
100 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
101 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
102 import com.android.systemui.statusbar.notification.collection.NotifCollection;
103 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
104 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
105 import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
106 import com.android.systemui.statusbar.notification.collection.PipelineDumper;
107 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
108 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
109 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
110 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
111 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
112 import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
113 import com.android.systemui.statusbar.notification.collection.render.NotifStats;
114 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
115 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
116 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
117 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
118 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
119 import com.android.systemui.statusbar.notification.init.NotificationsController;
120 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
121 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
122 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
123 import com.android.systemui.statusbar.notification.row.ExpandableView;
124 import com.android.systemui.statusbar.notification.row.NotificationGuts;
125 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
126 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
127 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
128 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
129 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
130 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
131 import com.android.systemui.statusbar.phone.KeyguardBypassController;
132 import com.android.systemui.statusbar.policy.ConfigurationController;
133 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
134 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
135 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
136 import com.android.systemui.statusbar.policy.HeadsUpManager;
137 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
138 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
139 import com.android.systemui.statusbar.policy.SplitShadeStateController;
140 import com.android.systemui.statusbar.policy.ZenModeController;
141 import com.android.systemui.tuner.TunerService;
142 import com.android.systemui.util.Compile;
143 import com.android.systemui.util.settings.SecureSettings;
144 
145 import java.io.PrintWriter;
146 import java.util.ArrayList;
147 import java.util.List;
148 import java.util.function.BiConsumer;
149 import java.util.function.Consumer;
150 
151 import javax.inject.Inject;
152 import javax.inject.Named;
153 import javax.inject.Provider;
154 
155 /**
156  * Controller for {@link NotificationStackScrollLayout}.
157  */
158 @SysUISingleton
159 public class NotificationStackScrollLayoutController implements Dumpable {
160     private static final String TAG = "StackScrollerController";
161     private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
162     private static final String HIGH_PRIORITY = "high_priority";
163     /** Delay in milli-seconds before shade closes for clear all. */
164     private static final int DELAY_BEFORE_SHADE_CLOSE = 200;
165 
166     private final boolean mAllowLongPress;
167     private final NotificationGutsManager mNotificationGutsManager;
168     private final NotificationsController mNotificationsController;
169     private final NotificationVisibilityProvider mVisibilityProvider;
170     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
171     private final HeadsUpManager mHeadsUpManager;
172     private final NotificationRoundnessManager mNotificationRoundnessManager;
173     private final TunerService mTunerService;
174     private final DeviceProvisionedController mDeviceProvisionedController;
175     private final DynamicPrivacyController mDynamicPrivacyController;
176     private final ConfigurationController mConfigurationController;
177     private final ZenModeController mZenModeController;
178     private final MetricsLogger mMetricsLogger;
179     private final ColorUpdateLogger mColorUpdateLogger;
180 
181     private final DumpManager mDumpManager;
182     private final FalsingCollector mFalsingCollector;
183     private final FalsingManager mFalsingManager;
184     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
185     private final NotifPipeline mNotifPipeline;
186     private final NotifCollection mNotifCollection;
187     private final UiEventLogger mUiEventLogger;
188     private final NotificationRemoteInputManager mRemoteInputManager;
189     private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
190     private final ShadeController mShadeController;
191     private final Provider<WindowRootView> mWindowRootView;
192     private final KeyguardMediaController mKeyguardMediaController;
193     private final SysuiStatusBarStateController mStatusBarStateController;
194     private final KeyguardBypassController mKeyguardBypassController;
195     private final PowerInteractor mPowerInteractor;
196     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
197     private final NotificationLockscreenUserManager mLockscreenUserManager;
198     private final SectionHeaderController mSilentHeaderController;
199     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
200     private final InteractionJankMonitor mJankMonitor;
201     private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
202     private final StackStateLogger mStackStateLogger;
203     private final NotificationStackScrollLogger mLogger;
204 
205     private final GroupExpansionManager mGroupExpansionManager;
206     private final SeenNotificationsInteractor mSeenNotificationsInteractor;
207     private final KeyguardTransitionRepository mKeyguardTransitionRepo;
208     private NotificationStackScrollLayout mView;
209     private TouchHandler mTouchHandler;
210     private NotificationSwipeHelper mSwipeHelper;
211     @Nullable
212     private Boolean mHistoryEnabled;
213     private int mBarState;
214     private HeadsUpAppearanceController mHeadsUpAppearanceController;
215     private boolean mIsInTransitionToAod = false;
216 
217     private final NotificationTargetsHelper mNotificationTargetsHelper;
218     private final SecureSettings mSecureSettings;
219     private final NotificationDismissibilityProvider mDismissibilityProvider;
220     private final ActivityStarter mActivityStarter;
221     private final SensitiveNotificationProtectionController
222             mSensitiveNotificationProtectionController;
223 
224     private View mLongPressedView;
225 
226     private final NotificationListContainerImpl mNotificationListContainer =
227             new NotificationListContainerImpl();
228     private final NotifStackController mNotifStackController =
229             new NotifStackControllerImpl();
230 
231     @Nullable
232     private NotificationActivityStarter mNotificationActivityStarter;
233 
234     @VisibleForTesting
235     final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
236             new View.OnAttachStateChangeListener() {
237                 @Override
238                 public void onViewAttachedToWindow(View v) {
239                     mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
240                     mConfigurationController.addCallback(mConfigurationListener);
241                     if (!FooterViewRefactor.isEnabled()) {
242                         mZenModeController.addCallback(mZenModeControllerCallback);
243                     }
244                     final int newBarState = mStatusBarStateController.getState();
245                     if (newBarState != mBarState) {
246                         mStateListener.onStateChanged(newBarState);
247                         mStateListener.onStatePostChange();
248                     }
249                     mStatusBarStateController.addCallback(
250                             mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
251                 }
252 
253                 @Override
254                 public void onViewDetachedFromWindow(View v) {
255                     mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
256                     mConfigurationController.removeCallback(mConfigurationListener);
257                     if (!FooterViewRefactor.isEnabled()) {
258                         mZenModeController.removeCallback(mZenModeControllerCallback);
259                     }
260                     mStatusBarStateController.removeCallback(mStateListener);
261                 }
262             };
263 
264     private static final Property<NotificationStackScrollLayoutController, Float>
265             HIDE_ALPHA_PROPERTY = new Property<>(Float.class, "HideNotificationsAlpha") {
266                 @Override
267                 public Float get(NotificationStackScrollLayoutController object) {
268                     return object.mMaxAlphaForUnhide;
269                 }
270 
271                 @Override
272                 public void set(NotificationStackScrollLayoutController object, Float value) {
273                     object.setMaxAlphaForUnhide(value);
274                 }
275             };
276 
277     @Nullable
278     private ObjectAnimator mHideAlphaAnimator = null;
279 
280     private final DeviceProvisionedListener mDeviceProvisionedListener =
281             new DeviceProvisionedListener() {
282                 @Override
283                 public void onDeviceProvisionedChanged() {
284                     updateCurrentUserIsSetup();
285                 }
286 
287                 @Override
288                 public void onUserSwitched() {
289                     updateCurrentUserIsSetup();
290                 }
291 
292                 @Override
293                 public void onUserSetupChanged() {
294                     updateCurrentUserIsSetup();
295                 }
296 
297                 private void updateCurrentUserIsSetup() {
298                     mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup());
299                 }
300             };
301 
302     private final Runnable mSensitiveStateChangedListener = new Runnable() {
303         @Override
304         public void run() {
305             // Animate false to protect against screen recording capturing content
306             // during the animation
307             updateSensitivenessWithAnimation(false);
308         }
309     };
310 
311     private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
312         if (!FooterViewRefactor.isEnabled()) {
313             // Let's update the footer once the notifications have been updated (in the next frame)
314             mView.post(this::updateFooter);
315         }
316     };
317 
318     @VisibleForTesting
319     final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
320         @Override
321         public void onDensityOrFontScaleChanged() {
322             if (!FooterViewRefactor.isEnabled()) {
323                 updateShowEmptyShadeView();
324             }
325             mView.reinflateViews();
326         }
327 
328         @Override
329         public void onUiModeChanged() {
330             mColorUpdateLogger.logTriggerEvent("NSSLC.onUiModeChanged()",
331                     "mode=" + mConfigurationController.getNightModeName());
332             mView.updateBgColor();
333             mView.updateDecorViews();
334         }
335 
336         @Override
337         public void onThemeChanged() {
338             mColorUpdateLogger.logTriggerEvent("NSSLC.onThemeChanged()",
339                     "mode=" + mConfigurationController.getNightModeName());
340             mView.updateCornerRadius();
341             mView.updateBgColor();
342             mView.updateDecorViews();
343             mView.reinflateViews();
344             if (!FooterViewRefactor.isEnabled()) {
345                 updateShowEmptyShadeView();
346                 updateFooter();
347             }
348         }
349 
350         @Override
351         public void onConfigChanged(Configuration newConfig) {
352             updateResources();
353         }
354     };
355 
356     private NotifStats mNotifStats = NotifStats.getEmpty();
357     private float mMaxAlphaForKeyguard = 1.0f;
358     private String mMaxAlphaForKeyguardSource = "constructor";
359     private float mMaxAlphaForUnhide = 1.0f;
360     private float mMaxAlphaFromView = 1.0f;
361 
362     /**
363      * Maximum alpha when to and from or sitting idle on the glanceable hub. Will be 1.0f when the
364      * hub is not visible or transitioning.
365      */
366     private float mMaxAlphaForGlanceableHub = 1.0f;
367 
368     private final NotificationListViewBinder mViewBinder;
369 
updateResources()370     private void updateResources() {
371         mNotificationStackSizeCalculator.updateResources();
372     }
373 
374     private final StatusBarStateController.StateListener mStateListener =
375             new StatusBarStateController.StateListener() {
376                 @Override
377                 public void onStatePreChange(int oldState, int newState) {
378                     if (oldState == StatusBarState.SHADE_LOCKED
379                             && newState == KEYGUARD) {
380                         mView.requestAnimateEverything();
381                     }
382                 }
383 
384                 @Override
385                 public void onStateChanged(int newState) {
386                     mBarState = newState;
387                     mView.setStatusBarState(mBarState);
388                     if (newState == KEYGUARD) {
389                         mGroupExpansionManager.collapseGroups();
390                     }
391                 }
392 
393                 @Override
394                 public void onUpcomingStateChanged(int newState) {
395                     if (!FooterViewRefactor.isEnabled()) {
396                         mView.setUpcomingStatusBarState(newState);
397                     }
398                 }
399 
400                 @Override
401                 public void onStatePostChange() {
402                     updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade());
403                     mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
404                     if (!FooterViewRefactor.isEnabled()) {
405                         updateImportantForAccessibility();
406                     }
407                 }
408             };
409 
410     private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
411         @Override
412         public void onUserChanged(int userId) {
413             updateSensitivenessWithAnimation(false);
414             mHistoryEnabled = null;
415             if (!FooterViewRefactor.isEnabled()) {
416                 updateFooter();
417             }
418         }
419     };
420 
421     /**
422      * Recalculate sensitiveness without animation; called when waking up while keyguard occluded.
423      */
updateSensitivenessForOccludedWakeup()424     public void updateSensitivenessForOccludedWakeup() {
425         updateSensitivenessWithAnimation(false);
426     }
427 
updateSensitivenessWithAnimation(boolean animate)428     private void updateSensitivenessWithAnimation(boolean animate) {
429         Trace.beginSection("NSSLC.updateSensitivenessWithAnimation");
430         if (screenshareNotificationHiding()) {
431             boolean isAnyProfilePublic = mLockscreenUserManager.isAnyProfilePublicMode();
432             boolean isSensitiveContentProtectionActive =
433                     mSensitiveNotificationProtectionController.isSensitiveStateActive();
434             boolean isSensitive = isAnyProfilePublic || isSensitiveContentProtectionActive;
435 
436             // Only animate if in a non-sensitive state (not screen sharing)
437             boolean shouldAnimate = animate && !isSensitiveContentProtectionActive;
438             mView.updateSensitiveness(shouldAnimate, isSensitive);
439         } else {
440             mView.updateSensitiveness(animate, mLockscreenUserManager.isAnyProfilePublicMode());
441         }
442         Trace.endSection();
443     }
444 
445     /**
446      * Set the overexpansion of the panel to be applied to the view.
447      */
setOverExpansion(float overExpansion)448     public void setOverExpansion(float overExpansion) {
449         mView.setOverExpansion(overExpansion);
450     }
451 
452     private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
453         @Override
454         public void onMenuClicked(
455                 View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) {
456             if (!mAllowLongPress) {
457                 return;
458             }
459             if (view instanceof ExpandableNotificationRow row) {
460                 mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
461                         .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
462                         .setType(MetricsEvent.TYPE_ACTION)
463                 );
464             }
465             mNotificationGutsManager.openGuts(view, x, y, item);
466         }
467 
468         @Override
469         public void onMenuReset(View row) {
470             View translatingParentView = mSwipeHelper.getTranslatingParentView();
471             if (translatingParentView != null && row == translatingParentView) {
472                 mSwipeHelper.clearExposedMenuView();
473                 mSwipeHelper.clearTranslatingParentView();
474             }
475         }
476 
477         @Override
478         public void onMenuShown(View row) {
479             if (row instanceof ExpandableNotificationRow notificationRow) {
480                 mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
481                         .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
482                         .setType(MetricsEvent.TYPE_ACTION));
483                 mSwipeHelper.onMenuShown(row);
484                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
485                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
486                         false /* resetMenu */);
487 
488                 // Check to see if we want to go directly to the notification guts
489                 NotificationMenuRowPlugin provider = notificationRow.getProvider();
490                 if (provider.shouldShowGutsOnSnapOpen()) {
491                     NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap();
492                     if (item != null) {
493                         Point origin = provider.getRevealAnimationOrigin();
494                         mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
495                     } else {
496                         Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
497                                 + "menu item in menuItemtoExposeOnSnap. Skipping.");
498                     }
499 
500                     // Close the menu row since we went directly to the guts
501                     mSwipeHelper.resetExposedMenuView(false, true);
502                 }
503             }
504         }
505     };
506 
507     @VisibleForTesting
508     final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
509             new NotificationSwipeHelper.NotificationCallback() {
510 
511                 @Override
512                 public void onDismiss() {
513                     mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
514                             false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
515                             false /* resetMenu */);
516                 }
517 
518                 @Override
519                 public float getTotalTranslationLength(View animView) {
520                     return mView.getTotalTranslationLength(animView);
521                 }
522 
523                 @Override
524                 public void onSnooze(StatusBarNotification sbn,
525                         NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
526                     mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
527                 }
528 
529                 @Override
530                 public boolean shouldDismissQuickly() {
531                     return mView.isExpanded() && mView.isFullyAwake();
532                 }
533 
534                 @Override
535                 public void onDragCancelled(View v) {
536                 }
537 
538                 /**
539                  * Handles cleanup after the given {@code view} has been fully swiped out (including
540                  * re-invoking dismiss logic in case the notification has not made its way out yet).
541                  */
542                 @Override
543                 public void onChildDismissed(View view) {
544                     if (!(view instanceof ActivatableNotificationView row)) {
545                         return;
546                     }
547                     if (!row.isDismissed()) {
548                         handleChildViewDismissed(view);
549                     }
550 
551                     row.removeFromTransientContainer();
552                     if (row instanceof ExpandableNotificationRow) {
553                         ((ExpandableNotificationRow) row).removeChildrenWithKeepInParent();
554                     }
555                 }
556 
557                 /**
558                  * Starts up notification dismiss and tells the notification, if any, to remove
559                  * itself from the layout.
560                  *
561                  * @param view view (e.g. notification) to dismiss from the layout
562                  */
563 
564                 public void handleChildViewDismissed(View view) {
565                     // The View needs to clean up the Swipe states, e.g. roundness.
566                     mView.onSwipeEnd();
567                     if (mView.getClearAllInProgress()) {
568                         return;
569                     }
570                     if (view instanceof ExpandableNotificationRow row) {
571                         if (row.isHeadsUp()) {
572                             mHeadsUpManager.addSwipedOutNotification(
573                                     row.getEntry().getSbn().getKey());
574                         }
575                         row.performDismiss(false /* fromAccessibility */);
576                     }
577 
578                     mView.addSwipedOutView(view);
579                     if (mFalsingCollector.shouldEnforceBouncer()) {
580                         mActivityStarter.executeRunnableDismissingKeyguard(
581                                 null,
582                                 null /* cancelAction */,
583                                 false /* dismissShade */,
584                                 true /* afterKeyguardGone */,
585                                 false /* deferred */);
586                     }
587                 }
588 
589                 @Override
590                 public boolean isAntiFalsingNeeded() {
591                     return mView.onKeyguard();
592                 }
593 
594                 @Override
595                 public View getChildAtPosition(MotionEvent ev) {
596                     View child = mView.getChildAtPosition(
597                             ev.getX(),
598                             ev.getY(),
599                             true /* requireMinHeight */,
600                             false /* ignoreDecors */,
601                             !confineNotificationTouchToViewWidth() /* ignoreWidth */);
602                     if (child instanceof ExpandableNotificationRow row) {
603                         ExpandableNotificationRow parent = row.getNotificationParent();
604                         if (parent != null && parent.areChildrenExpanded()
605                                 && (parent.areGutsExposed()
606                                 || mSwipeHelper.getExposedMenuView() == parent
607                                 || (parent.getAttachedChildren().size() == 1
608                                 && mDismissibilityProvider.isDismissable(parent.getEntry())))) {
609                             // In this case the group is expanded and showing the menu for the
610                             // group, further interaction should apply to the group, not any
611                             // child notifications so we use the parent of the child. We also do the
612                             // same if we only have a single child.
613                             child = parent;
614                         }
615                     }
616                     return child;
617                 }
618 
619                 @Override
620                 public void onLongPressSent(View v) {
621                     mLongPressedView = v;
622                 }
623 
624                 @Override
625                 public void onBeginDrag(View v) {
626                     mView.onSwipeBegin(v);
627                 }
628 
629                 @Override
630                 public void onChildSnappedBack(View animView, float targetLeft) {
631                     mView.onSwipeEnd();
632                     if (animView instanceof ExpandableNotificationRow row) {
633                         if (row.isPinned() && !canChildBeDismissed(row)
634                                 && row.getEntry().getSbn().getNotification().fullScreenIntent
635                                 == null) {
636                             mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
637                                     true /* removeImmediately */);
638                         }
639                     }
640                 }
641 
642                 @Override
643                 public boolean updateSwipeProgress(View animView, boolean dismissable,
644                         float swipeProgress) {
645                     // Returning true prevents alpha fading.
646                     return false;
647                 }
648 
649                 @Override
650                 public float getFalsingThresholdFactor() {
651                     return ShadeViewController.getFalsingThresholdFactor(
652                             mPowerInteractor.getDetailedWakefulness().getValue());
653                 }
654 
655                 @Override
656                 public int getConstrainSwipeStartPosition() {
657                     NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
658                     if (menuRow != null) {
659                         return Math.abs(menuRow.getMenuSnapTarget());
660                     }
661                     return 0;
662                 }
663 
664                 @Override
665                 public boolean canChildBeDismissed(View v) {
666                     return NotificationStackScrollLayout.canChildBeDismissed(v);
667                 }
668 
669                 @Override
670                 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
671                     //TODO: b/131242807 for why this doesn't do anything with direction
672                     return canChildBeDismissed(v);
673                 }
674             };
675 
676     private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
677             new OnHeadsUpChangedListener() {
678                 @Override
679                 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
680                     NotificationsHeadsUpRefactor.assertInLegacyMode();
681                     mView.setInHeadsUpPinnedMode(inPinnedMode);
682                 }
683 
684                 @Override
685                 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
686                     NotificationsHeadsUpRefactor.assertInLegacyMode();
687                     NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
688                     mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null);
689                     generateHeadsUpAnimation(entry, isHeadsUp);
690                 }
691             };
692 
693     private final ZenModeController.Callback mZenModeControllerCallback =
694             new ZenModeController.Callback() {
695                 @Override
696                 public void onZenChanged(int zen) {
697                     updateShowEmptyShadeView();
698                 }
699             };
700 
701     @Inject
NotificationStackScrollLayoutController( NotificationStackScrollLayout view, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationGutsManager notificationGutsManager, NotificationsController notificationsController, NotificationVisibilityProvider visibilityProvider, NotificationWakeUpCoordinator wakeUpCoordinator, HeadsUpManager headsUpManager, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DeviceProvisionedController deviceProvisionedController, DynamicPrivacyController dynamicPrivacyController, ConfigurationController configurationController, SysuiStatusBarStateController statusBarStateController, KeyguardMediaController keyguardMediaController, KeyguardBypassController keyguardBypassController, PowerInteractor powerInteractor, PrimaryBouncerInteractor primaryBouncerInteractor, KeyguardTransitionRepository keyguardTransitionRepo, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, ColorUpdateLogger colorUpdateLogger, DumpManager dumpManager, FalsingCollector falsingCollector, FalsingManager falsingManager, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, GroupExpansionManager groupManager, @SilentHeader SectionHeaderController silentHeaderController, NotifPipeline notifPipeline, NotifCollection notifCollection, LockscreenShadeTransitionController lockscreenShadeTransitionController, UiEventLogger uiEventLogger, NotificationRemoteInputManager remoteInputManager, VisibilityLocationProviderDelegator visibilityLocationProviderDelegator, SeenNotificationsInteractor seenNotificationsInteractor, NotificationListViewBinder viewBinder, ShadeController shadeController, Provider<WindowRootView> windowRootView, InteractionJankMonitor jankMonitor, StackStateLogger stackLogger, NotificationStackScrollLogger logger, NotificationStackSizeCalculator notificationStackSizeCalculator, NotificationTargetsHelper notificationTargetsHelper, SecureSettings secureSettings, NotificationDismissibilityProvider dismissibilityProvider, ActivityStarter activityStarter, SplitShadeStateController splitShadeStateController, SensitiveNotificationProtectionController sensitiveNotificationProtectionController)702     public NotificationStackScrollLayoutController(
703             NotificationStackScrollLayout view,
704             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
705             NotificationGutsManager notificationGutsManager,
706             NotificationsController notificationsController,
707             NotificationVisibilityProvider visibilityProvider,
708             NotificationWakeUpCoordinator wakeUpCoordinator,
709             HeadsUpManager headsUpManager,
710             NotificationRoundnessManager notificationRoundnessManager,
711             TunerService tunerService,
712             DeviceProvisionedController deviceProvisionedController,
713             DynamicPrivacyController dynamicPrivacyController,
714             ConfigurationController configurationController,
715             SysuiStatusBarStateController statusBarStateController,
716             KeyguardMediaController keyguardMediaController,
717             KeyguardBypassController keyguardBypassController,
718             PowerInteractor powerInteractor,
719             PrimaryBouncerInteractor primaryBouncerInteractor,
720             KeyguardTransitionRepository keyguardTransitionRepo,
721             ZenModeController zenModeController,
722             NotificationLockscreenUserManager lockscreenUserManager,
723             MetricsLogger metricsLogger,
724             ColorUpdateLogger colorUpdateLogger,
725             DumpManager dumpManager,
726             FalsingCollector falsingCollector,
727             FalsingManager falsingManager,
728             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
729             GroupExpansionManager groupManager,
730             @SilentHeader SectionHeaderController silentHeaderController,
731             NotifPipeline notifPipeline,
732             NotifCollection notifCollection,
733             LockscreenShadeTransitionController lockscreenShadeTransitionController,
734             UiEventLogger uiEventLogger,
735             NotificationRemoteInputManager remoteInputManager,
736             VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
737             SeenNotificationsInteractor seenNotificationsInteractor,
738             NotificationListViewBinder viewBinder,
739             ShadeController shadeController,
740             Provider<WindowRootView> windowRootView,
741             InteractionJankMonitor jankMonitor,
742             StackStateLogger stackLogger,
743             NotificationStackScrollLogger logger,
744             NotificationStackSizeCalculator notificationStackSizeCalculator,
745             NotificationTargetsHelper notificationTargetsHelper,
746             SecureSettings secureSettings,
747             NotificationDismissibilityProvider dismissibilityProvider,
748             ActivityStarter activityStarter,
749             SplitShadeStateController splitShadeStateController,
750             SensitiveNotificationProtectionController sensitiveNotificationProtectionController) {
751         mView = view;
752         mKeyguardTransitionRepo = keyguardTransitionRepo;
753         mViewBinder = viewBinder;
754         mStackStateLogger = stackLogger;
755         mLogger = logger;
756         mAllowLongPress = allowLongPress;
757         mNotificationGutsManager = notificationGutsManager;
758         mNotificationsController = notificationsController;
759         mVisibilityProvider = visibilityProvider;
760         mWakeUpCoordinator = wakeUpCoordinator;
761         mHeadsUpManager = headsUpManager;
762         mNotificationRoundnessManager = notificationRoundnessManager;
763         mTunerService = tunerService;
764         mDeviceProvisionedController = deviceProvisionedController;
765         mDynamicPrivacyController = dynamicPrivacyController;
766         mConfigurationController = configurationController;
767         mStatusBarStateController = statusBarStateController;
768         mKeyguardMediaController = keyguardMediaController;
769         mKeyguardBypassController = keyguardBypassController;
770         mPowerInteractor = powerInteractor;
771         mPrimaryBouncerInteractor = primaryBouncerInteractor;
772         mZenModeController = zenModeController;
773         mLockscreenUserManager = lockscreenUserManager;
774         mMetricsLogger = metricsLogger;
775         mColorUpdateLogger = colorUpdateLogger;
776         mDumpManager = dumpManager;
777         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
778         mFalsingCollector = falsingCollector;
779         mFalsingManager = falsingManager;
780         mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
781         mJankMonitor = jankMonitor;
782         mNotificationStackSizeCalculator = notificationStackSizeCalculator;
783         mGroupExpansionManager = groupManager;
784         mSilentHeaderController = silentHeaderController;
785         mNotifPipeline = notifPipeline;
786         mNotifCollection = notifCollection;
787         mUiEventLogger = uiEventLogger;
788         mRemoteInputManager = remoteInputManager;
789         mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
790         mSeenNotificationsInteractor = seenNotificationsInteractor;
791         mShadeController = shadeController;
792         mWindowRootView = windowRootView;
793         mNotificationTargetsHelper = notificationTargetsHelper;
794         mSecureSettings = secureSettings;
795         mDismissibilityProvider = dismissibilityProvider;
796         mActivityStarter = activityStarter;
797         mSensitiveNotificationProtectionController = sensitiveNotificationProtectionController;
798         mView.passSplitShadeStateController(splitShadeStateController);
799         if (SceneContainerFlag.isEnabled()) {
800             mWakeUpCoordinator.setStackScroller(this);
801         }
802         mDumpManager.registerDumpable(this);
803         updateResources();
804         setUpView();
805     }
806 
setUpView()807     private void setUpView() {
808         mView.setStackStateLogger(mStackStateLogger);
809         mView.setController(this);
810         mView.setLogger(mLogger);
811         mTouchHandler = new TouchHandler();
812         mView.setTouchHandler(mTouchHandler);
813         mView.setResetUserExpandedStatesRunnable(mNotificationsController::resetUserExpandedStates);
814         mView.setActivityStarter(mActivityStarter);
815         mView.setClearAllAnimationListener(this::onAnimationEnd);
816         mView.setClearAllListener((selection) -> mUiEventLogger.log(
817                 NotificationPanelEvent.fromSelection(selection)));
818         if (!FooterViewRefactor.isEnabled()) {
819             mView.setFooterClearAllListener(() ->
820                     mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
821             mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
822             mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
823                 @Override
824                 public void onRemoteInputActive(boolean active) {
825                     mView.setIsRemoteInputActive(active);
826                 }
827             });
828         }
829         mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> {
830             final Runnable doCollapseRunnable = () ->
831                     mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
832             mView.postDelayed(doCollapseRunnable, /* delayMillis = */ DELAY_BEFORE_SHADE_CLOSE);
833         });
834         mDumpManager.registerDumpable(mView);
835 
836         mKeyguardBypassController.registerOnBypassStateChangedListener(
837                 isEnabled -> mNotificationRoundnessManager.setShouldRoundPulsingViews(!isEnabled));
838         mNotificationRoundnessManager.setShouldRoundPulsingViews(
839                 !mKeyguardBypassController.getBypassEnabled());
840 
841         mSwipeHelper = mNotificationSwipeHelperBuilder
842                 .setNotificationCallback(mNotificationCallback)
843                 .setOnMenuEventListener(mMenuEventListener)
844                 .build();
845 
846         mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
847             @Override
848             public void onEntryUpdated(NotificationEntry entry) {
849                 mView.onEntryUpdated(entry);
850             }
851         });
852 
853         mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator);
854         mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
855         mKeyguardBypassController
856                 .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
857         if (!FooterViewRefactor.isEnabled()) {
858             mView.setManageButtonClickListener(v -> {
859                 if (mNotificationActivityStarter != null) {
860                     mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
861                 }
862             });
863         }
864 
865         if (!NotificationsHeadsUpRefactor.isEnabled()) {
866             mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
867         }
868         mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
869         mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
870 
871         mLockscreenShadeTransitionController.setStackScroller(this);
872 
873         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
874 
875         mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation);
876 
877         mTunerService.addTunable(
878                 (key, newValue) -> {
879                     switch (key) {
880                         case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
881                             mHistoryEnabled = null;  // invalidate
882                             if (!FooterViewRefactor.isEnabled()) {
883                                 updateFooter();
884                             }
885                             break;
886                         case HIGH_PRIORITY:
887                             mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
888                             break;
889                     }
890                 },
891                 HIGH_PRIORITY,
892                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
893 
894         mKeyguardMediaController.setVisibilityChangedListener(visible -> {
895             if (visible) {
896                 mView.generateAddAnimation(
897                         mKeyguardMediaController.getSinglePaneContainer(),
898                         false /*fromMoreCard */);
899             } else {
900                 mView.generateRemoveAnimation(mKeyguardMediaController.getSinglePaneContainer());
901             }
902             mView.requestChildrenUpdate();
903             return kotlin.Unit.INSTANCE;
904         });
905 
906         if (!FooterViewRefactor.isEnabled()) {
907             // attach callback, and then call it to update mView immediately
908             mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
909             mDeviceProvisionedListener.onDeviceProvisionedChanged();
910         }
911 
912         if (screenshareNotificationHiding()) {
913             mSensitiveNotificationProtectionController
914                     .registerSensitiveStateListener(mSensitiveStateChangedListener);
915         }
916 
917         if (mView.isAttachedToWindow()) {
918             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
919         }
920         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
921         if (!FooterViewRefactor.isEnabled()) {
922             mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications());
923         }
924 
925         mGroupExpansionManager.registerGroupExpansionChangeListener(
926                 (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
927 
928         mViewBinder.bindWhileAttached(mView, this);
929 
930         if (!FooterViewRefactor.isEnabled()) {
931             collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
932                     this::onKeyguardTransitionChanged);
933         }
934     }
935 
isInVisibleLocation(NotificationEntry entry)936     private boolean isInVisibleLocation(NotificationEntry entry) {
937         ExpandableNotificationRow row = entry.getRow();
938         if (row == null) {
939             return false;
940         }
941 
942         ExpandableViewState childViewState = row.getViewState();
943         if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
944             return false;
945         }
946 
947         return row.getVisibility() == View.VISIBLE;
948     }
949 
addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener)950     public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
951         mView.addOnExpandedHeightChangedListener(listener);
952     }
953 
removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener)954     public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
955         mView.removeOnExpandedHeightChangedListener(listener);
956     }
957 
addOnLayoutChangeListener(View.OnLayoutChangeListener listener)958     public void addOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
959         mView.addOnLayoutChangeListener(listener);
960     }
961 
removeOnLayoutChangeListener(View.OnLayoutChangeListener listener)962     public void removeOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
963         mView.removeOnLayoutChangeListener(listener);
964     }
965 
setHeadsUpAppearanceController(HeadsUpAppearanceController controller)966     public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) {
967         mHeadsUpAppearanceController = controller;
968         mView.setHeadsUpAppearanceController(controller);
969     }
970 
getAppearFraction()971     public float getAppearFraction() {
972         return mView.getAppearFraction();
973     }
974 
getExpandedHeight()975     public float getExpandedHeight() {
976         return mView.getExpandedHeight();
977     }
978 
requestLayout()979     public void requestLayout() {
980         mView.requestLayout();
981     }
982 
addOneShotPreDrawListener(Runnable runnable)983     public void addOneShotPreDrawListener(Runnable runnable) {
984         OneShotPreDrawListener.add(mView, runnable);
985     }
986 
getDisplay()987     public Display getDisplay() {
988         return mView.getDisplay();
989     }
990 
getRootWindowInsets()991     public WindowInsets getRootWindowInsets() {
992         return mView.getRootWindowInsets();
993     }
994 
getRight()995     public int getRight() {
996         return mView.getRight();
997     }
998 
isLayoutRtl()999     public boolean isLayoutRtl() {
1000         return mView.isLayoutRtl();
1001     }
1002 
1003     /**
1004      * @return the left of the view.
1005      */
getLeft()1006     public int getLeft() {
1007         return mView.getLeft();
1008     }
1009 
1010     /**
1011      * @return the top of the view.
1012      */
getTop()1013     public int getTop() {
1014         return mView.getTop();
1015     }
1016 
1017     /**
1018      * @return the bottom of the view.
1019      */
getBottom()1020     public int getBottom() {
1021         return mView.getBottom();
1022     }
1023 
getTranslationX()1024     public float getTranslationX() {
1025         return mView.getTranslationX();
1026     }
1027 
1028     /** Set view y-translation */
setTranslationY(float translationY)1029     public void setTranslationY(float translationY) {
1030         mView.setTranslationY(translationY);
1031     }
1032 
1033     /** Set view x-translation */
setTranslationX(float translationX)1034     public void setTranslationX(float translationX) {
1035         mView.setTranslationX(translationX);
1036     }
1037 
indexOfChild(View view)1038     public int indexOfChild(View view) {
1039         return mView.indexOfChild(view);
1040     }
1041 
setOnHeightChangedListener( ExpandableView.OnHeightChangedListener listener)1042     public void setOnHeightChangedListener(
1043             ExpandableView.OnHeightChangedListener listener) {
1044         mView.setOnHeightChangedListener(listener);
1045     }
1046 
1047     /**
1048      * Invoked in addition to {@see #setOnHeightChangedListener}
1049      */
setOnHeightChangedRunnable(Runnable r)1050     public void setOnHeightChangedRunnable(Runnable r) {
1051         mView.setOnHeightChangedRunnable(r);
1052     }
1053 
setOverscrollTopChangedListener( OnOverscrollTopChangedListener listener)1054     public void setOverscrollTopChangedListener(
1055             OnOverscrollTopChangedListener listener) {
1056         mView.setOverscrollTopChangedListener(listener);
1057     }
1058 
setOnEmptySpaceClickListener( OnEmptySpaceClickListener listener)1059     public void setOnEmptySpaceClickListener(
1060             OnEmptySpaceClickListener listener) {
1061         mView.setOnEmptySpaceClickListener(listener);
1062     }
1063 
setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow)1064     public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
1065         mView.setTrackingHeadsUp(expandableNotificationRow);
1066     }
1067 
wakeUpFromPulse()1068     public void wakeUpFromPulse() {
1069         mView.wakeUpFromPulse();
1070     }
1071 
isPulseExpanding()1072     public boolean isPulseExpanding() {
1073         return mView.isPulseExpanding();
1074     }
1075 
setOnPulseHeightChangedListener(Runnable listener)1076     public void setOnPulseHeightChangedListener(Runnable listener) {
1077         mView.setOnPulseHeightChangedListener(listener);
1078     }
1079 
setDozeAmount(float amount)1080     public void setDozeAmount(float amount) {
1081         mView.setDozeAmount(amount);
1082     }
1083 
getSpeedBumpIndex()1084     public int getSpeedBumpIndex() {
1085         return mView.getSpeedBumpIndex();
1086     }
1087 
setHideAmount(float linearAmount, float amount)1088     public void setHideAmount(float linearAmount, float amount) {
1089         mView.setHideAmount(linearAmount, amount);
1090     }
1091 
notifyHideAnimationStart(boolean hide)1092     public void notifyHideAnimationStart(boolean hide) {
1093         mView.notifyHideAnimationStart(hide);
1094     }
1095 
setPulseHeight(float height)1096     public float setPulseHeight(float height) {
1097         return mView.setPulseHeight(height);
1098     }
1099 
getLocationOnScreen(int[] outLocation)1100     public void getLocationOnScreen(int[] outLocation) {
1101         mView.getLocationOnScreen(outLocation);
1102     }
1103 
getChildAtRawPosition(float x, float y)1104     public ExpandableView getChildAtRawPosition(float x, float y) {
1105         return mView.getChildAtRawPosition(x, y);
1106     }
1107 
getLayoutParams()1108     public ViewGroup.LayoutParams getLayoutParams() {
1109         return mView.getLayoutParams();
1110     }
1111 
1112     /**
1113      * Updates layout parameters on the root view
1114      */
setLayoutParams(ViewGroup.LayoutParams lp)1115     public void setLayoutParams(ViewGroup.LayoutParams lp) {
1116         mView.setLayoutParams(lp);
1117     }
1118 
setIsFullWidth(boolean isFullWidth)1119     public void setIsFullWidth(boolean isFullWidth) {
1120         mView.setIsFullWidth(isFullWidth);
1121     }
1122 
isAddOrRemoveAnimationPending()1123     public boolean isAddOrRemoveAnimationPending() {
1124         SceneContainerFlag.assertInLegacyMode();
1125         return mView != null && mView.isAddOrRemoveAnimationPending();
1126     }
1127 
getVisibleNotificationCount()1128     public int getVisibleNotificationCount() {
1129         FooterViewRefactor.assertInLegacyMode();
1130         return mNotifStats.getNumActiveNotifs();
1131     }
1132 
isHistoryEnabled()1133     public boolean isHistoryEnabled() {
1134         Boolean historyEnabled = mHistoryEnabled;
1135         if (historyEnabled == null) {
1136             if (mView == null || mView.getContext() == null) {
1137                 Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
1138                 return false;
1139             }
1140             mHistoryEnabled = historyEnabled = mSecureSettings.getIntForUser(
1141                     Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
1142                     0,
1143                     UserHandle.USER_CURRENT) == 1;
1144         }
1145         return historyEnabled;
1146     }
1147 
getIntrinsicContentHeight()1148     public int getIntrinsicContentHeight() {
1149         return mView.getIntrinsicContentHeight();
1150     }
1151 
1152     /**
1153      * Dispatch a touch to the scene container framework.
1154      * TODO(b/316965302): Replace findViewById to avoid DFS
1155      */
sendTouchToSceneFramework(MotionEvent ev)1156     public void sendTouchToSceneFramework(MotionEvent ev) {
1157         View sceneContainer = mWindowRootView.get()
1158                 .findViewById(R.id.scene_container_root_composable);
1159         if (sceneContainer != null) {
1160             sceneContainer.dispatchTouchEvent(ev);
1161         }
1162     }
1163 
setIntrinsicPadding(int intrinsicPadding)1164     public void setIntrinsicPadding(int intrinsicPadding) {
1165         mView.setIntrinsicPadding(intrinsicPadding);
1166     }
1167 
getHeight()1168     public int getHeight() {
1169         return mView.getHeight();
1170     }
1171 
getChildCount()1172     public int getChildCount() {
1173         return mView.getChildCount();
1174     }
1175 
getChildAt(int i)1176     public ExpandableView getChildAt(int i) {
1177         return (ExpandableView) mView.getChildAt(i);
1178     }
1179 
goToFullShade(long delay)1180     public void goToFullShade(long delay) {
1181         mView.goToFullShade(delay);
1182     }
1183 
setOverScrollAmount(float amount, boolean onTop, boolean animate, boolean cancelAnimators)1184     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1185             boolean cancelAnimators) {
1186         mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
1187     }
1188 
setOverScrollAmount(float amount, boolean onTop, boolean animate)1189     public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1190         mView.setOverScrollAmount(amount, onTop, animate);
1191     }
1192 
resetScrollPosition()1193     public void resetScrollPosition() {
1194         mView.resetScrollPosition();
1195     }
1196 
setShouldShowShelfOnly(boolean shouldShowShelfOnly)1197     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
1198         mView.setShouldShowShelfOnly(shouldShowShelfOnly);
1199     }
1200 
cancelLongPress()1201     public void cancelLongPress() {
1202         mView.cancelLongPress();
1203     }
1204 
getX()1205     public float getX() {
1206         return mView.getX();
1207     }
1208 
isBelowLastNotification(float x, float y)1209     public boolean isBelowLastNotification(float x, float y) {
1210         return mView.isBelowLastNotification(x, y);
1211     }
1212 
getWidth()1213     public float getWidth() {
1214         return mView.getWidth();
1215     }
1216 
getOpeningHeight()1217     public float getOpeningHeight() {
1218         return mView.getOpeningHeight();
1219     }
1220 
getBottomMostNotificationBottom()1221     public float getBottomMostNotificationBottom() {
1222         return mView.getBottomMostNotificationBottom();
1223     }
1224 
checkSnoozeLeavebehind()1225     public void checkSnoozeLeavebehind() {
1226         if (mView.getCheckSnoozeLeaveBehind()) {
1227             mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
1228                     false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
1229                     false /* resetMenu */);
1230             mView.setCheckForLeaveBehind(false);
1231         }
1232     }
1233 
setQsFullScreen(boolean fullScreen)1234     public void setQsFullScreen(boolean fullScreen) {
1235         mView.setQsFullScreen(fullScreen);
1236         if (!FooterViewRefactor.isEnabled()) {
1237             updateShowEmptyShadeView();
1238         }
1239     }
1240 
setScrollingEnabled(boolean enabled)1241     public void setScrollingEnabled(boolean enabled) {
1242         SceneContainerFlag.assertInLegacyMode();
1243         mView.setScrollingEnabled(enabled);
1244     }
1245 
setQsExpansionFraction(float expansionFraction)1246     public void setQsExpansionFraction(float expansionFraction) {
1247         mView.setQsExpansionFraction(expansionFraction);
1248     }
1249 
setOnStackYChanged(Consumer<Boolean> onStackYChanged)1250     public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
1251         mView.setOnStackYChanged(onStackYChanged);
1252     }
1253 
getNotificationSquishinessFraction()1254     public float getNotificationSquishinessFraction() {
1255         return mView.getNotificationSquishinessFraction();
1256     }
1257 
calculateAppearFractionBypass()1258     public float calculateAppearFractionBypass() {
1259         return mView.calculateAppearFractionBypass();
1260     }
1261 
updateTopPadding(float qsHeight, boolean animate)1262     public void updateTopPadding(float qsHeight, boolean animate) {
1263         SceneContainerFlag.assertInLegacyMode();
1264         mView.updateTopPadding(qsHeight, animate);
1265     }
1266 
isScrolledToBottom()1267     public boolean isScrolledToBottom() {
1268         return mView.isScrolledToBottom();
1269     }
1270 
getNotGoneChildCount()1271     public int getNotGoneChildCount() {
1272         return mView.getNotGoneChildCount();
1273     }
1274 
getIntrinsicPadding()1275     public float getIntrinsicPadding() {
1276         return mView.getIntrinsicPadding();
1277     }
1278 
getLayoutMinHeight()1279     public float getLayoutMinHeight() {
1280         return mView.getLayoutMinHeight();
1281     }
1282 
getEmptyBottomMargin()1283     public int getEmptyBottomMargin() {
1284         return mView.getEmptyBottomMargin();
1285     }
1286 
getTopPaddingOverflow()1287     public float getTopPaddingOverflow() {
1288         return mView.getTopPaddingOverflow();
1289     }
1290 
getTopPadding()1291     public int getTopPadding() {
1292         return mView.getTopPadding();
1293     }
1294 
getEmptyShadeViewHeight()1295     public float getEmptyShadeViewHeight() {
1296         return mView.getEmptyShadeViewHeight();
1297     }
1298 
1299     /** Set the max alpha for keyguard */
setMaxAlphaForKeyguard(float alpha, String source)1300     public void setMaxAlphaForKeyguard(float alpha, String source) {
1301         mMaxAlphaForKeyguard = alpha;
1302         mMaxAlphaForKeyguardSource = source;
1303         updateAlpha();
1304         if (DEBUG) {
1305             Log.d(TAG, "setMaxAlphaForKeyguard=" + alpha + " --- from: " + source);
1306         }
1307     }
1308 
setMaxAlphaForUnhide(float alpha)1309     private void setMaxAlphaForUnhide(float alpha) {
1310         mMaxAlphaForUnhide = alpha;
1311         updateAlpha();
1312     }
1313 
1314     /**
1315      * Sets the max alpha value for notifications when idle on the glanceable hub or when
1316      * transitioning to/from the glanceable hub.
1317      */
setMaxAlphaForGlanceableHub(float alpha)1318     public void setMaxAlphaForGlanceableHub(float alpha) {
1319         mMaxAlphaForGlanceableHub = alpha;
1320         updateAlpha();
1321     }
1322 
setMaxAlphaFromView(float alpha)1323     void setMaxAlphaFromView(float alpha) {
1324         mMaxAlphaFromView = alpha;
1325         updateAlpha();
1326     }
1327 
updateAlpha()1328     private void updateAlpha() {
1329         if (mView != null) {
1330             mView.setAlpha(Math.min(Math.min(mMaxAlphaFromView, mMaxAlphaForKeyguard),
1331                     Math.min(mMaxAlphaForUnhide, mMaxAlphaForGlanceableHub)));
1332         }
1333     }
1334 
getAlpha()1335     public float getAlpha() {
1336         return mView.getAlpha();
1337     }
1338 
setSuppressChildrenMeasureAndLayout(boolean suppressLayout)1339     public void setSuppressChildrenMeasureAndLayout(boolean suppressLayout) {
1340         mView.suppressChildrenMeasureAndLayout(suppressLayout);
1341     }
1342 
updateNotificationsContainerVisibility(boolean visible, boolean animate)1343     public void updateNotificationsContainerVisibility(boolean visible, boolean animate) {
1344         if (mHideAlphaAnimator != null) {
1345             mHideAlphaAnimator.cancel();
1346         }
1347 
1348         final float targetAlpha = visible ? 1f : 0f;
1349 
1350         if (animate) {
1351             mHideAlphaAnimator = createAlphaAnimator(targetAlpha);
1352             mHideAlphaAnimator.start();
1353         } else {
1354             HIDE_ALPHA_PROPERTY.set(this, targetAlpha);
1355         }
1356     }
1357 
createAlphaAnimator(float targetAlpha)1358     private ObjectAnimator createAlphaAnimator(float targetAlpha) {
1359         final ObjectAnimator objectAnimator = ObjectAnimator
1360                 .ofFloat(this, HIDE_ALPHA_PROPERTY, targetAlpha);
1361         objectAnimator.setInterpolator(STANDARD);
1362         objectAnimator.setDuration(ANIMATION_DURATION_STANDARD);
1363         return objectAnimator;
1364     }
1365 
calculateAppearFraction(float height)1366     public float calculateAppearFraction(float height) {
1367         return mView.calculateAppearFraction(height);
1368     }
1369 
onExpansionStarted()1370     public void onExpansionStarted() {
1371         SceneContainerFlag.assertInLegacyMode();
1372         mView.onExpansionStarted();
1373         checkSnoozeLeavebehind();
1374     }
1375 
onExpansionStopped()1376     public void onExpansionStopped() {
1377         SceneContainerFlag.assertInLegacyMode();
1378         mView.setCheckForLeaveBehind(false);
1379         mView.onExpansionStopped();
1380     }
1381 
onPanelTrackingStarted()1382     public void onPanelTrackingStarted() {
1383         mView.onPanelTrackingStarted();
1384     }
1385 
onPanelTrackingStopped()1386     public void onPanelTrackingStopped() {
1387         mView.onPanelTrackingStopped();
1388     }
1389 
setHeadsUpBoundaries(int height, int bottomBarHeight)1390     public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
1391         mView.setHeadsUpBoundaries(height, bottomBarHeight);
1392     }
1393 
setPanelFlinging(boolean flinging)1394     public void setPanelFlinging(boolean flinging) {
1395         mView.setPanelFlinging(flinging);
1396     }
1397 
1398     /**
1399      * Set the visibility of the view, and propagate it to specific children.
1400      *
1401      * @param visible either the view is visible or not.
1402      */
updateVisibility(boolean visible)1403     public void updateVisibility(boolean visible) {
1404         mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1405 
1406         // Refactor note: the empty shade's visibility doesn't seem to actually depend on the
1407         // parent visibility (so this update seemingly doesn't do anything). Therefore, this is not
1408         // modeled in the refactored code.
1409         if (!FooterViewRefactor.isEnabled() && mView.getVisibility() == View.VISIBLE) {
1410             // Synchronize EmptyShadeView visibility with the parent container.
1411             updateShowEmptyShadeView();
1412             updateImportantForAccessibility();
1413         }
1414     }
1415 
1416     /**
1417      * Update whether we should show the empty shade view ("no notifications" in the shade).
1418      * <p>
1419      * When in split mode, notifications are always visible regardless of the state of the
1420      * QuickSettings panel. That being the case, empty view is always shown if the other conditions
1421      * are true.
1422      */
updateShowEmptyShadeView()1423     public void updateShowEmptyShadeView() {
1424         FooterViewRefactor.assertInLegacyMode();
1425 
1426         Trace.beginSection("NSSLC.updateShowEmptyShadeView");
1427 
1428         final boolean shouldShow = getVisibleNotificationCount() == 0
1429                 && !mView.isQsFullScreen()
1430                 // Hide empty shade view when in transition to AOD.
1431                 // That avoids "No Notifications" to blink when transitioning to AOD.
1432                 // For more details, see: b/228790482
1433                 && !mIsInTransitionToAod
1434                 // Don't show any notification content if the bouncer is showing. See b/267060171.
1435                 && !mPrimaryBouncerInteractor.isBouncerShowing();
1436 
1437         mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
1438 
1439         Trace.endSection();
1440     }
1441 
1442     /**
1443      * Update the importantForAccessibility of NotificationStackScrollLayout.
1444      * <p>
1445      * We want the NSSL to be unimportant for accessibility when there's no
1446      * notifications in it while the device is on lock screen, to avoid unlablel NSSL view.
1447      * Otherwise, we want it to be important for accessibility to enable accessibility
1448      * auto-scrolling in NSSL.
1449      */
updateImportantForAccessibility()1450     public void updateImportantForAccessibility() {
1451         FooterViewRefactor.assertInLegacyMode();
1452         if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) {
1453             mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
1454         } else {
1455             mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
1456         }
1457     }
1458 
isShowingEmptyShadeView()1459     public boolean isShowingEmptyShadeView() {
1460         return mView.isEmptyShadeViewVisible();
1461     }
1462 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)1463     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1464         NotificationsHeadsUpRefactor.assertInLegacyMode();
1465         mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
1466     }
1467 
getHeadsUpCallback()1468     public HeadsUpTouchHelper.Callback getHeadsUpCallback() {
1469         return mView.getHeadsUpCallback();
1470     }
1471 
forceNoOverlappingRendering(boolean force)1472     public void forceNoOverlappingRendering(boolean force) {
1473         mView.forceNoOverlappingRendering(force);
1474     }
1475 
setExpandingVelocity(float velocity)1476     public void setExpandingVelocity(float velocity) {
1477         mView.setExpandingVelocity(velocity);
1478     }
1479 
setExpandedHeight(float expandedHeight)1480     public void setExpandedHeight(float expandedHeight) {
1481         SceneContainerFlag.assertInLegacyMode();
1482         mView.setExpandedHeight(expandedHeight);
1483     }
1484 
1485     /**
1486      * Sets the QS header. Used to check if a touch is within its bounds.
1487      */
setQsHeader(ViewGroup view)1488     public void setQsHeader(ViewGroup view) {
1489         mView.setQsHeader(view);
1490     }
1491 
setAnimationsEnabled(boolean enabled)1492     public void setAnimationsEnabled(boolean enabled) {
1493         mView.setAnimationsEnabled(enabled);
1494     }
1495 
setDozing(boolean dozing, boolean animate)1496     public void setDozing(boolean dozing, boolean animate) {
1497         SceneContainerFlag.assertInLegacyMode();
1498         mView.setDozing(dozing);
1499     }
1500 
setPulsing(boolean pulsing, boolean animatePulse)1501     public void setPulsing(boolean pulsing, boolean animatePulse) {
1502         mView.setPulsing(pulsing, animatePulse);
1503     }
1504 
1505     /**
1506      * Return whether there are any clearable notifications
1507      */
hasActiveClearableNotifications(@electedRows int selection)1508     public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
1509         FooterViewRefactor.assertInLegacyMode();
1510         return hasNotifications(selection, true /* clearable */);
1511     }
1512 
hasNotifications(@electedRows int selection, boolean isClearable)1513     public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
1514         FooterViewRefactor.assertInLegacyMode();
1515         boolean hasAlertingMatchingClearable = isClearable
1516                 ? mNotifStats.getHasClearableAlertingNotifs()
1517                 : mNotifStats.getHasNonClearableAlertingNotifs();
1518         boolean hasSilentMatchingClearable = isClearable
1519                 ? mNotifStats.getHasClearableSilentNotifs()
1520                 : mNotifStats.getHasNonClearableSilentNotifs();
1521         switch (selection) {
1522             case ROWS_GENTLE:
1523                 return hasSilentMatchingClearable;
1524             case ROWS_HIGH_PRIORITY:
1525                 return hasAlertingMatchingClearable;
1526             case ROWS_ALL:
1527                 return hasSilentMatchingClearable || hasAlertingMatchingClearable;
1528             default:
1529                 throw new IllegalStateException("Bad selection: " + selection);
1530         }
1531     }
1532 
1533     /**
1534      * Set the maximum number of notifications that can currently be displayed
1535      */
setMaxDisplayedNotifications(int maxNotifications)1536     public void setMaxDisplayedNotifications(int maxNotifications) {
1537         mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
1538     }
1539 
1540     /**
1541      * This is used for debugging only; it will be used to draw the otherwise invisible line which
1542      * NotificationPanelViewController treats as the bottom when calculating how many notifications
1543      * appear on the keyguard.
1544      * Setting a negative number will disable rendering this line.
1545      */
setKeyguardBottomPaddingForDebug(float keyguardBottomPadding)1546     public void setKeyguardBottomPaddingForDebug(float keyguardBottomPadding) {
1547         mView.setKeyguardBottomPadding(keyguardBottomPadding);
1548     }
1549 
createDelegate()1550     public RemoteInputController.Delegate createDelegate() {
1551         return new RemoteInputController.Delegate() {
1552             public void setRemoteInputActive(NotificationEntry entry,
1553                     boolean remoteInputActive) {
1554                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
1555                 entry.notifyHeightChanged(true /* needsAnimation */);
1556                 if (!FooterViewRefactor.isEnabled()) {
1557                     updateFooter();
1558                 }
1559             }
1560 
1561             public void lockScrollTo(NotificationEntry entry) {
1562                 mView.lockScrollTo(entry.getRow());
1563             }
1564 
1565             public void requestDisallowLongPressAndDismiss() {
1566                 mView.requestDisallowLongPress();
1567                 mView.requestDisallowDismiss();
1568             }
1569         };
1570     }
1571 
1572     public void updateFooter() {
1573         FooterViewRefactor.assertInLegacyMode();
1574         Trace.beginSection("NSSLC.updateFooter");
1575         mView.updateFooter();
1576         Trace.endSection();
1577     }
1578 
1579     public void onUpdateRowStates() {
1580         mView.onUpdateRowStates();
1581     }
1582 
1583     public void runAfterAnimationFinished(Runnable r) {
1584         mView.runAfterAnimationFinished(r);
1585     }
1586 
1587     public ExpandableView getFirstChildNotGone() {
1588         return mView.getFirstChildNotGone();
1589     }
1590 
1591     public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
1592         mView.generateHeadsUpAnimation(entry, isHeadsUp);
1593     }
1594 
1595     public void setMaxTopPadding(int padding) {
1596         mView.setMaxTopPadding(padding);
1597     }
1598 
1599     public int getTransientViewCount() {
1600         return mView.getTransientViewCount();
1601     }
1602 
1603     public View getTransientView(int i) {
1604         return mView.getTransientView(i);
1605     }
1606 
1607     public int getPositionInLinearLayout(ExpandableView row) {
1608         return mView.getPositionInLinearLayout(row);
1609     }
1610 
1611     public NotificationStackScrollLayout getView() {
1612         return mView;
1613     }
1614 
1615     public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) {
1616         return mView.calculateGapHeight(previousView, child, count);
1617     }
1618 
1619     NotificationRoundnessManager getNotificationRoundnessManager() {
1620         return mNotificationRoundnessManager;
1621     }
1622 
1623     public NotificationListContainer getNotificationListContainer() {
1624         return mNotificationListContainer;
1625     }
1626 
1627     public NotifStackController getNotifStackController() {
1628         return mNotifStackController;
1629     }
1630 
1631     public void resetCheckSnoozeLeavebehind() {
1632         mView.resetCheckSnoozeLeavebehind();
1633     }
1634 
1635     private DismissedByUserStats getDismissedByUserStats(NotificationEntry entry) {
1636         return new DismissedByUserStats(
1637                 DISMISSAL_SHADE,
1638                 DISMISS_SENTIMENT_NEUTRAL,
1639                 mVisibilityProvider.obtain(entry, true));
1640     }
1641 
1642     public void closeControlsIfOutsideTouch(MotionEvent ev) {
1643         NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
1644         NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
1645         View translatingParentView = mSwipeHelper.getTranslatingParentView();
1646         View view = null;
1647         if (guts != null && !guts.getGutsContent().isLeavebehind()) {
1648             // Only close visible guts if they're not a leavebehind.
1649             view = guts;
1650         } else if (menuRow != null && menuRow.isMenuVisible()
1651                 && translatingParentView != null) {
1652             // Checking menu
1653             view = translatingParentView;
1654         }
1655         if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
1656             // Touch was outside visible guts / menu notification, close what's visible
1657             mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
1658                     false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
1659                     false /* resetMenu */);
1660             mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
1661         }
1662     }
1663 
1664     public void clearSilentNotifications() {
1665         FooterViewRefactor.assertInLegacyMode();
1666         // Leave the shade open if there will be other notifs left over to clear
1667         final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
1668         mView.clearNotifications(ROWS_GENTLE, closeShade);
1669     }
1670 
1671     private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
1672             @SelectedRows int selectedRows) {
1673         if (selectedRows == ROWS_ALL) {
1674             mNotifCollection.dismissAllNotifications(
1675                     mLockscreenUserManager.getCurrentUserId());
1676         } else {
1677             final List<Pair<NotificationEntry, DismissedByUserStats>>
1678                     entriesWithRowsDismissedFromShade = new ArrayList<>();
1679             for (ExpandableNotificationRow row : viewsToRemove) {
1680                 final NotificationEntry entry = row.getEntry();
1681                 entriesWithRowsDismissedFromShade.add(
1682                         new Pair<>(entry, getDismissedByUserStats(entry)));
1683             }
1684             mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
1685         }
1686     }
1687 
1688     /**
1689      * @return the expand helper callback.
1690      */
1691     public ExpandHelper.Callback getExpandHelperCallback() {
1692         return mView.getExpandHelperCallback();
1693     }
1694 
1695     /**
1696      * @return If the shade is in the locked down shade.
1697      */
1698     public boolean isInLockedDownShade() {
1699         return mDynamicPrivacyController.isInLockedDownShade();
1700     }
1701 
1702     public boolean isLongPressInProgress() {
1703         return mLongPressedView != null;
1704     }
1705 
1706     /**
1707      * @return the inset during the full shade transition, that needs to be added to the position
1708      * of the quick settings edge. This is relevant for media, that is transitioning
1709      * from the keyguard host to the quick settings one.
1710      */
1711     public int getFullShadeTransitionInset() {
1712         MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
1713         if (view == null || view.getHeight() == 0
1714                 || mStatusBarStateController.getState() != KEYGUARD) {
1715             return 0;
1716         }
1717         return view.getHeight() + mView.getPaddingAfterMedia();
1718     }
1719 
1720     /**
1721      * @param fraction The fraction of lockscreen to shade transition.
1722      *                 0f for all other states.
1723      *                 <p>
1724      *                 Once the lockscreen to shade transition completes and the shade is 100% open,
1725      *                 LockscreenShadeTransitionController resets amount and fraction to 0, where
1726      *                 they remain until the next lockscreen-to-shade transition.
1727      */
1728     public void setTransitionToFullShadeAmount(float fraction) {
1729         mView.setFractionToShade(fraction);
1730     }
1731 
1732     /**
1733      * Sets the amount of vertical over scroll that should be performed on NSSL.
1734      */
1735     public void setOverScrollAmount(int overScrollAmount) {
1736         mView.setExtraTopInsetForFullShadeTransition(overScrollAmount);
1737     }
1738 
1739     /**
1740      *
1741      */
1742     public void setWillExpand(boolean willExpand) {
1743         mView.setWillExpand(willExpand);
1744     }
1745 
1746     /**
1747      * Set a listener to when scrolling changes.
1748      */
1749     public void setOnScrollListener(Consumer<Integer> listener) {
1750         mView.setOnScrollListener(listener);
1751     }
1752 
1753     /**
1754      * Set rounded rect clipping bounds on this view.
1755      */
1756     public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
1757             int bottomRadius) {
1758         SceneContainerFlag.assertInLegacyMode();
1759         mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
1760     }
1761 
1762     /**
1763      * Request an animation whenever the toppadding changes next
1764      */
1765     public void animateNextTopPaddingChange() {
1766         mView.animateNextTopPaddingChange();
1767     }
1768 
1769     public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
1770         mNotificationActivityStarter = activityStarter;
1771     }
1772 
1773     public NotificationTargetsHelper getNotificationTargetsHelper() {
1774         return mNotificationTargetsHelper;
1775     }
1776 
1777     public void setShelf(NotificationShelf shelf) {
1778         mView.setShelf(shelf);
1779     }
1780 
1781     public int getShelfHeight() {
1782         ExpandableView shelf = mView.getShelf();
1783         return shelf == null ? 0 : shelf.getIntrinsicHeight();
1784     }
1785 
1786     @VisibleForTesting
1787     void onKeyguardTransitionChanged(TransitionStep transitionStep) {
1788         FooterViewRefactor.assertInLegacyMode();
1789         boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD)
1790                 && (transitionStep.getFrom().equals(KeyguardState.GONE)
1791                 || transitionStep.getFrom().equals(KeyguardState.OCCLUDED));
1792         if (mIsInTransitionToAod != isTransitionToAod) {
1793             mIsInTransitionToAod = isTransitionToAod;
1794             updateShowEmptyShadeView();
1795         }
1796     }
1797 
1798     @VisibleForTesting
1799     TouchHandler getTouchHandler() {
1800         return mTouchHandler;
1801     }
1802 
1803     @Override
1804     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
1805         pw.println("mMaxAlphaFromView=" + mMaxAlphaFromView);
1806         pw.println("mMaxAlphaForUnhide=" + mMaxAlphaForUnhide);
1807         pw.println("mMaxAlphaForGlanceableHub=" + mMaxAlphaForGlanceableHub);
1808         pw.println("mMaxAlphaForKeyguard=" + mMaxAlphaForKeyguard);
1809         pw.println("mMaxAlphaForKeyguardSource=" + mMaxAlphaForKeyguardSource);
1810     }
1811 
1812     /**
1813      * Enum for UiEvent logged from this class
1814      */
1815     enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
1816         INVALID(0),
1817         @UiEvent(doc = "User dismissed all notifications from notification panel.")
1818         DISMISS_ALL_NOTIFICATIONS_PANEL(312),
1819         @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
1820         DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
1821         private final int mId;
1822 
1823         NotificationPanelEvent(int id) {
1824             mId = id;
1825         }
1826 
1827         @Override
1828         public int getId() {
1829             return mId;
1830         }
1831 
1832         public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) {
1833             if (selection == ROWS_ALL) {
1834                 return DISMISS_ALL_NOTIFICATIONS_PANEL;
1835             }
1836             if (selection == NotificationStackScrollLayout.ROWS_GENTLE) {
1837                 return DISMISS_SILENT_NOTIFICATIONS_PANEL;
1838             }
1839             if (NotificationStackScrollLayoutController.DEBUG) {
1840                 throw new IllegalArgumentException("Unexpected selection" + selection);
1841             }
1842             return INVALID;
1843         }
1844     }
1845 
1846     private class NotificationListContainerImpl implements NotificationListContainer,
1847             PipelineDumpable {
1848 
1849         @Override
1850         public void setChildTransferInProgress(boolean childTransferInProgress) {
1851             mView.setChildTransferInProgress(childTransferInProgress);
1852         }
1853 
1854         @Override
1855         public void changeViewPosition(ExpandableView child, int newIndex) {
1856             mView.changeViewPosition(child, newIndex);
1857         }
1858 
1859         @Override
1860         public void notifyGroupChildAdded(ExpandableView row) {
1861             mView.notifyGroupChildAdded(row);
1862         }
1863 
1864         @Override
1865         public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
1866             mView.notifyGroupChildRemoved(row, childrenContainer);
1867         }
1868 
1869         @Override
1870         public int getContainerChildCount() {
1871             return mView.getContainerChildCount();
1872         }
1873 
1874         @Override
1875         public int getTopClippingStartLocation() {
1876             return mView.getTopClippingStartLocation();
1877         }
1878 
1879         @Override
1880         public View getContainerChildAt(int i) {
1881             return mView.getContainerChildAt(i);
1882         }
1883 
1884         @Override
1885         public void removeContainerView(View v) {
1886             mView.removeContainerView(v);
1887         }
1888 
1889         @Override
1890         public void addContainerView(View v) {
1891             mView.addContainerView(v);
1892         }
1893 
1894         @Override
1895         public void addContainerViewAt(View v, int index) {
1896             mView.addContainerViewAt(v, index);
1897         }
1898 
1899         @Override
1900         public void setMaxDisplayedNotifications(int maxNotifications) {
1901             mView.setMaxDisplayedNotifications(maxNotifications);
1902         }
1903 
1904         @Override
1905         public ViewGroup getViewParentForNotification(NotificationEntry entry) {
1906             return mView.getViewParentForNotification(entry);
1907         }
1908 
1909         @Override
1910         public void resetExposedMenuView(boolean animate, boolean force) {
1911             mSwipeHelper.resetExposedMenuView(animate, force);
1912         }
1913 
1914         @Override
1915         public NotificationSwipeActionHelper getSwipeActionHelper() {
1916             return mSwipeHelper;
1917         }
1918 
1919         @Override
1920         public void cleanUpViewStateForEntry(NotificationEntry entry) {
1921             mView.cleanUpViewStateForEntry(entry);
1922         }
1923 
1924         @Override
1925         public void setChildLocationsChangedListener(
1926                 NotificationLogger.OnChildLocationsChangedListener listener) {
1927             mView.setChildLocationsChangedListener(listener);
1928         }
1929 
1930         public boolean hasPulsingNotifications() {
1931             return mView.hasPulsingNotifications();
1932         }
1933 
1934         @Override
1935         public boolean isInVisibleLocation(NotificationEntry entry) {
1936             return NotificationStackScrollLayoutController.this.isInVisibleLocation(entry);
1937         }
1938 
1939         @Override
1940         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
1941             mView.onChildHeightChanged(view, needsAnimation);
1942         }
1943 
1944         @Override
1945         public void onReset(ExpandableView view) {
1946             mView.onChildHeightReset(view);
1947         }
1948 
1949         @Override
1950         public void bindRow(ExpandableNotificationRow row) {
1951             row.setHeadsUpAnimatingAwayListener(animatingAway -> {
1952                 NotificationEntry entry = row.getEntry();
1953                 mHeadsUpAppearanceController.updateHeader(entry);
1954                 mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry);
1955             });
1956         }
1957 
1958         @Override
1959         public void applyLaunchAnimationParams(LaunchAnimationParameters params) {
1960             mView.applyLaunchAnimationParams(params);
1961         }
1962 
1963         @Override
1964         public void setExpandingNotification(ExpandableNotificationRow row) {
1965             mView.setExpandingNotification(row);
1966         }
1967 
1968         @Override
1969         public void dumpPipeline(@NonNull PipelineDumper d) {
1970             d.dump("NotificationStackScrollLayoutController.this",
1971                     NotificationStackScrollLayoutController.this);
1972         }
1973     }
1974 
1975     class TouchHandler implements Gefingerpoken {
1976         @Override
1977         public boolean onInterceptTouchEvent(MotionEvent ev) {
1978             mView.initDownStates(ev);
1979             mView.handleEmptySpaceClick(ev);
1980 
1981             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
1982 
1983             boolean longPressWantsIt = false;
1984             if (mLongPressedView != null) {
1985                 longPressWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
1986             }
1987             boolean expandWantsIt = false;
1988             if (mLongPressedView == null && !mSwipeHelper.isSwiping()
1989                     && !mView.getOnlyScrollingInThisMotion() && guts == null) {
1990                 expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
1991             }
1992             boolean scrollWantsIt = false;
1993             if (mLongPressedView == null && !mSwipeHelper.isSwiping()
1994                     && !mView.isExpandingNotification()) {
1995                 scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
1996             }
1997             boolean swipeWantsIt = false;
1998             if (mLongPressedView == null && !mView.isBeingDragged()
1999                     && !mView.isExpandingNotification()
2000                     && !mView.getExpandedInThisMotion()
2001                     && !mView.getOnlyScrollingInThisMotion()
2002                     && !mView.getDisallowDismissInThisMotion()) {
2003                 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2004             }
2005             // Check if we need to clear any snooze leavebehinds
2006             boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
2007             if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
2008                     !expandWantsIt && !scrollWantsIt) {
2009                 mView.setCheckForLeaveBehind(false);
2010                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
2011                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2012                         false /* resetMenu */);
2013             }
2014             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2015                 mView.setCheckForLeaveBehind(true);
2016             }
2017 
2018             // When swiping directly on the NSSL, this would only get an onTouchEvent.
2019             // We log any touches other than down, which will be captured by onTouchEvent.
2020             // In the intercept we only start tracing when it's not a down (otherwise that down
2021             // would be duplicated when intercepted).
2022             if (mJankMonitor != null && scrollWantsIt
2023                     && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
2024                 mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
2025             }
2026             return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt;
2027         }
2028 
2029         @Override
2030         public boolean onTouchEvent(MotionEvent ev) {
2031             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
2032             boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
2033                     || ev.getActionMasked() == MotionEvent.ACTION_UP;
2034             mView.handleEmptySpaceClick(ev);
2035             boolean longPressWantsIt = false;
2036             if (guts != null && mLongPressedView != null) {
2037                 longPressWantsIt = mSwipeHelper.onTouchEvent(ev);
2038             }
2039             boolean expandWantsIt = false;
2040             boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
2041             boolean expandingNotification = mView.isExpandingNotification();
2042             if (mLongPressedView == null && mView.getIsExpanded()
2043                     && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion && guts == null) {
2044                 ExpandHelper expandHelper = mView.getExpandHelper();
2045                 if (isCancelOrUp) {
2046                     expandHelper.onlyObserveMovements(false);
2047                 }
2048                 boolean wasExpandingBefore = expandingNotification;
2049                 expandWantsIt = expandHelper.onTouchEvent(ev);
2050                 expandingNotification = mView.isExpandingNotification();
2051                 if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
2052                         && !mView.getDisallowScrollingInThisMotion()) {
2053                     // We need to dispatch the overscroll differently when Scene Container is on,
2054                     // since NSSL no longer controls its own scroll.
2055                     if (SceneContainerFlag.isEnabled() && !isCancelOrUp) {
2056                         mView.startOverscrollAfterExpanding();
2057                         return true;
2058                     } else {
2059                         mView.dispatchDownEventToScroller(ev);
2060                     }
2061                 }
2062             }
2063             boolean horizontalSwipeWantsIt = false;
2064             boolean scrollerWantsIt = false;
2065             // NOTE: the order of these is important. If reversed, onScrollTouch will reset on an
2066             // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
2067             if (mLongPressedView == null && !mView.isBeingDragged()
2068                     && !expandingNotification
2069                     && !mView.getExpandedInThisMotion()
2070                     && !onlyScrollingInThisMotion
2071                     && !mView.getDisallowDismissInThisMotion()) {
2072                 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
2073             }
2074             if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
2075                     && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
2076                 scrollerWantsIt = mView.onScrollTouch(ev);
2077             }
2078 
2079             // Check if we need to clear any snooze leavebehinds
2080             if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
2081                     && guts.getGutsContent() instanceof NotificationSnooze ns) {
2082                 if ((ns.isExpanded() && isCancelOrUp)
2083                         || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
2084                     // If the leavebehind is expanded we clear it on the next up event, otherwise we
2085                     // clear it on the next non-horizontal swipe or expand event.
2086                     checkSnoozeLeavebehind();
2087                 }
2088             }
2089             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2090                 // Ensure the falsing manager records the touch. we don't do anything with it
2091                 // at the moment, but it may trigger a global falsing event.
2092                 if (!horizontalSwipeWantsIt) {
2093                     mFalsingManager.isFalseTouch(Classifier.SHADE_DRAG);
2094                 }
2095                 mView.setCheckForLeaveBehind(true);
2096             }
2097             traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
2098             return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || longPressWantsIt;
2099         }
2100 
2101         private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
2102             if (mJankMonitor == null) {
2103                 Log.w(TAG, "traceJankOnTouchEvent, mJankMonitor is null");
2104                 return;
2105             }
2106             // Handle interaction jank monitor cases.
2107             switch (action) {
2108                 case MotionEvent.ACTION_DOWN:
2109                     if (scrollerWantsIt) {
2110                         mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
2111                     }
2112                     break;
2113                 case MotionEvent.ACTION_UP:
2114                     if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
2115                         mJankMonitor.end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
2116                     }
2117                     break;
2118                 case MotionEvent.ACTION_CANCEL:
2119                     if (scrollerWantsIt) {
2120                         mJankMonitor.cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
2121                     }
2122                     break;
2123             }
2124         }
2125     }
2126 
2127     private class NotifStackControllerImpl implements NotifStackController {
2128         @Override
2129         public void setNotifStats(@NonNull NotifStats notifStats) {
2130             FooterViewRefactor.assertInLegacyMode();
2131             mNotifStats = notifStats;
2132 
2133             if (!FooterViewRefactor.isEnabled()) {
2134                 mView.setHasFilteredOutSeenNotifications(
2135                         mSeenNotificationsInteractor
2136                                 .getHasFilteredOutSeenNotifications().getValue());
2137 
2138                 updateFooter();
2139                 updateShowEmptyShadeView();
2140                 updateImportantForAccessibility();
2141             }
2142         }
2143     }
2144 }
2145