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