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