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