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