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