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