1 /* 2 * Copyright (C) 2019 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.phone; 18 19 import static android.view.View.GONE; 20 21 import static androidx.constraintlayout.widget.ConstraintSet.END; 22 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; 23 import static androidx.constraintlayout.widget.ConstraintSet.START; 24 25 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; 26 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; 27 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; 28 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 29 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; 30 31 import static java.lang.Float.isNaN; 32 33 import android.animation.Animator; 34 import android.animation.AnimatorListenerAdapter; 35 import android.animation.ValueAnimator; 36 import android.app.ActivityManager; 37 import android.app.Fragment; 38 import android.app.StatusBarManager; 39 import android.content.ContentResolver; 40 import android.content.pm.ResolveInfo; 41 import android.content.res.Configuration; 42 import android.content.res.Resources; 43 import android.database.ContentObserver; 44 import android.graphics.Canvas; 45 import android.graphics.Color; 46 import android.graphics.ColorFilter; 47 import android.graphics.Insets; 48 import android.graphics.Paint; 49 import android.graphics.PointF; 50 import android.graphics.Rect; 51 import android.graphics.Region; 52 import android.graphics.drawable.Drawable; 53 import android.hardware.biometrics.BiometricSourceType; 54 import android.os.Bundle; 55 import android.os.Handler; 56 import android.os.PowerManager; 57 import android.os.SystemClock; 58 import android.os.UserManager; 59 import android.os.VibrationEffect; 60 import android.provider.Settings; 61 import android.util.Log; 62 import android.util.MathUtils; 63 import android.view.LayoutInflater; 64 import android.view.MotionEvent; 65 import android.view.VelocityTracker; 66 import android.view.View; 67 import android.view.ViewGroup; 68 import android.view.ViewPropertyAnimator; 69 import android.view.ViewStub; 70 import android.view.ViewTreeObserver; 71 import android.view.WindowInsets; 72 import android.view.accessibility.AccessibilityManager; 73 import android.view.accessibility.AccessibilityNodeInfo; 74 import android.widget.FrameLayout; 75 76 import androidx.constraintlayout.widget.ConstraintSet; 77 78 import com.android.internal.annotations.VisibleForTesting; 79 import com.android.internal.jank.InteractionJankMonitor; 80 import com.android.internal.logging.MetricsLogger; 81 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 82 import com.android.internal.policy.ScreenDecorationsUtils; 83 import com.android.internal.util.LatencyTracker; 84 import com.android.keyguard.KeyguardStatusView; 85 import com.android.keyguard.KeyguardStatusViewController; 86 import com.android.keyguard.KeyguardUpdateMonitor; 87 import com.android.keyguard.KeyguardUpdateMonitorCallback; 88 import com.android.keyguard.LockIconViewController; 89 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; 90 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; 91 import com.android.keyguard.dagger.KeyguardStatusViewComponent; 92 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; 93 import com.android.systemui.DejankUtils; 94 import com.android.systemui.R; 95 import com.android.systemui.animation.ActivityLaunchAnimator; 96 import com.android.systemui.animation.Interpolators; 97 import com.android.systemui.biometrics.AuthController; 98 import com.android.systemui.classifier.Classifier; 99 import com.android.systemui.classifier.FalsingCollector; 100 import com.android.systemui.controls.dagger.ControlsComponent; 101 import com.android.systemui.dagger.qualifiers.DisplayId; 102 import com.android.systemui.dagger.qualifiers.Main; 103 import com.android.systemui.doze.DozeLog; 104 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 105 import com.android.systemui.fragments.FragmentService; 106 import com.android.systemui.media.KeyguardMediaController; 107 import com.android.systemui.media.MediaDataManager; 108 import com.android.systemui.media.MediaHierarchyManager; 109 import com.android.systemui.navigationbar.NavigationModeController; 110 import com.android.systemui.plugins.FalsingManager; 111 import com.android.systemui.plugins.FalsingManager.FalsingTapListener; 112 import com.android.systemui.plugins.qs.DetailAdapter; 113 import com.android.systemui.plugins.qs.QS; 114 import com.android.systemui.plugins.statusbar.StatusBarStateController; 115 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 116 import com.android.systemui.qs.QSDetailDisplayer; 117 import com.android.systemui.screenrecord.RecordingController; 118 import com.android.systemui.shared.system.QuickStepContract; 119 import com.android.systemui.statusbar.CommandQueue; 120 import com.android.systemui.statusbar.FeatureFlags; 121 import com.android.systemui.statusbar.GestureRecorder; 122 import com.android.systemui.statusbar.KeyguardAffordanceView; 123 import com.android.systemui.statusbar.KeyguardIndicationController; 124 import com.android.systemui.statusbar.LockscreenShadeTransitionController; 125 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 126 import com.android.systemui.statusbar.NotificationRemoteInputManager; 127 import com.android.systemui.statusbar.NotificationShadeDepthController; 128 import com.android.systemui.statusbar.NotificationShelfController; 129 import com.android.systemui.statusbar.PulseExpansionHandler; 130 import com.android.systemui.statusbar.RemoteInputController; 131 import com.android.systemui.statusbar.StatusBarState; 132 import com.android.systemui.statusbar.SysuiStatusBarStateController; 133 import com.android.systemui.statusbar.VibratorHelper; 134 import com.android.systemui.statusbar.events.PrivacyDotViewController; 135 import com.android.systemui.statusbar.notification.AnimatableProperty; 136 import com.android.systemui.statusbar.notification.ConversationNotificationManager; 137 import com.android.systemui.statusbar.notification.DynamicPrivacyController; 138 import com.android.systemui.statusbar.notification.NotificationEntryManager; 139 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 140 import com.android.systemui.statusbar.notification.PropertyAnimator; 141 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; 142 import com.android.systemui.statusbar.notification.collection.ListEntry; 143 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 144 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; 145 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; 146 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 147 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 148 import com.android.systemui.statusbar.notification.row.ExpandableView; 149 import com.android.systemui.statusbar.notification.stack.AmbientState; 150 import com.android.systemui.statusbar.notification.stack.AnimationProperties; 151 import com.android.systemui.statusbar.notification.stack.MediaHeaderView; 152 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 153 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 154 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 155 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; 156 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; 157 import com.android.systemui.statusbar.policy.ConfigurationController; 158 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; 159 import com.android.systemui.statusbar.policy.KeyguardStateController; 160 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; 161 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; 162 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 163 import com.android.systemui.util.Utils; 164 import com.android.systemui.util.settings.SecureSettings; 165 import com.android.systemui.wallet.controller.QuickAccessWalletController; 166 import com.android.wm.shell.animation.FlingAnimationUtils; 167 168 import java.io.FileDescriptor; 169 import java.io.PrintWriter; 170 import java.util.ArrayList; 171 import java.util.Collections; 172 import java.util.List; 173 import java.util.concurrent.Executor; 174 import java.util.function.Consumer; 175 import java.util.function.Function; 176 177 import javax.inject.Inject; 178 import javax.inject.Provider; 179 180 @StatusBarComponent.StatusBarScope 181 public class NotificationPanelViewController extends PanelViewController { 182 183 private static final boolean DEBUG = false; 184 185 /** 186 * The parallax amount of the quick settings translation when dragging down the panel 187 */ 188 private static final float QS_PARALLAX_AMOUNT = 0.175f; 189 190 /** 191 * Fling expanding QS. 192 */ 193 private static final int FLING_EXPAND = 0; 194 195 /** 196 * Fling collapsing QS, potentially stopping when QS becomes QQS. 197 */ 198 private static final int FLING_COLLAPSE = 1; 199 200 /** 201 * Fling until QS is completely hidden. 202 */ 203 private static final int FLING_HIDE = 2; 204 private static final long ANIMATION_DELAY_ICON_FADE_IN = 205 ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION 206 - CollapsedStatusBarFragment.FADE_IN_DELAY - 48; 207 208 private final DozeParameters mDozeParameters; 209 private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener(); 210 private final OnClickListener mOnClickListener = new OnClickListener(); 211 private final OnOverscrollTopChangedListener 212 mOnOverscrollTopChangedListener = 213 new OnOverscrollTopChangedListener(); 214 private final KeyguardAffordanceHelperCallback 215 mKeyguardAffordanceHelperCallback = 216 new KeyguardAffordanceHelperCallback(); 217 private final OnEmptySpaceClickListener 218 mOnEmptySpaceClickListener = 219 new OnEmptySpaceClickListener(); 220 private final MyOnHeadsUpChangedListener 221 mOnHeadsUpChangedListener = 222 new MyOnHeadsUpChangedListener(); 223 private final HeightListener mHeightListener = new HeightListener(); 224 private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); 225 private final SettingsChangeObserver mSettingsChangeObserver; 226 227 @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = 228 new StatusBarStateListener(); 229 private final BiometricUnlockController mBiometricUnlockController; 230 private final NotificationPanelView mView; 231 private final VibratorHelper mVibratorHelper; 232 private final MetricsLogger mMetricsLogger; 233 private final ActivityManager mActivityManager; 234 private final ConfigurationController mConfigurationController; 235 private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder; 236 private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; 237 private final NotificationIconAreaController mNotificationIconAreaController; 238 239 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is 240 // changed. 241 private static final int CAP_HEIGHT = 1456; 242 private static final int FONT_HEIGHT = 2163; 243 244 /** 245 * Maximum time before which we will expand the panel even for slow motions when getting a 246 * touch passed over from launcher. 247 */ 248 private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; 249 250 private static final String COUNTER_PANEL_OPEN = "panel_open"; 251 private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; 252 private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; 253 254 private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1); 255 private static final Rect EMPTY_RECT = new Rect(); 256 257 private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from( 258 "KEYGUARD_HEADS_UP_SHOWING_AMOUNT", 259 (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat), 260 (Function<NotificationPanelView, Float>) notificationPanelView -> 261 getKeyguardHeadsUpShowingAmount(), 262 R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag, 263 R.id.keyguard_hun_animator_start_tag); 264 private static final AnimationProperties 265 KEYGUARD_HUN_PROPERTIES = 266 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 267 @VisibleForTesting 268 final KeyguardUpdateMonitorCallback 269 mKeyguardUpdateCallback = 270 new KeyguardUpdateMonitorCallback() { 271 272 @Override 273 public void onBiometricAuthenticated(int userId, 274 BiometricSourceType biometricSourceType, 275 boolean isStrongBiometric) { 276 if (mFirstBypassAttempt 277 && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) { 278 mDelayShowingKeyguardStatusBar = true; 279 } 280 } 281 282 @Override 283 public void onBiometricRunningStateChanged(boolean running, 284 BiometricSourceType biometricSourceType) { 285 boolean 286 keyguardOrShadeLocked = 287 mBarState == KEYGUARD 288 || mBarState == StatusBarState.SHADE_LOCKED; 289 if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing 290 && !mDelayShowingKeyguardStatusBar 291 && !mBiometricUnlockController.isBiometricUnlock()) { 292 mFirstBypassAttempt = false; 293 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 294 } 295 } 296 297 @Override 298 public void onFinishedGoingToSleep(int why) { 299 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); 300 mDelayShowingKeyguardStatusBar = false; 301 } 302 }; 303 304 private final LayoutInflater mLayoutInflater; 305 private final PowerManager mPowerManager; 306 private final AccessibilityManager mAccessibilityManager; 307 private final NotificationWakeUpCoordinator mWakeUpCoordinator; 308 private final PulseExpansionHandler mPulseExpansionHandler; 309 private final KeyguardBypassController mKeyguardBypassController; 310 private final KeyguardUpdateMonitor mUpdateMonitor; 311 private final ConversationNotificationManager mConversationNotificationManager; 312 private final AuthController mAuthController; 313 private final MediaHierarchyManager mMediaHierarchyManager; 314 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 315 private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; 316 private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; 317 private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; 318 private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; 319 private final QSDetailDisplayer mQSDetailDisplayer; 320 private final FragmentService mFragmentService; 321 private final FeatureFlags mFeatureFlags; 322 private final ScrimController mScrimController; 323 private final PrivacyDotViewController mPrivacyDotViewController; 324 private final QuickAccessWalletController mQuickAccessWalletController; 325 private final ControlsComponent mControlsComponent; 326 private final NotificationRemoteInputManager mRemoteInputManager; 327 328 // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. 329 // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications 330 private final int mMaxKeyguardNotifications; 331 private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; 332 private final TapAgainViewController mTapAgainViewController; 333 private final RecordingController mRecordingController; 334 private boolean mShouldUseSplitNotificationShade; 335 // Current max allowed keyguard notifications determined by measuring the panel 336 private int mMaxAllowedKeyguardNotifications; 337 338 private ViewGroup mPreviewContainer; 339 private KeyguardAffordanceHelper mAffordanceHelper; 340 private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; 341 private KeyguardUserSwitcherController mKeyguardUserSwitcherController; 342 private KeyguardStatusBarView mKeyguardStatusBar; 343 private KeyguardStatusBarViewController mKeyguarStatusBarViewController; 344 private ViewGroup mBigClockContainer; 345 @VisibleForTesting QS mQs; 346 private FrameLayout mQsFrame; 347 private KeyguardStatusViewController mKeyguardStatusViewController; 348 private LockIconViewController mLockIconViewController; 349 private NotificationsQuickSettingsContainer mNotificationContainerParent; 350 private boolean mAnimateNextPositionUpdate; 351 private float mQuickQsOffsetHeight; 352 private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 353 354 private int mTrackingPointer; 355 private VelocityTracker mQsVelocityTracker; 356 private boolean mQsTracking; 357 358 /** 359 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and 360 * the expansion for quick settings. 361 */ 362 private boolean mConflictingQsExpansionGesture; 363 364 private boolean mPanelExpanded; 365 private boolean mQsExpanded; 366 private boolean mQsExpandedWhenExpandingStarted; 367 private boolean mQsFullyExpanded; 368 private boolean mKeyguardShowing; 369 private boolean mKeyguardQsUserSwitchEnabled; 370 private boolean mKeyguardUserSwitcherEnabled; 371 private boolean mDozing; 372 private boolean mDozingOnDown; 373 private int mBarState; 374 private float mInitialHeightOnTouch; 375 private float mInitialTouchX; 376 private float mInitialTouchY; 377 private float mQsExpansionHeight; 378 private int mQsMinExpansionHeight; 379 private int mQsMaxExpansionHeight; 380 private int mQsPeekHeight; 381 private boolean mStackScrollerOverscrolling; 382 private boolean mQsExpansionFromOverscroll; 383 private float mLastOverscroll; 384 private boolean mQsExpansionEnabledPolicy = true; 385 private boolean mQsExpansionEnabledAmbient = true; 386 private ValueAnimator mQsExpansionAnimator; 387 private FlingAnimationUtils mFlingAnimationUtils; 388 private int mStatusBarMinHeight; 389 private int mStatusBarHeaderHeightKeyguard; 390 private int mNotificationsHeaderCollideDistance; 391 private float mOverStretchAmount; 392 private float mDownX; 393 private float mDownY; 394 private int mDisplayTopInset = 0; // in pixels 395 private int mDisplayRightInset = 0; // in pixels 396 private int mSplitShadeNotificationsTopPadding; 397 398 private final KeyguardClockPositionAlgorithm 399 mClockPositionAlgorithm = 400 new KeyguardClockPositionAlgorithm(); 401 private final KeyguardClockPositionAlgorithm.Result 402 mClockPositionResult = 403 new KeyguardClockPositionAlgorithm.Result(); 404 private boolean mIsExpanding; 405 406 private boolean mBlockTouches; 407 // Used for two finger gesture as well as accessibility shortcut to QS. 408 private boolean mQsExpandImmediate; 409 private boolean mTwoFingerQsExpandPossible; 410 private String mHeaderDebugInfo; 411 412 /** 413 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still 414 * need to take this into account in our panel height calculation. 415 */ 416 private boolean mQsAnimatorExpand; 417 private boolean mIsLaunchTransitionFinished; 418 private boolean mIsLaunchTransitionRunning; 419 private Runnable mLaunchAnimationEndRunnable; 420 private boolean mOnlyAffordanceInThisMotion; 421 private ValueAnimator mQsSizeChangeAnimator; 422 423 private boolean mQsScrimEnabled = true; 424 private boolean mQsTouchAboveFalsingThreshold; 425 private int mQsFalsingThreshold; 426 427 private float mKeyguardStatusBarAnimateAlpha = 1f; 428 private HeadsUpTouchHelper mHeadsUpTouchHelper; 429 private boolean mListenForHeadsUp; 430 private int mNavigationBarBottomHeight; 431 private boolean mExpandingFromHeadsUp; 432 private boolean mCollapsedOnDown; 433 private int mPositionMinSideMargin; 434 private int mLastOrientation = -1; 435 private boolean mClosingWithAlphaFadeOut; 436 private boolean mHeadsUpAnimatingAway; 437 private boolean mLaunchingAffordance; 438 private boolean mAffordanceHasPreview; 439 private final FalsingManager mFalsingManager; 440 private final FalsingCollector mFalsingCollector; 441 private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 442 443 private Runnable mHeadsUpExistenceChangedRunnable = () -> { 444 setHeadsUpAnimatingAway(false); 445 notifyBarPanelExpansionChanged(); 446 }; 447 // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager 448 private NotificationGroupManagerLegacy mGroupManager; 449 private boolean mShowIconsWhenExpanded; 450 private int mIndicationBottomPadding; 451 private int mAmbientIndicationBottomPadding; 452 private boolean mIsFullWidth; 453 private boolean mBlockingExpansionForCurrentTouch; 454 455 /** 456 * Following variables maintain state of events when input focus transfer may occur. 457 */ 458 private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event 459 private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event 460 461 /** 462 * Current dark amount that follows regular interpolation curve of animation. 463 */ 464 private float mInterpolatedDarkAmount; 465 466 /** 467 * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the 468 * interpolation curve is different. 469 */ 470 private float mLinearDarkAmount; 471 472 private boolean mPulsing; 473 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); 474 private boolean mUserSetupComplete; 475 private int mQsNotificationTopPadding; 476 private boolean mHideIconsDuringLaunchAnimation = true; 477 private int mStackScrollerMeasuringPass; 478 private ArrayList<Consumer<ExpandableNotificationRow>> 479 mTrackingHeadsUpListeners = 480 new ArrayList<>(); 481 private Runnable mVerticalTranslationListener; 482 private HeadsUpAppearanceController mHeadsUpAppearanceController; 483 484 private int mPanelAlpha; 485 private Runnable mPanelAlphaEndAction; 486 private float mBottomAreaShadeAlpha; 487 private final ValueAnimator mBottomAreaShadeAlphaAnimator; 488 private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", 489 NotificationPanelView::setPanelAlphaInternal, 490 NotificationPanelView::getCurrentPanelAlpha, 491 R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag, 492 R.id.panel_alpha_animator_end_tag); 493 private final AnimationProperties mPanelAlphaOutPropertiesAnimator = 494 new AnimationProperties().setDuration(150).setCustomInterpolator( 495 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT); 496 private final AnimationProperties mPanelAlphaInPropertiesAnimator = 497 new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> { 498 if (mPanelAlphaEndAction != null) { 499 mPanelAlphaEndAction.run(); 500 } 501 }).setCustomInterpolator( 502 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); 503 private final NotificationEntryManager mEntryManager; 504 505 private final CommandQueue mCommandQueue; 506 private final NotificationLockscreenUserManager mLockscreenUserManager; 507 private final UserManager mUserManager; 508 private final MediaDataManager mMediaDataManager; 509 private NotificationShadeDepthController mDepthController; 510 private int mDisplayId; 511 512 /** 513 * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. 514 * 515 * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary 516 * work, check the current id with the cached id. 517 */ 518 private int mThemeResId; 519 private KeyguardIndicationController mKeyguardIndicationController; 520 private int mShelfHeight; 521 private int mDarkIconSize; 522 private int mHeadsUpInset; 523 private boolean mHeadsUpPinnedMode; 524 private float mKeyguardHeadsUpShowingAmount = 0.0f; 525 private boolean mShowingKeyguardHeadsUp; 526 private boolean mAllowExpandForSmallExpansion; 527 private Runnable mExpandAfterLayoutRunnable; 528 529 /** 530 * The padding between the start of notifications and the qs boundary on the lockscreen. 531 * On lockscreen, notifications aren't inset this extra amount, but we still want the 532 * qs boundary to be padded. 533 */ 534 private int mLockscreenNotificationQSPadding; 535 536 /** 537 * The amount of progress we are currently in if we're transitioning to the full shade. 538 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 539 * shade. This value can also go beyond 1.1 when we're overshooting! 540 */ 541 private float mTransitioningToFullShadeProgress; 542 543 /** 544 * Position of the qs bottom during the full shade transition. This is needed as the toppadding 545 * can change during state changes, which makes it much harder to do animations 546 */ 547 private int mTransitionToFullShadeQSPosition; 548 549 /** 550 * Distance that the full shade transition takes in order for qs to fully transition to the 551 * shade. 552 */ 553 private int mDistanceForQSFullShadeTransition; 554 555 /** 556 * The translation amount for QS for the full shade transition 557 */ 558 private float mQsTranslationForFullShadeTransition; 559 560 /** 561 * The maximum overshoot allowed for the top padding for the full shade transition 562 */ 563 private int mMaxOverscrollAmountForPulse; 564 565 /** 566 * Should we animate the next bounds update 567 */ 568 private boolean mAnimateNextNotificationBounds; 569 /** 570 * The delay for the next bounds animation 571 */ 572 private long mNotificationBoundsAnimationDelay; 573 574 /** 575 * The duration of the notification bounds animation 576 */ 577 private long mNotificationBoundsAnimationDuration; 578 579 /** 580 * Is this a collapse that started on the panel where we should allow the panel to intercept 581 */ 582 private boolean mIsPanelCollapseOnQQS; 583 584 /** 585 * If face auth with bypass is running for the first time after you turn on the screen. 586 * (From aod or screen off) 587 */ 588 private boolean mFirstBypassAttempt; 589 /** 590 * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until 591 * the keyguard is dismissed to show the status bar. 592 */ 593 private boolean mDelayShowingKeyguardStatusBar; 594 595 private boolean mAnimatingQS; 596 597 /** 598 * The end bounds of a clipping animation. 599 */ 600 private final Rect mQsClippingAnimationEndBounds = new Rect(); 601 602 /** 603 * The animator for the qs clipping bounds. 604 */ 605 private ValueAnimator mQsClippingAnimation = null; 606 607 /** 608 * Is the current animator resetting the qs translation. 609 */ 610 private boolean mIsQsTranslationResetAnimator; 611 612 /** 613 * Is the current animator resetting the pulse expansion after a drag down 614 */ 615 private boolean mIsPulseExpansionResetAnimator; 616 private final Rect mKeyguardStatusAreaClipBounds = new Rect(); 617 private final Region mQsInterceptRegion = new Region(); 618 619 /** 620 * The alpha of the views which only show on the keyguard but not in shade / shade locked 621 */ 622 private float mKeyguardOnlyContentAlpha = 1.0f; 623 624 /** 625 * Are we currently in gesture navigation 626 */ 627 private boolean mIsGestureNavigation; 628 private int mOldLayoutDirection; 629 private NotificationShelfController mNotificationShelfController; 630 private int mScrimCornerRadius; 631 private int mScreenCornerRadius; 632 private boolean mQSAnimatingHiddenFromCollapsed; 633 634 private int mQsClipTop; 635 private int mQsClipBottom; 636 private boolean mQsVisible; 637 private final ContentResolver mContentResolver; 638 639 private final Executor mUiExecutor; 640 private final SecureSettings mSecureSettings; 641 642 private KeyguardMediaController mKeyguardMediaController; 643 644 private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { 645 @Override 646 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 647 super.onInitializeAccessibilityNodeInfo(host, info); 648 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); 649 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP); 650 } 651 652 @Override 653 public boolean performAccessibilityAction(View host, int action, Bundle args) { 654 if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() 655 || action 656 == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) { 657 mStatusBarKeyguardViewManager.showBouncer(true); 658 return true; 659 } 660 return super.performAccessibilityAction(host, action, args); 661 } 662 }; 663 664 private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { 665 @Override 666 public void onDoubleTapRequired() { 667 if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { 668 mTapAgainViewController.show(); 669 } else { 670 mKeyguardIndicationController.showTransientIndication( 671 R.string.notification_tap_again); 672 } 673 mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); 674 } 675 }; 676 677 @Inject NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, @Main Handler handler, LayoutInflater layoutInflater, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, QSDetailDisplayer qsDetailDisplayer, NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, ScrimController scrimController, UserManager userManager, MediaDataManager mediaDataManager, NotificationShadeDepthController notificationShadeDepthController, AmbientState ambientState, LockIconViewController lockIconViewController, FeatureFlags featureFlags, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, FragmentService fragmentService, ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, RecordingController recordingController, @Main Executor uiExecutor, SecureSettings secureSettings, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, NotificationRemoteInputManager remoteInputManager, ControlsComponent controlsComponent)678 public NotificationPanelViewController(NotificationPanelView view, 679 @Main Resources resources, 680 @Main Handler handler, 681 LayoutInflater layoutInflater, 682 NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, 683 DynamicPrivacyController dynamicPrivacyController, 684 KeyguardBypassController bypassController, FalsingManager falsingManager, 685 FalsingCollector falsingCollector, 686 NotificationLockscreenUserManager notificationLockscreenUserManager, 687 NotificationEntryManager notificationEntryManager, 688 KeyguardStateController keyguardStateController, 689 StatusBarStateController statusBarStateController, DozeLog dozeLog, 690 DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, 691 LatencyTracker latencyTracker, PowerManager powerManager, 692 AccessibilityManager accessibilityManager, @DisplayId int displayId, 693 KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, 694 ActivityManager activityManager, 695 ConfigurationController configurationController, 696 Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, 697 StatusBarTouchableRegionManager statusBarTouchableRegionManager, 698 ConversationNotificationManager conversationNotificationManager, 699 MediaHierarchyManager mediaHierarchyManager, 700 BiometricUnlockController biometricUnlockController, 701 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 702 NotificationStackScrollLayoutController notificationStackScrollLayoutController, 703 KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, 704 KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, 705 KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, 706 KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, 707 LockscreenShadeTransitionController lockscreenShadeTransitionController, 708 QSDetailDisplayer qsDetailDisplayer, 709 NotificationGroupManagerLegacy groupManager, 710 NotificationIconAreaController notificationIconAreaController, 711 AuthController authController, 712 ScrimController scrimController, 713 UserManager userManager, 714 MediaDataManager mediaDataManager, 715 NotificationShadeDepthController notificationShadeDepthController, 716 AmbientState ambientState, 717 LockIconViewController lockIconViewController, 718 FeatureFlags featureFlags, 719 KeyguardMediaController keyguardMediaController, 720 PrivacyDotViewController privacyDotViewController, 721 TapAgainViewController tapAgainViewController, 722 NavigationModeController navigationModeController, 723 FragmentService fragmentService, 724 ContentResolver contentResolver, 725 QuickAccessWalletController quickAccessWalletController, 726 RecordingController recordingController, 727 @Main Executor uiExecutor, 728 SecureSettings secureSettings, 729 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, 730 NotificationRemoteInputManager remoteInputManager, 731 ControlsComponent controlsComponent) { 732 super(view, falsingManager, dozeLog, keyguardStateController, 733 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, 734 statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(), 735 statusBarTouchableRegionManager, ambientState); 736 mView = view; 737 mVibratorHelper = vibratorHelper; 738 mKeyguardMediaController = keyguardMediaController; 739 mPrivacyDotViewController = privacyDotViewController; 740 mQuickAccessWalletController = quickAccessWalletController; 741 mControlsComponent = controlsComponent; 742 mMetricsLogger = metricsLogger; 743 mActivityManager = activityManager; 744 mConfigurationController = configurationController; 745 mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; 746 mMediaHierarchyManager = mediaHierarchyManager; 747 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 748 mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; 749 mGroupManager = groupManager; 750 mNotificationIconAreaController = notificationIconAreaController; 751 mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; 752 mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; 753 mDepthController = notificationShadeDepthController; 754 mFeatureFlags = featureFlags; 755 mContentResolver = contentResolver; 756 mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; 757 mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; 758 mQSDetailDisplayer = qsDetailDisplayer; 759 mFragmentService = fragmentService; 760 mSettingsChangeObserver = new SettingsChangeObserver(handler); 761 mShouldUseSplitNotificationShade = 762 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); 763 mView.setWillNotDraw(!DEBUG); 764 mLayoutInflater = layoutInflater; 765 mFalsingManager = falsingManager; 766 mFalsingCollector = falsingCollector; 767 mPowerManager = powerManager; 768 mWakeUpCoordinator = coordinator; 769 mAccessibilityManager = accessibilityManager; 770 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 771 setPanelAlpha(255, false /* animate */); 772 mCommandQueue = commandQueue; 773 mRecordingController = recordingController; 774 mDisplayId = displayId; 775 mPulseExpansionHandler = pulseExpansionHandler; 776 mDozeParameters = dozeParameters; 777 mBiometricUnlockController = biometricUnlockController; 778 mScrimController = scrimController; 779 mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); 780 mUserManager = userManager; 781 mMediaDataManager = mediaDataManager; 782 mTapAgainViewController = tapAgainViewController; 783 mUiExecutor = uiExecutor; 784 mSecureSettings = secureSettings; 785 pulseExpansionHandler.setPulseExpandAbortListener(() -> { 786 if (mQs != null) { 787 mQs.animateHeaderSlidingOut(); 788 } 789 }); 790 mThemeResId = mView.getContext().getThemeResId(); 791 mKeyguardBypassController = bypassController; 792 mUpdateMonitor = keyguardUpdateMonitor; 793 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); 794 KeyguardStateController.Callback 795 keyguardMonitorCallback = 796 new KeyguardStateController.Callback() { 797 @Override 798 public void onKeyguardFadingAwayChanged() { 799 if (!mKeyguardStateController.isKeyguardFadingAway()) { 800 mFirstBypassAttempt = false; 801 mDelayShowingKeyguardStatusBar = false; 802 } 803 } 804 }; 805 mLockscreenShadeTransitionController = lockscreenShadeTransitionController; 806 lockscreenShadeTransitionController.setNotificationPanelController(this); 807 mKeyguardStateController.addCallback(keyguardMonitorCallback); 808 DynamicPrivacyControlListener 809 dynamicPrivacyControlListener = 810 new DynamicPrivacyControlListener(); 811 dynamicPrivacyController.addListener(dynamicPrivacyControlListener); 812 813 mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); 814 mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> { 815 mBottomAreaShadeAlpha = (float) animation.getAnimatedValue(); 816 updateKeyguardBottomAreaAlpha(); 817 }); 818 mBottomAreaShadeAlphaAnimator.setDuration(160); 819 mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); 820 mLockscreenUserManager = notificationLockscreenUserManager; 821 mEntryManager = notificationEntryManager; 822 mConversationNotificationManager = conversationNotificationManager; 823 mAuthController = authController; 824 mLockIconViewController = lockIconViewController; 825 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 826 mRemoteInputManager = remoteInputManager; 827 828 int currentMode = navigationModeController.addListener( 829 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode)); 830 mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode); 831 832 mView.setBackgroundColor(Color.TRANSPARENT); 833 OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); 834 mView.addOnAttachStateChangeListener(onAttachStateChangeListener); 835 if (mView.isAttachedToWindow()) { 836 onAttachStateChangeListener.onViewAttachedToWindow(mView); 837 } 838 839 mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener()); 840 841 if (DEBUG) { 842 mView.getOverlay().add(new DebugDrawable()); 843 } 844 845 mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); 846 updateUserSwitcherFlags(); 847 onFinishInflate(); 848 } 849 onFinishInflate()850 private void onFinishInflate() { 851 loadDimens(); 852 mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); 853 mBigClockContainer = mView.findViewById(R.id.big_clock_container); 854 855 UserAvatarView userAvatarView = null; 856 KeyguardUserSwitcherView keyguardUserSwitcherView = null; 857 858 if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) { 859 if (mKeyguardQsUserSwitchEnabled) { 860 ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub); 861 userAvatarView = (UserAvatarView) stub.inflate(); 862 } else { 863 ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub); 864 keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate(); 865 } 866 } 867 868 updateViewControllers( 869 mView.findViewById(R.id.keyguard_status_view), 870 userAvatarView, 871 mKeyguardStatusBar, 872 keyguardUserSwitcherView); 873 mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); 874 NotificationStackScrollLayout stackScrollLayout = mView.findViewById( 875 R.id.notification_stack_scroller); 876 mNotificationStackScrollLayoutController.attach(stackScrollLayout); 877 mNotificationStackScrollLayoutController.setOnHeightChangedListener( 878 mOnHeightChangedListener); 879 mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( 880 mOnOverscrollTopChangedListener); 881 mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); 882 mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); 883 mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( 884 mOnEmptySpaceClickListener); 885 addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); 886 mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); 887 mPreviewContainer = mView.findViewById(R.id.preview_container); 888 mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); 889 mLastOrientation = mResources.getConfiguration().orientation; 890 891 initBottomArea(); 892 893 mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); 894 mQsFrame = mView.findViewById(R.id.qs_frame); 895 mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController); 896 mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { 897 @Override 898 public void onFullyHiddenChanged(boolean isFullyHidden) { 899 updateKeyguardStatusBarForHeadsUp(); 900 } 901 902 @Override 903 public void onPulseExpansionChanged(boolean expandingChanged) { 904 if (mKeyguardBypassController.getBypassEnabled()) { 905 // Position the notifications while dragging down while pulsing 906 requestScrollerTopPaddingUpdate(false /* animate */); 907 } 908 } 909 }); 910 911 mView.setRtlChangeListener(layoutDirection -> { 912 if (layoutDirection != mOldLayoutDirection) { 913 mAffordanceHelper.onRtlPropertiesChanged(); 914 mOldLayoutDirection = layoutDirection; 915 } 916 }); 917 918 mView.setAccessibilityDelegate(mAccessibilityDelegate); 919 if (mShouldUseSplitNotificationShade) { 920 updateResources(); 921 } 922 923 mTapAgainViewController.init(); 924 } 925 926 @Override loadDimens()927 protected void loadDimens() { 928 super.loadDimens(); 929 mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get() 930 .setMaxLengthSeconds(0.4f).build(); 931 mStatusBarMinHeight = mResources.getDimensionPixelSize( 932 com.android.internal.R.dimen.status_bar_height); 933 mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize( 934 R.dimen.status_bar_header_height_keyguard); 935 mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); 936 mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize( 937 R.dimen.header_notifications_collide_distance); 938 mClockPositionAlgorithm.loadDimens(mResources); 939 mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold); 940 mPositionMinSideMargin = mResources.getDimensionPixelSize( 941 R.dimen.notification_panel_min_side_margin); 942 mIndicationBottomPadding = mResources.getDimensionPixelSize( 943 R.dimen.keyguard_indication_bottom_padding); 944 mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height); 945 mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); 946 int statusbarHeight = mResources.getDimensionPixelSize( 947 com.android.internal.R.dimen.status_bar_height); 948 mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( 949 R.dimen.heads_up_status_bar_padding); 950 mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize( 951 R.dimen.lockscreen_shade_qs_transition_distance); 952 mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize( 953 R.dimen.pulse_expansion_max_top_overshoot); 954 mScrimCornerRadius = mResources.getDimensionPixelSize( 955 R.dimen.notification_scrim_corner_radius); 956 mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mResources); 957 mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( 958 R.dimen.notification_side_paddings); 959 } 960 updateViewControllers(KeyguardStatusView keyguardStatusView, UserAvatarView userAvatarView, KeyguardStatusBarView keyguardStatusBarView, KeyguardUserSwitcherView keyguardUserSwitcherView)961 private void updateViewControllers(KeyguardStatusView keyguardStatusView, 962 UserAvatarView userAvatarView, 963 KeyguardStatusBarView keyguardStatusBarView, 964 KeyguardUserSwitcherView keyguardUserSwitcherView) { 965 // Re-associate the KeyguardStatusViewController 966 KeyguardStatusViewComponent statusViewComponent = 967 mKeyguardStatusViewComponentFactory.build(keyguardStatusView); 968 mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); 969 mKeyguardStatusViewController.init(); 970 971 KeyguardStatusBarViewComponent statusBarViewComponent = 972 mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView); 973 mKeyguarStatusBarViewController = 974 statusBarViewComponent.getKeyguardStatusBarViewController(); 975 mKeyguarStatusBarViewController.init(); 976 977 if (mKeyguardUserSwitcherController != null) { 978 // Try to close the switcher so that callbacks are triggered if necessary. 979 // Otherwise, NPV can get into a state where some of the views are still hidden 980 mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false); 981 } 982 983 mKeyguardQsUserSwitchController = null; 984 mKeyguardUserSwitcherController = null; 985 986 // Re-associate the KeyguardUserSwitcherController 987 if (userAvatarView != null) { 988 KeyguardQsUserSwitchComponent userSwitcherComponent = 989 mKeyguardQsUserSwitchComponentFactory.build(userAvatarView); 990 mKeyguardQsUserSwitchController = 991 userSwitcherComponent.getKeyguardQsUserSwitchController(); 992 mKeyguardQsUserSwitchController.setNotificationPanelViewController(this); 993 mKeyguardQsUserSwitchController.init(); 994 mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); 995 } else if (keyguardUserSwitcherView != null) { 996 KeyguardUserSwitcherComponent userSwitcherComponent = 997 mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView); 998 mKeyguardUserSwitcherController = 999 userSwitcherComponent.getKeyguardUserSwitcherController(); 1000 mKeyguardUserSwitcherController.init(); 1001 mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); 1002 } else { 1003 mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false); 1004 } 1005 } 1006 1007 /** 1008 * Returns if there's a custom clock being presented. 1009 */ hasCustomClock()1010 public boolean hasCustomClock() { 1011 return mKeyguardStatusViewController.hasCustomClock(); 1012 } 1013 setStatusBar(StatusBar bar)1014 private void setStatusBar(StatusBar bar) { 1015 // TODO: this can be injected. 1016 mStatusBar = bar; 1017 mKeyguardBottomArea.setStatusBar(mStatusBar); 1018 } 1019 updateResources()1020 public void updateResources() { 1021 mQuickQsOffsetHeight = mResources.getDimensionPixelSize( 1022 com.android.internal.R.dimen.quick_qs_offset_height); 1023 mSplitShadeNotificationsTopPadding = 1024 mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade); 1025 int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); 1026 int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); 1027 mShouldUseSplitNotificationShade = 1028 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); 1029 mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); 1030 if (mQs != null) { 1031 mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); 1032 } 1033 // To change the constraints at runtime, all children of the ConstraintLayout must have ids 1034 ensureAllViewsHaveIds(mNotificationContainerParent); 1035 ConstraintSet constraintSet = new ConstraintSet(); 1036 constraintSet.clone(mNotificationContainerParent); 1037 if (mShouldUseSplitNotificationShade) { 1038 // width = 0 to take up all available space within constraints 1039 qsWidth = 0; 1040 panelWidth = 0; 1041 constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END); 1042 constraintSet.connect( 1043 R.id.notification_stack_scroller, START, 1044 R.id.qs_edge_guideline, START); 1045 constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END); 1046 } else { 1047 constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END); 1048 constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START); 1049 constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END); 1050 } 1051 constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; 1052 constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; 1053 constraintSet.applyTo(mNotificationContainerParent); 1054 1055 mKeyguardMediaController.refreshMediaPosition(); 1056 } 1057 ensureAllViewsHaveIds(ViewGroup parentView)1058 private static void ensureAllViewsHaveIds(ViewGroup parentView) { 1059 for (int i = 0; i < parentView.getChildCount(); i++) { 1060 View childView = parentView.getChildAt(i); 1061 if (childView.getId() == View.NO_ID) { 1062 childView.setId(View.generateViewId()); 1063 } 1064 } 1065 } 1066 reInflateStub(int viewId, int stubId, int layoutId, boolean enabled)1067 private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) { 1068 View view = mView.findViewById(viewId); 1069 if (view != null) { 1070 int index = mView.indexOfChild(view); 1071 mView.removeView(view); 1072 if (enabled) { 1073 view = mLayoutInflater.inflate(layoutId, mView, false); 1074 mView.addView(view, index); 1075 } else { 1076 // Add the stub back so we can re-inflate it again if necessary 1077 ViewStub stub = new ViewStub(mView.getContext(), layoutId); 1078 stub.setId(stubId); 1079 mView.addView(stub, index); 1080 view = null; 1081 } 1082 } else if (enabled) { 1083 // It's possible the stub was never inflated if the configuration changed 1084 ViewStub stub = mView.findViewById(stubId); 1085 view = stub.inflate(); 1086 } 1087 return view; 1088 } 1089 reInflateViews()1090 private void reInflateViews() { 1091 if (DEBUG) Log.d(TAG, "reInflateViews"); 1092 // Re-inflate the status view group. 1093 KeyguardStatusView keyguardStatusView = 1094 mNotificationContainerParent.findViewById(R.id.keyguard_status_view); 1095 int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView); 1096 mNotificationContainerParent.removeView(keyguardStatusView); 1097 keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( 1098 R.layout.keyguard_status_view, mNotificationContainerParent, false); 1099 mNotificationContainerParent.addView(keyguardStatusView, statusIndex); 1100 attachSplitShadeMediaPlayerContainer( 1101 keyguardStatusView.findViewById(R.id.status_view_media_container)); 1102 1103 // we need to update KeyguardStatusView constraints after reinflating it 1104 updateResources(); 1105 1106 // Re-inflate the keyguard user switcher group. 1107 updateUserSwitcherFlags(); 1108 boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(); 1109 boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled; 1110 boolean showKeyguardUserSwitcher = 1111 !mKeyguardQsUserSwitchEnabled 1112 && mKeyguardUserSwitcherEnabled 1113 && isUserSwitcherEnabled; 1114 UserAvatarView userAvatarView = (UserAvatarView) reInflateStub( 1115 R.id.keyguard_qs_user_switch_view /* viewId */, 1116 R.id.keyguard_qs_user_switch_stub /* stubId */, 1117 R.layout.keyguard_qs_user_switch /* layoutId */, 1118 showQsUserSwitch /* enabled */); 1119 KeyguardUserSwitcherView keyguardUserSwitcherView = 1120 (KeyguardUserSwitcherView) reInflateStub( 1121 R.id.keyguard_user_switcher_view /* viewId */, 1122 R.id.keyguard_user_switcher_stub /* stubId */, 1123 R.layout.keyguard_user_switcher /* layoutId */, 1124 showKeyguardUserSwitcher /* enabled */); 1125 1126 mBigClockContainer.removeAllViews(); 1127 updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, 1128 mKeyguardStatusBar, keyguardUserSwitcherView); 1129 1130 // Update keyguard bottom area 1131 int index = mView.indexOfChild(mKeyguardBottomArea); 1132 mView.removeView(mKeyguardBottomArea); 1133 KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; 1134 mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate( 1135 R.layout.keyguard_bottom_area, mView, false); 1136 mKeyguardBottomArea.initFrom(oldBottomArea); 1137 mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); 1138 mView.addView(mKeyguardBottomArea, index); 1139 initBottomArea(); 1140 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 1141 mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), 1142 mStatusBarStateController.getInterpolatedDozeAmount()); 1143 1144 if (mKeyguardStatusBar != null) { 1145 mKeyguardStatusBar.onThemeChanged(); 1146 } 1147 1148 mKeyguardStatusViewController.setKeyguardStatusViewVisibility( 1149 mBarState, 1150 false, 1151 false, 1152 mBarState); 1153 if (mKeyguardQsUserSwitchController != null) { 1154 mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility( 1155 mBarState, 1156 false, 1157 false, 1158 mBarState); 1159 } 1160 if (mKeyguardUserSwitcherController != null) { 1161 mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility( 1162 mBarState, 1163 false, 1164 false, 1165 mBarState); 1166 } 1167 setKeyguardBottomAreaVisibility(mBarState, false); 1168 } 1169 attachSplitShadeMediaPlayerContainer(FrameLayout container)1170 private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { 1171 mKeyguardMediaController.attachSplitShadeContainer(container); 1172 } 1173 initBottomArea()1174 private void initBottomArea() { 1175 mAffordanceHelper = new KeyguardAffordanceHelper( 1176 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager); 1177 mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); 1178 mKeyguardBottomArea.setStatusBar(mStatusBar); 1179 mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); 1180 mKeyguardBottomArea.setFalsingManager(mFalsingManager); 1181 mKeyguardBottomArea.initWallet(mQuickAccessWalletController); 1182 mKeyguardBottomArea.initControls(mControlsComponent); 1183 } 1184 updateMaxDisplayedNotifications(boolean recompute)1185 private void updateMaxDisplayedNotifications(boolean recompute) { 1186 if (recompute) { 1187 mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1); 1188 } 1189 1190 if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { 1191 mNotificationStackScrollLayoutController.setMaxDisplayedNotifications( 1192 mMaxAllowedKeyguardNotifications); 1193 } else { 1194 // no max when not on the keyguard 1195 mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); 1196 } 1197 } 1198 setKeyguardIndicationController(KeyguardIndicationController indicationController)1199 public void setKeyguardIndicationController(KeyguardIndicationController indicationController) { 1200 mKeyguardIndicationController = indicationController; 1201 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 1202 } 1203 updateGestureExclusionRect()1204 private void updateGestureExclusionRect() { 1205 Rect exclusionRect = calculateGestureExclusionRect(); 1206 mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST 1207 : Collections.singletonList(exclusionRect)); 1208 } 1209 calculateGestureExclusionRect()1210 private Rect calculateGestureExclusionRect() { 1211 Rect exclusionRect = null; 1212 Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion(); 1213 if (isFullyCollapsed() && touchableRegion != null) { 1214 // Note: The manager also calculates the non-pinned touchable region 1215 exclusionRect = touchableRegion.getBounds(); 1216 } 1217 return exclusionRect != null ? exclusionRect : EMPTY_RECT; 1218 } 1219 setIsFullWidth(boolean isFullWidth)1220 private void setIsFullWidth(boolean isFullWidth) { 1221 mIsFullWidth = isFullWidth; 1222 mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth); 1223 } 1224 startQsSizeChangeAnimation(int oldHeight, final int newHeight)1225 private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { 1226 if (mQsSizeChangeAnimator != null) { 1227 oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 1228 mQsSizeChangeAnimator.cancel(); 1229 } 1230 mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); 1231 mQsSizeChangeAnimator.setDuration(300); 1232 mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 1233 mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 1234 @Override 1235 public void onAnimationUpdate(ValueAnimator animation) { 1236 requestScrollerTopPaddingUpdate(false /* animate */); 1237 requestPanelHeightUpdate(); 1238 int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); 1239 mQs.setHeightOverride(height); 1240 } 1241 }); 1242 mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { 1243 @Override 1244 public void onAnimationEnd(Animator animation) { 1245 mQsSizeChangeAnimator = null; 1246 } 1247 }); 1248 mQsSizeChangeAnimator.start(); 1249 } 1250 1251 /** 1252 * Positions the clock and notifications dynamically depending on how many notifications are 1253 * showing. 1254 */ positionClockAndNotifications()1255 private void positionClockAndNotifications() { 1256 positionClockAndNotifications(false /* forceUpdate */); 1257 } 1258 1259 /** 1260 * Positions the clock and notifications dynamically depending on how many notifications are 1261 * showing. 1262 * 1263 * @param forceClockUpdate Should the clock be updated even when not on keyguard 1264 */ positionClockAndNotifications(boolean forceClockUpdate)1265 private void positionClockAndNotifications(boolean forceClockUpdate) { 1266 boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); 1267 int stackScrollerPadding; 1268 boolean onKeyguard = isOnKeyguard(); 1269 if (onKeyguard || forceClockUpdate) { 1270 updateClockAppearance(); 1271 } 1272 if (!onKeyguard) { 1273 stackScrollerPadding = getUnlockedStackScrollerPadding(); 1274 } else { 1275 stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; 1276 } 1277 1278 mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); 1279 mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); 1280 1281 mStackScrollerMeasuringPass++; 1282 requestScrollerTopPaddingUpdate(animate); 1283 mStackScrollerMeasuringPass = 0; 1284 mAnimateNextPositionUpdate = false; 1285 } 1286 updateClockAppearance()1287 private void updateClockAppearance() { 1288 int totalHeight = mView.getHeight(); 1289 int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); 1290 int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; 1291 boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); 1292 final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController 1293 .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); 1294 mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); 1295 int userIconHeight = mKeyguardQsUserSwitchController != null 1296 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; 1297 float expandedFraction = 1298 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() 1299 ? 1.0f : getExpandedFraction(); 1300 float darkamount = 1301 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() 1302 ? 1.0f : mInterpolatedDarkAmount; 1303 mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, 1304 totalHeight - bottomPadding, 1305 mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), 1306 expandedFraction, 1307 totalHeight, 1308 mKeyguardStatusViewController.getLockscreenHeight(), 1309 userIconHeight, 1310 userSwitcherPreferredY, hasCustomClock(), 1311 hasVisibleNotifications, darkamount, mOverStretchAmount, 1312 bypassEnabled, getUnlockedStackScrollerPadding(), 1313 computeQsExpansionFraction(), 1314 mDisplayTopInset, 1315 mShouldUseSplitNotificationShade); 1316 mClockPositionAlgorithm.run(mClockPositionResult); 1317 boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); 1318 boolean animateClock = animate || mAnimateNextPositionUpdate; 1319 mKeyguardStatusViewController.updatePosition( 1320 mClockPositionResult.clockX, mClockPositionResult.clockY, 1321 mClockPositionResult.clockScale, animateClock); 1322 if (mKeyguardQsUserSwitchController != null) { 1323 mKeyguardQsUserSwitchController.updatePosition( 1324 mClockPositionResult.clockX, 1325 mClockPositionResult.userSwitchY, 1326 animateClock); 1327 } 1328 if (mKeyguardUserSwitcherController != null) { 1329 mKeyguardUserSwitcherController.updatePosition( 1330 mClockPositionResult.clockX, 1331 mClockPositionResult.userSwitchY, 1332 animateClock); 1333 } 1334 updateNotificationTranslucency(); 1335 updateClock(); 1336 } 1337 1338 /** 1339 * @return the padding of the stackscroller when unlocked 1340 */ getUnlockedStackScrollerPadding()1341 private int getUnlockedStackScrollerPadding() { 1342 return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight; 1343 } 1344 1345 /** 1346 * @return the maximum keyguard notifications that can fit on the screen 1347 */ computeMaxKeyguardNotifications()1348 private int computeMaxKeyguardNotifications() { 1349 float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); 1350 int notificationPadding = Math.max( 1351 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); 1352 float shelfSize = 1353 mNotificationShelfController.getVisibility() == View.GONE 1354 ? 0 1355 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; 1356 1357 float lockIconPadding = 0; 1358 if (mLockIconViewController.getTop() != 0) { 1359 lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop() 1360 + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); 1361 } 1362 1363 float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); 1364 bottomPadding = Math.max(lockIconPadding, bottomPadding); 1365 1366 float availableSpace = 1367 mNotificationStackScrollLayoutController.getHeight() 1368 - minPadding 1369 - shelfSize 1370 - bottomPadding; 1371 1372 int count = 0; 1373 ExpandableView previousView = null; 1374 for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { 1375 ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); 1376 if (child instanceof ExpandableNotificationRow) { 1377 ExpandableNotificationRow row = (ExpandableNotificationRow) child; 1378 boolean suppressedSummary = mGroupManager != null 1379 && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn()); 1380 if (suppressedSummary) { 1381 continue; 1382 } 1383 if (!canShowViewOnLockscreen(child)) { 1384 continue; 1385 } 1386 if (row.isRemoved()) { 1387 continue; 1388 } 1389 } else if (child instanceof MediaHeaderView) { 1390 if (child.getVisibility() == GONE) { 1391 continue; 1392 } 1393 if (child.getIntrinsicHeight() == 0) { 1394 continue; 1395 } 1396 } else { 1397 continue; 1398 } 1399 availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); 1400 availableSpace -= count == 0 ? 0 : notificationPadding; 1401 availableSpace -= mNotificationStackScrollLayoutController 1402 .calculateGapHeight(previousView, child, count); 1403 previousView = child; 1404 if (availableSpace >= 0 1405 && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) { 1406 count++; 1407 } else if (availableSpace > -shelfSize) { 1408 // if we are exactly the last view, then we can show us still! 1409 int childCount = mNotificationStackScrollLayoutController.getChildCount(); 1410 for (int j = i + 1; j < childCount; j++) { 1411 ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); 1412 if (view instanceof ExpandableNotificationRow 1413 && canShowViewOnLockscreen(view)) { 1414 return count; 1415 } 1416 } 1417 count++; 1418 return count; 1419 } else { 1420 return count; 1421 } 1422 } 1423 return count; 1424 } 1425 1426 /** 1427 * Can a view be shown on the lockscreen when calculating the number of allowed notifications 1428 * to show? 1429 * 1430 * @param child the view in question 1431 * @return true if it can be shown 1432 */ canShowViewOnLockscreen(ExpandableView child)1433 private boolean canShowViewOnLockscreen(ExpandableView child) { 1434 if (child.hasNoContentHeight()) { 1435 return false; 1436 } 1437 if (child instanceof ExpandableNotificationRow && 1438 !canShowRowOnLockscreen((ExpandableNotificationRow) child)) { 1439 return false; 1440 } else if (child.getVisibility() == GONE) { 1441 // ENRs can be gone and count because their visibility is only set after 1442 // this calculation, but all other views should be up to date 1443 return false; 1444 } 1445 return true; 1446 } 1447 1448 /** 1449 * Can a row be shown on the lockscreen when calculating the number of allowed notifications 1450 * to show? 1451 * 1452 * @param row the row in question 1453 * @return true if it can be shown 1454 */ canShowRowOnLockscreen(ExpandableNotificationRow row)1455 private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) { 1456 boolean suppressedSummary = 1457 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( 1458 row.getEntry().getSbn()); 1459 if (suppressedSummary) { 1460 return false; 1461 } 1462 if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { 1463 return false; 1464 } 1465 if (row.isRemoved()) { 1466 return false; 1467 } 1468 return true; 1469 } 1470 updateClock()1471 private void updateClock() { 1472 float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha; 1473 mKeyguardStatusViewController.setAlpha(alpha); 1474 if (mKeyguardQsUserSwitchController != null) { 1475 mKeyguardQsUserSwitchController.setAlpha(alpha); 1476 } 1477 if (mKeyguardUserSwitcherController != null) { 1478 mKeyguardUserSwitcherController.setAlpha(alpha); 1479 } 1480 } 1481 animateToFullShade(long delay)1482 public void animateToFullShade(long delay) { 1483 mNotificationStackScrollLayoutController.goToFullShade(delay); 1484 mView.requestLayout(); 1485 mAnimateNextPositionUpdate = true; 1486 } 1487 setQsExpansionEnabled()1488 private void setQsExpansionEnabled() { 1489 if (mQs == null) return; 1490 mQs.setHeaderClickable(isQsExpansionEnabled()); 1491 } 1492 setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy)1493 public void setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy) { 1494 mQsExpansionEnabledPolicy = qsExpansionEnabledPolicy; 1495 setQsExpansionEnabled(); 1496 } 1497 1498 @Override resetViews(boolean animate)1499 public void resetViews(boolean animate) { 1500 mIsLaunchTransitionFinished = false; 1501 mBlockTouches = false; 1502 if (!mLaunchingAffordance) { 1503 mAffordanceHelper.reset(false); 1504 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 1505 } 1506 mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, 1507 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); 1508 if (animate && !isFullyCollapsed()) { 1509 animateCloseQs(true /* animateAway */); 1510 } else { 1511 closeQs(); 1512 } 1513 mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate, 1514 !animate /* cancelAnimators */); 1515 mNotificationStackScrollLayoutController.resetScrollPosition(); 1516 } 1517 1518 @Override collapse(boolean delayed, float speedUpFactor)1519 public void collapse(boolean delayed, float speedUpFactor) { 1520 if (!canPanelBeCollapsed()) { 1521 return; 1522 } 1523 1524 if (mQsExpanded) { 1525 mQsExpandImmediate = true; 1526 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1527 } 1528 super.collapse(delayed, speedUpFactor); 1529 } 1530 closeQs()1531 public void closeQs() { 1532 cancelQsAnimation(); 1533 setQsExpansion(mQsMinExpansionHeight); 1534 } 1535 cancelAnimation()1536 public void cancelAnimation() { 1537 mView.animate().cancel(); 1538 } 1539 1540 1541 /** 1542 * Animate QS closing by flinging it. 1543 * If QS is expanded, it will collapse into QQS and stop. 1544 * 1545 * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. 1546 */ animateCloseQs(boolean animateAway)1547 public void animateCloseQs(boolean animateAway) { 1548 if (mQsExpansionAnimator != null) { 1549 if (!mQsAnimatorExpand) { 1550 return; 1551 } 1552 float height = mQsExpansionHeight; 1553 mQsExpansionAnimator.cancel(); 1554 setQsExpansion(height); 1555 } 1556 flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); 1557 } 1558 isQsExpansionEnabled()1559 private boolean isQsExpansionEnabled() { 1560 return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient 1561 && !mRemoteInputManager.getController().isRemoteInputActive(); 1562 } 1563 expandWithQs()1564 public void expandWithQs() { 1565 if (isQsExpansionEnabled()) { 1566 mQsExpandImmediate = true; 1567 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1568 } 1569 if (isFullyCollapsed()) { 1570 expand(true /* animate */); 1571 } else { 1572 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1573 flingSettings(0 /* velocity */, FLING_EXPAND); 1574 } 1575 } 1576 expandWithQsDetail(DetailAdapter qsDetailAdapter)1577 public void expandWithQsDetail(DetailAdapter qsDetailAdapter) { 1578 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1579 flingSettings(0 /* velocity */, FLING_EXPAND); 1580 // When expanding with a panel, there's no meaningful touch point to correspond to. Set the 1581 // origin to somewhere above the screen. This is used for animations. 1582 int x = mQsFrame.getWidth() / 2; 1583 mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight()); 1584 if (mAccessibilityManager.isEnabled()) { 1585 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 1586 } 1587 } 1588 expandWithoutQs()1589 public void expandWithoutQs() { 1590 if (isQsExpanded()) { 1591 flingSettings(0 /* velocity */, FLING_COLLAPSE); 1592 } else { 1593 expand(true /* animate */); 1594 } 1595 } 1596 1597 @Override fling(float vel, boolean expand)1598 public void fling(float vel, boolean expand) { 1599 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); 1600 if (gr != null) { 1601 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); 1602 } 1603 super.fling(vel, expand); 1604 } 1605 1606 @Override flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1607 protected void flingToHeight(float vel, boolean expand, float target, 1608 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { 1609 mHeadsUpTouchHelper.notifyFling(!expand); 1610 mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */); 1611 setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); 1612 super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); 1613 } 1614 onQsIntercept(MotionEvent event)1615 private boolean onQsIntercept(MotionEvent event) { 1616 int pointerIndex = event.findPointerIndex(mTrackingPointer); 1617 if (pointerIndex < 0) { 1618 pointerIndex = 0; 1619 mTrackingPointer = event.getPointerId(pointerIndex); 1620 } 1621 final float x = event.getX(pointerIndex); 1622 final float y = event.getY(pointerIndex); 1623 1624 switch (event.getActionMasked()) { 1625 case MotionEvent.ACTION_DOWN: 1626 mInitialTouchY = y; 1627 mInitialTouchX = x; 1628 initVelocityTracker(); 1629 trackMovement(event); 1630 if (mKeyguardShowing 1631 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { 1632 // Dragging down on the lockscreen statusbar should prohibit other interactions 1633 // immediately, otherwise we'll wait on the touchslop. This is to allow 1634 // dragging down to expanded quick settings directly on the lockscreen. 1635 mView.getParent().requestDisallowInterceptTouchEvent(true); 1636 } 1637 if (mQsExpansionAnimator != null) { 1638 mInitialHeightOnTouch = mQsExpansionHeight; 1639 mQsTracking = true; 1640 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1641 mNotificationStackScrollLayoutController.cancelLongPress(); 1642 } 1643 break; 1644 case MotionEvent.ACTION_POINTER_UP: 1645 final int upPointer = event.getPointerId(event.getActionIndex()); 1646 if (mTrackingPointer == upPointer) { 1647 // gesture is ongoing, find a new pointer to track 1648 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 1649 mTrackingPointer = event.getPointerId(newIndex); 1650 mInitialTouchX = event.getX(newIndex); 1651 mInitialTouchY = event.getY(newIndex); 1652 } 1653 break; 1654 1655 case MotionEvent.ACTION_MOVE: 1656 final float h = y - mInitialTouchY; 1657 trackMovement(event); 1658 if (mQsTracking) { 1659 1660 // Already tracking because onOverscrolled was called. We need to update here 1661 // so we don't stop for a frame until the next touch event gets handled in 1662 // onTouchEvent. 1663 setQsExpansion(h + mInitialHeightOnTouch); 1664 trackMovement(event); 1665 return true; 1666 } 1667 if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded)) 1668 && Math.abs(h) > Math.abs(x - mInitialTouchX) 1669 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { 1670 mView.getParent().requestDisallowInterceptTouchEvent(true); 1671 mQsTracking = true; 1672 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1673 onQsExpansionStarted(); 1674 notifyExpandingFinished(); 1675 mInitialHeightOnTouch = mQsExpansionHeight; 1676 mInitialTouchY = y; 1677 mInitialTouchX = x; 1678 mNotificationStackScrollLayoutController.cancelLongPress(); 1679 return true; 1680 } 1681 break; 1682 1683 case MotionEvent.ACTION_CANCEL: 1684 case MotionEvent.ACTION_UP: 1685 trackMovement(event); 1686 mQsTracking = false; 1687 break; 1688 } 1689 return false; 1690 } 1691 1692 @Override isInContentBounds(float x, float y)1693 protected boolean isInContentBounds(float x, float y) { 1694 float stackScrollerX = mNotificationStackScrollLayoutController.getX(); 1695 return !mNotificationStackScrollLayoutController 1696 .isBelowLastNotification(x - stackScrollerX, y) 1697 && stackScrollerX < x 1698 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth(); 1699 } 1700 traceQsJank(boolean startTracing, boolean wasCancelled)1701 private void traceQsJank(boolean startTracing, boolean wasCancelled) { 1702 InteractionJankMonitor monitor = InteractionJankMonitor.getInstance(); 1703 if (startTracing) { 1704 monitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1705 } else { 1706 if (wasCancelled) { 1707 monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1708 } else { 1709 monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1710 } 1711 } 1712 } 1713 initDownStates(MotionEvent event)1714 private void initDownStates(MotionEvent event) { 1715 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1716 mOnlyAffordanceInThisMotion = false; 1717 mQsTouchAboveFalsingThreshold = mQsFullyExpanded; 1718 mDozingOnDown = isDozing(); 1719 mDownX = event.getX(); 1720 mDownY = event.getY(); 1721 mCollapsedOnDown = isFullyCollapsed(); 1722 mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY); 1723 mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); 1724 mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; 1725 mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; 1726 if (mExpectingSynthesizedDown) { 1727 mLastEventSynthesizedDown = true; 1728 } else { 1729 // down but not synthesized motion event. 1730 mLastEventSynthesizedDown = false; 1731 } 1732 } else { 1733 // not down event at all. 1734 mLastEventSynthesizedDown = false; 1735 } 1736 } 1737 1738 /** 1739 * Can the panel collapse in this motion because it was started on QQS? 1740 * 1741 * @param downX the x location where the touch started 1742 * @param downY the y location where the touch started 1743 * 1744 * @return true if the panel could be collapsed because it stared on QQS 1745 */ canPanelCollapseOnQQS(float downX, float downY)1746 private boolean canPanelCollapseOnQQS(float downX, float downY) { 1747 if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) { 1748 return false; 1749 } 1750 View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 1751 return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth() 1752 && downY <= header.getBottom(); 1753 1754 } 1755 flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)1756 private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { 1757 float vel = getCurrentQSVelocity(); 1758 boolean expandsQs = flingExpandsQs(vel); 1759 if (expandsQs) { 1760 if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) { 1761 expandsQs = false; 1762 } else { 1763 logQsSwipeDown(y); 1764 } 1765 } else if (vel < 0) { 1766 mFalsingManager.isFalseTouch(QS_COLLAPSE); 1767 } 1768 1769 flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE); 1770 } 1771 logQsSwipeDown(float y)1772 private void logQsSwipeDown(float y) { 1773 float vel = getCurrentQSVelocity(); 1774 final int 1775 gesture = 1776 mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS 1777 : MetricsEvent.ACTION_SHADE_QS_PULL; 1778 mLockscreenGestureLogger.write(gesture, 1779 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), 1780 (int) (vel / mStatusBar.getDisplayDensity())); 1781 } 1782 flingExpandsQs(float vel)1783 private boolean flingExpandsQs(float vel) { 1784 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 1785 return computeQsExpansionFraction() > 0.5f; 1786 } else { 1787 return vel > 0; 1788 } 1789 } 1790 isFalseTouch(@lassifier.InteractionType int interactionType)1791 private boolean isFalseTouch(@Classifier.InteractionType int interactionType) { 1792 if (mFalsingManager.isClassifierEnabled()) { 1793 return mFalsingManager.isFalseTouch(interactionType); 1794 } 1795 return !mQsTouchAboveFalsingThreshold; 1796 } 1797 computeQsExpansionFraction()1798 private float computeQsExpansionFraction() { 1799 if (mQSAnimatingHiddenFromCollapsed) { 1800 // When hiding QS from collapsed state, the expansion can sometimes temporarily 1801 // be larger than 0 because of the timing, leading to flickers. 1802 return 0.0f; 1803 } 1804 return Math.min( 1805 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight 1806 - mQsMinExpansionHeight)); 1807 } 1808 1809 @Override shouldExpandWhenNotFlinging()1810 protected boolean shouldExpandWhenNotFlinging() { 1811 if (super.shouldExpandWhenNotFlinging()) { 1812 return true; 1813 } 1814 if (mAllowExpandForSmallExpansion) { 1815 // When we get a touch that came over from launcher, the velocity isn't always correct 1816 // Let's err on expanding if the gesture has been reasonably slow 1817 long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; 1818 return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; 1819 } 1820 return false; 1821 } 1822 1823 @Override getOpeningHeight()1824 protected float getOpeningHeight() { 1825 return mNotificationStackScrollLayoutController.getOpeningHeight(); 1826 } 1827 1828 handleQsTouch(MotionEvent event)1829 private boolean handleQsTouch(MotionEvent event) { 1830 final int action = event.getActionMasked(); 1831 if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f 1832 && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) { 1833 // Down in the empty area while fully expanded - go to QS. 1834 mQsTracking = true; 1835 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1836 mConflictingQsExpansionGesture = true; 1837 onQsExpansionStarted(); 1838 mInitialHeightOnTouch = mQsExpansionHeight; 1839 mInitialTouchY = event.getX(); 1840 mInitialTouchX = event.getY(); 1841 } 1842 if (!isFullyCollapsed()) { 1843 handleQsDown(event); 1844 } 1845 if (!mQsExpandImmediate && mQsTracking) { 1846 onQsTouch(event); 1847 if (!mConflictingQsExpansionGesture) { 1848 return true; 1849 } 1850 } 1851 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 1852 mConflictingQsExpansionGesture = false; 1853 } 1854 if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && isQsExpansionEnabled()) { 1855 mTwoFingerQsExpandPossible = true; 1856 } 1857 if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) 1858 < mStatusBarMinHeight) { 1859 mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); 1860 mQsExpandImmediate = true; 1861 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1862 requestPanelHeightUpdate(); 1863 1864 // Normally, we start listening when the panel is expanded, but here we need to start 1865 // earlier so the state is already up to date when dragging down. 1866 setListening(true); 1867 } 1868 return false; 1869 } 1870 isInQsArea(float x, float y)1871 private boolean isInQsArea(float x, float y) { 1872 if (x < mQsFrame.getX() || x > mQsFrame.getX() + mQsFrame.getWidth()) { 1873 return false; 1874 } 1875 // Let's reject anything at the very bottom around the home handle in gesture nav 1876 if (mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight) { 1877 return false; 1878 } 1879 return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom() 1880 || y <= mQs.getView().getY() + mQs.getView().getHeight(); 1881 } 1882 isOpenQsEvent(MotionEvent event)1883 private boolean isOpenQsEvent(MotionEvent event) { 1884 final int pointerCount = event.getPointerCount(); 1885 final int action = event.getActionMasked(); 1886 1887 final boolean 1888 twoFingerDrag = 1889 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2; 1890 1891 final boolean 1892 stylusButtonClickDrag = 1893 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1894 MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed( 1895 MotionEvent.BUTTON_STYLUS_SECONDARY)); 1896 1897 final boolean 1898 mouseButtonClickDrag = 1899 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1900 MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed( 1901 MotionEvent.BUTTON_TERTIARY)); 1902 1903 return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag; 1904 } 1905 handleQsDown(MotionEvent event)1906 private void handleQsDown(MotionEvent event) { 1907 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( 1908 event.getX(), event.getY(), -1)) { 1909 mFalsingCollector.onQsDown(); 1910 mQsTracking = true; 1911 onQsExpansionStarted(); 1912 mInitialHeightOnTouch = mQsExpansionHeight; 1913 mInitialTouchY = event.getX(); 1914 mInitialTouchX = event.getY(); 1915 1916 // If we interrupt an expansion gesture here, make sure to update the state correctly. 1917 notifyExpandingFinished(); 1918 } 1919 } 1920 1921 /** 1922 * Input focus transfer is about to happen. 1923 */ startWaitingForOpenPanelGesture()1924 public void startWaitingForOpenPanelGesture() { 1925 if (!isFullyCollapsed()) { 1926 return; 1927 } 1928 mExpectingSynthesizedDown = true; 1929 onTrackingStarted(); 1930 updatePanelExpanded(); 1931 } 1932 1933 /** 1934 * Called when this view is no longer waiting for input focus transfer. 1935 * 1936 * There are two scenarios behind this function call. First, input focus transfer 1937 * has successfully happened and this view already received synthetic DOWN event. 1938 * (mExpectingSynthesizedDown == false). Do nothing. 1939 * 1940 * Second, before input focus transfer finished, user may have lifted finger 1941 * in previous window and this window never received synthetic DOWN event. 1942 * (mExpectingSynthesizedDown == true). 1943 * In this case, we use the velocity to trigger fling event. 1944 * 1945 * @param velocity unit is in px / millis 1946 */ stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)1947 public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) { 1948 if (mExpectingSynthesizedDown) { 1949 mExpectingSynthesizedDown = false; 1950 if (cancel) { 1951 collapse(false /* delayed */, 1.0f /* speedUpFactor */); 1952 } else { 1953 maybeVibrateOnOpening(); 1954 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */); 1955 } 1956 onTrackingStopped(false); 1957 } 1958 } 1959 1960 @Override flingExpands(float vel, float vectorVel, float x, float y)1961 protected boolean flingExpands(float vel, float vectorVel, float x, float y) { 1962 boolean expands = super.flingExpands(vel, vectorVel, x, y); 1963 1964 // If we are already running a QS expansion, make sure that we keep the panel open. 1965 if (mQsExpansionAnimator != null) { 1966 expands = true; 1967 } 1968 return expands; 1969 } 1970 1971 @Override shouldGestureWaitForTouchSlop()1972 protected boolean shouldGestureWaitForTouchSlop() { 1973 if (mExpectingSynthesizedDown) { 1974 mExpectingSynthesizedDown = false; 1975 return false; 1976 } 1977 return isFullyCollapsed() || mBarState != StatusBarState.SHADE; 1978 } 1979 1980 @Override shouldGestureIgnoreXTouchSlop(float x, float y)1981 protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { 1982 return !mAffordanceHelper.isOnAffordanceIcon(x, y); 1983 } 1984 onQsTouch(MotionEvent event)1985 private void onQsTouch(MotionEvent event) { 1986 int pointerIndex = event.findPointerIndex(mTrackingPointer); 1987 if (pointerIndex < 0) { 1988 pointerIndex = 0; 1989 mTrackingPointer = event.getPointerId(pointerIndex); 1990 } 1991 final float y = event.getY(pointerIndex); 1992 final float x = event.getX(pointerIndex); 1993 final float h = y - mInitialTouchY; 1994 1995 switch (event.getActionMasked()) { 1996 case MotionEvent.ACTION_DOWN: 1997 mQsTracking = true; 1998 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1999 mInitialTouchY = y; 2000 mInitialTouchX = x; 2001 onQsExpansionStarted(); 2002 mInitialHeightOnTouch = mQsExpansionHeight; 2003 initVelocityTracker(); 2004 trackMovement(event); 2005 break; 2006 2007 case MotionEvent.ACTION_POINTER_UP: 2008 final int upPointer = event.getPointerId(event.getActionIndex()); 2009 if (mTrackingPointer == upPointer) { 2010 // gesture is ongoing, find a new pointer to track 2011 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 2012 final float newY = event.getY(newIndex); 2013 final float newX = event.getX(newIndex); 2014 mTrackingPointer = event.getPointerId(newIndex); 2015 mInitialHeightOnTouch = mQsExpansionHeight; 2016 mInitialTouchY = newY; 2017 mInitialTouchX = newX; 2018 } 2019 break; 2020 2021 case MotionEvent.ACTION_MOVE: 2022 setQsExpansion(h + mInitialHeightOnTouch); 2023 if (h >= getFalsingThreshold()) { 2024 mQsTouchAboveFalsingThreshold = true; 2025 } 2026 trackMovement(event); 2027 break; 2028 2029 case MotionEvent.ACTION_UP: 2030 case MotionEvent.ACTION_CANCEL: 2031 mQsTracking = false; 2032 mTrackingPointer = -1; 2033 trackMovement(event); 2034 float fraction = computeQsExpansionFraction(); 2035 if (fraction != 0f || y >= mInitialTouchY) { 2036 flingQsWithCurrentVelocity(y, 2037 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 2038 } else { 2039 traceQsJank(false /* startTracing */, 2040 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 2041 } 2042 if (mQsVelocityTracker != null) { 2043 mQsVelocityTracker.recycle(); 2044 mQsVelocityTracker = null; 2045 } 2046 break; 2047 } 2048 } 2049 getFalsingThreshold()2050 private int getFalsingThreshold() { 2051 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 2052 return (int) (mQsFalsingThreshold * factor); 2053 } 2054 setOverScrolling(boolean overscrolling)2055 private void setOverScrolling(boolean overscrolling) { 2056 mStackScrollerOverscrolling = overscrolling; 2057 if (mQs == null) return; 2058 mQs.setOverscrolling(overscrolling); 2059 } 2060 onQsExpansionStarted()2061 private void onQsExpansionStarted() { 2062 onQsExpansionStarted(0); 2063 } 2064 onQsExpansionStarted(int overscrollAmount)2065 protected void onQsExpansionStarted(int overscrollAmount) { 2066 cancelQsAnimation(); 2067 cancelHeightAnimator(); 2068 2069 // Reset scroll position and apply that position to the expanded height. 2070 float height = mQsExpansionHeight - overscrollAmount; 2071 setQsExpansion(height); 2072 requestPanelHeightUpdate(); 2073 mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); 2074 2075 // When expanding QS, let's authenticate the user if possible, 2076 // this will speed up notification actions. 2077 if (height == 0) { 2078 mStatusBar.requestFaceAuth(false); 2079 } 2080 } 2081 setQsExpanded(boolean expanded)2082 @VisibleForTesting void setQsExpanded(boolean expanded) { 2083 boolean changed = mQsExpanded != expanded; 2084 if (changed) { 2085 mQsExpanded = expanded; 2086 updateQsState(); 2087 requestPanelHeightUpdate(); 2088 mFalsingCollector.setQsExpanded(expanded); 2089 mStatusBar.setQsExpanded(expanded); 2090 mNotificationContainerParent.setQsExpanded(expanded); 2091 mPulseExpansionHandler.setQsExpanded(expanded); 2092 mKeyguardBypassController.setQSExpanded(expanded); 2093 mStatusBarKeyguardViewManager.setQsExpanded(expanded); 2094 mLockIconViewController.setQsExpanded(expanded); 2095 mPrivacyDotViewController.setQsExpanded(expanded); 2096 } 2097 } 2098 maybeAnimateBottomAreaAlpha()2099 private void maybeAnimateBottomAreaAlpha() { 2100 mBottomAreaShadeAlphaAnimator.cancel(); 2101 if (mBarState == StatusBarState.SHADE_LOCKED) { 2102 mBottomAreaShadeAlphaAnimator.setFloatValues(mBottomAreaShadeAlpha, 0.0f); 2103 mBottomAreaShadeAlphaAnimator.start(); 2104 } else { 2105 mBottomAreaShadeAlpha = 1f; 2106 } 2107 } 2108 2109 private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { 2110 @Override 2111 public void run() { 2112 mKeyguardStatusBar.setVisibility(View.INVISIBLE); 2113 mKeyguardStatusBar.setAlpha(1f); 2114 mKeyguardStatusBarAnimateAlpha = 1f; 2115 } 2116 }; 2117 animateKeyguardStatusBarOut()2118 private void animateKeyguardStatusBarOut() { 2119 ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); 2120 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 2121 anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway() 2122 ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0); 2123 2124 long duration; 2125 if (mKeyguardStateController.isKeyguardFadingAway()) { 2126 duration = mKeyguardStateController.getShortenedFadingAwayDuration(); 2127 } else { 2128 duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; 2129 } 2130 anim.setDuration(duration); 2131 2132 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 2133 anim.addListener(new AnimatorListenerAdapter() { 2134 @Override 2135 public void onAnimationEnd(Animator animation) { 2136 mAnimateKeyguardStatusBarInvisibleEndRunnable.run(); 2137 } 2138 }); 2139 anim.start(); 2140 } 2141 2142 private final ValueAnimator.AnimatorUpdateListener 2143 mStatusBarAnimateAlphaListener = 2144 new ValueAnimator.AnimatorUpdateListener() { 2145 @Override 2146 public void onAnimationUpdate(ValueAnimator animation) { 2147 mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue(); 2148 updateHeaderKeyguardAlpha(); 2149 } 2150 }; 2151 animateKeyguardStatusBarIn(long duration)2152 private void animateKeyguardStatusBarIn(long duration) { 2153 mKeyguardStatusBar.setVisibility(View.VISIBLE); 2154 mKeyguardStatusBar.setAlpha(0f); 2155 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 2156 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 2157 anim.setDuration(duration); 2158 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 2159 anim.start(); 2160 } 2161 2162 private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { 2163 @Override 2164 public void run() { 2165 mKeyguardBottomArea.setVisibility(View.GONE); 2166 } 2167 }; 2168 setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)2169 private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { 2170 mKeyguardBottomArea.animate().cancel(); 2171 if (goingToFullShade) { 2172 mKeyguardBottomArea.animate().alpha(0f).setStartDelay( 2173 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( 2174 mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator( 2175 Interpolators.ALPHA_OUT).withEndAction( 2176 mAnimateKeyguardBottomAreaInvisibleEndRunnable).start(); 2177 } else if (statusBarState == KEYGUARD 2178 || statusBarState == StatusBarState.SHADE_LOCKED) { 2179 mKeyguardBottomArea.setVisibility(View.VISIBLE); 2180 mKeyguardBottomArea.setAlpha(1f); 2181 } else { 2182 mKeyguardBottomArea.setVisibility(View.GONE); 2183 } 2184 } 2185 updateQsState()2186 private void updateQsState() { 2187 mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); 2188 mNotificationStackScrollLayoutController.setScrollingEnabled( 2189 mBarState != KEYGUARD 2190 && (!mQsExpanded 2191 || mQsExpansionFromOverscroll 2192 || mShouldUseSplitNotificationShade)); 2193 2194 if (mKeyguardUserSwitcherController != null && mQsExpanded 2195 && !mStackScrollerOverscrolling) { 2196 mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true); 2197 } 2198 if (mQs == null) return; 2199 mQs.setExpanded(mQsExpanded); 2200 } 2201 setQsExpansion(float height)2202 private void setQsExpansion(float height) { 2203 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); 2204 mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; 2205 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling 2206 && !mDozing) { 2207 setQsExpanded(true); 2208 } else if (height <= mQsMinExpansionHeight && mQsExpanded) { 2209 setQsExpanded(false); 2210 } 2211 mQsExpansionHeight = height; 2212 updateQsExpansion(); 2213 requestScrollerTopPaddingUpdate(false /* animate */); 2214 updateHeaderKeyguardAlpha(); 2215 if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) { 2216 updateKeyguardBottomAreaAlpha(); 2217 positionClockAndNotifications(); 2218 updateBigClockAlpha(); 2219 } 2220 2221 if (mAccessibilityManager.isEnabled()) { 2222 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 2223 } 2224 2225 if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded 2226 && mFalsingCollector.shouldEnforceBouncer()) { 2227 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, 2228 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); 2229 } 2230 for (int i = 0; i < mExpansionListeners.size(); i++) { 2231 mExpansionListeners.get(i).onQsExpansionChanged( 2232 mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0); 2233 } 2234 if (DEBUG) { 2235 mView.invalidate(); 2236 } 2237 } 2238 updateQsExpansion()2239 private void updateQsExpansion() { 2240 if (mQs == null) return; 2241 float qsExpansionFraction = computeQsExpansionFraction(); 2242 mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); 2243 mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); 2244 int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction); 2245 mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY); 2246 setQSClippingBounds(); 2247 mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction); 2248 mDepthController.setQsPanelExpansion(qsExpansionFraction); 2249 } 2250 onStackYChanged(boolean shouldAnimate)2251 private void onStackYChanged(boolean shouldAnimate) { 2252 if (mQs != null) { 2253 if (shouldAnimate) { 2254 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD, 2255 0 /* delay */); 2256 mNotificationBoundsAnimationDelay = 0; 2257 } 2258 setQSClippingBounds(); 2259 } 2260 }; 2261 onNotificationScrolled(int newScrollPosition)2262 private void onNotificationScrolled(int newScrollPosition) { 2263 updateQSExpansionEnabledAmbient(); 2264 } 2265 2266 @Override setIsShadeOpening(boolean opening)2267 public void setIsShadeOpening(boolean opening) { 2268 mAmbientState.setIsShadeOpening(opening); 2269 updateQSExpansionEnabledAmbient(); 2270 } 2271 updateQSExpansionEnabledAmbient()2272 private void updateQSExpansionEnabledAmbient() { 2273 final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight; 2274 mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop; 2275 setQsExpansionEnabled(); 2276 } 2277 2278 /** 2279 * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade 2280 * and QS state. 2281 */ setQSClippingBounds()2282 private void setQSClippingBounds() { 2283 int top; 2284 int bottom; 2285 int left; 2286 int right; 2287 2288 final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction()); 2289 final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0); 2290 2291 if (!mShouldUseSplitNotificationShade) { 2292 if (mTransitioningToFullShadeProgress > 0.0f) { 2293 // If we're transitioning, let's use the actual value. The else case 2294 // can be wrong during transitions when waiting for the keyguard to unlock 2295 top = mTransitionToFullShadeQSPosition; 2296 } else { 2297 final float notificationTop = getQSEdgePosition(); 2298 if (isOnKeyguard()) { 2299 if (mKeyguardBypassController.getBypassEnabled()) { 2300 // When bypassing on the keyguard, let's use the panel bottom. 2301 // this should go away once we unify the stackY position and don't have 2302 // to do this min anymore below. 2303 top = qsPanelBottomY; 2304 } else { 2305 top = (int) Math.min(qsPanelBottomY, notificationTop); 2306 } 2307 } else { 2308 top = (int) notificationTop; 2309 } 2310 } 2311 top += mOverStretchAmount; 2312 bottom = getView().getBottom(); 2313 // notification bounds should take full screen width regardless of insets 2314 left = 0; 2315 right = getView().getRight() + mDisplayRightInset; 2316 } else { 2317 top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding); 2318 bottom = mNotificationStackScrollLayoutController.getHeight(); 2319 left = mNotificationStackScrollLayoutController.getLeft(); 2320 right = mNotificationStackScrollLayoutController.getRight(); 2321 } 2322 // top should never be lower than bottom, otherwise it will be invisible. 2323 top = Math.min(top, bottom); 2324 applyQSClippingBounds(left, top, right, bottom, qsVisible); 2325 } 2326 applyQSClippingBounds(int left, int top, int right, int bottom, boolean qsVisible)2327 private void applyQSClippingBounds(int left, int top, int right, int bottom, 2328 boolean qsVisible) { 2329 if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) { 2330 if (mQsClippingAnimation != null) { 2331 // update the end position of the animator 2332 mQsClippingAnimationEndBounds.set(left, top, right, bottom); 2333 } else { 2334 applyQSClippingImmediately(left, top, right, bottom, qsVisible); 2335 } 2336 } else { 2337 mQsClippingAnimationEndBounds.set(left, top, right, bottom); 2338 final int startLeft = mKeyguardStatusAreaClipBounds.left; 2339 final int startTop = mKeyguardStatusAreaClipBounds.top; 2340 final int startRight = mKeyguardStatusAreaClipBounds.right; 2341 final int startBottom = mKeyguardStatusAreaClipBounds.bottom; 2342 if (mQsClippingAnimation != null) { 2343 mQsClippingAnimation.cancel(); 2344 } 2345 mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f); 2346 mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 2347 mQsClippingAnimation.setDuration(mNotificationBoundsAnimationDuration); 2348 mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay); 2349 mQsClippingAnimation.addUpdateListener(animation -> { 2350 float fraction = animation.getAnimatedFraction(); 2351 int animLeft = (int) MathUtils.lerp(startLeft, 2352 mQsClippingAnimationEndBounds.left, fraction); 2353 int animTop = (int) MathUtils.lerp(startTop, 2354 mQsClippingAnimationEndBounds.top, fraction); 2355 int animRight = (int) MathUtils.lerp(startRight, 2356 mQsClippingAnimationEndBounds.right, fraction); 2357 int animBottom = (int) MathUtils.lerp(startBottom, 2358 mQsClippingAnimationEndBounds.bottom, fraction); 2359 applyQSClippingImmediately(animLeft, animTop, animRight, animBottom, 2360 qsVisible /* qsVisible */); 2361 }); 2362 mQsClippingAnimation.addListener(new AnimatorListenerAdapter() { 2363 @Override 2364 public void onAnimationEnd(Animator animation) { 2365 mQsClippingAnimation = null; 2366 mIsQsTranslationResetAnimator = false; 2367 mIsPulseExpansionResetAnimator = false; 2368 } 2369 }); 2370 mQsClippingAnimation.start(); 2371 } 2372 mAnimateNextNotificationBounds = false; 2373 mNotificationBoundsAnimationDelay = 0; 2374 } 2375 applyQSClippingImmediately(int left, int top, int right, int bottom, boolean qsVisible)2376 private void applyQSClippingImmediately(int left, int top, int right, int bottom, 2377 boolean qsVisible) { 2378 // Fancy clipping for quick settings 2379 int radius = mScrimCornerRadius; 2380 int statusBarClipTop = 0; 2381 boolean clipStatusView = false; 2382 if (!mShouldUseSplitNotificationShade) { 2383 // The padding on this area is large enough that we can use a cheaper clipping strategy 2384 mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); 2385 clipStatusView = qsVisible; 2386 float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; 2387 radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, 2388 Math.min(top / (float) mScrimCornerRadius, 1f)); 2389 statusBarClipTop = top - mKeyguardStatusBar.getTop(); 2390 } 2391 if (mQs != null) { 2392 float qsTranslation = 0; 2393 boolean pulseExpanding = mPulseExpansionHandler.isExpanding(); 2394 if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding 2395 || (mQsClippingAnimation != null 2396 && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) { 2397 if (pulseExpanding || mIsPulseExpansionResetAnimator) { 2398 // qsTranslation should only be positive during pulse expansion because it's 2399 // already translating in from the top 2400 qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f); 2401 } else { 2402 qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT; 2403 } 2404 } 2405 mQsTranslationForFullShadeTransition = qsTranslation; 2406 updateQsFrameTranslation(); 2407 float currentTranslation = mQsFrame.getTranslationY(); 2408 mQsClipTop = (int) (top - currentTranslation); 2409 mQsClipBottom = (int) (bottom - currentTranslation); 2410 mQsVisible = qsVisible; 2411 mQs.setFancyClipping( 2412 mQsClipTop, 2413 mQsClipBottom, 2414 radius, qsVisible 2415 && !mShouldUseSplitNotificationShade); 2416 } 2417 mKeyguardStatusViewController.setClipBounds( 2418 clipStatusView ? mKeyguardStatusAreaClipBounds : null); 2419 if (!qsVisible && mShouldUseSplitNotificationShade) { 2420 // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to 2421 // be visible, otherwise you can see the bounds once swiping up to see bouncer 2422 mScrimController.setNotificationsBounds(0, 0, 0, 0); 2423 } else { 2424 mScrimController.setNotificationsBounds(left, top, right, bottom); 2425 } 2426 2427 mScrimController.setScrimCornerRadius(radius); 2428 mKeyguardStatusBar.setTopClipping(statusBarClipTop); 2429 int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft(); 2430 int nsslRight = right - mNotificationStackScrollLayoutController.getLeft(); 2431 int nsslTop = top - mNotificationStackScrollLayoutController.getTop(); 2432 int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop(); 2433 int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0; 2434 mNotificationStackScrollLayoutController.setRoundedClippingBounds( 2435 nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius); 2436 } 2437 getQSEdgePosition()2438 private float getQSEdgePosition() { 2439 // TODO: replace StackY with unified calculation 2440 return Math.max(mQuickQsOffsetHeight * mAmbientState.getExpansionFraction(), 2441 mAmbientState.getStackY() - mAmbientState.getScrollY()); 2442 } 2443 calculateQsBottomPosition(float qsExpansionFraction)2444 private int calculateQsBottomPosition(float qsExpansionFraction) { 2445 if (mTransitioningToFullShadeProgress > 0.0f) { 2446 return mTransitionToFullShadeQSPosition; 2447 } else { 2448 int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); 2449 if (qsExpansionFraction != 0.0) { 2450 qsBottomY = (int) MathUtils.lerp( 2451 qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction); 2452 } 2453 return qsBottomY; 2454 } 2455 } 2456 determineAccessibilityPaneTitle()2457 private String determineAccessibilityPaneTitle() { 2458 if (mQs != null && mQs.isCustomizing()) { 2459 return mResources.getString(R.string.accessibility_desc_quick_settings_edit); 2460 } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) { 2461 // Upon initialisation when we are not layouted yet we don't want to announce that we 2462 // are fully expanded, hence the != 0.0f check. 2463 return mResources.getString(R.string.accessibility_desc_quick_settings); 2464 } else if (mBarState == KEYGUARD) { 2465 return mResources.getString(R.string.accessibility_desc_lock_screen); 2466 } else { 2467 return mResources.getString(R.string.accessibility_desc_notification_shade); 2468 } 2469 } 2470 calculateNotificationsTopPadding()2471 private float calculateNotificationsTopPadding() { 2472 if (mShouldUseSplitNotificationShade && !mKeyguardShowing) { 2473 return mSplitShadeNotificationsTopPadding; 2474 } 2475 if (mKeyguardShowing && (mQsExpandImmediate 2476 || mIsExpanding && mQsExpandedWhenExpandingStarted)) { 2477 2478 // Either QS pushes the notifications down when fully expanded, or QS is fully above the 2479 // notifications (mostly on tablets). maxNotificationPadding denotes the normal top 2480 // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings 2481 // panel. We need to take the maximum and linearly interpolate with the panel expansion 2482 // for a nice motion. 2483 int maxNotificationPadding = getKeyguardNotificationStaticPadding(); 2484 int maxQsPadding = mQsMaxExpansionHeight; 2485 int max = mBarState == KEYGUARD ? Math.max( 2486 maxNotificationPadding, maxQsPadding) : maxQsPadding; 2487 return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, 2488 getExpandedFraction()); 2489 } else if (mQsSizeChangeAnimator != null) { 2490 return Math.max( 2491 (int) mQsSizeChangeAnimator.getAnimatedValue(), 2492 getKeyguardNotificationStaticPadding()); 2493 } else if (mKeyguardShowing) { 2494 // We can only do the smoother transition on Keyguard when we also are not collapsing 2495 // from a scrolled quick settings. 2496 return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), 2497 (float) (mQsMaxExpansionHeight), 2498 computeQsExpansionFraction()); 2499 } else { 2500 return mQsExpansionHeight; 2501 } 2502 } 2503 2504 /** 2505 * @return the topPadding of notifications when on keyguard not respecting quick settings 2506 * expansion 2507 */ getKeyguardNotificationStaticPadding()2508 private int getKeyguardNotificationStaticPadding() { 2509 if (!mKeyguardShowing) { 2510 return 0; 2511 } 2512 if (!mKeyguardBypassController.getBypassEnabled()) { 2513 return mClockPositionResult.stackScrollerPadding; 2514 } 2515 int collapsedPosition = mHeadsUpInset; 2516 if (!mNotificationStackScrollLayoutController.isPulseExpanding()) { 2517 return collapsedPosition; 2518 } else { 2519 int expandedPosition = mClockPositionResult.stackScrollerPadding; 2520 return (int) MathUtils.lerp(collapsedPosition, expandedPosition, 2521 mNotificationStackScrollLayoutController.calculateAppearFractionBypass()); 2522 } 2523 } 2524 2525 requestScrollerTopPaddingUpdate(boolean animate)2526 protected void requestScrollerTopPaddingUpdate(boolean animate) { 2527 mNotificationStackScrollLayoutController.updateTopPadding( 2528 calculateNotificationsTopPadding(), animate); 2529 if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { 2530 // update the position of the header 2531 updateQsExpansion(); 2532 } 2533 } 2534 2535 /** 2536 * Set the amount of pixels we have currently dragged down if we're transitioning to the full 2537 * shade. 0.0f means we're not transitioning yet. 2538 */ setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay)2539 public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) { 2540 if (animate && !mShouldUseSplitNotificationShade) { 2541 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 2542 delay); 2543 mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f; 2544 } 2545 2546 float endPosition = 0; 2547 if (pxAmount > 0.0f) { 2548 if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0 2549 && !mMediaDataManager.hasActiveMedia()) { 2550 // No notifications are visible, let's animate to the height of qs instead 2551 if (mQs != null) { 2552 // Let's interpolate to the header height instead of the top padding, 2553 // because the toppadding is way too low because of the large clock. 2554 // we still want to take into account the edgePosition though as that nicely 2555 // overshoots in the stackscroller 2556 endPosition = getQSEdgePosition() 2557 - mNotificationStackScrollLayoutController.getTopPadding() 2558 + mQs.getHeader().getHeight(); 2559 } 2560 } else { 2561 // Interpolating to the new bottom edge position! 2562 endPosition = getQSEdgePosition() 2563 + mNotificationStackScrollLayoutController.getFullShadeTransitionInset(); 2564 if (isOnKeyguard()) { 2565 endPosition -= mLockscreenNotificationQSPadding; 2566 } 2567 } 2568 } 2569 2570 // Calculate the overshoot amount such that we're reaching the target after our desired 2571 // distance, but only reach it fully once we drag a full shade length. 2572 mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( 2573 MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition)); 2574 2575 int position = (int) MathUtils.lerp((float) 0, endPosition, 2576 mTransitioningToFullShadeProgress); 2577 if (mTransitioningToFullShadeProgress > 0.0f) { 2578 // we want at least 1 pixel otherwise the panel won't be clipped 2579 position = Math.max(1, position); 2580 } 2581 mTransitionToFullShadeQSPosition = position; 2582 updateQsExpansion(); 2583 } 2584 2585 /** 2586 * Notify the panel that the pulse expansion has finished and that we're going to the full 2587 * shade 2588 */ onPulseExpansionFinished()2589 public void onPulseExpansionFinished() { 2590 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 0); 2591 mIsPulseExpansionResetAnimator = true; 2592 } 2593 2594 /** 2595 * Set the alpha of the keyguard elements which only show on the lockscreen, but not in 2596 * shade locked / shade. This is used when dragging down to the full shade. 2597 */ setKeyguardOnlyContentAlpha(float keyguardAlpha)2598 public void setKeyguardOnlyContentAlpha(float keyguardAlpha) { 2599 mKeyguardOnlyContentAlpha = Interpolators.ALPHA_IN.getInterpolation(keyguardAlpha); 2600 if (mBarState == KEYGUARD) { 2601 // If the animator is running, it's already fading out the content and this is a reset 2602 mBottomAreaShadeAlpha = mKeyguardOnlyContentAlpha; 2603 updateKeyguardBottomAreaAlpha(); 2604 } 2605 updateClock(); 2606 } 2607 trackMovement(MotionEvent event)2608 private void trackMovement(MotionEvent event) { 2609 if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); 2610 } 2611 initVelocityTracker()2612 private void initVelocityTracker() { 2613 if (mQsVelocityTracker != null) { 2614 mQsVelocityTracker.recycle(); 2615 } 2616 mQsVelocityTracker = VelocityTracker.obtain(); 2617 } 2618 getCurrentQSVelocity()2619 private float getCurrentQSVelocity() { 2620 if (mQsVelocityTracker == null) { 2621 return 0; 2622 } 2623 mQsVelocityTracker.computeCurrentVelocity(1000); 2624 return mQsVelocityTracker.getYVelocity(); 2625 } 2626 cancelQsAnimation()2627 private void cancelQsAnimation() { 2628 if (mQsExpansionAnimator != null) { 2629 mQsExpansionAnimator.cancel(); 2630 } 2631 } 2632 2633 /** 2634 * @see #flingSettings(float, int, Runnable, boolean) 2635 */ flingSettings(float vel, int type)2636 public void flingSettings(float vel, int type) { 2637 flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */); 2638 } 2639 2640 /** 2641 * Animates QS or QQS as if the user had swiped up or down. 2642 * 2643 * @param vel Finger velocity or 0 when not initiated by touch events. 2644 * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link 2645 * #FLING_HIDE}. 2646 * @param onFinishRunnable Runnable to be executed at the end of animation. 2647 * @param isClick If originated by click (different interpolator and duration.) 2648 */ flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick)2649 protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, 2650 boolean isClick) { 2651 float target; 2652 switch (type) { 2653 case FLING_EXPAND: 2654 target = mQsMaxExpansionHeight; 2655 break; 2656 case FLING_COLLAPSE: 2657 target = mQsMinExpansionHeight; 2658 break; 2659 case FLING_HIDE: 2660 default: 2661 if (mQs != null) { 2662 mQs.closeDetail(); 2663 } 2664 target = 0; 2665 } 2666 if (target == mQsExpansionHeight) { 2667 if (onFinishRunnable != null) { 2668 onFinishRunnable.run(); 2669 } 2670 traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */); 2671 return; 2672 } 2673 2674 // If we move in the opposite direction, reset velocity and use a different duration. 2675 boolean oppositeDirection = false; 2676 boolean expanding = type == FLING_EXPAND; 2677 if (vel > 0 && !expanding || vel < 0 && expanding) { 2678 vel = 0; 2679 oppositeDirection = true; 2680 } 2681 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); 2682 if (isClick) { 2683 animator.setInterpolator(Interpolators.TOUCH_RESPONSE); 2684 animator.setDuration(368); 2685 } else { 2686 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); 2687 } 2688 if (oppositeDirection) { 2689 animator.setDuration(350); 2690 } 2691 animator.addUpdateListener(animation -> { 2692 setQsExpansion((Float) animation.getAnimatedValue()); 2693 }); 2694 animator.addListener(new AnimatorListenerAdapter() { 2695 private boolean mIsCanceled; 2696 @Override 2697 public void onAnimationStart(Animator animation) { 2698 notifyExpandingStarted(); 2699 } 2700 2701 @Override 2702 public void onAnimationCancel(Animator animation) { 2703 mIsCanceled = true; 2704 } 2705 2706 @Override 2707 public void onAnimationEnd(Animator animation) { 2708 mQSAnimatingHiddenFromCollapsed = false; 2709 mAnimatingQS = false; 2710 notifyExpandingFinished(); 2711 mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); 2712 mQsExpansionAnimator = null; 2713 if (onFinishRunnable != null) { 2714 onFinishRunnable.run(); 2715 } 2716 traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */); 2717 } 2718 }); 2719 // Let's note that we're animating QS. Moving the animator here will cancel it immediately, 2720 // so we need a separate flag. 2721 mAnimatingQS = true; 2722 animator.start(); 2723 mQsExpansionAnimator = animator; 2724 mQsAnimatorExpand = expanding; 2725 mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0; 2726 } 2727 2728 /** 2729 * @return Whether we should intercept a gesture to open Quick Settings. 2730 */ shouldQuickSettingsIntercept(float x, float y, float yDiff)2731 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { 2732 if (!isQsExpansionEnabled() || mCollapsedOnDown || (mKeyguardShowing 2733 && mKeyguardBypassController.getBypassEnabled())) { 2734 return false; 2735 } 2736 View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 2737 2738 mQsInterceptRegion.set( 2739 /* left= */ (int) mQsFrame.getX(), 2740 /* top= */ header.getTop(), 2741 /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(), 2742 /* bottom= */ header.getBottom()); 2743 // Also allow QS to intercept if the touch is near the notch. 2744 mStatusBarTouchableRegionManager.updateRegionForNotch(mQsInterceptRegion); 2745 final boolean onHeader = mQsInterceptRegion.contains((int) x, (int) y); 2746 2747 if (mQsExpanded) { 2748 return onHeader || (yDiff < 0 && isInQsArea(x, y)); 2749 } else { 2750 return onHeader; 2751 } 2752 } 2753 2754 @Override canCollapsePanelOnTouch()2755 protected boolean canCollapsePanelOnTouch() { 2756 if (!isInSettings() && mBarState == KEYGUARD) { 2757 return true; 2758 } 2759 2760 if (mNotificationStackScrollLayoutController.isScrolledToBottom()) { 2761 return true; 2762 } 2763 2764 return !mShouldUseSplitNotificationShade && (isInSettings() || mIsPanelCollapseOnQQS); 2765 } 2766 2767 @Override getMaxPanelHeight()2768 protected int getMaxPanelHeight() { 2769 int min = mStatusBarMinHeight; 2770 if (!(mBarState == KEYGUARD) 2771 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) { 2772 int minHeight = mQsMinExpansionHeight; 2773 min = Math.max(min, minHeight); 2774 } 2775 int maxHeight; 2776 if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted 2777 || mPulsing) { 2778 maxHeight = calculatePanelHeightQsExpanded(); 2779 } else { 2780 maxHeight = calculatePanelHeightShade(); 2781 } 2782 maxHeight = Math.max(min, maxHeight); 2783 if (maxHeight == 0 || isNaN(maxHeight)) { 2784 Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: " 2785 + mOverExpansion + ", calculatePanelHeightQsExpanded: " 2786 + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " 2787 + calculatePanelHeightShade() + ", mStatusBarMinHeight = " 2788 + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight); 2789 } 2790 return maxHeight; 2791 } 2792 isInSettings()2793 public boolean isInSettings() { 2794 return mQsExpanded; 2795 } 2796 isExpanding()2797 public boolean isExpanding() { 2798 return mIsExpanding; 2799 } 2800 2801 @Override onHeightUpdated(float expandedHeight)2802 protected void onHeightUpdated(float expandedHeight) { 2803 if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { 2804 // Updating the clock position will set the top padding which might 2805 // trigger a new panel height and re-position the clock. 2806 // This is a circular dependency and should be avoided, otherwise we'll have 2807 // a stack overflow. 2808 if (mStackScrollerMeasuringPass > 2) { 2809 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); 2810 } else { 2811 positionClockAndNotifications(); 2812 } 2813 } 2814 if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null 2815 && !mQsExpansionFromOverscroll) { 2816 float t; 2817 if (mKeyguardShowing) { 2818 2819 // On Keyguard, interpolate the QS expansion linearly to the panel expansion 2820 t = expandedHeight / (getMaxPanelHeight()); 2821 } else { 2822 // In Shade, interpolate linearly such that QS is closed whenever panel height is 2823 // minimum QS expansion + minStackHeight 2824 float 2825 panelHeightQsCollapsed = 2826 mNotificationStackScrollLayoutController.getIntrinsicPadding() 2827 + mNotificationStackScrollLayoutController.getLayoutMinHeight(); 2828 float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); 2829 t = 2830 (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded 2831 - panelHeightQsCollapsed); 2832 } 2833 float 2834 targetHeight = 2835 mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); 2836 setQsExpansion(targetHeight); 2837 } 2838 updateExpandedHeight(expandedHeight); 2839 updateHeader(); 2840 updateNotificationTranslucency(); 2841 updatePanelExpanded(); 2842 updateGestureExclusionRect(); 2843 if (DEBUG) { 2844 mView.invalidate(); 2845 } 2846 } 2847 updatePanelExpanded()2848 private void updatePanelExpanded() { 2849 boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; 2850 if (mPanelExpanded != isExpanded) { 2851 mHeadsUpManager.setIsPanelExpanded(isExpanded); 2852 mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded); 2853 mStatusBar.setPanelExpanded(isExpanded); 2854 mPanelExpanded = isExpanded; 2855 } 2856 } 2857 calculatePanelHeightShade()2858 private int calculatePanelHeightShade() { 2859 int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); 2860 int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; 2861 2862 if (mBarState == KEYGUARD) { 2863 int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight() 2864 + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); 2865 return Math.max(maxHeight, minKeyguardPanelBottom); 2866 } else { 2867 return maxHeight; 2868 } 2869 } 2870 calculatePanelHeightQsExpanded()2871 private int calculatePanelHeightQsExpanded() { 2872 float 2873 notificationHeight = 2874 mNotificationStackScrollLayoutController.getHeight() 2875 - mNotificationStackScrollLayoutController.getEmptyBottomMargin() 2876 - mNotificationStackScrollLayoutController.getTopPadding(); 2877 2878 // When only empty shade view is visible in QS collapsed state, simulate that we would have 2879 // it in expanded QS state as well so we don't run into troubles when fading the view in/out 2880 // and expanding/collapsing the whole panel from/to quick settings. 2881 if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0 2882 && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) { 2883 notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight(); 2884 } 2885 int maxQsHeight = mQsMaxExpansionHeight; 2886 2887 // If an animation is changing the size of the QS panel, take the animated value. 2888 if (mQsSizeChangeAnimator != null) { 2889 maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 2890 } 2891 float totalHeight = Math.max(maxQsHeight, 2892 mBarState == KEYGUARD ? mClockPositionResult.stackScrollerPadding 2893 : 0) + notificationHeight 2894 + mNotificationStackScrollLayoutController.getTopPaddingOverflow(); 2895 if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) { 2896 float 2897 fullyCollapsedHeight = 2898 maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight(); 2899 totalHeight = Math.max(fullyCollapsedHeight, 2900 mNotificationStackScrollLayoutController.getHeight()); 2901 } 2902 return (int) totalHeight; 2903 } 2904 updateNotificationTranslucency()2905 private void updateNotificationTranslucency() { 2906 float alpha = 1f; 2907 if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp 2908 && !mHeadsUpManager.hasPinnedHeadsUp()) { 2909 alpha = getFadeoutAlpha(); 2910 } 2911 if (mBarState == KEYGUARD && !mHintAnimationRunning 2912 && !mKeyguardBypassController.getBypassEnabled()) { 2913 alpha *= mClockPositionResult.clockAlpha; 2914 } 2915 mNotificationStackScrollLayoutController.setAlpha(alpha); 2916 } 2917 getFadeoutAlpha()2918 private float getFadeoutAlpha() { 2919 float alpha; 2920 if (mQsMinExpansionHeight == 0) { 2921 return 1.0f; 2922 } 2923 alpha = getExpandedHeight() / mQsMinExpansionHeight; 2924 alpha = Math.max(0, Math.min(alpha, 1)); 2925 alpha = (float) Math.pow(alpha, 0.75); 2926 return alpha; 2927 } 2928 2929 /** 2930 * Hides the header when notifications are colliding with it. 2931 */ updateHeader()2932 private void updateHeader() { 2933 if (mBarState == KEYGUARD) { 2934 updateHeaderKeyguardAlpha(); 2935 } 2936 updateQsExpansion(); 2937 } 2938 getHeaderTranslation()2939 protected float getHeaderTranslation() { 2940 if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { 2941 return -mQs.getQsMinExpansionHeight(); 2942 } 2943 float appearAmount = mNotificationStackScrollLayoutController 2944 .calculateAppearFraction(mExpandedHeight); 2945 float startHeight = -mQsExpansionHeight; 2946 if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) { 2947 // Small parallax as we pull down and clip QS 2948 startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT; 2949 } 2950 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { 2951 appearAmount = mNotificationStackScrollLayoutController.calculateAppearFractionBypass(); 2952 startHeight = -mQs.getQsMinExpansionHeight(); 2953 } 2954 float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)); 2955 return Math.min(0, translation); 2956 } 2957 2958 /** 2959 * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area) 2960 * during swiping up 2961 */ getKeyguardContentsAlpha()2962 private float getKeyguardContentsAlpha() { 2963 float alpha; 2964 if (mBarState == KEYGUARD) { 2965 2966 // When on Keyguard, we hide the header as soon as we expanded close enough to the 2967 // header 2968 alpha = 2969 getExpandedHeight() / (mKeyguardStatusBar.getHeight() 2970 + mNotificationsHeaderCollideDistance); 2971 } else { 2972 2973 // In SHADE_LOCKED, the top card is already really close to the header. Hide it as 2974 // soon as we start translating the stack. 2975 alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight(); 2976 } 2977 alpha = MathUtils.saturate(alpha); 2978 alpha = (float) Math.pow(alpha, 0.75); 2979 return alpha; 2980 } 2981 updateHeaderKeyguardAlpha()2982 private void updateHeaderKeyguardAlpha() { 2983 if (!mKeyguardShowing) { 2984 return; 2985 } 2986 float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2); 2987 float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) 2988 * mKeyguardStatusBarAnimateAlpha; 2989 newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; 2990 mKeyguardStatusBar.setAlpha(newAlpha); 2991 boolean 2992 hideForBypass = 2993 mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace() 2994 || mDelayShowingKeyguardStatusBar; 2995 mKeyguardStatusBar.setVisibility( 2996 newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE); 2997 } 2998 updateKeyguardBottomAreaAlpha()2999 private void updateKeyguardBottomAreaAlpha() { 3000 // There are two possible panel expansion behaviors: 3001 // • User dragging up to unlock: we want to fade out as quick as possible 3002 // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area. 3003 // • User tapping on lock screen: bouncer won't be visible but panel expansion will 3004 // change due to "unlock hint animation." In this case, fading out the bottom area 3005 // would also hide the message that says "swipe to unlock," we don't want to do that. 3006 float expansionAlpha = MathUtils.map( 3007 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, 3008 getExpandedFraction()); 3009 float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); 3010 alpha *= mBottomAreaShadeAlpha; 3011 mKeyguardBottomArea.setAffordanceAlpha(alpha); 3012 mKeyguardBottomArea.setImportantForAccessibility( 3013 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 3014 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); 3015 View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer(); 3016 if (ambientIndicationContainer != null) { 3017 ambientIndicationContainer.setAlpha(alpha); 3018 } 3019 mLockIconViewController.setAlpha(alpha); 3020 } 3021 3022 /** 3023 * Custom clock fades away when user drags up to unlock or pulls down quick settings. 3024 * 3025 * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See 3026 * {@link #updateKeyguardBottomAreaAlpha}. 3027 */ updateBigClockAlpha()3028 private void updateBigClockAlpha() { 3029 float expansionAlpha = MathUtils.map( 3030 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, 3031 getExpandedFraction()); 3032 float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); 3033 mBigClockContainer.setAlpha(alpha); 3034 } 3035 3036 @Override onExpandingStarted()3037 protected void onExpandingStarted() { 3038 super.onExpandingStarted(); 3039 mNotificationStackScrollLayoutController.onExpansionStarted(); 3040 mIsExpanding = true; 3041 mQsExpandedWhenExpandingStarted = mQsFullyExpanded; 3042 mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted && 3043 /* We also start expanding when flinging closed Qs. Let's exclude that */ 3044 !mAnimatingQS); 3045 if (mQsExpanded) { 3046 onQsExpansionStarted(); 3047 } 3048 // Since there are QS tiles in the header now, we need to make sure we start listening 3049 // immediately so they can be up to date. 3050 if (mQs == null) return; 3051 mQs.setHeaderListening(true); 3052 } 3053 3054 @Override onExpandingFinished()3055 protected void onExpandingFinished() { 3056 super.onExpandingFinished(); 3057 mNotificationStackScrollLayoutController.onExpansionStopped(); 3058 mHeadsUpManager.onExpandingFinished(); 3059 mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); 3060 mIsExpanding = false; 3061 mMediaHierarchyManager.setCollapsingShadeFromQS(false); 3062 mMediaHierarchyManager.setQsExpanded(mQsExpanded); 3063 if (isFullyCollapsed()) { 3064 DejankUtils.postAfterTraversal(new Runnable() { 3065 @Override 3066 public void run() { 3067 setListening(false); 3068 } 3069 }); 3070 3071 // Workaround b/22639032: Make sure we invalidate something because else RenderThread 3072 // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go 3073 // ahead with rendering and we jank. 3074 mView.postOnAnimation(new Runnable() { 3075 @Override 3076 public void run() { 3077 mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT); 3078 } 3079 }); 3080 } else { 3081 setListening(true); 3082 } 3083 mQsExpandImmediate = false; 3084 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false); 3085 mTwoFingerQsExpandPossible = false; 3086 notifyListenersTrackingHeadsUp(null); 3087 mExpandingFromHeadsUp = false; 3088 setPanelScrimMinFraction(0.0f); 3089 } 3090 notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild)3091 private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) { 3092 for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { 3093 Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i); 3094 listener.accept(pickedChild); 3095 } 3096 } 3097 setListening(boolean listening)3098 private void setListening(boolean listening) { 3099 mKeyguardStatusBar.setListening(listening); 3100 if (mQs == null) return; 3101 mQs.setListening(listening); 3102 } 3103 3104 @Override expand(boolean animate)3105 public void expand(boolean animate) { 3106 super.expand(animate); 3107 setListening(true); 3108 } 3109 3110 @Override setOverExpansion(float overExpansion)3111 public void setOverExpansion(float overExpansion) { 3112 if (overExpansion == mOverExpansion) { 3113 return; 3114 } 3115 super.setOverExpansion(overExpansion); 3116 // Translating the quick settings by half the overexpansion to center it in the background 3117 // frame 3118 updateQsFrameTranslation(); 3119 mNotificationStackScrollLayoutController.setOverExpansion(overExpansion); 3120 } 3121 updateQsFrameTranslation()3122 private void updateQsFrameTranslation() { 3123 float translation = mOverExpansion / 2.0f + mQsTranslationForFullShadeTransition; 3124 mQsFrame.setTranslationY(translation); 3125 } 3126 3127 @Override onTrackingStarted()3128 protected void onTrackingStarted() { 3129 mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); 3130 super.onTrackingStarted(); 3131 if (mQsFullyExpanded) { 3132 mQsExpandImmediate = true; 3133 if (!mShouldUseSplitNotificationShade) { 3134 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 3135 } 3136 } 3137 if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 3138 mAffordanceHelper.animateHideLeftRightIcon(); 3139 } 3140 mNotificationStackScrollLayoutController.onPanelTrackingStarted(); 3141 } 3142 3143 @Override onTrackingStopped(boolean expand)3144 protected void onTrackingStopped(boolean expand) { 3145 mFalsingCollector.onTrackingStopped(); 3146 super.onTrackingStopped(expand); 3147 if (expand) { 3148 mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */, 3149 true /* animate */); 3150 } 3151 mNotificationStackScrollLayoutController.onPanelTrackingStopped(); 3152 if (expand && (mBarState == KEYGUARD 3153 || mBarState == StatusBarState.SHADE_LOCKED)) { 3154 if (!mHintAnimationRunning) { 3155 mAffordanceHelper.reset(true); 3156 } 3157 } 3158 } 3159 updateMaxHeadsUpTranslation()3160 private void updateMaxHeadsUpTranslation() { 3161 mNotificationStackScrollLayoutController.setHeadsUpBoundaries( 3162 getHeight(), mNavigationBarBottomHeight); 3163 } 3164 3165 @Override startUnlockHintAnimation()3166 protected void startUnlockHintAnimation() { 3167 if (mPowerManager.isPowerSaveMode()) { 3168 onUnlockHintStarted(); 3169 onUnlockHintFinished(); 3170 return; 3171 } 3172 super.startUnlockHintAnimation(); 3173 } 3174 3175 @Override onUnlockHintFinished()3176 protected void onUnlockHintFinished() { 3177 super.onUnlockHintFinished(); 3178 mNotificationStackScrollLayoutController.setUnlockHintRunning(false); 3179 } 3180 3181 @Override onUnlockHintStarted()3182 protected void onUnlockHintStarted() { 3183 super.onUnlockHintStarted(); 3184 mNotificationStackScrollLayoutController.setUnlockHintRunning(true); 3185 } 3186 3187 @Override shouldUseDismissingAnimation()3188 protected boolean shouldUseDismissingAnimation() { 3189 return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen() 3190 || !isTracking()); 3191 } 3192 3193 @Override isTrackingBlocked()3194 protected boolean isTrackingBlocked() { 3195 return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; 3196 } 3197 isQsExpanded()3198 public boolean isQsExpanded() { 3199 return mQsExpanded; 3200 } 3201 isQsDetailShowing()3202 public boolean isQsDetailShowing() { 3203 return mQs.isShowingDetail(); 3204 } 3205 closeQsDetail()3206 public void closeQsDetail() { 3207 mQs.closeDetail(); 3208 } 3209 isLaunchTransitionFinished()3210 public boolean isLaunchTransitionFinished() { 3211 return mIsLaunchTransitionFinished; 3212 } 3213 isLaunchTransitionRunning()3214 public boolean isLaunchTransitionRunning() { 3215 return mIsLaunchTransitionRunning; 3216 } 3217 setLaunchTransitionEndRunnable(Runnable r)3218 public void setLaunchTransitionEndRunnable(Runnable r) { 3219 mLaunchAnimationEndRunnable = r; 3220 } 3221 updateDozingVisibilities(boolean animate)3222 private void updateDozingVisibilities(boolean animate) { 3223 mKeyguardBottomArea.setDozing(mDozing, animate); 3224 if (!mDozing && animate) { 3225 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 3226 } 3227 } 3228 3229 @Override isDozing()3230 public boolean isDozing() { 3231 return mDozing; 3232 } 3233 setQsScrimEnabled(boolean qsScrimEnabled)3234 public void setQsScrimEnabled(boolean qsScrimEnabled) { 3235 boolean changed = mQsScrimEnabled != qsScrimEnabled; 3236 mQsScrimEnabled = qsScrimEnabled; 3237 if (changed) { 3238 updateQsState(); 3239 } 3240 } 3241 onScreenTurningOn()3242 public void onScreenTurningOn() { 3243 mKeyguardStatusViewController.dozeTimeTick(); 3244 } 3245 3246 @Override onMiddleClicked()3247 protected boolean onMiddleClicked() { 3248 switch (mBarState) { 3249 case KEYGUARD: 3250 if (!mDozingOnDown) { 3251 if (mUpdateMonitor.isFaceEnrolled() 3252 && !mUpdateMonitor.isFaceDetectionRunning() 3253 && !mUpdateMonitor.getUserCanSkipBouncer( 3254 KeyguardUpdateMonitor.getCurrentUser())) { 3255 mUpdateMonitor.requestFaceAuth(true); 3256 } else { 3257 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT, 3258 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 3259 mLockscreenGestureLogger 3260 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); 3261 startUnlockHintAnimation(); 3262 } 3263 } 3264 return true; 3265 case StatusBarState.SHADE_LOCKED: 3266 if (!mQsExpanded) { 3267 mStatusBarStateController.setState(KEYGUARD); 3268 } 3269 return true; 3270 case StatusBarState.SHADE: 3271 3272 // This gets called in the middle of the touch handling, where the state is still 3273 // that we are tracking the panel. Collapse the panel after this is done. 3274 mView.post(mPostCollapseRunnable); 3275 return false; 3276 default: 3277 return true; 3278 } 3279 } 3280 setPanelAlpha(int alpha, boolean animate)3281 public void setPanelAlpha(int alpha, boolean animate) { 3282 if (mPanelAlpha != alpha) { 3283 mPanelAlpha = alpha; 3284 PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255 3285 ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator, 3286 animate); 3287 } 3288 } 3289 setPanelAlphaEndAction(Runnable r)3290 public void setPanelAlphaEndAction(Runnable r) { 3291 mPanelAlphaEndAction = r; 3292 } 3293 updateKeyguardStatusBarForHeadsUp()3294 private void updateKeyguardStatusBarForHeadsUp() { 3295 boolean 3296 showingKeyguardHeadsUp = 3297 mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible(); 3298 if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) { 3299 mShowingKeyguardHeadsUp = showingKeyguardHeadsUp; 3300 if (mKeyguardShowing) { 3301 PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 3302 showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES, 3303 true /* animate */); 3304 } else { 3305 PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f); 3306 } 3307 } 3308 } 3309 setKeyguardHeadsUpShowingAmount(float amount)3310 private void setKeyguardHeadsUpShowingAmount(float amount) { 3311 mKeyguardHeadsUpShowingAmount = amount; 3312 updateHeaderKeyguardAlpha(); 3313 } 3314 getKeyguardHeadsUpShowingAmount()3315 private float getKeyguardHeadsUpShowingAmount() { 3316 return mKeyguardHeadsUpShowingAmount; 3317 } 3318 setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)3319 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { 3320 mHeadsUpAnimatingAway = headsUpAnimatingAway; 3321 mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); 3322 updateHeadsUpVisibility(); 3323 } 3324 updateHeadsUpVisibility()3325 private void updateHeadsUpVisibility() { 3326 ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode); 3327 } 3328 3329 @Override setHeadsUpManager(HeadsUpManagerPhone headsUpManager)3330 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { 3331 super.setHeadsUpManager(headsUpManager); 3332 mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, 3333 mNotificationStackScrollLayoutController.getHeadsUpCallback(), 3334 NotificationPanelViewController.this); 3335 } 3336 setTrackedHeadsUp(ExpandableNotificationRow pickedChild)3337 public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { 3338 if (pickedChild != null) { 3339 notifyListenersTrackingHeadsUp(pickedChild); 3340 mExpandingFromHeadsUp = true; 3341 } 3342 // otherwise we update the state when the expansion is finished 3343 } 3344 3345 @Override onClosingFinished()3346 protected void onClosingFinished() { 3347 super.onClosingFinished(); 3348 resetHorizontalPanelPosition(); 3349 setClosingWithAlphaFadeout(false); 3350 mMediaHierarchyManager.closeGuts(); 3351 } 3352 setClosingWithAlphaFadeout(boolean closing)3353 private void setClosingWithAlphaFadeout(boolean closing) { 3354 mClosingWithAlphaFadeOut = closing; 3355 mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing); 3356 } 3357 3358 /** 3359 * Updates the horizontal position of the panel so it is positioned closer to the touch 3360 * responsible for opening the panel. 3361 * 3362 * @param x the x-coordinate the touch event 3363 */ updateHorizontalPanelPosition(float x)3364 protected void updateHorizontalPanelPosition(float x) { 3365 if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth() 3366 || mShouldUseSplitNotificationShade) { 3367 resetHorizontalPanelPosition(); 3368 return; 3369 } 3370 float leftMost = mPositionMinSideMargin 3371 + mNotificationStackScrollLayoutController.getWidth() / 2; 3372 float 3373 rightMost = 3374 mView.getWidth() - mPositionMinSideMargin 3375 - mNotificationStackScrollLayoutController.getWidth() / 2; 3376 if (Math.abs(x - mView.getWidth() / 2) 3377 < mNotificationStackScrollLayoutController.getWidth() / 4) { 3378 x = mView.getWidth() / 2; 3379 } 3380 x = Math.min(rightMost, Math.max(leftMost, x)); 3381 float 3382 center = mNotificationStackScrollLayoutController.getLeft() 3383 + mNotificationStackScrollLayoutController.getWidth() / 2; 3384 setHorizontalPanelTranslation(x - center); 3385 } 3386 resetHorizontalPanelPosition()3387 private void resetHorizontalPanelPosition() { 3388 setHorizontalPanelTranslation(0f); 3389 } 3390 setHorizontalPanelTranslation(float translation)3391 protected void setHorizontalPanelTranslation(float translation) { 3392 mNotificationStackScrollLayoutController.setTranslationX(translation); 3393 mQsFrame.setTranslationX(translation); 3394 if (mVerticalTranslationListener != null) { 3395 mVerticalTranslationListener.run(); 3396 } 3397 } 3398 updateExpandedHeight(float expandedHeight)3399 protected void updateExpandedHeight(float expandedHeight) { 3400 if (mTracking) { 3401 mNotificationStackScrollLayoutController 3402 .setExpandingVelocity(getCurrentExpandVelocity()); 3403 } 3404 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { 3405 // The expandedHeight is always the full panel Height when bypassing 3406 expandedHeight = getMaxPanelHeight(); 3407 } 3408 mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); 3409 updateKeyguardBottomAreaAlpha(); 3410 updateBigClockAlpha(); 3411 updateStatusBarIcons(); 3412 } 3413 3414 /** 3415 * @return whether the notifications are displayed full width and don't have any margins on 3416 * the side. 3417 */ isFullWidth()3418 public boolean isFullWidth() { 3419 return mIsFullWidth; 3420 } 3421 updateStatusBarIcons()3422 private void updateStatusBarIcons() { 3423 boolean 3424 showIconsWhenExpanded = 3425 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) 3426 && getExpandedHeight() < getOpeningHeight(); 3427 boolean noVisibleNotifications = true; 3428 if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) { 3429 showIconsWhenExpanded = false; 3430 } 3431 if (showIconsWhenExpanded != mShowIconsWhenExpanded) { 3432 mShowIconsWhenExpanded = showIconsWhenExpanded; 3433 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 3434 } 3435 } 3436 3437 private boolean isOnKeyguard() { 3438 return mBarState == KEYGUARD; 3439 } 3440 3441 public void setPanelScrimMinFraction(float minFraction) { 3442 mBar.panelScrimMinFractionChanged(minFraction); 3443 } 3444 3445 public void clearNotificationEffects() { 3446 mStatusBar.clearNotificationEffects(); 3447 } 3448 3449 @Override 3450 protected boolean isPanelVisibleBecauseOfHeadsUp() { 3451 return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway) 3452 && mBarState == StatusBarState.SHADE; 3453 } 3454 3455 public void launchCamera(boolean animate, int source) { 3456 if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { 3457 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; 3458 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { 3459 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; 3460 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) { 3461 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER; 3462 } else { 3463 3464 // Default. 3465 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 3466 } 3467 3468 // If we are launching it when we are occluded already we don't want it to animate, 3469 // nor setting these flags, since the occluded state doesn't change anymore, hence it's 3470 // never reset. 3471 if (!isFullyCollapsed()) { 3472 setLaunchingAffordance(true); 3473 } else { 3474 animate = false; 3475 } 3476 mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null; 3477 mAffordanceHelper.launchAffordance( 3478 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 3479 } 3480 3481 public void onAffordanceLaunchEnded() { 3482 setLaunchingAffordance(false); 3483 } 3484 3485 /** 3486 * Set whether we are currently launching an affordance. This is currently only set when 3487 * launched via a camera gesture. 3488 */ 3489 private void setLaunchingAffordance(boolean launchingAffordance) { 3490 mLaunchingAffordance = launchingAffordance; 3491 mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance); 3492 mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance( 3493 launchingAffordance); 3494 mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); 3495 } 3496 3497 /** 3498 * Return true when a bottom affordance is launching an occluded activity with a splash screen. 3499 */ 3500 public boolean isLaunchingAffordanceWithPreview() { 3501 return mLaunchingAffordance && mAffordanceHasPreview; 3502 } 3503 3504 /** 3505 * Whether the camera application can be launched for the camera launch gesture. 3506 */ 3507 public boolean canCameraGestureBeLaunched() { 3508 if (!mStatusBar.isCameraAllowedByAdmin()) { 3509 return false; 3510 } 3511 3512 ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); 3513 String 3514 packageToLaunch = 3515 (resolveInfo == null || resolveInfo.activityInfo == null) ? null 3516 : resolveInfo.activityInfo.packageName; 3517 return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp( 3518 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress(); 3519 } 3520 3521 /** 3522 * Return true if the applications with the package name is running in foreground. 3523 * 3524 * @param pkgName application package name. 3525 */ 3526 private boolean isForegroundApp(String pkgName) { 3527 List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); 3528 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 3529 } 3530 3531 public boolean hideStatusBarIconsWhenExpanded() { 3532 if (mIsLaunchAnimationRunning) { 3533 return mHideIconsDuringLaunchAnimation; 3534 } 3535 if (mHeadsUpAppearanceController != null 3536 && mHeadsUpAppearanceController.shouldBeVisible()) { 3537 return false; 3538 } 3539 return !isFullWidth() || !mShowIconsWhenExpanded; 3540 } 3541 3542 public final QS.ScrollListener mScrollListener = scrollY -> { 3543 if (scrollY > 0 && !mQsFullyExpanded) { 3544 if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand"); 3545 // If we are scrolling QS, we should be fully expanded. 3546 expandWithQs(); 3547 } 3548 }; 3549 3550 private final FragmentListener mFragmentListener = new FragmentListener() { 3551 @Override 3552 public void onFragmentViewCreated(String tag, Fragment fragment) { 3553 mQs = (QS) fragment; 3554 mQs.setPanelView(mHeightListener); 3555 mQs.setExpandClickListener(mOnClickListener); 3556 mQs.setHeaderClickable(isQsExpansionEnabled()); 3557 mQs.setOverscrolling(mStackScrollerOverscrolling); 3558 mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); 3559 3560 // recompute internal state when qspanel height changes 3561 mQs.getView().addOnLayoutChangeListener( 3562 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 3563 final int height = bottom - top; 3564 final int oldHeight = oldBottom - oldTop; 3565 if (height != oldHeight) { 3566 mHeightListener.onQsHeightChanged(); 3567 } 3568 }); 3569 mQs.setCollapsedMediaVisibilityChangedListener((visible) -> { 3570 if (mQs.getHeader().isShown()) { 3571 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD, 3572 0 /* delay */); 3573 mNotificationStackScrollLayoutController.animateNextTopPaddingChange(); 3574 } 3575 }); 3576 mLockscreenShadeTransitionController.setQS(mQs); 3577 mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView()); 3578 mQs.setScrollListener(mScrollListener); 3579 updateQsExpansion(); 3580 } 3581 3582 @Override 3583 public void onFragmentViewDestroyed(String tag, Fragment fragment) { 3584 // Manual handling of fragment lifecycle is only required because this bridges 3585 // non-fragment and fragment code. Once we are using a fragment for the notification 3586 // panel, mQs will not need to be null cause it will be tied to the same lifecycle. 3587 if (fragment == mQs) { 3588 mQs = null; 3589 } 3590 } 3591 }; 3592 animateNextNotificationBounds(long duration, long delay)3593 private void animateNextNotificationBounds(long duration, long delay) { 3594 mAnimateNextNotificationBounds = true; 3595 mNotificationBoundsAnimationDuration = duration; 3596 mNotificationBoundsAnimationDelay = delay; 3597 } 3598 3599 @Override setTouchAndAnimationDisabled(boolean disabled)3600 public void setTouchAndAnimationDisabled(boolean disabled) { 3601 super.setTouchAndAnimationDisabled(disabled); 3602 if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { 3603 mAffordanceHelper.reset(false /* animate */); 3604 } 3605 mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled); 3606 } 3607 3608 /** 3609 * Sets the dozing state. 3610 * 3611 * @param dozing {@code true} when dozing. 3612 * @param animate if transition should be animated. 3613 * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor. 3614 */ setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)3615 public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { 3616 if (dozing == mDozing) return; 3617 mView.setDozing(dozing); 3618 mDozing = dozing; 3619 mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation); 3620 mKeyguardBottomArea.setDozing(mDozing, animate); 3621 3622 if (dozing) { 3623 mBottomAreaShadeAlphaAnimator.cancel(); 3624 } 3625 3626 if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 3627 updateDozingVisibilities(animate); 3628 } 3629 3630 final float dozeAmount = dozing ? 1 : 0; 3631 mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate); 3632 } 3633 setPulsing(boolean pulsing)3634 public void setPulsing(boolean pulsing) { 3635 mPulsing = pulsing; 3636 final boolean 3637 animatePulse = 3638 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn(); 3639 if (animatePulse) { 3640 mAnimateNextPositionUpdate = true; 3641 } 3642 // Do not animate the clock when waking up from a pulse. 3643 // The height callback will take care of pushing the clock to the right position. 3644 if (!mPulsing && !mDozing) { 3645 mAnimateNextPositionUpdate = false; 3646 } 3647 mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse); 3648 } 3649 setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding)3650 public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { 3651 if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { 3652 mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; 3653 updateMaxDisplayedNotifications(true); 3654 } 3655 } 3656 dozeTimeTick()3657 public void dozeTimeTick() { 3658 mKeyguardBottomArea.dozeTimeTick(); 3659 mKeyguardStatusViewController.dozeTimeTick(); 3660 if (mInterpolatedDarkAmount > 0) { 3661 positionClockAndNotifications(); 3662 } 3663 } 3664 setStatusAccessibilityImportance(int mode)3665 public void setStatusAccessibilityImportance(int mode) { 3666 mKeyguardStatusViewController.setStatusAccessibilityImportance(mode); 3667 } 3668 3669 /** 3670 * TODO: this should be removed. 3671 * It's not correct to pass this view forward because other classes will end up adding 3672 * children to it. Theme will be out of sync. 3673 * 3674 * @return bottom area view 3675 */ getKeyguardBottomAreaView()3676 public KeyguardBottomAreaView getKeyguardBottomAreaView() { 3677 return mKeyguardBottomArea; 3678 } 3679 setUserSetupComplete(boolean userSetupComplete)3680 public void setUserSetupComplete(boolean userSetupComplete) { 3681 mUserSetupComplete = userSetupComplete; 3682 mKeyguardBottomArea.setUserSetupComplete(userSetupComplete); 3683 } 3684 applyLaunchAnimationProgress(float linearProgress)3685 public void applyLaunchAnimationProgress(float linearProgress) { 3686 boolean hideIcons = ActivityLaunchAnimator.getProgress(linearProgress, 3687 ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; 3688 if (hideIcons != mHideIconsDuringLaunchAnimation) { 3689 mHideIconsDuringLaunchAnimation = hideIcons; 3690 if (!hideIcons) { 3691 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); 3692 } 3693 } 3694 } 3695 addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3696 public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 3697 mTrackingHeadsUpListeners.add(listener); 3698 } 3699 removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3700 public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 3701 mTrackingHeadsUpListeners.remove(listener); 3702 } 3703 setVerticalTranslationListener(Runnable verticalTranslationListener)3704 public void setVerticalTranslationListener(Runnable verticalTranslationListener) { 3705 mVerticalTranslationListener = verticalTranslationListener; 3706 } 3707 setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)3708 public void setHeadsUpAppearanceController( 3709 HeadsUpAppearanceController headsUpAppearanceController) { 3710 mHeadsUpAppearanceController = headsUpAppearanceController; 3711 } 3712 3713 /** 3714 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the 3715 * security view of the bouncer. 3716 */ onBouncerPreHideAnimation()3717 public void onBouncerPreHideAnimation() { 3718 if (mKeyguardQsUserSwitchController != null) { 3719 mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility( 3720 mBarState, 3721 true /* keyguardFadingAway */, 3722 false /* goingToFullShade */, 3723 mBarState); 3724 } 3725 if (mKeyguardUserSwitcherController != null) { 3726 mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility( 3727 mBarState, 3728 true /* keyguardFadingAway */, 3729 false /* goingToFullShade */, 3730 mBarState); 3731 } 3732 } 3733 3734 /** 3735 * Do not let the user drag the shade up and down for the current touch session. 3736 * This is necessary to avoid shade expansion while/after the bouncer is dismissed. 3737 */ blockExpansionForCurrentTouch()3738 public void blockExpansionForCurrentTouch() { 3739 mBlockingExpansionForCurrentTouch = mTracking; 3740 } 3741 3742 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)3743 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3744 super.dump(fd, pw, args); 3745 pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect() 3746 + " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom 3747 + ") qsVisible(" + mQsVisible 3748 ); 3749 if (mKeyguardStatusBar != null) { 3750 mKeyguardStatusBar.dump(fd, pw, args); 3751 } 3752 } 3753 hasActiveClearableNotifications()3754 public boolean hasActiveClearableNotifications() { 3755 return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL); 3756 } 3757 createRemoteInputDelegate()3758 public RemoteInputController.Delegate createRemoteInputDelegate() { 3759 return mNotificationStackScrollLayoutController.createDelegate(); 3760 } 3761 3762 /** 3763 * Updates the notification views' sections and status bar icons. This is 3764 * triggered by the NotificationPresenter whenever there are changes to the underlying 3765 * notification data being displayed. In the new notification pipeline, this is handled in 3766 * {@link ShadeViewManager}. 3767 */ updateNotificationViews(String reason)3768 public void updateNotificationViews(String reason) { 3769 mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); 3770 mNotificationStackScrollLayoutController.updateFooter(); 3771 3772 mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList()); 3773 } 3774 createVisibleEntriesList()3775 private List<ListEntry> createVisibleEntriesList() { 3776 List<ListEntry> entries = new ArrayList<>( 3777 mNotificationStackScrollLayoutController.getChildCount()); 3778 for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { 3779 View view = mNotificationStackScrollLayoutController.getChildAt(i); 3780 if (view instanceof ExpandableNotificationRow) { 3781 entries.add(((ExpandableNotificationRow) view).getEntry()); 3782 } 3783 } 3784 return entries; 3785 } 3786 onUpdateRowStates()3787 public void onUpdateRowStates() { 3788 mNotificationStackScrollLayoutController.onUpdateRowStates(); 3789 } 3790 hasPulsingNotifications()3791 public boolean hasPulsingNotifications() { 3792 return mNotificationStackScrollLayoutController 3793 .getNotificationListContainer().hasPulsingNotifications(); 3794 } 3795 getActivatedChild()3796 public ActivatableNotificationView getActivatedChild() { 3797 return mNotificationStackScrollLayoutController.getActivatedChild(); 3798 } 3799 setActivatedChild(ActivatableNotificationView o)3800 public void setActivatedChild(ActivatableNotificationView o) { 3801 mNotificationStackScrollLayoutController.setActivatedChild(o); 3802 } 3803 runAfterAnimationFinished(Runnable r)3804 public void runAfterAnimationFinished(Runnable r) { 3805 mNotificationStackScrollLayoutController.runAfterAnimationFinished(r); 3806 } 3807 setScrollingEnabled(boolean b)3808 public void setScrollingEnabled(boolean b) { 3809 mNotificationStackScrollLayoutController.setScrollingEnabled(b); 3810 } 3811 3812 /** 3813 * Initialize objects instead of injecting to avoid circular dependencies. 3814 */ initDependencies( StatusBar statusBar, NotificationShelfController notificationShelfController)3815 public void initDependencies( 3816 StatusBar statusBar, 3817 NotificationShelfController notificationShelfController) { 3818 setStatusBar(statusBar); 3819 mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); 3820 mNotificationShelfController = notificationShelfController; 3821 mLockscreenShadeTransitionController.bindController(notificationShelfController); 3822 updateMaxDisplayedNotifications(true); 3823 } 3824 setAlpha(float alpha)3825 public void setAlpha(float alpha) { 3826 mView.setAlpha(alpha); 3827 } 3828 fadeOut(long startDelayMs, long durationMs, Runnable endAction)3829 public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { 3830 return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( 3831 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( 3832 endAction); 3833 } 3834 resetViewGroupFade()3835 public void resetViewGroupFade() { 3836 ViewGroupFadeHelper.reset(mView); 3837 } 3838 addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3839 public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3840 mView.getViewTreeObserver().addOnGlobalLayoutListener(listener); 3841 } 3842 removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3843 public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3844 mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); 3845 } 3846 getOnHeadsUpChangedListener()3847 public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() { 3848 return mOnHeadsUpChangedListener; 3849 } 3850 getHeight()3851 public int getHeight() { 3852 return mView.getHeight(); 3853 } 3854 setHeaderDebugInfo(String text)3855 public void setHeaderDebugInfo(String text) { 3856 if (DEBUG) mHeaderDebugInfo = text; 3857 } 3858 onThemeChanged()3859 public void onThemeChanged() { 3860 mConfigurationListener.onThemeChanged(); 3861 } 3862 3863 @Override createLayoutChangeListener()3864 public OnLayoutChangeListener createLayoutChangeListener() { 3865 return new OnLayoutChangeListener(); 3866 } 3867 3868 @Override createTouchHandler()3869 protected TouchHandler createTouchHandler() { 3870 return new TouchHandler() { 3871 3872 private long mLastTouchDownTime = -1L; 3873 3874 @Override 3875 public boolean onInterceptTouchEvent(MotionEvent event) { 3876 if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) { 3877 return false; 3878 } 3879 initDownStates(event); 3880 // Do not let touches go to shade or QS if the bouncer is visible, 3881 // but still let user swipe down to expand the panel, dismissing the bouncer. 3882 if (mStatusBar.isBouncerShowing()) { 3883 return true; 3884 } 3885 if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3886 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3887 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3888 return true; 3889 } 3890 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) 3891 && mPulseExpansionHandler.onInterceptTouchEvent(event)) { 3892 return true; 3893 } 3894 3895 if (!isFullyCollapsed() && onQsIntercept(event)) { 3896 return true; 3897 } 3898 return super.onInterceptTouchEvent(event); 3899 } 3900 3901 @Override 3902 public boolean onTouch(View v, MotionEvent event) { 3903 if (event.getAction() == MotionEvent.ACTION_DOWN) { 3904 if (event.getDownTime() == mLastTouchDownTime) { 3905 // An issue can occur when swiping down after unlock, where multiple down 3906 // events are received in this handler with identical downTimes. Until the 3907 // source of the issue can be located, detect this case and ignore. 3908 // see b/193350347 3909 Log.w(TAG, "Duplicate down event detected... ignoring"); 3910 return true; 3911 } 3912 mLastTouchDownTime = event.getDownTime(); 3913 } 3914 3915 3916 if (mBlockTouches || (mQsFullyExpanded && mQs != null 3917 && mQs.disallowPanelTouches())) { 3918 return false; 3919 } 3920 3921 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able 3922 // to pull down QS or expand the shade. 3923 if (mStatusBar.isBouncerShowingScrimmed()) { 3924 return false; 3925 } 3926 3927 // Make sure the next touch won't the blocked after the current ends. 3928 if (event.getAction() == MotionEvent.ACTION_UP 3929 || event.getAction() == MotionEvent.ACTION_CANCEL) { 3930 mBlockingExpansionForCurrentTouch = false; 3931 } 3932 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately 3933 // without any ACTION_MOVE event. 3934 // In such case, simply expand the panel instead of being stuck at the bottom bar. 3935 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { 3936 expand(true /* animate */); 3937 } 3938 initDownStates(event); 3939 3940 // If pulse is expanding already, let's give it the touch. There are situations 3941 // where the panel starts expanding even though we're also pulsing 3942 boolean pulseShouldGetTouch = (!mIsExpanding 3943 && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)) 3944 || mPulseExpansionHandler.isExpanding(); 3945 if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) { 3946 // We're expanding all the other ones shouldn't get this anymore 3947 return true; 3948 } 3949 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() 3950 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3951 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3952 } 3953 boolean handled = false; 3954 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded 3955 && mBarState != StatusBarState.SHADE && !mDozing) { 3956 handled |= mAffordanceHelper.onTouchEvent(event); 3957 } 3958 if (mOnlyAffordanceInThisMotion) { 3959 return true; 3960 } 3961 handled |= mHeadsUpTouchHelper.onTouchEvent(event); 3962 3963 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { 3964 return true; 3965 } 3966 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { 3967 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3968 updateHorizontalPanelPosition(event.getX()); 3969 handled = true; 3970 } 3971 3972 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded() 3973 && mStatusBarKeyguardViewManager.isShowing()) { 3974 mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX()); 3975 } 3976 3977 if (mLockIconViewController.onTouchEvent(event)) { 3978 return true; 3979 } 3980 3981 handled |= super.onTouch(v, event); 3982 return !mDozing || mPulsing || handled; 3983 } 3984 }; 3985 } 3986 3987 @Override 3988 protected PanelViewController.OnConfigurationChangedListener createOnConfigurationChangedListener()3989 createOnConfigurationChangedListener() { 3990 return new OnConfigurationChangedListener(); 3991 } 3992 getNotificationStackScrollLayoutController()3993 public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() { 3994 return mNotificationStackScrollLayoutController; 3995 } 3996 3997 /** 3998 * Close the keyguard user switcher if it is open and capable of closing. 3999 * 4000 * Has no effect if user switcher isn't supported, if the user switcher is already closed, or 4001 * if the user switcher uses "simple" mode. The simple user switcher cannot be closed. 4002 * 4003 * @return true if the keyguard user switcher was open, and is now closed 4004 */ closeUserSwitcherIfOpen()4005 public boolean closeUserSwitcherIfOpen() { 4006 if (mKeyguardUserSwitcherController != null) { 4007 return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple( 4008 true /* animate */); 4009 } 4010 return false; 4011 } 4012 updateUserSwitcherFlags()4013 private void updateUserSwitcherFlags() { 4014 mKeyguardUserSwitcherEnabled = mResources.getBoolean( 4015 com.android.internal.R.bool.config_keyguardUserSwitcher); 4016 mKeyguardQsUserSwitchEnabled = 4017 mKeyguardUserSwitcherEnabled && mResources.getBoolean( 4018 R.bool.config_keyguard_user_switch_opens_qs_details); 4019 } 4020 registerSettingsChangeListener()4021 private void registerSettingsChangeListener() { 4022 mContentResolver.registerContentObserver( 4023 Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), 4024 /* notifyForDescendants */ false, 4025 mSettingsChangeObserver 4026 ); 4027 } 4028 unregisterSettingsChangeListener()4029 private void unregisterSettingsChangeListener() { 4030 mContentResolver.unregisterContentObserver(mSettingsChangeObserver); 4031 } 4032 4033 private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { 4034 @Override onHeightChanged(ExpandableView view, boolean needsAnimation)4035 public void onHeightChanged(ExpandableView view, boolean needsAnimation) { 4036 4037 // Block update if we are in quick settings and just the top padding changed 4038 // (i.e. view == null). 4039 if (view == null && mQsExpanded) { 4040 return; 4041 } 4042 if (needsAnimation && mInterpolatedDarkAmount == 0) { 4043 mAnimateNextPositionUpdate = true; 4044 } 4045 ExpandableView firstChildNotGone = 4046 mNotificationStackScrollLayoutController.getFirstChildNotGone(); 4047 ExpandableNotificationRow 4048 firstRow = 4049 firstChildNotGone instanceof ExpandableNotificationRow 4050 ? (ExpandableNotificationRow) firstChildNotGone : null; 4051 if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent() 4052 == firstRow))) { 4053 requestScrollerTopPaddingUpdate(false /* animate */); 4054 } 4055 requestPanelHeightUpdate(); 4056 } 4057 4058 @Override onReset(ExpandableView view)4059 public void onReset(ExpandableView view) { 4060 } 4061 } 4062 4063 private class OnClickListener implements View.OnClickListener { 4064 @Override onClick(View v)4065 public void onClick(View v) { 4066 onQsExpansionStarted(); 4067 if (mQsExpanded) { 4068 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, 4069 true /* isClick */); 4070 } else if (isQsExpansionEnabled()) { 4071 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); 4072 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, 4073 true /* isClick */); 4074 } 4075 } 4076 } 4077 4078 private class OnOverscrollTopChangedListener implements 4079 NotificationStackScrollLayout.OnOverscrollTopChangedListener { 4080 @Override onOverscrollTopChanged(float amount, boolean isRubberbanded)4081 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { 4082 // When in split shade, overscroll shouldn't carry through to QS 4083 if (mShouldUseSplitNotificationShade) { 4084 return; 4085 } 4086 cancelQsAnimation(); 4087 if (!isQsExpansionEnabled()) { 4088 amount = 0f; 4089 } 4090 float rounded = amount >= 1f ? amount : 0f; 4091 setOverScrolling(rounded != 0f && isRubberbanded); 4092 mQsExpansionFromOverscroll = rounded != 0f; 4093 mLastOverscroll = rounded; 4094 updateQsState(); 4095 setQsExpansion(mQsMinExpansionHeight + rounded); 4096 } 4097 4098 @Override flingTopOverscroll(float velocity, boolean open)4099 public void flingTopOverscroll(float velocity, boolean open) { 4100 // in split shade mode we want to expand/collapse QS only when touch happens within QS 4101 if (mShouldUseSplitNotificationShade 4102 && (mInitialTouchX < mQsFrame.getX() 4103 || mInitialTouchX > mQsFrame.getX() + mQsFrame.getWidth())) { 4104 return; 4105 } 4106 mLastOverscroll = 0f; 4107 mQsExpansionFromOverscroll = false; 4108 if (open) { 4109 // During overscrolling, qsExpansion doesn't actually change that the qs is 4110 // becoming expanded. Any layout could therefore reset the position again. Let's 4111 // make sure we can expand 4112 setOverScrolling(false); 4113 } 4114 setQsExpansion(mQsExpansionHeight); 4115 boolean canExpand = isQsExpansionEnabled(); 4116 flingSettings(!canExpand && open ? 0f : velocity, 4117 open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> { 4118 setOverScrolling(false); 4119 updateQsState(); 4120 }, false /* isClick */); 4121 } 4122 } 4123 4124 private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener { 4125 @Override onDynamicPrivacyChanged()4126 public void onDynamicPrivacyChanged() { 4127 // Do not request animation when pulsing or waking up, otherwise the clock wiill be out 4128 // of sync with the notification panel. 4129 if (mLinearDarkAmount != 0) { 4130 return; 4131 } 4132 mAnimateNextPositionUpdate = true; 4133 } 4134 } 4135 4136 private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback { 4137 @Override onAnimationToSideStarted(boolean rightPage, float translation, float vel)4138 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { 4139 boolean 4140 start = 4141 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage 4142 : !rightPage; 4143 mIsLaunchTransitionRunning = true; 4144 mLaunchAnimationEndRunnable = null; 4145 float displayDensity = mStatusBar.getDisplayDensity(); 4146 int lengthDp = Math.abs((int) (translation / displayDensity)); 4147 int velocityDp = Math.abs((int) (vel / displayDensity)); 4148 if (start) { 4149 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); 4150 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER); 4151 mFalsingCollector.onLeftAffordanceOn(); 4152 if (mFalsingCollector.shouldEnforceBouncer()) { 4153 mStatusBar.executeRunnableDismissingKeyguard( 4154 () -> mKeyguardBottomArea.launchLeftAffordance(), null, 4155 true /* dismissShade */, false /* afterKeyguardGone */, 4156 true /* deferred */); 4157 } else { 4158 mKeyguardBottomArea.launchLeftAffordance(); 4159 } 4160 } else { 4161 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( 4162 mLastCameraLaunchSource)) { 4163 mLockscreenGestureLogger.write( 4164 MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); 4165 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA); 4166 } 4167 mFalsingCollector.onCameraOn(); 4168 if (mFalsingCollector.shouldEnforceBouncer()) { 4169 mStatusBar.executeRunnableDismissingKeyguard( 4170 () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, 4171 true /* dismissShade */, false /* afterKeyguardGone */, 4172 true /* deferred */); 4173 } else { 4174 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); 4175 } 4176 } 4177 mStatusBar.startLaunchTransitionTimeout(); 4178 mBlockTouches = true; 4179 } 4180 4181 @Override onAnimationToSideEnded()4182 public void onAnimationToSideEnded() { 4183 mIsLaunchTransitionRunning = false; 4184 mIsLaunchTransitionFinished = true; 4185 if (mLaunchAnimationEndRunnable != null) { 4186 mLaunchAnimationEndRunnable.run(); 4187 mLaunchAnimationEndRunnable = null; 4188 } 4189 mStatusBar.readyForKeyguardDone(); 4190 } 4191 4192 @Override getMaxTranslationDistance()4193 public float getMaxTranslationDistance() { 4194 return (float) Math.hypot(mView.getWidth(), getHeight()); 4195 } 4196 4197 @Override onSwipingStarted(boolean rightIcon)4198 public void onSwipingStarted(boolean rightIcon) { 4199 mFalsingCollector.onAffordanceSwipingStarted(rightIcon); 4200 boolean 4201 camera = 4202 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 4203 : rightIcon; 4204 if (camera) { 4205 mKeyguardBottomArea.bindCameraPrewarmService(); 4206 } 4207 mView.requestDisallowInterceptTouchEvent(true); 4208 mOnlyAffordanceInThisMotion = true; 4209 mQsTracking = false; 4210 } 4211 4212 @Override onSwipingAborted()4213 public void onSwipingAborted() { 4214 mFalsingCollector.onAffordanceSwipingAborted(); 4215 mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); 4216 } 4217 4218 @Override onIconClicked(boolean rightIcon)4219 public void onIconClicked(boolean rightIcon) { 4220 if (mHintAnimationRunning) { 4221 return; 4222 } 4223 mHintAnimationRunning = true; 4224 mAffordanceHelper.startHintAnimation(rightIcon, () -> { 4225 mHintAnimationRunning = false; 4226 mStatusBar.onHintFinished(); 4227 }); 4228 rightIcon = 4229 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 4230 : rightIcon; 4231 if (rightIcon) { 4232 mStatusBar.onCameraHintStarted(); 4233 } else { 4234 if (mKeyguardBottomArea.isLeftVoiceAssist()) { 4235 mStatusBar.onVoiceAssistHintStarted(); 4236 } else { 4237 mStatusBar.onPhoneHintStarted(); 4238 } 4239 } 4240 } 4241 4242 @Override getLeftIcon()4243 public KeyguardAffordanceView getLeftIcon() { 4244 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4245 ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView(); 4246 } 4247 4248 @Override getRightIcon()4249 public KeyguardAffordanceView getRightIcon() { 4250 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4251 ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView(); 4252 } 4253 4254 @Override getLeftPreview()4255 public View getLeftPreview() { 4256 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4257 ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview(); 4258 } 4259 4260 @Override getRightPreview()4261 public View getRightPreview() { 4262 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4263 ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview(); 4264 } 4265 4266 @Override getAffordanceFalsingFactor()4267 public float getAffordanceFalsingFactor() { 4268 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 4269 } 4270 4271 @Override needsAntiFalsing()4272 public boolean needsAntiFalsing() { 4273 return mBarState == KEYGUARD; 4274 } 4275 } 4276 4277 private class OnEmptySpaceClickListener implements 4278 NotificationStackScrollLayout.OnEmptySpaceClickListener { 4279 @Override onEmptySpaceClicked(float x, float y)4280 public void onEmptySpaceClicked(float x, float y) { 4281 onEmptySpaceClick(x); 4282 } 4283 } 4284 4285 private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { 4286 @Override onHeadsUpPinnedModeChanged(final boolean inPinnedMode)4287 public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { 4288 if (inPinnedMode) { 4289 mHeadsUpExistenceChangedRunnable.run(); 4290 updateNotificationTranslucency(); 4291 } else { 4292 setHeadsUpAnimatingAway(true); 4293 mNotificationStackScrollLayoutController.runAfterAnimationFinished( 4294 mHeadsUpExistenceChangedRunnable); 4295 } 4296 updateGestureExclusionRect(); 4297 mHeadsUpPinnedMode = inPinnedMode; 4298 updateHeadsUpVisibility(); 4299 updateKeyguardStatusBarForHeadsUp(); 4300 } 4301 4302 @Override onHeadsUpPinned(NotificationEntry entry)4303 public void onHeadsUpPinned(NotificationEntry entry) { 4304 if (!isOnKeyguard()) { 4305 mNotificationStackScrollLayoutController.generateHeadsUpAnimation( 4306 entry.getHeadsUpAnimationView(), true); 4307 } 4308 } 4309 4310 @Override onHeadsUpUnPinned(NotificationEntry entry)4311 public void onHeadsUpUnPinned(NotificationEntry entry) { 4312 4313 // When we're unpinning the notification via active edge they remain heads-upped, 4314 // we need to make sure that an animation happens in this case, otherwise the 4315 // notification 4316 // will stick to the top without any interaction. 4317 if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { 4318 mNotificationStackScrollLayoutController.generateHeadsUpAnimation( 4319 entry.getHeadsUpAnimationView(), false); 4320 entry.setHeadsUpIsVisible(); 4321 } 4322 } 4323 } 4324 4325 private class HeightListener implements QS.HeightListener { onQsHeightChanged()4326 public void onQsHeightChanged() { 4327 mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0; 4328 if (mQsExpanded && mQsFullyExpanded) { 4329 mQsExpansionHeight = mQsMaxExpansionHeight; 4330 requestScrollerTopPaddingUpdate(false /* animate */); 4331 requestPanelHeightUpdate(); 4332 } 4333 if (mAccessibilityManager.isEnabled()) { 4334 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 4335 } 4336 mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight); 4337 } 4338 } 4339 4340 private class ConfigurationListener implements ConfigurationController.ConfigurationListener { 4341 @Override onThemeChanged()4342 public void onThemeChanged() { 4343 if (DEBUG) Log.d(TAG, "onThemeChanged"); 4344 final int themeResId = mView.getContext().getThemeResId(); 4345 if (mThemeResId == themeResId) { 4346 return; 4347 } 4348 mThemeResId = themeResId; 4349 4350 reInflateViews(); 4351 } 4352 4353 @Override onSmallestScreenWidthChanged()4354 public void onSmallestScreenWidthChanged() { 4355 if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged"); 4356 4357 // Can affect multi-user switcher visibility as it depends on screen size by default: 4358 // it is enabled only for devices with large screens (see config_keyguardUserSwitcher) 4359 reInflateViews(); 4360 } 4361 4362 @Override onOverlayChanged()4363 public void onOverlayChanged() { 4364 if (DEBUG) Log.d(TAG, "onOverlayChanged"); 4365 reInflateViews(); 4366 } 4367 4368 @Override onDensityOrFontScaleChanged()4369 public void onDensityOrFontScaleChanged() { 4370 if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged"); 4371 reInflateViews(); 4372 } 4373 } 4374 4375 private class SettingsChangeObserver extends ContentObserver { 4376 SettingsChangeObserver(Handler handler)4377 SettingsChangeObserver(Handler handler) { 4378 super(handler); 4379 } 4380 4381 @Override onChange(boolean selfChange)4382 public void onChange(boolean selfChange) { 4383 if (DEBUG) Log.d(TAG, "onSettingsChanged"); 4384 4385 // Can affect multi-user switcher visibility 4386 reInflateViews(); 4387 } 4388 } 4389 4390 private class StatusBarStateListener implements StateListener { 4391 @Override onStateChanged(int statusBarState)4392 public void onStateChanged(int statusBarState) { 4393 boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); 4394 boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); 4395 int oldState = mBarState; 4396 boolean keyguardShowing = statusBarState == KEYGUARD; 4397 4398 if (mDozeParameters.shouldControlUnlockedScreenOff() 4399 && oldState == StatusBarState.SHADE 4400 && statusBarState == KEYGUARD) { 4401 // This means we're doing the screen off animation - position the keyguard status 4402 // view where it'll be on AOD, so we can animate it in. 4403 mKeyguardStatusViewController.updatePosition( 4404 mClockPositionResult.clockX, 4405 mClockPositionResult.clockYFullyDozing, 4406 mClockPositionResult.clockScale, 4407 false /* animate */); 4408 } 4409 4410 mKeyguardStatusViewController.setKeyguardStatusViewVisibility( 4411 statusBarState, 4412 keyguardFadingAway, 4413 goingToFullShade, 4414 mBarState); 4415 setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); 4416 4417 mBarState = statusBarState; 4418 mKeyguardShowing = keyguardShowing; 4419 4420 if (oldState == KEYGUARD && (goingToFullShade 4421 || statusBarState == StatusBarState.SHADE_LOCKED)) { 4422 animateKeyguardStatusBarOut(); 4423 updateQSMinHeight(); 4424 } else if (oldState == StatusBarState.SHADE_LOCKED 4425 && statusBarState == KEYGUARD) { 4426 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 4427 mNotificationStackScrollLayoutController.resetScrollPosition(); 4428 // Only animate header if the header is visible. If not, it will partially 4429 // animate out 4430 // the top of QS 4431 if (!mQsExpanded) { 4432 // TODO(b/185683835) Nicer clipping when using new spacial model 4433 if (mShouldUseSplitNotificationShade) { 4434 mQs.animateHeaderSlidingOut(); 4435 } 4436 } 4437 } else { 4438 mKeyguardStatusBar.setAlpha(1f); 4439 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); 4440 if (keyguardShowing && oldState != mBarState) { 4441 if (mQs != null) { 4442 mQs.hideImmediately(); 4443 } 4444 } 4445 } 4446 updateKeyguardStatusBarForHeadsUp(); 4447 if (keyguardShowing) { 4448 updateDozingVisibilities(false /* animate */); 4449 } 4450 4451 updateMaxDisplayedNotifications(false); 4452 // The update needs to happen after the headerSlide in above, otherwise the translation 4453 // would reset 4454 maybeAnimateBottomAreaAlpha(); 4455 resetHorizontalPanelPosition(); 4456 updateQsState(); 4457 } 4458 4459 @Override onDozeAmountChanged(float linearAmount, float amount)4460 public void onDozeAmountChanged(float linearAmount, float amount) { 4461 mInterpolatedDarkAmount = amount; 4462 mLinearDarkAmount = linearAmount; 4463 mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount); 4464 mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); 4465 positionClockAndNotifications(); 4466 } 4467 } 4468 4469 /** 4470 * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the 4471 * screen off animation controller in order to animate in AOD without "actually" fully switching 4472 * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the 4473 * change. 4474 */ showAodUi()4475 public void showAodUi() { 4476 setDozing(true /* dozing */, false /* animate */, null); 4477 mStatusBarStateController.setUpcomingState(KEYGUARD); 4478 mEntryManager.updateNotifications("showAodUi"); 4479 mStatusBarStateListener.onStateChanged(KEYGUARD); 4480 mStatusBarStateListener.onDozeAmountChanged(1f, 1f); 4481 setExpandedFraction(1f); 4482 } 4483 4484 /** 4485 * Sets the overstretch amount in raw pixels when dragging down. 4486 */ setOverStrechAmount(float amount)4487 public void setOverStrechAmount(float amount) { 4488 float progress = amount / mView.getHeight(); 4489 float overstretch = Interpolators.getOvershootInterpolation(progress); 4490 mOverStretchAmount = overstretch * mMaxOverscrollAmountForPulse; 4491 positionClockAndNotifications(true /* forceUpdate */); 4492 } 4493 4494 private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { 4495 @Override onViewAttachedToWindow(View v)4496 public void onViewAttachedToWindow(View v) { 4497 mFragmentService.getFragmentHostManager(mView) 4498 .addTagListener(QS.TAG, mFragmentListener); 4499 mStatusBarStateController.addCallback(mStatusBarStateListener); 4500 mConfigurationController.addCallback(mConfigurationListener); 4501 mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); 4502 // Theme might have changed between inflating this view and attaching it to the 4503 // window, so 4504 // force a call to onThemeChanged 4505 mConfigurationListener.onThemeChanged(); 4506 mFalsingManager.addTapListener(mFalsingTapListener); 4507 mKeyguardIndicationController.init(); 4508 registerSettingsChangeListener(); 4509 } 4510 4511 @Override onViewDetachedFromWindow(View v)4512 public void onViewDetachedFromWindow(View v) { 4513 unregisterSettingsChangeListener(); 4514 mFragmentService.getFragmentHostManager(mView) 4515 .removeTagListener(QS.TAG, mFragmentListener); 4516 mStatusBarStateController.removeCallback(mStatusBarStateListener); 4517 mConfigurationController.removeCallback(mConfigurationListener); 4518 mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); 4519 mFalsingManager.removeTapListener(mFalsingTapListener); 4520 } 4521 } 4522 4523 private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener { 4524 4525 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)4526 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 4527 int oldTop, int oldRight, int oldBottom) { 4528 DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); 4529 super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); 4530 updateMaxDisplayedNotifications(true); 4531 setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); 4532 4533 // Update Clock Pivot 4534 mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2); 4535 mKeyguardStatusViewController.setPivotY( 4536 (FONT_HEIGHT - CAP_HEIGHT) / 2048f 4537 * mKeyguardStatusViewController.getClockTextSize()); 4538 4539 // Calculate quick setting heights. 4540 int oldMaxHeight = mQsMaxExpansionHeight; 4541 if (mQs != null) { 4542 updateQSMinHeight(); 4543 mQsMaxExpansionHeight = mQs.getDesiredHeight(); 4544 mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight); 4545 } 4546 positionClockAndNotifications(); 4547 if (mQsExpanded && mQsFullyExpanded) { 4548 mQsExpansionHeight = mQsMaxExpansionHeight; 4549 requestScrollerTopPaddingUpdate(false /* animate */); 4550 requestPanelHeightUpdate(); 4551 4552 // Size has changed, start an animation. 4553 if (mQsMaxExpansionHeight != oldMaxHeight) { 4554 startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); 4555 } 4556 } else if (!mQsExpanded && mQsExpansionAnimator == null) { 4557 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); 4558 } 4559 updateExpandedHeight(getExpandedHeight()); 4560 updateHeader(); 4561 4562 // If we are running a size change animation, the animation takes care of the height of 4563 // the container. However, if we are not animating, we always need to make the QS 4564 // container 4565 // the desired height so when closing the QS detail, it stays smaller after the size 4566 // change 4567 // animation is finished but the detail view is still being animated away (this 4568 // animation 4569 // takes longer than the size change animation). 4570 if (mQsSizeChangeAnimator == null && mQs != null) { 4571 mQs.setHeightOverride(mQs.getDesiredHeight()); 4572 } 4573 updateMaxHeadsUpTranslation(); 4574 updateGestureExclusionRect(); 4575 if (mExpandAfterLayoutRunnable != null) { 4576 mExpandAfterLayoutRunnable.run(); 4577 mExpandAfterLayoutRunnable = null; 4578 } 4579 DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout"); 4580 } 4581 } 4582 updateQSMinHeight()4583 private void updateQSMinHeight() { 4584 float previousMin = mQsMinExpansionHeight; 4585 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); 4586 if (mQsExpansionHeight == previousMin) { 4587 mQsExpansionHeight = mQsMinExpansionHeight; 4588 } 4589 } 4590 4591 private class DebugDrawable extends Drawable { 4592 4593 @Override draw(Canvas canvas)4594 public void draw(Canvas canvas) { 4595 Paint p = new Paint(); 4596 p.setColor(Color.RED); 4597 p.setStrokeWidth(2); 4598 p.setStyle(Paint.Style.STROKE); 4599 canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p); 4600 p.setTextSize(24); 4601 if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p); 4602 p.setColor(Color.BLUE); 4603 canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p); 4604 p.setColor(Color.GREEN); 4605 canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(), 4606 calculatePanelHeightQsExpanded(), p); 4607 p.setColor(Color.YELLOW); 4608 canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(), 4609 calculatePanelHeightShade(), p); 4610 p.setColor(Color.MAGENTA); 4611 canvas.drawLine( 4612 0, calculateNotificationsTopPadding(), mView.getWidth(), 4613 calculateNotificationsTopPadding(), p); 4614 p.setColor(Color.CYAN); 4615 canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), 4616 mNotificationStackScrollLayoutController.getTopPadding(), p); 4617 p.setColor(Color.GRAY); 4618 canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), 4619 mClockPositionResult.clockY, p); 4620 } 4621 4622 @Override setAlpha(int alpha)4623 public void setAlpha(int alpha) { 4624 4625 } 4626 4627 @Override setColorFilter(ColorFilter colorFilter)4628 public void setColorFilter(ColorFilter colorFilter) { 4629 4630 } 4631 4632 @Override getOpacity()4633 public int getOpacity() { 4634 return 0; 4635 } 4636 } 4637 4638 private class OnConfigurationChangedListener extends 4639 PanelViewController.OnConfigurationChangedListener { 4640 @Override onConfigurationChanged(Configuration newConfig)4641 public void onConfigurationChanged(Configuration newConfig) { 4642 super.onConfigurationChanged(newConfig); 4643 mAffordanceHelper.onConfigurationChanged(); 4644 if (newConfig.orientation != mLastOrientation) { 4645 resetHorizontalPanelPosition(); 4646 } 4647 mLastOrientation = newConfig.orientation; 4648 } 4649 } 4650 4651 private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { onApplyWindowInsets(View v, WindowInsets insets)4652 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { 4653 // the same types of insets that are handled in NotificationShadeWindowView 4654 int insetTypes = WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout(); 4655 Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes); 4656 mDisplayTopInset = combinedInsets.top; 4657 mDisplayRightInset = combinedInsets.right; 4658 4659 mNavigationBarBottomHeight = insets.getStableInsetBottom(); 4660 updateMaxHeadsUpTranslation(); 4661 return insets; 4662 } 4663 } 4664 } 4665