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 com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; 22 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; 23 24 import static java.lang.Float.isNaN; 25 26 import android.animation.Animator; 27 import android.animation.AnimatorListenerAdapter; 28 import android.animation.ValueAnimator; 29 import android.app.ActivityManager; 30 import android.app.Fragment; 31 import android.app.StatusBarManager; 32 import android.content.pm.ResolveInfo; 33 import android.content.res.Configuration; 34 import android.graphics.Canvas; 35 import android.graphics.Color; 36 import android.graphics.ColorFilter; 37 import android.graphics.Paint; 38 import android.graphics.PointF; 39 import android.graphics.Rect; 40 import android.graphics.Region; 41 import android.graphics.drawable.Drawable; 42 import android.hardware.biometrics.BiometricSourceType; 43 import android.os.Bundle; 44 import android.os.PowerManager; 45 import android.os.SystemClock; 46 import android.util.Log; 47 import android.util.MathUtils; 48 import android.view.LayoutInflater; 49 import android.view.MotionEvent; 50 import android.view.VelocityTracker; 51 import android.view.View; 52 import android.view.ViewGroup; 53 import android.view.ViewPropertyAnimator; 54 import android.view.ViewTreeObserver; 55 import android.view.WindowInsets; 56 import android.view.accessibility.AccessibilityManager; 57 import android.view.accessibility.AccessibilityNodeInfo; 58 import android.widget.FrameLayout; 59 import android.widget.TextView; 60 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.internal.logging.MetricsLogger; 63 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 64 import com.android.internal.util.LatencyTracker; 65 import com.android.keyguard.KeyguardClockSwitch; 66 import com.android.keyguard.KeyguardStatusView; 67 import com.android.keyguard.KeyguardUpdateMonitor; 68 import com.android.keyguard.KeyguardUpdateMonitorCallback; 69 import com.android.systemui.DejankUtils; 70 import com.android.systemui.Interpolators; 71 import com.android.systemui.R; 72 import com.android.systemui.dagger.qualifiers.DisplayId; 73 import com.android.systemui.doze.DozeLog; 74 import com.android.systemui.fragments.FragmentHostManager; 75 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 76 import com.android.systemui.media.MediaHierarchyManager; 77 import com.android.systemui.plugins.FalsingManager; 78 import com.android.systemui.plugins.qs.QS; 79 import com.android.systemui.plugins.statusbar.StatusBarStateController; 80 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 81 import com.android.systemui.qs.QSFragment; 82 import com.android.systemui.statusbar.CommandQueue; 83 import com.android.systemui.statusbar.FlingAnimationUtils; 84 import com.android.systemui.statusbar.GestureRecorder; 85 import com.android.systemui.statusbar.KeyguardAffordanceView; 86 import com.android.systemui.statusbar.KeyguardIndicationController; 87 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 88 import com.android.systemui.statusbar.NotificationShelf; 89 import com.android.systemui.statusbar.PulseExpansionHandler; 90 import com.android.systemui.statusbar.RemoteInputController; 91 import com.android.systemui.statusbar.StatusBarState; 92 import com.android.systemui.statusbar.SysuiStatusBarStateController; 93 import com.android.systemui.statusbar.VibratorHelper; 94 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; 95 import com.android.systemui.statusbar.notification.AnimatableProperty; 96 import com.android.systemui.statusbar.notification.ConversationNotificationManager; 97 import com.android.systemui.statusbar.notification.DynamicPrivacyController; 98 import com.android.systemui.statusbar.notification.NotificationEntryManager; 99 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 100 import com.android.systemui.statusbar.notification.PropertyAnimator; 101 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; 102 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 103 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 104 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 105 import com.android.systemui.statusbar.notification.row.ExpandableView; 106 import com.android.systemui.statusbar.notification.stack.AnimationProperties; 107 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 108 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 109 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; 110 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; 111 import com.android.systemui.statusbar.policy.ConfigurationController; 112 import com.android.systemui.statusbar.policy.KeyguardStateController; 113 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 114 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 115 import com.android.systemui.statusbar.policy.ZenModeController; 116 import com.android.systemui.util.InjectionInflationController; 117 118 import java.io.FileDescriptor; 119 import java.io.PrintWriter; 120 import java.util.ArrayList; 121 import java.util.Collections; 122 import java.util.List; 123 import java.util.function.Consumer; 124 import java.util.function.Function; 125 126 import javax.inject.Inject; 127 128 @StatusBarComponent.StatusBarScope 129 public class NotificationPanelViewController extends PanelViewController { 130 131 private static final boolean DEBUG = false; 132 133 /** 134 * Fling expanding QS. 135 */ 136 private static final int FLING_EXPAND = 0; 137 138 /** 139 * Fling collapsing QS, potentially stopping when QS becomes QQS. 140 */ 141 private static final int FLING_COLLAPSE = 1; 142 143 /** 144 * Fling until QS is completely hidden. 145 */ 146 private static final int FLING_HIDE = 2; 147 private final DozeParameters mDozeParameters; 148 private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener(); 149 private final OnClickListener mOnClickListener = new OnClickListener(); 150 private final OnOverscrollTopChangedListener 151 mOnOverscrollTopChangedListener = 152 new OnOverscrollTopChangedListener(); 153 private final KeyguardAffordanceHelperCallback 154 mKeyguardAffordanceHelperCallback = 155 new KeyguardAffordanceHelperCallback(); 156 private final OnEmptySpaceClickListener 157 mOnEmptySpaceClickListener = 158 new OnEmptySpaceClickListener(); 159 private final MyOnHeadsUpChangedListener 160 mOnHeadsUpChangedListener = 161 new MyOnHeadsUpChangedListener(); 162 private final HeightListener mHeightListener = new HeightListener(); 163 private final ZenModeControllerCallback 164 mZenModeControllerCallback = 165 new ZenModeControllerCallback(); 166 private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); 167 private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); 168 private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); 169 private final BiometricUnlockController mBiometricUnlockController; 170 private final NotificationPanelView mView; 171 private final MetricsLogger mMetricsLogger; 172 private final ActivityManager mActivityManager; 173 private final ZenModeController mZenModeController; 174 private final ConfigurationController mConfigurationController; 175 private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; 176 177 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is 178 // changed. 179 private static final int CAP_HEIGHT = 1456; 180 private static final int FONT_HEIGHT = 2163; 181 182 /** 183 * Maximum time before which we will expand the panel even for slow motions when getting a 184 * touch passed over from launcher. 185 */ 186 private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; 187 188 private static final String COUNTER_PANEL_OPEN = "panel_open"; 189 private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; 190 private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; 191 192 private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1); 193 private static final Rect EMPTY_RECT = new Rect(); 194 195 private static final AnimationProperties 196 CLOCK_ANIMATION_PROPERTIES = 197 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 198 private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from( 199 "KEYGUARD_HEADS_UP_SHOWING_AMOUNT", 200 (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat), 201 (Function<NotificationPanelView, Float>) notificationPanelView -> 202 getKeyguardHeadsUpShowingAmount(), 203 R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag, 204 R.id.keyguard_hun_animator_start_tag); 205 private static final AnimationProperties 206 KEYGUARD_HUN_PROPERTIES = 207 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 208 @VisibleForTesting 209 final KeyguardUpdateMonitorCallback 210 mKeyguardUpdateCallback = 211 new KeyguardUpdateMonitorCallback() { 212 213 @Override 214 public void onBiometricAuthenticated(int userId, 215 BiometricSourceType biometricSourceType, 216 boolean isStrongBiometric) { 217 if (mFirstBypassAttempt 218 && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) { 219 mDelayShowingKeyguardStatusBar = true; 220 } 221 } 222 223 @Override 224 public void onBiometricRunningStateChanged(boolean running, 225 BiometricSourceType biometricSourceType) { 226 boolean 227 keyguardOrShadeLocked = 228 mBarState == StatusBarState.KEYGUARD 229 || mBarState == StatusBarState.SHADE_LOCKED; 230 if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing 231 && !mDelayShowingKeyguardStatusBar 232 && !mBiometricUnlockController.isBiometricUnlock()) { 233 mFirstBypassAttempt = false; 234 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 235 } 236 } 237 238 @Override 239 public void onFinishedGoingToSleep(int why) { 240 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); 241 mDelayShowingKeyguardStatusBar = false; 242 } 243 }; 244 245 private final InjectionInflationController mInjectionInflationController; 246 private final PowerManager mPowerManager; 247 private final AccessibilityManager mAccessibilityManager; 248 private final NotificationWakeUpCoordinator mWakeUpCoordinator; 249 private final PulseExpansionHandler mPulseExpansionHandler; 250 private final KeyguardBypassController mKeyguardBypassController; 251 private final KeyguardUpdateMonitor mUpdateMonitor; 252 private final ConversationNotificationManager mConversationNotificationManager; 253 private final MediaHierarchyManager mMediaHierarchyManager; 254 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 255 256 private KeyguardAffordanceHelper mAffordanceHelper; 257 private KeyguardUserSwitcher mKeyguardUserSwitcher; 258 private KeyguardStatusBarView mKeyguardStatusBar; 259 private ViewGroup mBigClockContainer; 260 private QS mQs; 261 private FrameLayout mQsFrame; 262 private KeyguardStatusView mKeyguardStatusView; 263 private View mQsNavbarScrim; 264 private NotificationsQuickSettingsContainer mNotificationContainerParent; 265 private NotificationStackScrollLayout mNotificationStackScroller; 266 private boolean mAnimateNextPositionUpdate; 267 268 private int mTrackingPointer; 269 private VelocityTracker mQsVelocityTracker; 270 private boolean mQsTracking; 271 272 /** 273 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and 274 * the expansion for quick settings. 275 */ 276 private boolean mConflictingQsExpansionGesture; 277 278 private boolean mPanelExpanded; 279 private boolean mQsExpanded; 280 private boolean mQsExpandedWhenExpandingStarted; 281 private boolean mQsFullyExpanded; 282 private boolean mKeyguardShowing; 283 private boolean mDozing; 284 private boolean mDozingOnDown; 285 private int mBarState; 286 private float mInitialHeightOnTouch; 287 private float mInitialTouchX; 288 private float mInitialTouchY; 289 private float mQsExpansionHeight; 290 private int mQsMinExpansionHeight; 291 private int mQsMaxExpansionHeight; 292 private int mQsPeekHeight; 293 private boolean mStackScrollerOverscrolling; 294 private boolean mQsExpansionFromOverscroll; 295 private float mLastOverscroll; 296 private boolean mQsExpansionEnabled = true; 297 private ValueAnimator mQsExpansionAnimator; 298 private FlingAnimationUtils mFlingAnimationUtils; 299 private int mStatusBarMinHeight; 300 private int mNotificationsHeaderCollideDistance; 301 private float mEmptyDragAmount; 302 private float mDownX; 303 private float mDownY; 304 305 private final KeyguardClockPositionAlgorithm 306 mClockPositionAlgorithm = 307 new KeyguardClockPositionAlgorithm(); 308 private final KeyguardClockPositionAlgorithm.Result 309 mClockPositionResult = 310 new KeyguardClockPositionAlgorithm.Result(); 311 private boolean mIsExpanding; 312 313 private boolean mBlockTouches; 314 // Used for two finger gesture as well as accessibility shortcut to QS. 315 private boolean mQsExpandImmediate; 316 private boolean mTwoFingerQsExpandPossible; 317 318 /** 319 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still 320 * need to take this into account in our panel height calculation. 321 */ 322 private boolean mQsAnimatorExpand; 323 private boolean mIsLaunchTransitionFinished; 324 private boolean mIsLaunchTransitionRunning; 325 private Runnable mLaunchAnimationEndRunnable; 326 private boolean mOnlyAffordanceInThisMotion; 327 private boolean mKeyguardStatusViewAnimating; 328 private ValueAnimator mQsSizeChangeAnimator; 329 330 private boolean mShowEmptyShadeView; 331 332 private boolean mQsScrimEnabled = true; 333 private boolean mQsTouchAboveFalsingThreshold; 334 private int mQsFalsingThreshold; 335 336 private float mKeyguardStatusBarAnimateAlpha = 1f; 337 private HeadsUpTouchHelper mHeadsUpTouchHelper; 338 private boolean mListenForHeadsUp; 339 private int mNavigationBarBottomHeight; 340 private boolean mExpandingFromHeadsUp; 341 private boolean mCollapsedOnDown; 342 private int mPositionMinSideMargin; 343 private int mLastOrientation = -1; 344 private boolean mClosingWithAlphaFadeOut; 345 private boolean mHeadsUpAnimatingAway; 346 private boolean mLaunchingAffordance; 347 private boolean mAffordanceHasPreview; 348 private FalsingManager mFalsingManager; 349 private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 350 351 private Runnable mHeadsUpExistenceChangedRunnable = () -> { 352 setHeadsUpAnimatingAway(false); 353 notifyBarPanelExpansionChanged(); 354 }; 355 private NotificationGroupManager mGroupManager; 356 private boolean mShowIconsWhenExpanded; 357 private int mIndicationBottomPadding; 358 private int mAmbientIndicationBottomPadding; 359 private boolean mIsFullWidth; 360 private boolean mBlockingExpansionForCurrentTouch; 361 362 /** 363 * Following variables maintain state of events when input focus transfer may occur. 364 */ 365 private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event 366 private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event 367 368 /** 369 * Current dark amount that follows regular interpolation curve of animation. 370 */ 371 private float mInterpolatedDarkAmount; 372 373 /** 374 * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the 375 * interpolation curve is different. 376 */ 377 private float mLinearDarkAmount; 378 379 private boolean mPulsing; 380 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); 381 private boolean mUserSetupComplete; 382 private int mQsNotificationTopPadding; 383 private float mExpandOffset; 384 private boolean mHideIconsDuringNotificationLaunch = true; 385 private int mStackScrollerMeasuringPass; 386 private ArrayList<Consumer<ExpandableNotificationRow>> 387 mTrackingHeadsUpListeners = 388 new ArrayList<>(); 389 private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>(); 390 private HeadsUpAppearanceController mHeadsUpAppearanceController; 391 392 private int mPanelAlpha; 393 private Runnable mPanelAlphaEndAction; 394 private float mBottomAreaShadeAlpha; 395 private final ValueAnimator mBottomAreaShadeAlphaAnimator; 396 private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", 397 NotificationPanelView::setPanelAlphaInternal, 398 NotificationPanelView::getCurrentPanelAlpha, 399 R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag, 400 R.id.panel_alpha_animator_end_tag); 401 private final AnimationProperties mPanelAlphaOutPropertiesAnimator = 402 new AnimationProperties().setDuration(150).setCustomInterpolator( 403 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT); 404 private final AnimationProperties mPanelAlphaInPropertiesAnimator = 405 new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> { 406 if (mPanelAlphaEndAction != null) { 407 mPanelAlphaEndAction.run(); 408 } 409 }).setCustomInterpolator( 410 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); 411 private final NotificationEntryManager mEntryManager; 412 413 private final CommandQueue mCommandQueue; 414 private final NotificationLockscreenUserManager mLockscreenUserManager; 415 private final ShadeController mShadeController; 416 private int mDisplayId; 417 418 /** 419 * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. 420 * 421 * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary 422 * work, check the current id with the cached id. 423 */ 424 private int mThemeResId; 425 private KeyguardIndicationController mKeyguardIndicationController; 426 private Consumer<Boolean> mAffordanceLaunchListener; 427 private int mShelfHeight; 428 private Runnable mOnReinflationListener; 429 private int mDarkIconSize; 430 private int mHeadsUpInset; 431 private boolean mHeadsUpPinnedMode; 432 private float mKeyguardHeadsUpShowingAmount = 0.0f; 433 private boolean mShowingKeyguardHeadsUp; 434 private boolean mAllowExpandForSmallExpansion; 435 private Runnable mExpandAfterLayoutRunnable; 436 437 /** 438 * Is this a collapse that started on the panel where we should allow the panel to intercept 439 */ 440 private boolean mIsPanelCollapseOnQQS; 441 442 /** 443 * If face auth with bypass is running for the first time after you turn on the screen. 444 * (From aod or screen off) 445 */ 446 private boolean mFirstBypassAttempt; 447 /** 448 * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until 449 * the keyguard is dismissed to show the status bar. 450 */ 451 private boolean mDelayShowingKeyguardStatusBar; 452 453 private boolean mAnimatingQS; 454 private int mOldLayoutDirection; 455 456 private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { 457 @Override 458 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 459 super.onInitializeAccessibilityNodeInfo(host, info); 460 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); 461 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP); 462 } 463 464 @Override 465 public boolean performAccessibilityAction(View host, int action, Bundle args) { 466 if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() 467 || action 468 == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) { 469 mStatusBarKeyguardViewManager.showBouncer(true); 470 return true; 471 } 472 return super.performAccessibilityAction(host, action, args); 473 } 474 }; 475 476 @Inject NotificationPanelViewController(NotificationPanelView view, InjectionInflationController injectionInflationController, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, ShadeController shadeController, 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, ZenModeController zenModeController, ConfigurationController configurationController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager)477 public NotificationPanelViewController(NotificationPanelView view, 478 InjectionInflationController injectionInflationController, 479 NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, 480 DynamicPrivacyController dynamicPrivacyController, 481 KeyguardBypassController bypassController, FalsingManager falsingManager, 482 ShadeController shadeController, 483 NotificationLockscreenUserManager notificationLockscreenUserManager, 484 NotificationEntryManager notificationEntryManager, 485 KeyguardStateController keyguardStateController, 486 StatusBarStateController statusBarStateController, DozeLog dozeLog, 487 DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, 488 LatencyTracker latencyTracker, PowerManager powerManager, 489 AccessibilityManager accessibilityManager, @DisplayId int displayId, 490 KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, 491 ActivityManager activityManager, ZenModeController zenModeController, 492 ConfigurationController configurationController, 493 FlingAnimationUtils.Builder flingAnimationUtilsBuilder, 494 StatusBarTouchableRegionManager statusBarTouchableRegionManager, 495 ConversationNotificationManager conversationNotificationManager, 496 MediaHierarchyManager mediaHierarchyManager, 497 BiometricUnlockController biometricUnlockController, 498 StatusBarKeyguardViewManager statusBarKeyguardViewManager) { 499 super(view, falsingManager, dozeLog, keyguardStateController, 500 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, 501 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); 502 mView = view; 503 mMetricsLogger = metricsLogger; 504 mActivityManager = activityManager; 505 mZenModeController = zenModeController; 506 mConfigurationController = configurationController; 507 mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; 508 mMediaHierarchyManager = mediaHierarchyManager; 509 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 510 mView.setWillNotDraw(!DEBUG); 511 mInjectionInflationController = injectionInflationController; 512 mFalsingManager = falsingManager; 513 mPowerManager = powerManager; 514 mWakeUpCoordinator = coordinator; 515 mAccessibilityManager = accessibilityManager; 516 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 517 setPanelAlpha(255, false /* animate */); 518 mCommandQueue = commandQueue; 519 mDisplayId = displayId; 520 mPulseExpansionHandler = pulseExpansionHandler; 521 mDozeParameters = dozeParameters; 522 mBiometricUnlockController = biometricUnlockController; 523 pulseExpansionHandler.setPulseExpandAbortListener(() -> { 524 if (mQs != null) { 525 mQs.animateHeaderSlidingOut(); 526 } 527 }); 528 mThemeResId = mView.getContext().getThemeResId(); 529 mKeyguardBypassController = bypassController; 530 mUpdateMonitor = keyguardUpdateMonitor; 531 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); 532 KeyguardStateController.Callback 533 keyguardMonitorCallback = 534 new KeyguardStateController.Callback() { 535 @Override 536 public void onKeyguardFadingAwayChanged() { 537 if (!mKeyguardStateController.isKeyguardFadingAway()) { 538 mFirstBypassAttempt = false; 539 mDelayShowingKeyguardStatusBar = false; 540 } 541 } 542 }; 543 mKeyguardStateController.addCallback(keyguardMonitorCallback); 544 DynamicPrivacyControlListener 545 dynamicPrivacyControlListener = 546 new DynamicPrivacyControlListener(); 547 dynamicPrivacyController.addListener(dynamicPrivacyControlListener); 548 549 mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); 550 mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> { 551 mBottomAreaShadeAlpha = (float) animation.getAnimatedValue(); 552 updateKeyguardBottomAreaAlpha(); 553 }); 554 mBottomAreaShadeAlphaAnimator.setDuration(160); 555 mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); 556 mShadeController = shadeController; 557 mLockscreenUserManager = notificationLockscreenUserManager; 558 mEntryManager = notificationEntryManager; 559 mConversationNotificationManager = conversationNotificationManager; 560 561 mView.setBackgroundColor(Color.TRANSPARENT); 562 OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); 563 mView.addOnAttachStateChangeListener(onAttachStateChangeListener); 564 if (mView.isAttachedToWindow()) { 565 onAttachStateChangeListener.onViewAttachedToWindow(mView); 566 } 567 568 mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener()); 569 570 if (DEBUG) { 571 mView.getOverlay().add(new DebugDrawable()); 572 } 573 574 onFinishInflate(); 575 } 576 onFinishInflate()577 private void onFinishInflate() { 578 loadDimens(); 579 mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); 580 mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view); 581 582 KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container); 583 mBigClockContainer = mView.findViewById(R.id.big_clock_container); 584 keyguardClockSwitch.setBigClockContainer(mBigClockContainer); 585 586 mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); 587 mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller); 588 mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); 589 mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener); 590 mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener); 591 addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); 592 mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); 593 mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); 594 mLastOrientation = mResources.getConfiguration().orientation; 595 596 initBottomArea(); 597 598 mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); 599 mQsFrame = mView.findViewById(R.id.qs_frame); 600 mPulseExpansionHandler.setUp( 601 mNotificationStackScroller, mExpansionCallback, mShadeController); 602 mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { 603 @Override 604 public void onFullyHiddenChanged(boolean isFullyHidden) { 605 updateKeyguardStatusBarForHeadsUp(); 606 } 607 608 @Override 609 public void onPulseExpansionChanged(boolean expandingChanged) { 610 if (mKeyguardBypassController.getBypassEnabled()) { 611 // Position the notifications while dragging down while pulsing 612 requestScrollerTopPaddingUpdate(false /* animate */); 613 updateQSPulseExpansion(); 614 } 615 } 616 }); 617 618 mView.setRtlChangeListener(layoutDirection -> { 619 if (layoutDirection != mOldLayoutDirection) { 620 mAffordanceHelper.onRtlPropertiesChanged(); 621 mOldLayoutDirection = layoutDirection; 622 } 623 }); 624 625 mView.setAccessibilityDelegate(mAccessibilityDelegate); 626 } 627 628 @Override loadDimens()629 protected void loadDimens() { 630 super.loadDimens(); 631 mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset() 632 .setMaxLengthSeconds(0.4f).build(); 633 mStatusBarMinHeight = mResources.getDimensionPixelSize( 634 com.android.internal.R.dimen.status_bar_height); 635 mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); 636 mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize( 637 R.dimen.header_notifications_collide_distance); 638 mClockPositionAlgorithm.loadDimens(mResources); 639 mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold); 640 mPositionMinSideMargin = mResources.getDimensionPixelSize( 641 R.dimen.notification_panel_min_side_margin); 642 mIndicationBottomPadding = mResources.getDimensionPixelSize( 643 R.dimen.keyguard_indication_bottom_padding); 644 mQsNotificationTopPadding = mResources.getDimensionPixelSize( 645 R.dimen.qs_notification_padding); 646 mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height); 647 mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); 648 int statusbarHeight = mResources.getDimensionPixelSize( 649 com.android.internal.R.dimen.status_bar_height); 650 mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( 651 R.dimen.heads_up_status_bar_padding); 652 } 653 654 /** 655 * Returns if there's a custom clock being presented. 656 */ hasCustomClock()657 public boolean hasCustomClock() { 658 return mKeyguardStatusView.hasCustomClock(); 659 } 660 setStatusBar(StatusBar bar)661 private void setStatusBar(StatusBar bar) { 662 // TODO: this can be injected. 663 mStatusBar = bar; 664 mKeyguardBottomArea.setStatusBar(mStatusBar); 665 } 666 /** 667 * @see #launchCamera(boolean, int) 668 * @see #setLaunchingAffordance(boolean) 669 */ setLaunchAffordanceListener(Consumer<Boolean> listener)670 public void setLaunchAffordanceListener(Consumer<Boolean> listener) { 671 mAffordanceLaunchListener = listener; 672 } 673 updateResources()674 public void updateResources() { 675 int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); 676 int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity); 677 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams(); 678 if (lp.width != qsWidth || lp.gravity != panelGravity) { 679 lp.width = qsWidth; 680 lp.gravity = panelGravity; 681 mQsFrame.setLayoutParams(lp); 682 } 683 684 int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); 685 lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); 686 if (lp.width != panelWidth || lp.gravity != panelGravity) { 687 lp.width = panelWidth; 688 lp.gravity = panelGravity; 689 mNotificationStackScroller.setLayoutParams(lp); 690 } 691 } 692 reInflateViews()693 private void reInflateViews() { 694 updateShowEmptyShadeView(); 695 696 // Re-inflate the status view group. 697 int index = mView.indexOfChild(mKeyguardStatusView); 698 mView.removeView(mKeyguardStatusView); 699 mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable( 700 LayoutInflater.from(mView.getContext())).inflate( 701 R.layout.keyguard_status_view, mView, false); 702 mView.addView(mKeyguardStatusView, index); 703 704 // Re-associate the clock container with the keyguard clock switch. 705 mBigClockContainer.removeAllViews(); 706 KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container); 707 keyguardClockSwitch.setBigClockContainer(mBigClockContainer); 708 709 // Update keyguard bottom area 710 index = mView.indexOfChild(mKeyguardBottomArea); 711 mView.removeView(mKeyguardBottomArea); 712 KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; 713 mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable( 714 LayoutInflater.from(mView.getContext())).inflate( 715 R.layout.keyguard_bottom_area, mView, false); 716 mKeyguardBottomArea.initFrom(oldBottomArea); 717 mView.addView(mKeyguardBottomArea, index); 718 initBottomArea(); 719 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 720 mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), 721 mStatusBarStateController.getInterpolatedDozeAmount()); 722 723 if (mKeyguardStatusBar != null) { 724 mKeyguardStatusBar.onThemeChanged(); 725 } 726 727 setKeyguardStatusViewVisibility(mBarState, false, false); 728 setKeyguardBottomAreaVisibility(mBarState, false); 729 if (mOnReinflationListener != null) { 730 mOnReinflationListener.run(); 731 } 732 } 733 initBottomArea()734 private void initBottomArea() { 735 mAffordanceHelper = new KeyguardAffordanceHelper( 736 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager); 737 mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); 738 mKeyguardBottomArea.setStatusBar(mStatusBar); 739 mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); 740 } 741 setKeyguardIndicationController(KeyguardIndicationController indicationController)742 public void setKeyguardIndicationController(KeyguardIndicationController indicationController) { 743 mKeyguardIndicationController = indicationController; 744 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 745 } 746 updateGestureExclusionRect()747 private void updateGestureExclusionRect() { 748 Rect exclusionRect = calculateGestureExclusionRect(); 749 mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST 750 : Collections.singletonList(exclusionRect)); 751 } 752 calculateGestureExclusionRect()753 private Rect calculateGestureExclusionRect() { 754 Rect exclusionRect = null; 755 Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion(); 756 if (isFullyCollapsed() && touchableRegion != null) { 757 // Note: The manager also calculates the non-pinned touchable region 758 exclusionRect = touchableRegion.getBounds(); 759 } 760 return exclusionRect != null ? exclusionRect : EMPTY_RECT; 761 } 762 setIsFullWidth(boolean isFullWidth)763 private void setIsFullWidth(boolean isFullWidth) { 764 mIsFullWidth = isFullWidth; 765 mNotificationStackScroller.setIsFullWidth(isFullWidth); 766 } 767 startQsSizeChangeAnimation(int oldHeight, final int newHeight)768 private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { 769 if (mQsSizeChangeAnimator != null) { 770 oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 771 mQsSizeChangeAnimator.cancel(); 772 } 773 mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); 774 mQsSizeChangeAnimator.setDuration(300); 775 mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 776 mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 777 @Override 778 public void onAnimationUpdate(ValueAnimator animation) { 779 requestScrollerTopPaddingUpdate(false /* animate */); 780 requestPanelHeightUpdate(); 781 int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); 782 mQs.setHeightOverride(height); 783 } 784 }); 785 mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { 786 @Override 787 public void onAnimationEnd(Animator animation) { 788 mQsSizeChangeAnimator = null; 789 } 790 }); 791 mQsSizeChangeAnimator.start(); 792 } 793 794 /** 795 * Positions the clock and notifications dynamically depending on how many notifications are 796 * showing. 797 */ positionClockAndNotifications()798 private void positionClockAndNotifications() { 799 boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); 800 boolean animateClock = animate || mAnimateNextPositionUpdate; 801 int stackScrollerPadding; 802 if (mBarState != StatusBarState.KEYGUARD) { 803 stackScrollerPadding = getUnlockedStackScrollerPadding(); 804 } else { 805 int totalHeight = mView.getHeight(); 806 int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); 807 int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); 808 boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); 809 final boolean 810 hasVisibleNotifications = 811 !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0; 812 mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); 813 mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, 814 mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(), 815 totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f 816 - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), 817 hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, 818 bypassEnabled, getUnlockedStackScrollerPadding()); 819 mClockPositionAlgorithm.run(mClockPositionResult); 820 PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, 821 mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); 822 PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, 823 mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); 824 updateNotificationTranslucency(); 825 updateClock(); 826 stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; 827 } 828 mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); 829 mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); 830 831 mStackScrollerMeasuringPass++; 832 requestScrollerTopPaddingUpdate(animate); 833 mStackScrollerMeasuringPass = 0; 834 mAnimateNextPositionUpdate = false; 835 } 836 837 /** 838 * @return the padding of the stackscroller when unlocked 839 */ getUnlockedStackScrollerPadding()840 private int getUnlockedStackScrollerPadding() { 841 return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight 842 + mQsNotificationTopPadding; 843 } 844 845 /** 846 * @param maximum the maximum to return at most 847 * @return the maximum keyguard notifications that can fit on the screen 848 */ computeMaxKeyguardNotifications(int maximum)849 public int computeMaxKeyguardNotifications(int maximum) { 850 float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); 851 int notificationPadding = Math.max( 852 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); 853 NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf(); 854 float 855 shelfSize = 856 shelf.getVisibility() == View.GONE ? 0 857 : shelf.getIntrinsicHeight() + notificationPadding; 858 float 859 availableSpace = 860 mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max( 861 mIndicationBottomPadding, mAmbientIndicationBottomPadding) 862 - mKeyguardStatusView.getLogoutButtonHeight(); 863 int count = 0; 864 ExpandableView previousView = null; 865 for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { 866 ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); 867 if (!canShowViewOnLockscreen(child)) { 868 continue; 869 } 870 availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); 871 availableSpace -= count == 0 ? 0 : notificationPadding; 872 availableSpace -= mNotificationStackScroller.calculateGapHeight(previousView, child, 873 count); 874 previousView = child; 875 if (availableSpace >= 0 && count < maximum) { 876 count++; 877 } else if (availableSpace > -shelfSize) { 878 // if we are exactly the last view, then we can show us still! 879 for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) { 880 ExpandableView view = (ExpandableView) mNotificationStackScroller.getChildAt(j); 881 if (view instanceof ExpandableNotificationRow && 882 canShowViewOnLockscreen(view)) { 883 return count; 884 } 885 } 886 count++; 887 return count; 888 } else { 889 return count; 890 } 891 } 892 return count; 893 } 894 895 /** 896 * Can a view be shown on the lockscreen when calculating the number of allowed notifications 897 * to show? 898 * 899 * @param child the view in question 900 * @return true if it can be shown 901 */ canShowViewOnLockscreen(ExpandableView child)902 private boolean canShowViewOnLockscreen(ExpandableView child) { 903 if (child.hasNoContentHeight()) { 904 return false; 905 } 906 if (child instanceof ExpandableNotificationRow && 907 !canShowRowOnLockscreen((ExpandableNotificationRow) child)) { 908 return false; 909 } else if (child.getVisibility() == GONE) { 910 // ENRs can be gone and count because their visibility is only set after 911 // this calculation, but all other views should be up to date 912 return false; 913 } 914 return true; 915 } 916 917 /** 918 * Can a row be shown on the lockscreen when calculating the number of allowed notifications 919 * to show? 920 * 921 * @param row the row in question 922 * @return true if it can be shown 923 */ canShowRowOnLockscreen(ExpandableNotificationRow row)924 private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) { 925 boolean suppressedSummary = 926 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( 927 row.getEntry().getSbn()); 928 if (suppressedSummary) { 929 return false; 930 } 931 if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { 932 return false; 933 } 934 if (row.isRemoved()) { 935 return false; 936 } 937 return true; 938 } 939 updateClock()940 private void updateClock() { 941 if (!mKeyguardStatusViewAnimating) { 942 mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha); 943 } 944 } 945 animateToFullShade(long delay)946 public void animateToFullShade(long delay) { 947 mNotificationStackScroller.goToFullShade(delay); 948 mView.requestLayout(); 949 mAnimateNextPositionUpdate = true; 950 } 951 setQsExpansionEnabled(boolean qsExpansionEnabled)952 public void setQsExpansionEnabled(boolean qsExpansionEnabled) { 953 mQsExpansionEnabled = qsExpansionEnabled; 954 if (mQs == null) return; 955 mQs.setHeaderClickable(qsExpansionEnabled); 956 } 957 958 @Override resetViews(boolean animate)959 public void resetViews(boolean animate) { 960 mIsLaunchTransitionFinished = false; 961 mBlockTouches = false; 962 if (!mLaunchingAffordance) { 963 mAffordanceHelper.reset(false); 964 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 965 } 966 mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, 967 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); 968 if (animate) { 969 animateCloseQs(true /* animateAway */); 970 } else { 971 closeQs(); 972 } 973 mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate, 974 !animate /* cancelAnimators */); 975 mNotificationStackScroller.resetScrollPosition(); 976 } 977 978 @Override collapse(boolean delayed, float speedUpFactor)979 public void collapse(boolean delayed, float speedUpFactor) { 980 if (!canPanelBeCollapsed()) { 981 return; 982 } 983 984 if (mQsExpanded) { 985 mQsExpandImmediate = true; 986 mNotificationStackScroller.setShouldShowShelfOnly(true); 987 } 988 super.collapse(delayed, speedUpFactor); 989 } 990 closeQs()991 public void closeQs() { 992 cancelQsAnimation(); 993 setQsExpansion(mQsMinExpansionHeight); 994 } 995 cancelAnimation()996 public void cancelAnimation() { 997 mView.animate().cancel(); 998 } 999 1000 1001 /** 1002 * Animate QS closing by flinging it. 1003 * If QS is expanded, it will collapse into QQS and stop. 1004 * 1005 * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. 1006 */ animateCloseQs(boolean animateAway)1007 public void animateCloseQs(boolean animateAway) { 1008 if (mQsExpansionAnimator != null) { 1009 if (!mQsAnimatorExpand) { 1010 return; 1011 } 1012 float height = mQsExpansionHeight; 1013 mQsExpansionAnimator.cancel(); 1014 setQsExpansion(height); 1015 } 1016 flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); 1017 } 1018 expandWithQs()1019 public void expandWithQs() { 1020 if (mQsExpansionEnabled) { 1021 mQsExpandImmediate = true; 1022 mNotificationStackScroller.setShouldShowShelfOnly(true); 1023 } 1024 if (isFullyCollapsed()) { 1025 expand(true /* animate */); 1026 } else { 1027 flingSettings(0 /* velocity */, FLING_EXPAND); 1028 } 1029 } 1030 expandWithoutQs()1031 public void expandWithoutQs() { 1032 if (isQsExpanded()) { 1033 flingSettings(0 /* velocity */, FLING_COLLAPSE); 1034 } else { 1035 expand(true /* animate */); 1036 } 1037 } 1038 1039 @Override fling(float vel, boolean expand)1040 public void fling(float vel, boolean expand) { 1041 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); 1042 if (gr != null) { 1043 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); 1044 } 1045 super.fling(vel, expand); 1046 } 1047 1048 @Override flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1049 protected void flingToHeight(float vel, boolean expand, float target, 1050 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { 1051 mHeadsUpTouchHelper.notifyFling(!expand); 1052 setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); 1053 super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); 1054 } 1055 1056 onQsIntercept(MotionEvent event)1057 private boolean onQsIntercept(MotionEvent event) { 1058 int pointerIndex = event.findPointerIndex(mTrackingPointer); 1059 if (pointerIndex < 0) { 1060 pointerIndex = 0; 1061 mTrackingPointer = event.getPointerId(pointerIndex); 1062 } 1063 final float x = event.getX(pointerIndex); 1064 final float y = event.getY(pointerIndex); 1065 1066 switch (event.getActionMasked()) { 1067 case MotionEvent.ACTION_DOWN: 1068 mInitialTouchY = y; 1069 mInitialTouchX = x; 1070 initVelocityTracker(); 1071 trackMovement(event); 1072 if (mKeyguardShowing 1073 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { 1074 // Dragging down on the lockscreen statusbar should prohibit other interactions 1075 // immediately, otherwise we'll wait on the touchslop. This is to allow 1076 // dragging down to expanded quick settings directly on the lockscreen. 1077 mView.getParent().requestDisallowInterceptTouchEvent(true); 1078 } 1079 if (mQsExpansionAnimator != null) { 1080 onQsExpansionStarted(); 1081 mInitialHeightOnTouch = mQsExpansionHeight; 1082 mQsTracking = true; 1083 mNotificationStackScroller.cancelLongPress(); 1084 } 1085 break; 1086 case MotionEvent.ACTION_POINTER_UP: 1087 final int upPointer = event.getPointerId(event.getActionIndex()); 1088 if (mTrackingPointer == upPointer) { 1089 // gesture is ongoing, find a new pointer to track 1090 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 1091 mTrackingPointer = event.getPointerId(newIndex); 1092 mInitialTouchX = event.getX(newIndex); 1093 mInitialTouchY = event.getY(newIndex); 1094 } 1095 break; 1096 1097 case MotionEvent.ACTION_MOVE: 1098 final float h = y - mInitialTouchY; 1099 trackMovement(event); 1100 if (mQsTracking) { 1101 1102 // Already tracking because onOverscrolled was called. We need to update here 1103 // so we don't stop for a frame until the next touch event gets handled in 1104 // onTouchEvent. 1105 setQsExpansion(h + mInitialHeightOnTouch); 1106 trackMovement(event); 1107 return true; 1108 } 1109 if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded)) 1110 && Math.abs(h) > Math.abs(x - mInitialTouchX) 1111 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { 1112 mView.getParent().requestDisallowInterceptTouchEvent(true); 1113 mQsTracking = true; 1114 onQsExpansionStarted(); 1115 notifyExpandingFinished(); 1116 mInitialHeightOnTouch = mQsExpansionHeight; 1117 mInitialTouchY = y; 1118 mInitialTouchX = x; 1119 mNotificationStackScroller.cancelLongPress(); 1120 return true; 1121 } 1122 break; 1123 1124 case MotionEvent.ACTION_CANCEL: 1125 case MotionEvent.ACTION_UP: 1126 trackMovement(event); 1127 if (mQsTracking) { 1128 flingQsWithCurrentVelocity(y, 1129 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 1130 mQsTracking = false; 1131 } 1132 break; 1133 } 1134 return false; 1135 } 1136 1137 @Override isInContentBounds(float x, float y)1138 protected boolean isInContentBounds(float x, float y) { 1139 float stackScrollerX = mNotificationStackScroller.getX(); 1140 return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) 1141 && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); 1142 } 1143 initDownStates(MotionEvent event)1144 private void initDownStates(MotionEvent event) { 1145 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1146 mOnlyAffordanceInThisMotion = false; 1147 mQsTouchAboveFalsingThreshold = mQsFullyExpanded; 1148 mDozingOnDown = isDozing(); 1149 mDownX = event.getX(); 1150 mDownY = event.getY(); 1151 mCollapsedOnDown = isFullyCollapsed(); 1152 mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY); 1153 mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); 1154 mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; 1155 mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; 1156 if (mExpectingSynthesizedDown) { 1157 mLastEventSynthesizedDown = true; 1158 } else { 1159 // down but not synthesized motion event. 1160 mLastEventSynthesizedDown = false; 1161 } 1162 } else { 1163 // not down event at all. 1164 mLastEventSynthesizedDown = false; 1165 } 1166 } 1167 1168 /** 1169 * Can the panel collapse in this motion because it was started on QQS? 1170 * 1171 * @param downX the x location where the touch started 1172 * @param downY the y location where the touch started 1173 * 1174 * @return true if the panel could be collapsed because it stared on QQS 1175 */ canPanelCollapseOnQQS(float downX, float downY)1176 private boolean canPanelCollapseOnQQS(float downX, float downY) { 1177 if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) { 1178 return false; 1179 } 1180 View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 1181 return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth() 1182 && downY <= header.getBottom(); 1183 1184 } 1185 flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)1186 private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { 1187 float vel = getCurrentQSVelocity(); 1188 final boolean expandsQs = flingExpandsQs(vel); 1189 if (expandsQs) { 1190 logQsSwipeDown(y); 1191 } 1192 flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE); 1193 } 1194 logQsSwipeDown(float y)1195 private void logQsSwipeDown(float y) { 1196 float vel = getCurrentQSVelocity(); 1197 final int 1198 gesture = 1199 mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS 1200 : MetricsEvent.ACTION_SHADE_QS_PULL; 1201 mLockscreenGestureLogger.write(gesture, 1202 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), 1203 (int) (vel / mStatusBar.getDisplayDensity())); 1204 } 1205 flingExpandsQs(float vel)1206 private boolean flingExpandsQs(float vel) { 1207 if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) { 1208 return false; 1209 } 1210 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 1211 return getQsExpansionFraction() > 0.5f; 1212 } else { 1213 return vel > 0; 1214 } 1215 } 1216 isFalseTouch()1217 private boolean isFalseTouch() { 1218 if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) { 1219 return false; 1220 } 1221 if (mFalsingManager.isClassifierEnabled()) { 1222 return mFalsingManager.isFalseTouch(); 1223 } 1224 return !mQsTouchAboveFalsingThreshold; 1225 } 1226 getQsExpansionFraction()1227 private float getQsExpansionFraction() { 1228 return Math.min( 1229 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight 1230 - mQsMinExpansionHeight)); 1231 } 1232 1233 @Override shouldExpandWhenNotFlinging()1234 protected boolean shouldExpandWhenNotFlinging() { 1235 if (super.shouldExpandWhenNotFlinging()) { 1236 return true; 1237 } 1238 if (mAllowExpandForSmallExpansion) { 1239 // When we get a touch that came over from launcher, the velocity isn't always correct 1240 // Let's err on expanding if the gesture has been reasonably slow 1241 long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; 1242 return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; 1243 } 1244 return false; 1245 } 1246 1247 @Override getOpeningHeight()1248 protected float getOpeningHeight() { 1249 return mNotificationStackScroller.getOpeningHeight(); 1250 } 1251 1252 handleQsTouch(MotionEvent event)1253 private boolean handleQsTouch(MotionEvent event) { 1254 final int action = event.getActionMasked(); 1255 if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f 1256 && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) { 1257 1258 // Down in the empty area while fully expanded - go to QS. 1259 mQsTracking = true; 1260 mConflictingQsExpansionGesture = true; 1261 onQsExpansionStarted(); 1262 mInitialHeightOnTouch = mQsExpansionHeight; 1263 mInitialTouchY = event.getX(); 1264 mInitialTouchX = event.getY(); 1265 } 1266 if (!isFullyCollapsed()) { 1267 handleQsDown(event); 1268 } 1269 if (!mQsExpandImmediate && mQsTracking) { 1270 onQsTouch(event); 1271 if (!mConflictingQsExpansionGesture) { 1272 return true; 1273 } 1274 } 1275 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 1276 mConflictingQsExpansionGesture = false; 1277 } 1278 if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) { 1279 mTwoFingerQsExpandPossible = true; 1280 } 1281 if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) 1282 < mStatusBarMinHeight) { 1283 mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); 1284 mQsExpandImmediate = true; 1285 mNotificationStackScroller.setShouldShowShelfOnly(true); 1286 requestPanelHeightUpdate(); 1287 1288 // Normally, we start listening when the panel is expanded, but here we need to start 1289 // earlier so the state is already up to date when dragging down. 1290 setListening(true); 1291 } 1292 return false; 1293 } 1294 isInQsArea(float x, float y)1295 private boolean isInQsArea(float x, float y) { 1296 return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && ( 1297 y <= mNotificationStackScroller.getBottomMostNotificationBottom() 1298 || y <= mQs.getView().getY() + mQs.getView().getHeight()); 1299 } 1300 isOpenQsEvent(MotionEvent event)1301 private boolean isOpenQsEvent(MotionEvent event) { 1302 final int pointerCount = event.getPointerCount(); 1303 final int action = event.getActionMasked(); 1304 1305 final boolean 1306 twoFingerDrag = 1307 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2; 1308 1309 final boolean 1310 stylusButtonClickDrag = 1311 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1312 MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed( 1313 MotionEvent.BUTTON_STYLUS_SECONDARY)); 1314 1315 final boolean 1316 mouseButtonClickDrag = 1317 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1318 MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed( 1319 MotionEvent.BUTTON_TERTIARY)); 1320 1321 return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag; 1322 } 1323 handleQsDown(MotionEvent event)1324 private void handleQsDown(MotionEvent event) { 1325 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( 1326 event.getX(), event.getY(), -1)) { 1327 mFalsingManager.onQsDown(); 1328 mQsTracking = true; 1329 onQsExpansionStarted(); 1330 mInitialHeightOnTouch = mQsExpansionHeight; 1331 mInitialTouchY = event.getX(); 1332 mInitialTouchX = event.getY(); 1333 1334 // If we interrupt an expansion gesture here, make sure to update the state correctly. 1335 notifyExpandingFinished(); 1336 } 1337 } 1338 1339 /** 1340 * Input focus transfer is about to happen. 1341 */ startWaitingForOpenPanelGesture()1342 public void startWaitingForOpenPanelGesture() { 1343 if (!isFullyCollapsed()) { 1344 return; 1345 } 1346 mExpectingSynthesizedDown = true; 1347 onTrackingStarted(); 1348 updatePanelExpanded(); 1349 } 1350 1351 /** 1352 * Called when this view is no longer waiting for input focus transfer. 1353 * 1354 * There are two scenarios behind this function call. First, input focus transfer 1355 * has successfully happened and this view already received synthetic DOWN event. 1356 * (mExpectingSynthesizedDown == false). Do nothing. 1357 * 1358 * Second, before input focus transfer finished, user may have lifted finger 1359 * in previous window and this window never received synthetic DOWN event. 1360 * (mExpectingSynthesizedDown == true). 1361 * In this case, we use the velocity to trigger fling event. 1362 * 1363 * @param velocity unit is in px / millis 1364 */ stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)1365 public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) { 1366 if (mExpectingSynthesizedDown) { 1367 mExpectingSynthesizedDown = false; 1368 if (cancel) { 1369 collapse(false /* delayed */, 1.0f /* speedUpFactor */); 1370 } else { 1371 maybeVibrateOnOpening(); 1372 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */); 1373 } 1374 onTrackingStopped(false); 1375 } 1376 } 1377 1378 @Override flingExpands(float vel, float vectorVel, float x, float y)1379 protected boolean flingExpands(float vel, float vectorVel, float x, float y) { 1380 boolean expands = super.flingExpands(vel, vectorVel, x, y); 1381 1382 // If we are already running a QS expansion, make sure that we keep the panel open. 1383 if (mQsExpansionAnimator != null) { 1384 expands = true; 1385 } 1386 return expands; 1387 } 1388 1389 @Override shouldGestureWaitForTouchSlop()1390 protected boolean shouldGestureWaitForTouchSlop() { 1391 if (mExpectingSynthesizedDown) { 1392 mExpectingSynthesizedDown = false; 1393 return false; 1394 } 1395 return isFullyCollapsed() || mBarState != StatusBarState.SHADE; 1396 } 1397 1398 @Override shouldGestureIgnoreXTouchSlop(float x, float y)1399 protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { 1400 return !mAffordanceHelper.isOnAffordanceIcon(x, y); 1401 } 1402 onQsTouch(MotionEvent event)1403 private void onQsTouch(MotionEvent event) { 1404 int pointerIndex = event.findPointerIndex(mTrackingPointer); 1405 if (pointerIndex < 0) { 1406 pointerIndex = 0; 1407 mTrackingPointer = event.getPointerId(pointerIndex); 1408 } 1409 final float y = event.getY(pointerIndex); 1410 final float x = event.getX(pointerIndex); 1411 final float h = y - mInitialTouchY; 1412 1413 switch (event.getActionMasked()) { 1414 case MotionEvent.ACTION_DOWN: 1415 mQsTracking = true; 1416 mInitialTouchY = y; 1417 mInitialTouchX = x; 1418 onQsExpansionStarted(); 1419 mInitialHeightOnTouch = mQsExpansionHeight; 1420 initVelocityTracker(); 1421 trackMovement(event); 1422 break; 1423 1424 case MotionEvent.ACTION_POINTER_UP: 1425 final int upPointer = event.getPointerId(event.getActionIndex()); 1426 if (mTrackingPointer == upPointer) { 1427 // gesture is ongoing, find a new pointer to track 1428 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 1429 final float newY = event.getY(newIndex); 1430 final float newX = event.getX(newIndex); 1431 mTrackingPointer = event.getPointerId(newIndex); 1432 mInitialHeightOnTouch = mQsExpansionHeight; 1433 mInitialTouchY = newY; 1434 mInitialTouchX = newX; 1435 } 1436 break; 1437 1438 case MotionEvent.ACTION_MOVE: 1439 setQsExpansion(h + mInitialHeightOnTouch); 1440 if (h >= getFalsingThreshold()) { 1441 mQsTouchAboveFalsingThreshold = true; 1442 } 1443 trackMovement(event); 1444 break; 1445 1446 case MotionEvent.ACTION_UP: 1447 case MotionEvent.ACTION_CANCEL: 1448 mQsTracking = false; 1449 mTrackingPointer = -1; 1450 trackMovement(event); 1451 float fraction = getQsExpansionFraction(); 1452 if (fraction != 0f || y >= mInitialTouchY) { 1453 flingQsWithCurrentVelocity(y, 1454 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 1455 } 1456 if (mQsVelocityTracker != null) { 1457 mQsVelocityTracker.recycle(); 1458 mQsVelocityTracker = null; 1459 } 1460 break; 1461 } 1462 } 1463 getFalsingThreshold()1464 private int getFalsingThreshold() { 1465 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 1466 return (int) (mQsFalsingThreshold * factor); 1467 } 1468 setOverScrolling(boolean overscrolling)1469 private void setOverScrolling(boolean overscrolling) { 1470 mStackScrollerOverscrolling = overscrolling; 1471 if (mQs == null) return; 1472 mQs.setOverscrolling(overscrolling); 1473 } 1474 onQsExpansionStarted()1475 private void onQsExpansionStarted() { 1476 onQsExpansionStarted(0); 1477 } 1478 onQsExpansionStarted(int overscrollAmount)1479 protected void onQsExpansionStarted(int overscrollAmount) { 1480 cancelQsAnimation(); 1481 cancelHeightAnimator(); 1482 1483 // Reset scroll position and apply that position to the expanded height. 1484 float height = mQsExpansionHeight - overscrollAmount; 1485 setQsExpansion(height); 1486 requestPanelHeightUpdate(); 1487 mNotificationStackScroller.checkSnoozeLeavebehind(); 1488 1489 // When expanding QS, let's authenticate the user if possible, 1490 // this will speed up notification actions. 1491 if (height == 0) { 1492 mStatusBar.requestFaceAuth(); 1493 } 1494 } 1495 setQsExpanded(boolean expanded)1496 private void setQsExpanded(boolean expanded) { 1497 boolean changed = mQsExpanded != expanded; 1498 if (changed) { 1499 mQsExpanded = expanded; 1500 updateQsState(); 1501 requestPanelHeightUpdate(); 1502 mFalsingManager.setQsExpanded(expanded); 1503 mStatusBar.setQsExpanded(expanded); 1504 mNotificationContainerParent.setQsExpanded(expanded); 1505 mPulseExpansionHandler.setQsExpanded(expanded); 1506 mKeyguardBypassController.setQSExpanded(expanded); 1507 } 1508 } 1509 maybeAnimateBottomAreaAlpha()1510 private void maybeAnimateBottomAreaAlpha() { 1511 mBottomAreaShadeAlphaAnimator.cancel(); 1512 if (mBarState == StatusBarState.SHADE_LOCKED) { 1513 mBottomAreaShadeAlphaAnimator.start(); 1514 } else { 1515 mBottomAreaShadeAlpha = 1f; 1516 } 1517 } 1518 1519 private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() { 1520 @Override 1521 public void run() { 1522 mKeyguardStatusViewAnimating = false; 1523 mKeyguardStatusView.setVisibility(View.INVISIBLE); 1524 } 1525 }; 1526 1527 private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() { 1528 @Override 1529 public void run() { 1530 mKeyguardStatusViewAnimating = false; 1531 mKeyguardStatusView.setVisibility(View.GONE); 1532 } 1533 }; 1534 1535 private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() { 1536 @Override 1537 public void run() { 1538 mKeyguardStatusViewAnimating = false; 1539 } 1540 }; 1541 1542 private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { 1543 @Override 1544 public void run() { 1545 mKeyguardStatusBar.setVisibility(View.INVISIBLE); 1546 mKeyguardStatusBar.setAlpha(1f); 1547 mKeyguardStatusBarAnimateAlpha = 1f; 1548 } 1549 }; 1550 animateKeyguardStatusBarOut()1551 private void animateKeyguardStatusBarOut() { 1552 ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); 1553 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 1554 anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway() 1555 ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0); 1556 1557 long duration; 1558 if (mKeyguardStateController.isKeyguardFadingAway()) { 1559 duration = mKeyguardStateController.getShortenedFadingAwayDuration(); 1560 } else { 1561 duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; 1562 } 1563 anim.setDuration(duration); 1564 1565 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 1566 anim.addListener(new AnimatorListenerAdapter() { 1567 @Override 1568 public void onAnimationEnd(Animator animation) { 1569 mAnimateKeyguardStatusBarInvisibleEndRunnable.run(); 1570 } 1571 }); 1572 anim.start(); 1573 } 1574 1575 private final ValueAnimator.AnimatorUpdateListener 1576 mStatusBarAnimateAlphaListener = 1577 new ValueAnimator.AnimatorUpdateListener() { 1578 @Override 1579 public void onAnimationUpdate(ValueAnimator animation) { 1580 mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue(); 1581 updateHeaderKeyguardAlpha(); 1582 } 1583 }; 1584 animateKeyguardStatusBarIn(long duration)1585 private void animateKeyguardStatusBarIn(long duration) { 1586 mKeyguardStatusBar.setVisibility(View.VISIBLE); 1587 mKeyguardStatusBar.setAlpha(0f); 1588 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1589 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 1590 anim.setDuration(duration); 1591 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 1592 anim.start(); 1593 } 1594 1595 private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { 1596 @Override 1597 public void run() { 1598 mKeyguardBottomArea.setVisibility(View.GONE); 1599 } 1600 }; 1601 setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)1602 private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { 1603 mKeyguardBottomArea.animate().cancel(); 1604 if (goingToFullShade) { 1605 mKeyguardBottomArea.animate().alpha(0f).setStartDelay( 1606 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( 1607 mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator( 1608 Interpolators.ALPHA_OUT).withEndAction( 1609 mAnimateKeyguardBottomAreaInvisibleEndRunnable).start(); 1610 } else if (statusBarState == StatusBarState.KEYGUARD 1611 || statusBarState == StatusBarState.SHADE_LOCKED) { 1612 mKeyguardBottomArea.setVisibility(View.VISIBLE); 1613 mKeyguardBottomArea.setAlpha(1f); 1614 } else { 1615 mKeyguardBottomArea.setVisibility(View.GONE); 1616 } 1617 } 1618 setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, boolean goingToFullShade)1619 private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, 1620 boolean goingToFullShade) { 1621 mKeyguardStatusView.animate().cancel(); 1622 mKeyguardStatusViewAnimating = false; 1623 if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD 1624 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) { 1625 mKeyguardStatusViewAnimating = true; 1626 mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration( 1627 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction( 1628 mAnimateKeyguardStatusViewGoneEndRunnable); 1629 if (keyguardFadingAway) { 1630 mKeyguardStatusView.animate().setStartDelay( 1631 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( 1632 mKeyguardStateController.getShortenedFadingAwayDuration()).start(); 1633 } 1634 } else if (mBarState == StatusBarState.SHADE_LOCKED 1635 && statusBarState == StatusBarState.KEYGUARD) { 1636 mKeyguardStatusView.setVisibility(View.VISIBLE); 1637 mKeyguardStatusViewAnimating = true; 1638 mKeyguardStatusView.setAlpha(0f); 1639 mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration( 1640 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction( 1641 mAnimateKeyguardStatusViewVisibleEndRunnable); 1642 } else if (statusBarState == StatusBarState.KEYGUARD) { 1643 if (keyguardFadingAway) { 1644 mKeyguardStatusViewAnimating = true; 1645 mKeyguardStatusView.animate().alpha(0).translationYBy( 1646 -getHeight() * 0.05f).setInterpolator( 1647 Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay( 1648 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start(); 1649 } else { 1650 mKeyguardStatusView.setVisibility(View.VISIBLE); 1651 mKeyguardStatusView.setAlpha(1f); 1652 } 1653 } else { 1654 mKeyguardStatusView.setVisibility(View.GONE); 1655 mKeyguardStatusView.setAlpha(1f); 1656 } 1657 } 1658 updateQsState()1659 private void updateQsState() { 1660 mNotificationStackScroller.setQsExpanded(mQsExpanded); 1661 mNotificationStackScroller.setScrollingEnabled( 1662 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded 1663 || mQsExpansionFromOverscroll)); 1664 updateEmptyShadeView(); 1665 1666 mQsNavbarScrim.setVisibility( 1667 mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling 1668 && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE); 1669 if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { 1670 mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); 1671 } 1672 if (mQs == null) return; 1673 mQs.setExpanded(mQsExpanded); 1674 } 1675 setQsExpansion(float height)1676 private void setQsExpansion(float height) { 1677 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); 1678 mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; 1679 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling 1680 && !mDozing) { 1681 setQsExpanded(true); 1682 } else if (height <= mQsMinExpansionHeight && mQsExpanded) { 1683 setQsExpanded(false); 1684 } 1685 mQsExpansionHeight = height; 1686 updateQsExpansion(); 1687 requestScrollerTopPaddingUpdate(false /* animate */); 1688 updateHeaderKeyguardAlpha(); 1689 if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) { 1690 updateKeyguardBottomAreaAlpha(); 1691 updateBigClockAlpha(); 1692 } 1693 if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling 1694 && mQsScrimEnabled) { 1695 mQsNavbarScrim.setAlpha(getQsExpansionFraction()); 1696 } 1697 1698 if (mAccessibilityManager.isEnabled()) { 1699 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 1700 } 1701 1702 if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded 1703 && mFalsingManager.shouldEnforceBouncer()) { 1704 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, 1705 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); 1706 } 1707 for (int i = 0; i < mExpansionListeners.size(); i++) { 1708 mExpansionListeners.get(i).onQsExpansionChanged( 1709 mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0); 1710 } 1711 if (DEBUG) { 1712 mView.invalidate(); 1713 } 1714 } 1715 updateQsExpansion()1716 protected void updateQsExpansion() { 1717 if (mQs == null) return; 1718 float qsExpansionFraction = getQsExpansionFraction(); 1719 mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); 1720 mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); 1721 mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); 1722 } 1723 determineAccessibilityPaneTitle()1724 private String determineAccessibilityPaneTitle() { 1725 if (mQs != null && mQs.isCustomizing()) { 1726 return mResources.getString(R.string.accessibility_desc_quick_settings_edit); 1727 } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) { 1728 // Upon initialisation when we are not layouted yet we don't want to announce that we 1729 // are fully expanded, hence the != 0.0f check. 1730 return mResources.getString(R.string.accessibility_desc_quick_settings); 1731 } else if (mBarState == StatusBarState.KEYGUARD) { 1732 return mResources.getString(R.string.accessibility_desc_lock_screen); 1733 } else { 1734 return mResources.getString(R.string.accessibility_desc_notification_shade); 1735 } 1736 } 1737 calculateQsTopPadding()1738 private float calculateQsTopPadding() { 1739 if (mKeyguardShowing && (mQsExpandImmediate 1740 || mIsExpanding && mQsExpandedWhenExpandingStarted)) { 1741 1742 // Either QS pushes the notifications down when fully expanded, or QS is fully above the 1743 // notifications (mostly on tablets). maxNotificationPadding denotes the normal top 1744 // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings 1745 // panel. We need to take the maximum and linearly interpolate with the panel expansion 1746 // for a nice motion. 1747 int maxNotificationPadding = getKeyguardNotificationStaticPadding(); 1748 int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding; 1749 int max = mBarState == StatusBarState.KEYGUARD ? Math.max( 1750 maxNotificationPadding, maxQsPadding) : maxQsPadding; 1751 return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, 1752 getExpandedFraction()); 1753 } else if (mQsSizeChangeAnimator != null) { 1754 return Math.max( 1755 (int) mQsSizeChangeAnimator.getAnimatedValue(), 1756 getKeyguardNotificationStaticPadding()); 1757 } else if (mKeyguardShowing) { 1758 // We can only do the smoother transition on Keyguard when we also are not collapsing 1759 // from a scrolled quick settings. 1760 return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), 1761 (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding), 1762 getQsExpansionFraction()); 1763 } else { 1764 return mQsExpansionHeight + mQsNotificationTopPadding; 1765 } 1766 } 1767 1768 /** 1769 * @return the topPadding of notifications when on keyguard not respecting quick settings 1770 * expansion 1771 */ getKeyguardNotificationStaticPadding()1772 private int getKeyguardNotificationStaticPadding() { 1773 if (!mKeyguardShowing) { 1774 return 0; 1775 } 1776 if (!mKeyguardBypassController.getBypassEnabled()) { 1777 return mClockPositionResult.stackScrollerPadding; 1778 } 1779 int collapsedPosition = mHeadsUpInset; 1780 if (!mNotificationStackScroller.isPulseExpanding()) { 1781 return collapsedPosition; 1782 } else { 1783 int expandedPosition = mClockPositionResult.stackScrollerPadding; 1784 return (int) MathUtils.lerp(collapsedPosition, expandedPosition, 1785 mNotificationStackScroller.calculateAppearFractionBypass()); 1786 } 1787 } 1788 1789 requestScrollerTopPaddingUpdate(boolean animate)1790 protected void requestScrollerTopPaddingUpdate(boolean animate) { 1791 mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); 1792 if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { 1793 // update the position of the header 1794 updateQsExpansion(); 1795 } 1796 } 1797 1798 updateQSPulseExpansion()1799 private void updateQSPulseExpansion() { 1800 if (mQs != null) { 1801 mQs.setShowCollapsedOnKeyguard( 1802 mKeyguardShowing && mKeyguardBypassController.getBypassEnabled() 1803 && mNotificationStackScroller.isPulseExpanding()); 1804 } 1805 } 1806 trackMovement(MotionEvent event)1807 private void trackMovement(MotionEvent event) { 1808 if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); 1809 } 1810 initVelocityTracker()1811 private void initVelocityTracker() { 1812 if (mQsVelocityTracker != null) { 1813 mQsVelocityTracker.recycle(); 1814 } 1815 mQsVelocityTracker = VelocityTracker.obtain(); 1816 } 1817 getCurrentQSVelocity()1818 private float getCurrentQSVelocity() { 1819 if (mQsVelocityTracker == null) { 1820 return 0; 1821 } 1822 mQsVelocityTracker.computeCurrentVelocity(1000); 1823 return mQsVelocityTracker.getYVelocity(); 1824 } 1825 cancelQsAnimation()1826 private void cancelQsAnimation() { 1827 if (mQsExpansionAnimator != null) { 1828 mQsExpansionAnimator.cancel(); 1829 } 1830 } 1831 1832 /** 1833 * @see #flingSettings(float, int, Runnable, boolean) 1834 */ flingSettings(float vel, int type)1835 public void flingSettings(float vel, int type) { 1836 flingSettings(vel, type, null, false /* isClick */); 1837 } 1838 1839 /** 1840 * Animates QS or QQS as if the user had swiped up or down. 1841 * 1842 * @param vel Finger velocity or 0 when not initiated by touch events. 1843 * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link 1844 * #FLING_HIDE}. 1845 * @param onFinishRunnable Runnable to be executed at the end of animation. 1846 * @param isClick If originated by click (different interpolator and duration.) 1847 */ flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick)1848 protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, 1849 boolean isClick) { 1850 float target; 1851 switch (type) { 1852 case FLING_EXPAND: 1853 target = mQsMaxExpansionHeight; 1854 break; 1855 case FLING_COLLAPSE: 1856 target = mQsMinExpansionHeight; 1857 break; 1858 case FLING_HIDE: 1859 default: 1860 target = 0; 1861 } 1862 if (target == mQsExpansionHeight) { 1863 if (onFinishRunnable != null) { 1864 onFinishRunnable.run(); 1865 } 1866 return; 1867 } 1868 1869 // If we move in the opposite direction, reset velocity and use a different duration. 1870 boolean oppositeDirection = false; 1871 boolean expanding = type == FLING_EXPAND; 1872 if (vel > 0 && !expanding || vel < 0 && expanding) { 1873 vel = 0; 1874 oppositeDirection = true; 1875 } 1876 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); 1877 if (isClick) { 1878 animator.setInterpolator(Interpolators.TOUCH_RESPONSE); 1879 animator.setDuration(368); 1880 } else { 1881 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); 1882 } 1883 if (oppositeDirection) { 1884 animator.setDuration(350); 1885 } 1886 animator.addUpdateListener(animation -> { 1887 setQsExpansion((Float) animation.getAnimatedValue()); 1888 }); 1889 animator.addListener(new AnimatorListenerAdapter() { 1890 @Override 1891 public void onAnimationStart(Animator animation) { 1892 notifyExpandingStarted(); 1893 } 1894 1895 @Override 1896 public void onAnimationEnd(Animator animation) { 1897 mAnimatingQS = false; 1898 notifyExpandingFinished(); 1899 mNotificationStackScroller.resetCheckSnoozeLeavebehind(); 1900 mQsExpansionAnimator = null; 1901 if (onFinishRunnable != null) { 1902 onFinishRunnable.run(); 1903 } 1904 } 1905 }); 1906 // Let's note that we're animating QS. Moving the animator here will cancel it immediately, 1907 // so we need a separate flag. 1908 mAnimatingQS = true; 1909 animator.start(); 1910 mQsExpansionAnimator = animator; 1911 mQsAnimatorExpand = expanding; 1912 } 1913 1914 /** 1915 * @return Whether we should intercept a gesture to open Quick Settings. 1916 */ shouldQuickSettingsIntercept(float x, float y, float yDiff)1917 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { 1918 if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing 1919 && mKeyguardBypassController.getBypassEnabled())) { 1920 return false; 1921 } 1922 View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 1923 final boolean 1924 onHeader = 1925 x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth() 1926 && y >= header.getTop() && y <= header.getBottom(); 1927 if (mQsExpanded) { 1928 return onHeader || (yDiff < 0 && isInQsArea(x, y)); 1929 } else { 1930 return onHeader; 1931 } 1932 } 1933 1934 @Override canCollapsePanelOnTouch()1935 protected boolean canCollapsePanelOnTouch() { 1936 if (!isInSettings()) { 1937 return mBarState == StatusBarState.KEYGUARD 1938 || mNotificationStackScroller.isScrolledToBottom() 1939 || mIsPanelCollapseOnQQS; 1940 } else { 1941 return true; 1942 } 1943 } 1944 1945 @Override getMaxPanelHeight()1946 protected int getMaxPanelHeight() { 1947 if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) { 1948 return getMaxPanelHeightBypass(); 1949 } else { 1950 return getMaxPanelHeightNonBypass(); 1951 } 1952 } 1953 getMaxPanelHeightNonBypass()1954 private int getMaxPanelHeightNonBypass() { 1955 int min = mStatusBarMinHeight; 1956 if (!(mBarState == StatusBarState.KEYGUARD) 1957 && mNotificationStackScroller.getNotGoneChildCount() == 0) { 1958 int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); 1959 min = Math.max(min, minHeight); 1960 } 1961 int maxHeight; 1962 if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted 1963 || mPulsing) { 1964 maxHeight = calculatePanelHeightQsExpanded(); 1965 } else { 1966 maxHeight = calculatePanelHeightShade(); 1967 } 1968 maxHeight = Math.max(min, maxHeight); 1969 if (maxHeight == 0) { 1970 Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): " 1971 + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: " 1972 + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " 1973 + calculatePanelHeightShade() + ", mStatusBarMinHeight = " 1974 + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight); 1975 } 1976 return maxHeight; 1977 } 1978 getMaxPanelHeightBypass()1979 private int getMaxPanelHeightBypass() { 1980 int position = 1981 mClockPositionAlgorithm.getExpandedClockPosition() 1982 + mKeyguardStatusView.getHeight(); 1983 if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { 1984 position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; 1985 } 1986 return position; 1987 } 1988 isInSettings()1989 public boolean isInSettings() { 1990 return mQsExpanded; 1991 } 1992 isExpanding()1993 public boolean isExpanding() { 1994 return mIsExpanding; 1995 } 1996 1997 @Override onHeightUpdated(float expandedHeight)1998 protected void onHeightUpdated(float expandedHeight) { 1999 if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { 2000 // Updating the clock position will set the top padding which might 2001 // trigger a new panel height and re-position the clock. 2002 // This is a circular dependency and should be avoided, otherwise we'll have 2003 // a stack overflow. 2004 if (mStackScrollerMeasuringPass > 2) { 2005 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); 2006 } else { 2007 positionClockAndNotifications(); 2008 } 2009 } 2010 if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null 2011 && !mQsExpansionFromOverscroll) { 2012 float t; 2013 if (mKeyguardShowing) { 2014 2015 // On Keyguard, interpolate the QS expansion linearly to the panel expansion 2016 t = expandedHeight / (getMaxPanelHeight()); 2017 } else { 2018 // In Shade, interpolate linearly such that QS is closed whenever panel height is 2019 // minimum QS expansion + minStackHeight 2020 float 2021 panelHeightQsCollapsed = 2022 mNotificationStackScroller.getIntrinsicPadding() 2023 + mNotificationStackScroller.getLayoutMinHeight(); 2024 float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); 2025 t = 2026 (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded 2027 - panelHeightQsCollapsed); 2028 } 2029 float 2030 targetHeight = 2031 mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); 2032 setQsExpansion(targetHeight); 2033 } 2034 updateExpandedHeight(expandedHeight); 2035 updateHeader(); 2036 updateNotificationTranslucency(); 2037 updatePanelExpanded(); 2038 updateGestureExclusionRect(); 2039 if (DEBUG) { 2040 mView.invalidate(); 2041 } 2042 } 2043 updatePanelExpanded()2044 private void updatePanelExpanded() { 2045 boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; 2046 if (mPanelExpanded != isExpanded) { 2047 mHeadsUpManager.setIsPanelExpanded(isExpanded); 2048 mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded); 2049 mStatusBar.setPanelExpanded(isExpanded); 2050 mPanelExpanded = isExpanded; 2051 } 2052 } 2053 calculatePanelHeightShade()2054 private int calculatePanelHeightShade() { 2055 int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); 2056 int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin; 2057 maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); 2058 2059 if (mBarState == StatusBarState.KEYGUARD) { 2060 int 2061 minKeyguardPanelBottom = 2062 mClockPositionAlgorithm.getExpandedClockPosition() 2063 + mKeyguardStatusView.getHeight() 2064 + mNotificationStackScroller.getIntrinsicContentHeight(); 2065 return Math.max(maxHeight, minKeyguardPanelBottom); 2066 } else { 2067 return maxHeight; 2068 } 2069 } 2070 calculatePanelHeightQsExpanded()2071 private int calculatePanelHeightQsExpanded() { 2072 float 2073 notificationHeight = 2074 mNotificationStackScroller.getHeight() 2075 - mNotificationStackScroller.getEmptyBottomMargin() 2076 - mNotificationStackScroller.getTopPadding(); 2077 2078 // When only empty shade view is visible in QS collapsed state, simulate that we would have 2079 // it in expanded QS state as well so we don't run into troubles when fading the view in/out 2080 // and expanding/collapsing the whole panel from/to quick settings. 2081 if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) { 2082 notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight(); 2083 } 2084 int maxQsHeight = mQsMaxExpansionHeight; 2085 2086 if (mKeyguardShowing) { 2087 maxQsHeight += mQsNotificationTopPadding; 2088 } 2089 2090 // If an animation is changing the size of the QS panel, take the animated value. 2091 if (mQsSizeChangeAnimator != null) { 2092 maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 2093 } 2094 float totalHeight = Math.max(maxQsHeight, 2095 mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding 2096 : 0) + notificationHeight 2097 + mNotificationStackScroller.getTopPaddingOverflow(); 2098 if (totalHeight > mNotificationStackScroller.getHeight()) { 2099 float 2100 fullyCollapsedHeight = 2101 maxQsHeight + mNotificationStackScroller.getLayoutMinHeight(); 2102 totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); 2103 } 2104 return (int) totalHeight; 2105 } 2106 updateNotificationTranslucency()2107 private void updateNotificationTranslucency() { 2108 float alpha = 1f; 2109 if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp 2110 && !mHeadsUpManager.hasPinnedHeadsUp()) { 2111 alpha = getFadeoutAlpha(); 2112 } 2113 if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning 2114 && !mKeyguardBypassController.getBypassEnabled()) { 2115 alpha *= mClockPositionResult.clockAlpha; 2116 } 2117 mNotificationStackScroller.setAlpha(alpha); 2118 } 2119 getFadeoutAlpha()2120 private float getFadeoutAlpha() { 2121 float alpha; 2122 if (mQsMinExpansionHeight == 0) { 2123 return 1.0f; 2124 } 2125 alpha = getExpandedHeight() / mQsMinExpansionHeight; 2126 alpha = Math.max(0, Math.min(alpha, 1)); 2127 alpha = (float) Math.pow(alpha, 0.75); 2128 return alpha; 2129 } 2130 2131 @Override getOverExpansionAmount()2132 protected float getOverExpansionAmount() { 2133 float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); 2134 if (isNaN(result)) { 2135 Log.wtf(TAG, "OverExpansionAmount is NaN!"); 2136 } 2137 2138 return result; 2139 } 2140 2141 @Override getOverExpansionPixels()2142 protected float getOverExpansionPixels() { 2143 return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); 2144 } 2145 2146 /** 2147 * Hides the header when notifications are colliding with it. 2148 */ updateHeader()2149 private void updateHeader() { 2150 if (mBarState == StatusBarState.KEYGUARD) { 2151 updateHeaderKeyguardAlpha(); 2152 } 2153 updateQsExpansion(); 2154 } 2155 getHeaderTranslation()2156 protected float getHeaderTranslation() { 2157 if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { 2158 return -mQs.getQsMinExpansionHeight(); 2159 } 2160 float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight); 2161 float startHeight = -mQsExpansionHeight; 2162 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() 2163 && mNotificationStackScroller.isPulseExpanding()) { 2164 if (!mPulseExpansionHandler.isExpanding() 2165 && !mPulseExpansionHandler.getLeavingLockscreen()) { 2166 // If we aborted the expansion we need to make sure the header doesn't reappear 2167 // again after the header has animated away 2168 appearAmount = 0; 2169 } else { 2170 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); 2171 } 2172 startHeight = -mQs.getQsMinExpansionHeight(); 2173 } 2174 float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)) 2175 + mExpandOffset; 2176 return Math.min(0, translation); 2177 } 2178 2179 /** 2180 * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area) 2181 * during swiping up 2182 */ getKeyguardContentsAlpha()2183 private float getKeyguardContentsAlpha() { 2184 float alpha; 2185 if (mBarState == StatusBarState.KEYGUARD) { 2186 2187 // When on Keyguard, we hide the header as soon as we expanded close enough to the 2188 // header 2189 alpha = 2190 getExpandedHeight() / (mKeyguardStatusBar.getHeight() 2191 + mNotificationsHeaderCollideDistance); 2192 } else { 2193 2194 // In SHADE_LOCKED, the top card is already really close to the header. Hide it as 2195 // soon as we start translating the stack. 2196 alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight(); 2197 } 2198 alpha = MathUtils.saturate(alpha); 2199 alpha = (float) Math.pow(alpha, 0.75); 2200 return alpha; 2201 } 2202 updateHeaderKeyguardAlpha()2203 private void updateHeaderKeyguardAlpha() { 2204 if (!mKeyguardShowing) { 2205 return; 2206 } 2207 float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); 2208 float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) 2209 * mKeyguardStatusBarAnimateAlpha; 2210 newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; 2211 mKeyguardStatusBar.setAlpha(newAlpha); 2212 boolean 2213 hideForBypass = 2214 mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace() 2215 || mDelayShowingKeyguardStatusBar; 2216 mKeyguardStatusBar.setVisibility( 2217 newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE); 2218 } 2219 updateKeyguardBottomAreaAlpha()2220 private void updateKeyguardBottomAreaAlpha() { 2221 // There are two possible panel expansion behaviors: 2222 // • User dragging up to unlock: we want to fade out as quick as possible 2223 // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area. 2224 // • User tapping on lock screen: bouncer won't be visible but panel expansion will 2225 // change due to "unlock hint animation." In this case, fading out the bottom area 2226 // would also hide the message that says "swipe to unlock," we don't want to do that. 2227 float expansionAlpha = MathUtils.map( 2228 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, 2229 getExpandedFraction()); 2230 float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); 2231 alpha *= mBottomAreaShadeAlpha; 2232 mKeyguardBottomArea.setAffordanceAlpha(alpha); 2233 mKeyguardBottomArea.setImportantForAccessibility( 2234 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 2235 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); 2236 View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer(); 2237 if (ambientIndicationContainer != null) { 2238 ambientIndicationContainer.setAlpha(alpha); 2239 } 2240 } 2241 2242 /** 2243 * Custom clock fades away when user drags up to unlock or pulls down quick settings. 2244 * 2245 * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See 2246 * {@link #updateKeyguardBottomAreaAlpha}. 2247 */ updateBigClockAlpha()2248 private void updateBigClockAlpha() { 2249 float expansionAlpha = MathUtils.map( 2250 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, 2251 getExpandedFraction()); 2252 float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); 2253 mBigClockContainer.setAlpha(alpha); 2254 } 2255 2256 @Override onExpandingStarted()2257 protected void onExpandingStarted() { 2258 super.onExpandingStarted(); 2259 mNotificationStackScroller.onExpansionStarted(); 2260 mIsExpanding = true; 2261 mQsExpandedWhenExpandingStarted = mQsFullyExpanded; 2262 mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted && 2263 /* We also start expanding when flinging closed Qs. Let's exclude that */ 2264 !mAnimatingQS); 2265 if (mQsExpanded) { 2266 onQsExpansionStarted(); 2267 } 2268 // Since there are QS tiles in the header now, we need to make sure we start listening 2269 // immediately so they can be up to date. 2270 if (mQs == null) return; 2271 mQs.setHeaderListening(true); 2272 } 2273 2274 @Override onExpandingFinished()2275 protected void onExpandingFinished() { 2276 super.onExpandingFinished(); 2277 mNotificationStackScroller.onExpansionStopped(); 2278 mHeadsUpManager.onExpandingFinished(); 2279 mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); 2280 mIsExpanding = false; 2281 mMediaHierarchyManager.setCollapsingShadeFromQS(false); 2282 if (isFullyCollapsed()) { 2283 DejankUtils.postAfterTraversal(new Runnable() { 2284 @Override 2285 public void run() { 2286 setListening(false); 2287 } 2288 }); 2289 2290 // Workaround b/22639032: Make sure we invalidate something because else RenderThread 2291 // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go 2292 // ahead with rendering and we jank. 2293 mView.postOnAnimation(new Runnable() { 2294 @Override 2295 public void run() { 2296 mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT); 2297 } 2298 }); 2299 } else { 2300 setListening(true); 2301 } 2302 mQsExpandImmediate = false; 2303 mNotificationStackScroller.setShouldShowShelfOnly(false); 2304 mTwoFingerQsExpandPossible = false; 2305 notifyListenersTrackingHeadsUp(null); 2306 mExpandingFromHeadsUp = false; 2307 setPanelScrimMinFraction(0.0f); 2308 } 2309 notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild)2310 private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) { 2311 for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { 2312 Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i); 2313 listener.accept(pickedChild); 2314 } 2315 } 2316 setListening(boolean listening)2317 private void setListening(boolean listening) { 2318 mKeyguardStatusBar.setListening(listening); 2319 if (mQs == null) return; 2320 mQs.setListening(listening); 2321 } 2322 2323 @Override expand(boolean animate)2324 public void expand(boolean animate) { 2325 super.expand(animate); 2326 setListening(true); 2327 } 2328 2329 @Override setOverExpansion(float overExpansion, boolean isPixels)2330 protected void setOverExpansion(float overExpansion, boolean isPixels) { 2331 if (mConflictingQsExpansionGesture || mQsExpandImmediate) { 2332 return; 2333 } 2334 if (mBarState != StatusBarState.KEYGUARD) { 2335 mNotificationStackScroller.setOnHeightChangedListener(null); 2336 if (isPixels) { 2337 mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */, 2338 false /* animate */); 2339 } else { 2340 mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */, 2341 false /* animate */); 2342 } 2343 mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); 2344 } 2345 } 2346 2347 @Override onTrackingStarted()2348 protected void onTrackingStarted() { 2349 mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); 2350 super.onTrackingStarted(); 2351 if (mQsFullyExpanded) { 2352 mQsExpandImmediate = true; 2353 mNotificationStackScroller.setShouldShowShelfOnly(true); 2354 } 2355 if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 2356 mAffordanceHelper.animateHideLeftRightIcon(); 2357 } 2358 mNotificationStackScroller.onPanelTrackingStarted(); 2359 } 2360 2361 @Override onTrackingStopped(boolean expand)2362 protected void onTrackingStopped(boolean expand) { 2363 mFalsingManager.onTrackingStopped(); 2364 super.onTrackingStopped(expand); 2365 if (expand) { 2366 mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, 2367 true /* animate */); 2368 } 2369 mNotificationStackScroller.onPanelTrackingStopped(); 2370 if (expand && (mBarState == StatusBarState.KEYGUARD 2371 || mBarState == StatusBarState.SHADE_LOCKED)) { 2372 if (!mHintAnimationRunning) { 2373 mAffordanceHelper.reset(true); 2374 } 2375 } 2376 } 2377 updateMaxHeadsUpTranslation()2378 private void updateMaxHeadsUpTranslation() { 2379 mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); 2380 } 2381 2382 @Override startUnlockHintAnimation()2383 protected void startUnlockHintAnimation() { 2384 if (mPowerManager.isPowerSaveMode()) { 2385 onUnlockHintStarted(); 2386 onUnlockHintFinished(); 2387 return; 2388 } 2389 super.startUnlockHintAnimation(); 2390 } 2391 2392 @Override onUnlockHintFinished()2393 protected void onUnlockHintFinished() { 2394 super.onUnlockHintFinished(); 2395 mNotificationStackScroller.setUnlockHintRunning(false); 2396 } 2397 2398 @Override onUnlockHintStarted()2399 protected void onUnlockHintStarted() { 2400 super.onUnlockHintStarted(); 2401 mNotificationStackScroller.setUnlockHintRunning(true); 2402 } 2403 2404 @Override getPeekHeight()2405 protected float getPeekHeight() { 2406 if (mNotificationStackScroller.getNotGoneChildCount() > 0) { 2407 return mNotificationStackScroller.getPeekHeight(); 2408 } else { 2409 return mQsMinExpansionHeight; 2410 } 2411 } 2412 2413 @Override shouldExpandToTopOfClearAll(float targetHeight)2414 protected boolean shouldExpandToTopOfClearAll(float targetHeight) { 2415 boolean perform = super.shouldExpandToTopOfClearAll(targetHeight); 2416 if (!perform) { 2417 return false; 2418 } 2419 // Let's make sure we're not appearing but the animation will end below the appear. 2420 // Otherwise quick settings would jump at the end of the animation. 2421 float fraction = mNotificationStackScroller.calculateAppearFraction(targetHeight); 2422 return fraction >= 1.0f; 2423 } 2424 2425 @Override shouldUseDismissingAnimation()2426 protected boolean shouldUseDismissingAnimation() { 2427 return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen() 2428 || !isTracking()); 2429 } 2430 2431 @Override fullyExpandedClearAllVisible()2432 protected boolean fullyExpandedClearAllVisible() { 2433 return mNotificationStackScroller.isFooterViewNotGone() 2434 && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; 2435 } 2436 2437 @Override isClearAllVisible()2438 protected boolean isClearAllVisible() { 2439 return mNotificationStackScroller.isFooterViewContentVisible(); 2440 } 2441 2442 @Override getClearAllHeightWithPadding()2443 protected int getClearAllHeightWithPadding() { 2444 return mNotificationStackScroller.getFooterViewHeightWithPadding(); 2445 } 2446 2447 @Override isTrackingBlocked()2448 protected boolean isTrackingBlocked() { 2449 return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; 2450 } 2451 isQsExpanded()2452 public boolean isQsExpanded() { 2453 return mQsExpanded; 2454 } 2455 isQsDetailShowing()2456 public boolean isQsDetailShowing() { 2457 return mQs.isShowingDetail(); 2458 } 2459 closeQsDetail()2460 public void closeQsDetail() { 2461 mQs.closeDetail(); 2462 } 2463 isLaunchTransitionFinished()2464 public boolean isLaunchTransitionFinished() { 2465 return mIsLaunchTransitionFinished; 2466 } 2467 isLaunchTransitionRunning()2468 public boolean isLaunchTransitionRunning() { 2469 return mIsLaunchTransitionRunning; 2470 } 2471 setLaunchTransitionEndRunnable(Runnable r)2472 public void setLaunchTransitionEndRunnable(Runnable r) { 2473 mLaunchAnimationEndRunnable = r; 2474 } 2475 updateDozingVisibilities(boolean animate)2476 private void updateDozingVisibilities(boolean animate) { 2477 mKeyguardBottomArea.setDozing(mDozing, animate); 2478 if (!mDozing && animate) { 2479 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 2480 } 2481 } 2482 2483 @Override isDozing()2484 public boolean isDozing() { 2485 return mDozing; 2486 } 2487 showEmptyShadeView(boolean emptyShadeViewVisible)2488 public void showEmptyShadeView(boolean emptyShadeViewVisible) { 2489 mShowEmptyShadeView = emptyShadeViewVisible; 2490 updateEmptyShadeView(); 2491 } 2492 updateEmptyShadeView()2493 private void updateEmptyShadeView() { 2494 // Hide "No notifications" in QS. 2495 mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded); 2496 } 2497 setQsScrimEnabled(boolean qsScrimEnabled)2498 public void setQsScrimEnabled(boolean qsScrimEnabled) { 2499 boolean changed = mQsScrimEnabled != qsScrimEnabled; 2500 mQsScrimEnabled = qsScrimEnabled; 2501 if (changed) { 2502 updateQsState(); 2503 } 2504 } 2505 setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher)2506 public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { 2507 mKeyguardUserSwitcher = keyguardUserSwitcher; 2508 } 2509 onScreenTurningOn()2510 public void onScreenTurningOn() { 2511 mKeyguardStatusView.dozeTimeTick(); 2512 } 2513 2514 @Override onMiddleClicked()2515 protected boolean onMiddleClicked() { 2516 switch (mBarState) { 2517 case StatusBarState.KEYGUARD: 2518 if (!mDozingOnDown) { 2519 if (mKeyguardBypassController.getBypassEnabled()) { 2520 mUpdateMonitor.requestFaceAuth(); 2521 } else { 2522 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT, 2523 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 2524 mLockscreenGestureLogger 2525 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); 2526 startUnlockHintAnimation(); 2527 } 2528 } 2529 return true; 2530 case StatusBarState.SHADE_LOCKED: 2531 if (!mQsExpanded) { 2532 mStatusBarStateController.setState(StatusBarState.KEYGUARD); 2533 } 2534 return true; 2535 case StatusBarState.SHADE: 2536 2537 // This gets called in the middle of the touch handling, where the state is still 2538 // that we are tracking the panel. Collapse the panel after this is done. 2539 mView.post(mPostCollapseRunnable); 2540 return false; 2541 default: 2542 return true; 2543 } 2544 } 2545 setPanelAlpha(int alpha, boolean animate)2546 public void setPanelAlpha(int alpha, boolean animate) { 2547 if (mPanelAlpha != alpha) { 2548 mPanelAlpha = alpha; 2549 PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255 2550 ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator, 2551 animate); 2552 } 2553 } 2554 setPanelAlphaEndAction(Runnable r)2555 public void setPanelAlphaEndAction(Runnable r) { 2556 mPanelAlphaEndAction = r; 2557 } 2558 updateKeyguardStatusBarForHeadsUp()2559 private void updateKeyguardStatusBarForHeadsUp() { 2560 boolean 2561 showingKeyguardHeadsUp = 2562 mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible(); 2563 if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) { 2564 mShowingKeyguardHeadsUp = showingKeyguardHeadsUp; 2565 if (mKeyguardShowing) { 2566 PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 2567 showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES, 2568 true /* animate */); 2569 } else { 2570 PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f); 2571 } 2572 } 2573 } 2574 setKeyguardHeadsUpShowingAmount(float amount)2575 private void setKeyguardHeadsUpShowingAmount(float amount) { 2576 mKeyguardHeadsUpShowingAmount = amount; 2577 updateHeaderKeyguardAlpha(); 2578 } 2579 getKeyguardHeadsUpShowingAmount()2580 private float getKeyguardHeadsUpShowingAmount() { 2581 return mKeyguardHeadsUpShowingAmount; 2582 } 2583 setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)2584 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { 2585 mHeadsUpAnimatingAway = headsUpAnimatingAway; 2586 mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway); 2587 updateHeadsUpVisibility(); 2588 } 2589 updateHeadsUpVisibility()2590 private void updateHeadsUpVisibility() { 2591 ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode); 2592 } 2593 2594 @Override setHeadsUpManager(HeadsUpManagerPhone headsUpManager)2595 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { 2596 super.setHeadsUpManager(headsUpManager); 2597 mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, 2598 mNotificationStackScroller.getHeadsUpCallback(), 2599 NotificationPanelViewController.this); 2600 } 2601 setTrackedHeadsUp(ExpandableNotificationRow pickedChild)2602 public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { 2603 if (pickedChild != null) { 2604 notifyListenersTrackingHeadsUp(pickedChild); 2605 mExpandingFromHeadsUp = true; 2606 } 2607 // otherwise we update the state when the expansion is finished 2608 } 2609 2610 @Override onClosingFinished()2611 protected void onClosingFinished() { 2612 super.onClosingFinished(); 2613 resetHorizontalPanelPosition(); 2614 setClosingWithAlphaFadeout(false); 2615 } 2616 setClosingWithAlphaFadeout(boolean closing)2617 private void setClosingWithAlphaFadeout(boolean closing) { 2618 mClosingWithAlphaFadeOut = closing; 2619 mNotificationStackScroller.forceNoOverlappingRendering(closing); 2620 } 2621 2622 /** 2623 * Updates the vertical position of the panel so it is positioned closer to the touch 2624 * responsible for opening the panel. 2625 * 2626 * @param x the x-coordinate the touch event 2627 */ updateVerticalPanelPosition(float x)2628 protected void updateVerticalPanelPosition(float x) { 2629 if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) { 2630 resetHorizontalPanelPosition(); 2631 return; 2632 } 2633 float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; 2634 float 2635 rightMost = 2636 mView.getWidth() - mPositionMinSideMargin 2637 - mNotificationStackScroller.getWidth() / 2; 2638 if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { 2639 x = mView.getWidth() / 2; 2640 } 2641 x = Math.min(rightMost, Math.max(leftMost, x)); 2642 float 2643 center = 2644 mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2; 2645 setHorizontalPanelTranslation(x - center); 2646 } 2647 resetHorizontalPanelPosition()2648 private void resetHorizontalPanelPosition() { 2649 setHorizontalPanelTranslation(0f); 2650 } 2651 setHorizontalPanelTranslation(float translation)2652 protected void setHorizontalPanelTranslation(float translation) { 2653 mNotificationStackScroller.setTranslationX(translation); 2654 mQsFrame.setTranslationX(translation); 2655 int size = mVerticalTranslationListener.size(); 2656 for (int i = 0; i < size; i++) { 2657 mVerticalTranslationListener.get(i).run(); 2658 } 2659 } 2660 updateExpandedHeight(float expandedHeight)2661 protected void updateExpandedHeight(float expandedHeight) { 2662 if (mTracking) { 2663 mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); 2664 } 2665 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { 2666 // The expandedHeight is always the full panel Height when bypassing 2667 expandedHeight = getMaxPanelHeightNonBypass(); 2668 } 2669 mNotificationStackScroller.setExpandedHeight(expandedHeight); 2670 updateKeyguardBottomAreaAlpha(); 2671 updateBigClockAlpha(); 2672 updateStatusBarIcons(); 2673 } 2674 2675 /** 2676 * @return whether the notifications are displayed full width and don't have any margins on 2677 * the side. 2678 */ isFullWidth()2679 public boolean isFullWidth() { 2680 return mIsFullWidth; 2681 } 2682 updateStatusBarIcons()2683 private void updateStatusBarIcons() { 2684 boolean 2685 showIconsWhenExpanded = 2686 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) 2687 && getExpandedHeight() < getOpeningHeight(); 2688 boolean noVisibleNotifications = true; 2689 if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) { 2690 showIconsWhenExpanded = false; 2691 } 2692 if (showIconsWhenExpanded != mShowIconsWhenExpanded) { 2693 mShowIconsWhenExpanded = showIconsWhenExpanded; 2694 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 2695 } 2696 } 2697 2698 private boolean isOnKeyguard() { 2699 return mBarState == StatusBarState.KEYGUARD; 2700 } 2701 2702 public void setPanelScrimMinFraction(float minFraction) { 2703 mBar.panelScrimMinFractionChanged(minFraction); 2704 } 2705 2706 public void clearNotificationEffects() { 2707 mStatusBar.clearNotificationEffects(); 2708 } 2709 2710 @Override 2711 protected boolean isPanelVisibleBecauseOfHeadsUp() { 2712 return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway) 2713 && mBarState == StatusBarState.SHADE; 2714 } 2715 2716 public void launchCamera(boolean animate, int source) { 2717 if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { 2718 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; 2719 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { 2720 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; 2721 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) { 2722 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER; 2723 } else { 2724 2725 // Default. 2726 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 2727 } 2728 2729 // If we are launching it when we are occluded already we don't want it to animate, 2730 // nor setting these flags, since the occluded state doesn't change anymore, hence it's 2731 // never reset. 2732 if (!isFullyCollapsed()) { 2733 setLaunchingAffordance(true); 2734 } else { 2735 animate = false; 2736 } 2737 mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null; 2738 mAffordanceHelper.launchAffordance( 2739 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 2740 } 2741 2742 public void onAffordanceLaunchEnded() { 2743 setLaunchingAffordance(false); 2744 } 2745 2746 /** 2747 * Set whether we are currently launching an affordance. This is currently only set when 2748 * launched via a camera gesture. 2749 */ 2750 private void setLaunchingAffordance(boolean launchingAffordance) { 2751 mLaunchingAffordance = launchingAffordance; 2752 mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance); 2753 mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance( 2754 launchingAffordance); 2755 mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); 2756 if (mAffordanceLaunchListener != null) { 2757 mAffordanceLaunchListener.accept(launchingAffordance); 2758 } 2759 } 2760 2761 /** 2762 * Return true when a bottom affordance is launching an occluded activity with a splash screen. 2763 */ 2764 public boolean isLaunchingAffordanceWithPreview() { 2765 return mLaunchingAffordance && mAffordanceHasPreview; 2766 } 2767 2768 /** 2769 * Whether the camera application can be launched for the camera launch gesture. 2770 */ 2771 public boolean canCameraGestureBeLaunched() { 2772 if (!mStatusBar.isCameraAllowedByAdmin()) { 2773 return false; 2774 } 2775 2776 ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); 2777 String 2778 packageToLaunch = 2779 (resolveInfo == null || resolveInfo.activityInfo == null) ? null 2780 : resolveInfo.activityInfo.packageName; 2781 return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp( 2782 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress(); 2783 } 2784 2785 /** 2786 * Return true if the applications with the package name is running in foreground. 2787 * 2788 * @param pkgName application package name. 2789 */ 2790 private boolean isForegroundApp(String pkgName) { 2791 List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); 2792 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 2793 } 2794 2795 private void setGroupManager(NotificationGroupManager groupManager) { 2796 mGroupManager = groupManager; 2797 } 2798 2799 public boolean hideStatusBarIconsWhenExpanded() { 2800 if (mLaunchingNotification) { 2801 return mHideIconsDuringNotificationLaunch; 2802 } 2803 if (mHeadsUpAppearanceController != null 2804 && mHeadsUpAppearanceController.shouldBeVisible()) { 2805 return false; 2806 } 2807 return !isFullWidth() || !mShowIconsWhenExpanded; 2808 } 2809 2810 private final FragmentListener mFragmentListener = new FragmentListener() { 2811 @Override 2812 public void onFragmentViewCreated(String tag, Fragment fragment) { 2813 mQs = (QS) fragment; 2814 mQs.setPanelView(mHeightListener); 2815 mQs.setExpandClickListener(mOnClickListener); 2816 mQs.setHeaderClickable(mQsExpansionEnabled); 2817 updateQSPulseExpansion(); 2818 mQs.setOverscrolling(mStackScrollerOverscrolling); 2819 2820 // recompute internal state when qspanel height changes 2821 mQs.getView().addOnLayoutChangeListener( 2822 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 2823 final int height = bottom - top; 2824 final int oldHeight = oldBottom - oldTop; 2825 if (height != oldHeight) { 2826 mHeightListener.onQsHeightChanged(); 2827 } 2828 }); 2829 mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView()); 2830 if (mQs instanceof QSFragment) { 2831 mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel()); 2832 } 2833 updateQsExpansion(); 2834 } 2835 2836 @Override onFragmentViewDestroyed(String tag, Fragment fragment)2837 public void onFragmentViewDestroyed(String tag, Fragment fragment) { 2838 // Manual handling of fragment lifecycle is only required because this bridges 2839 // non-fragment and fragment code. Once we are using a fragment for the notification 2840 // panel, mQs will not need to be null cause it will be tied to the same lifecycle. 2841 if (fragment == mQs) { 2842 mQs = null; 2843 } 2844 } 2845 }; 2846 2847 @Override setTouchAndAnimationDisabled(boolean disabled)2848 public void setTouchAndAnimationDisabled(boolean disabled) { 2849 super.setTouchAndAnimationDisabled(disabled); 2850 if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { 2851 mAffordanceHelper.reset(false /* animate */); 2852 } 2853 mNotificationStackScroller.setAnimationsEnabled(!disabled); 2854 } 2855 2856 /** 2857 * Sets the dozing state. 2858 * 2859 * @param dozing {@code true} when dozing. 2860 * @param animate if transition should be animated. 2861 * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor. 2862 */ setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)2863 public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { 2864 if (dozing == mDozing) return; 2865 mView.setDozing(dozing); 2866 mDozing = dozing; 2867 mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation); 2868 mKeyguardBottomArea.setDozing(mDozing, animate); 2869 2870 if (dozing) { 2871 mBottomAreaShadeAlphaAnimator.cancel(); 2872 } 2873 2874 if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 2875 updateDozingVisibilities(animate); 2876 } 2877 2878 final float dozeAmount = dozing ? 1 : 0; 2879 mStatusBarStateController.setDozeAmount(dozeAmount, animate); 2880 } 2881 setPulsing(boolean pulsing)2882 public void setPulsing(boolean pulsing) { 2883 mPulsing = pulsing; 2884 final boolean 2885 animatePulse = 2886 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn(); 2887 if (animatePulse) { 2888 mAnimateNextPositionUpdate = true; 2889 } 2890 // Do not animate the clock when waking up from a pulse. 2891 // The height callback will take care of pushing the clock to the right position. 2892 if (!mPulsing && !mDozing) { 2893 mAnimateNextPositionUpdate = false; 2894 } 2895 mNotificationStackScroller.setPulsing(pulsing, animatePulse); 2896 mKeyguardStatusView.setPulsing(pulsing); 2897 } 2898 setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding)2899 public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { 2900 if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { 2901 mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; 2902 mStatusBar.updateKeyguardMaxNotifications(); 2903 } 2904 } 2905 dozeTimeTick()2906 public void dozeTimeTick() { 2907 mKeyguardBottomArea.dozeTimeTick(); 2908 mKeyguardStatusView.dozeTimeTick(); 2909 if (mInterpolatedDarkAmount > 0) { 2910 positionClockAndNotifications(); 2911 } 2912 } 2913 setStatusAccessibilityImportance(int mode)2914 public void setStatusAccessibilityImportance(int mode) { 2915 mKeyguardStatusView.setImportantForAccessibility(mode); 2916 } 2917 2918 /** 2919 * TODO: this should be removed. 2920 * It's not correct to pass this view forward because other classes will end up adding 2921 * children to it. Theme will be out of sync. 2922 * 2923 * @return bottom area view 2924 */ getKeyguardBottomAreaView()2925 public KeyguardBottomAreaView getKeyguardBottomAreaView() { 2926 return mKeyguardBottomArea; 2927 } 2928 setUserSetupComplete(boolean userSetupComplete)2929 public void setUserSetupComplete(boolean userSetupComplete) { 2930 mUserSetupComplete = userSetupComplete; 2931 mKeyguardBottomArea.setUserSetupComplete(userSetupComplete); 2932 } 2933 applyExpandAnimationParams(ExpandAnimationParameters params)2934 public void applyExpandAnimationParams(ExpandAnimationParameters params) { 2935 mExpandOffset = params != null ? params.getTopChange() : 0; 2936 updateQsExpansion(); 2937 if (params != null) { 2938 boolean hideIcons = params.getProgress( 2939 ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; 2940 if (hideIcons != mHideIconsDuringNotificationLaunch) { 2941 mHideIconsDuringNotificationLaunch = hideIcons; 2942 if (!hideIcons) { 2943 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); 2944 } 2945 } 2946 } 2947 } 2948 addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2949 public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 2950 mTrackingHeadsUpListeners.add(listener); 2951 } 2952 removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2953 public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 2954 mTrackingHeadsUpListeners.remove(listener); 2955 } 2956 addVerticalTranslationListener(Runnable verticalTranslationListener)2957 public void addVerticalTranslationListener(Runnable verticalTranslationListener) { 2958 mVerticalTranslationListener.add(verticalTranslationListener); 2959 } 2960 removeVerticalTranslationListener(Runnable verticalTranslationListener)2961 public void removeVerticalTranslationListener(Runnable verticalTranslationListener) { 2962 mVerticalTranslationListener.remove(verticalTranslationListener); 2963 } 2964 setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)2965 public void setHeadsUpAppearanceController( 2966 HeadsUpAppearanceController headsUpAppearanceController) { 2967 mHeadsUpAppearanceController = headsUpAppearanceController; 2968 } 2969 2970 /** 2971 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the 2972 * security view of the bouncer. 2973 */ onBouncerPreHideAnimation()2974 public void onBouncerPreHideAnimation() { 2975 setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */, 2976 false /* goingToFullShade */); 2977 } 2978 2979 /** 2980 * Do not let the user drag the shade up and down for the current touch session. 2981 * This is necessary to avoid shade expansion while/after the bouncer is dismissed. 2982 */ blockExpansionForCurrentTouch()2983 public void blockExpansionForCurrentTouch() { 2984 mBlockingExpansionForCurrentTouch = mTracking; 2985 } 2986 2987 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)2988 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2989 super.dump(fd, pw, args); 2990 pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect()); 2991 if (mKeyguardStatusBar != null) { 2992 mKeyguardStatusBar.dump(fd, pw, args); 2993 } 2994 if (mKeyguardStatusView != null) { 2995 mKeyguardStatusView.dump(fd, pw, args); 2996 } 2997 } 2998 hasActiveClearableNotifications()2999 public boolean hasActiveClearableNotifications() { 3000 return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); 3001 } 3002 updateShowEmptyShadeView()3003 private void updateShowEmptyShadeView() { 3004 boolean 3005 showEmptyShadeView = 3006 mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications(); 3007 showEmptyShadeView(showEmptyShadeView); 3008 } 3009 createRemoteInputDelegate()3010 public RemoteInputController.Delegate createRemoteInputDelegate() { 3011 return mNotificationStackScroller.createDelegate(); 3012 } 3013 updateNotificationViews(String reason)3014 void updateNotificationViews(String reason) { 3015 mNotificationStackScroller.updateSectionBoundaries(reason); 3016 mNotificationStackScroller.updateSpeedBumpIndex(); 3017 mNotificationStackScroller.updateFooter(); 3018 updateShowEmptyShadeView(); 3019 mNotificationStackScroller.updateIconAreaViews(); 3020 } 3021 onUpdateRowStates()3022 public void onUpdateRowStates() { 3023 mNotificationStackScroller.onUpdateRowStates(); 3024 } 3025 hasPulsingNotifications()3026 public boolean hasPulsingNotifications() { 3027 return mNotificationStackScroller.hasPulsingNotifications(); 3028 } 3029 getActivatedChild()3030 public ActivatableNotificationView getActivatedChild() { 3031 return mNotificationStackScroller.getActivatedChild(); 3032 } 3033 setActivatedChild(ActivatableNotificationView o)3034 public void setActivatedChild(ActivatableNotificationView o) { 3035 mNotificationStackScroller.setActivatedChild(o); 3036 } 3037 runAfterAnimationFinished(Runnable r)3038 public void runAfterAnimationFinished(Runnable r) { 3039 mNotificationStackScroller.runAfterAnimationFinished(r); 3040 } 3041 setScrollingEnabled(boolean b)3042 public void setScrollingEnabled(boolean b) { 3043 mNotificationStackScroller.setScrollingEnabled(b); 3044 } 3045 initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, NotificationShelf notificationShelf, NotificationIconAreaController notificationIconAreaController, ScrimController scrimController)3046 public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, 3047 NotificationShelf notificationShelf, 3048 NotificationIconAreaController notificationIconAreaController, 3049 ScrimController scrimController) { 3050 setStatusBar(statusBar); 3051 setGroupManager(mGroupManager); 3052 mNotificationStackScroller.setNotificationPanelController(this); 3053 mNotificationStackScroller.setIconAreaController(notificationIconAreaController); 3054 mNotificationStackScroller.setStatusBar(statusBar); 3055 mNotificationStackScroller.setGroupManager(groupManager); 3056 mNotificationStackScroller.setShelf(notificationShelf); 3057 mNotificationStackScroller.setScrimController(scrimController); 3058 updateShowEmptyShadeView(); 3059 } 3060 showTransientIndication(int id)3061 public void showTransientIndication(int id) { 3062 mKeyguardIndicationController.showTransientIndication(id); 3063 } 3064 setOnReinflationListener(Runnable onReinflationListener)3065 public void setOnReinflationListener(Runnable onReinflationListener) { 3066 mOnReinflationListener = onReinflationListener; 3067 } 3068 setAlpha(float alpha)3069 public void setAlpha(float alpha) { 3070 mView.setAlpha(alpha); 3071 } 3072 fadeOut(long startDelayMs, long durationMs, Runnable endAction)3073 public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { 3074 return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( 3075 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( 3076 endAction); 3077 } 3078 resetViewGroupFade()3079 public void resetViewGroupFade() { 3080 ViewGroupFadeHelper.reset(mView); 3081 } 3082 addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3083 public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3084 mView.getViewTreeObserver().addOnGlobalLayoutListener(listener); 3085 } 3086 removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3087 public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3088 mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); 3089 } 3090 getOnHeadsUpChangedListener()3091 public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() { 3092 return mOnHeadsUpChangedListener; 3093 } 3094 getHeight()3095 public int getHeight() { 3096 return mView.getHeight(); 3097 } 3098 getHeaderDebugInfo()3099 public TextView getHeaderDebugInfo() { 3100 return mView.findViewById(R.id.header_debug_info); 3101 } 3102 onThemeChanged()3103 public void onThemeChanged() { 3104 mConfigurationListener.onThemeChanged(); 3105 } 3106 3107 @Override createLayoutChangeListener()3108 public OnLayoutChangeListener createLayoutChangeListener() { 3109 return new OnLayoutChangeListener(); 3110 } 3111 setEmptyDragAmount(float amount)3112 public void setEmptyDragAmount(float amount) { 3113 mExpansionCallback.setEmptyDragAmount(amount); 3114 } 3115 3116 @Override createTouchHandler()3117 protected TouchHandler createTouchHandler() { 3118 return new TouchHandler() { 3119 @Override 3120 public boolean onInterceptTouchEvent(MotionEvent event) { 3121 if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) { 3122 return false; 3123 } 3124 initDownStates(event); 3125 // Do not let touches go to shade or QS if the bouncer is visible, 3126 // but still let user swipe down to expand the panel, dismissing the bouncer. 3127 if (mStatusBar.isBouncerShowing()) { 3128 return true; 3129 } 3130 if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3131 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3132 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3133 return true; 3134 } 3135 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) 3136 && mPulseExpansionHandler.onInterceptTouchEvent(event)) { 3137 return true; 3138 } 3139 3140 if (!isFullyCollapsed() && onQsIntercept(event)) { 3141 return true; 3142 } 3143 return super.onInterceptTouchEvent(event); 3144 } 3145 3146 @Override 3147 public boolean onTouch(View v, MotionEvent event) { 3148 if (mBlockTouches || (mQsFullyExpanded && mQs != null 3149 && mQs.disallowPanelTouches())) { 3150 return false; 3151 } 3152 3153 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able 3154 // to pull down QS or expand the shade. 3155 if (mStatusBar.isBouncerShowingScrimmed()) { 3156 return false; 3157 } 3158 3159 // Make sure the next touch won't the blocked after the current ends. 3160 if (event.getAction() == MotionEvent.ACTION_UP 3161 || event.getAction() == MotionEvent.ACTION_CANCEL) { 3162 mBlockingExpansionForCurrentTouch = false; 3163 } 3164 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately 3165 // without any ACTION_MOVE event. 3166 // In such case, simply expand the panel instead of being stuck at the bottom bar. 3167 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { 3168 expand(true /* animate */); 3169 } 3170 initDownStates(event); 3171 if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0) 3172 && mPulseExpansionHandler.onTouchEvent(event)) { 3173 // We're expanding all the other ones shouldn't get this anymore 3174 return true; 3175 } 3176 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() 3177 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3178 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3179 } 3180 boolean handled = false; 3181 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded 3182 && mBarState != StatusBarState.SHADE && !mDozing) { 3183 handled |= mAffordanceHelper.onTouchEvent(event); 3184 } 3185 if (mOnlyAffordanceInThisMotion) { 3186 return true; 3187 } 3188 handled |= mHeadsUpTouchHelper.onTouchEvent(event); 3189 3190 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { 3191 return true; 3192 } 3193 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { 3194 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3195 updateVerticalPanelPosition(event.getX()); 3196 handled = true; 3197 } 3198 handled |= super.onTouch(v, event); 3199 return !mDozing || mPulsing || handled; 3200 } 3201 }; 3202 } 3203 3204 @Override 3205 protected PanelViewController.OnConfigurationChangedListener 3206 createOnConfigurationChangedListener() { 3207 return new OnConfigurationChangedListener(); 3208 } 3209 3210 private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { 3211 @Override 3212 public void onHeightChanged(ExpandableView view, boolean needsAnimation) { 3213 3214 // Block update if we are in quick settings and just the top padding changed 3215 // (i.e. view == null). 3216 if (view == null && mQsExpanded) { 3217 return; 3218 } 3219 if (needsAnimation && mInterpolatedDarkAmount == 0) { 3220 mAnimateNextPositionUpdate = true; 3221 } 3222 ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); 3223 ExpandableNotificationRow 3224 firstRow = 3225 firstChildNotGone instanceof ExpandableNotificationRow 3226 ? (ExpandableNotificationRow) firstChildNotGone : null; 3227 if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent() 3228 == firstRow))) { 3229 requestScrollerTopPaddingUpdate(false /* animate */); 3230 } 3231 requestPanelHeightUpdate(); 3232 } 3233 3234 @Override 3235 public void onReset(ExpandableView view) { 3236 } 3237 } 3238 3239 private class OnClickListener implements View.OnClickListener { 3240 @Override 3241 public void onClick(View v) { 3242 onQsExpansionStarted(); 3243 if (mQsExpanded) { 3244 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, 3245 true /* isClick */); 3246 } else if (mQsExpansionEnabled) { 3247 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); 3248 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, 3249 true /* isClick */); 3250 } 3251 } 3252 } 3253 3254 private class OnOverscrollTopChangedListener implements 3255 NotificationStackScrollLayout.OnOverscrollTopChangedListener { 3256 @Override 3257 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { 3258 cancelQsAnimation(); 3259 if (!mQsExpansionEnabled) { 3260 amount = 0f; 3261 } 3262 float rounded = amount >= 1f ? amount : 0f; 3263 setOverScrolling(rounded != 0f && isRubberbanded); 3264 mQsExpansionFromOverscroll = rounded != 0f; 3265 mLastOverscroll = rounded; 3266 updateQsState(); 3267 setQsExpansion(mQsMinExpansionHeight + rounded); 3268 } 3269 3270 @Override 3271 public void flingTopOverscroll(float velocity, boolean open) { 3272 mLastOverscroll = 0f; 3273 mQsExpansionFromOverscroll = false; 3274 setQsExpansion(mQsExpansionHeight); 3275 flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, 3276 open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> { 3277 mStackScrollerOverscrolling = false; 3278 setOverScrolling(false); 3279 updateQsState(); 3280 }, false /* isClick */); 3281 } 3282 } 3283 3284 private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener { 3285 @Override 3286 public void onDynamicPrivacyChanged() { 3287 // Do not request animation when pulsing or waking up, otherwise the clock wiill be out 3288 // of sync with the notification panel. 3289 if (mLinearDarkAmount != 0) { 3290 return; 3291 } 3292 mAnimateNextPositionUpdate = true; 3293 } 3294 } 3295 3296 private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback { 3297 @Override 3298 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { 3299 boolean 3300 start = 3301 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage 3302 : !rightPage; 3303 mIsLaunchTransitionRunning = true; 3304 mLaunchAnimationEndRunnable = null; 3305 float displayDensity = mStatusBar.getDisplayDensity(); 3306 int lengthDp = Math.abs((int) (translation / displayDensity)); 3307 int velocityDp = Math.abs((int) (vel / displayDensity)); 3308 if (start) { 3309 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); 3310 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER); 3311 mFalsingManager.onLeftAffordanceOn(); 3312 if (mFalsingManager.shouldEnforceBouncer()) { 3313 mStatusBar.executeRunnableDismissingKeyguard( 3314 () -> mKeyguardBottomArea.launchLeftAffordance(), null, 3315 true /* dismissShade */, false /* afterKeyguardGone */, 3316 true /* deferred */); 3317 } else { 3318 mKeyguardBottomArea.launchLeftAffordance(); 3319 } 3320 } else { 3321 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( 3322 mLastCameraLaunchSource)) { 3323 mLockscreenGestureLogger.write( 3324 MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); 3325 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA); 3326 } 3327 mFalsingManager.onCameraOn(); 3328 if (mFalsingManager.shouldEnforceBouncer()) { 3329 mStatusBar.executeRunnableDismissingKeyguard( 3330 () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, 3331 true /* dismissShade */, false /* afterKeyguardGone */, 3332 true /* deferred */); 3333 } else { 3334 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); 3335 } 3336 } 3337 mStatusBar.startLaunchTransitionTimeout(); 3338 mBlockTouches = true; 3339 } 3340 3341 @Override 3342 public void onAnimationToSideEnded() { 3343 mIsLaunchTransitionRunning = false; 3344 mIsLaunchTransitionFinished = true; 3345 if (mLaunchAnimationEndRunnable != null) { 3346 mLaunchAnimationEndRunnable.run(); 3347 mLaunchAnimationEndRunnable = null; 3348 } 3349 mStatusBar.readyForKeyguardDone(); 3350 } 3351 3352 @Override 3353 public float getMaxTranslationDistance() { 3354 return (float) Math.hypot(mView.getWidth(), getHeight()); 3355 } 3356 3357 @Override 3358 public void onSwipingStarted(boolean rightIcon) { 3359 mFalsingManager.onAffordanceSwipingStarted(rightIcon); 3360 boolean 3361 camera = 3362 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 3363 : rightIcon; 3364 if (camera) { 3365 mKeyguardBottomArea.bindCameraPrewarmService(); 3366 } 3367 mView.requestDisallowInterceptTouchEvent(true); 3368 mOnlyAffordanceInThisMotion = true; 3369 mQsTracking = false; 3370 } 3371 3372 @Override 3373 public void onSwipingAborted() { 3374 mFalsingManager.onAffordanceSwipingAborted(); 3375 mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); 3376 } 3377 3378 @Override 3379 public void onIconClicked(boolean rightIcon) { 3380 if (mHintAnimationRunning) { 3381 return; 3382 } 3383 mHintAnimationRunning = true; 3384 mAffordanceHelper.startHintAnimation(rightIcon, () -> { 3385 mHintAnimationRunning = false; 3386 mStatusBar.onHintFinished(); 3387 }); 3388 rightIcon = 3389 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 3390 : rightIcon; 3391 if (rightIcon) { 3392 mStatusBar.onCameraHintStarted(); 3393 } else { 3394 if (mKeyguardBottomArea.isLeftVoiceAssist()) { 3395 mStatusBar.onVoiceAssistHintStarted(); 3396 } else { 3397 mStatusBar.onPhoneHintStarted(); 3398 } 3399 } 3400 } 3401 3402 @Override 3403 public KeyguardAffordanceView getLeftIcon() { 3404 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 3405 ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView(); 3406 } 3407 3408 @Override 3409 public KeyguardAffordanceView getRightIcon() { 3410 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 3411 ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView(); 3412 } 3413 3414 @Override 3415 public View getLeftPreview() { 3416 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 3417 ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview(); 3418 } 3419 3420 @Override 3421 public View getRightPreview() { 3422 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 3423 ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview(); 3424 } 3425 3426 @Override 3427 public float getAffordanceFalsingFactor() { 3428 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 3429 } 3430 3431 @Override 3432 public boolean needsAntiFalsing() { 3433 return mBarState == StatusBarState.KEYGUARD; 3434 } 3435 } 3436 3437 private class OnEmptySpaceClickListener implements 3438 NotificationStackScrollLayout.OnEmptySpaceClickListener { 3439 @Override 3440 public void onEmptySpaceClicked(float x, float y) { 3441 onEmptySpaceClick(x); 3442 } 3443 } 3444 3445 private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { 3446 @Override 3447 public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { 3448 mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode); 3449 if (inPinnedMode) { 3450 mHeadsUpExistenceChangedRunnable.run(); 3451 updateNotificationTranslucency(); 3452 } else { 3453 setHeadsUpAnimatingAway(true); 3454 mNotificationStackScroller.runAfterAnimationFinished( 3455 mHeadsUpExistenceChangedRunnable); 3456 } 3457 updateGestureExclusionRect(); 3458 mHeadsUpPinnedMode = inPinnedMode; 3459 updateHeadsUpVisibility(); 3460 updateKeyguardStatusBarForHeadsUp(); 3461 } 3462 3463 @Override 3464 public void onHeadsUpPinned(NotificationEntry entry) { 3465 if (!isOnKeyguard()) { 3466 mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), 3467 true); 3468 } 3469 } 3470 3471 @Override 3472 public void onHeadsUpUnPinned(NotificationEntry entry) { 3473 3474 // When we're unpinning the notification via active edge they remain heads-upped, 3475 // we need to make sure that an animation happens in this case, otherwise the 3476 // notification 3477 // will stick to the top without any interaction. 3478 if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { 3479 mNotificationStackScroller.generateHeadsUpAnimation( 3480 entry.getHeadsUpAnimationView(), false); 3481 entry.setHeadsUpIsVisible(); 3482 } 3483 } 3484 3485 @Override 3486 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { 3487 mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); 3488 } 3489 } 3490 3491 private class HeightListener implements QS.HeightListener { 3492 public void onQsHeightChanged() { 3493 mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0; 3494 if (mQsExpanded && mQsFullyExpanded) { 3495 mQsExpansionHeight = mQsMaxExpansionHeight; 3496 requestScrollerTopPaddingUpdate(false /* animate */); 3497 requestPanelHeightUpdate(); 3498 } 3499 if (mAccessibilityManager.isEnabled()) { 3500 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 3501 } 3502 mNotificationStackScroller.setMaxTopPadding( 3503 mQsMaxExpansionHeight + mQsNotificationTopPadding); 3504 } 3505 } 3506 3507 private class ZenModeControllerCallback implements ZenModeController.Callback { 3508 @Override 3509 public void onZenChanged(int zen) { 3510 updateShowEmptyShadeView(); 3511 } 3512 } 3513 3514 private class ConfigurationListener implements ConfigurationController.ConfigurationListener { 3515 @Override 3516 public void onDensityOrFontScaleChanged() { 3517 updateShowEmptyShadeView(); 3518 } 3519 3520 @Override 3521 public void onThemeChanged() { 3522 final int themeResId = mView.getContext().getThemeResId(); 3523 if (mThemeResId == themeResId) { 3524 return; 3525 } 3526 mThemeResId = themeResId; 3527 3528 reInflateViews(); 3529 } 3530 3531 @Override 3532 public void onOverlayChanged() { 3533 reInflateViews(); 3534 } 3535 3536 @Override 3537 public void onUiModeChanged() {} 3538 } 3539 3540 private class StatusBarStateListener implements StateListener { 3541 @Override 3542 public void onStateChanged(int statusBarState) { 3543 boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); 3544 boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); 3545 int oldState = mBarState; 3546 boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD; 3547 setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); 3548 setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); 3549 3550 mBarState = statusBarState; 3551 mKeyguardShowing = keyguardShowing; 3552 3553 if (oldState == StatusBarState.KEYGUARD && (goingToFullShade 3554 || statusBarState == StatusBarState.SHADE_LOCKED)) { 3555 animateKeyguardStatusBarOut(); 3556 long 3557 delay = 3558 mBarState == StatusBarState.SHADE_LOCKED ? 0 3559 : mKeyguardStateController.calculateGoingToFullShadeDelay(); 3560 mQs.animateHeaderSlidingIn(delay); 3561 } else if (oldState == StatusBarState.SHADE_LOCKED 3562 && statusBarState == StatusBarState.KEYGUARD) { 3563 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 3564 mNotificationStackScroller.resetScrollPosition(); 3565 // Only animate header if the header is visible. If not, it will partially 3566 // animate out 3567 // the top of QS 3568 if (!mQsExpanded) { 3569 mQs.animateHeaderSlidingOut(); 3570 } 3571 } else { 3572 mKeyguardStatusBar.setAlpha(1f); 3573 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); 3574 if (keyguardShowing && oldState != mBarState) { 3575 if (mQs != null) { 3576 mQs.hideImmediately(); 3577 } 3578 } 3579 } 3580 updateKeyguardStatusBarForHeadsUp(); 3581 if (keyguardShowing) { 3582 updateDozingVisibilities(false /* animate */); 3583 } 3584 // THe update needs to happen after the headerSlide in above, otherwise the translation 3585 // would reset 3586 updateQSPulseExpansion(); 3587 maybeAnimateBottomAreaAlpha(); 3588 resetHorizontalPanelPosition(); 3589 updateQsState(); 3590 } 3591 3592 @Override 3593 public void onDozeAmountChanged(float linearAmount, float amount) { 3594 mInterpolatedDarkAmount = amount; 3595 mLinearDarkAmount = linearAmount; 3596 mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount); 3597 mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); 3598 positionClockAndNotifications(); 3599 } 3600 } 3601 3602 private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback { 3603 public void setEmptyDragAmount(float amount) { 3604 mEmptyDragAmount = amount * 0.2f; 3605 positionClockAndNotifications(); 3606 } 3607 } 3608 3609 private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { 3610 @Override 3611 public void onViewAttachedToWindow(View v) { 3612 FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); 3613 mStatusBarStateController.addCallback(mStatusBarStateListener); 3614 mZenModeController.addCallback(mZenModeControllerCallback); 3615 mConfigurationController.addCallback(mConfigurationListener); 3616 mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); 3617 // Theme might have changed between inflating this view and attaching it to the 3618 // window, so 3619 // force a call to onThemeChanged 3620 mConfigurationListener.onThemeChanged(); 3621 } 3622 3623 @Override 3624 public void onViewDetachedFromWindow(View v) { 3625 FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener); 3626 mStatusBarStateController.removeCallback(mStatusBarStateListener); 3627 mZenModeController.removeCallback(mZenModeControllerCallback); 3628 mConfigurationController.removeCallback(mConfigurationListener); 3629 mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); 3630 } 3631 } 3632 3633 private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener { 3634 3635 @Override 3636 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 3637 int oldTop, int oldRight, int oldBottom) { 3638 DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); 3639 super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); 3640 setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth()); 3641 3642 // Update Clock Pivot 3643 mKeyguardStatusView.setPivotX(mView.getWidth() / 2); 3644 mKeyguardStatusView.setPivotY( 3645 (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize()); 3646 3647 // Calculate quick setting heights. 3648 int oldMaxHeight = mQsMaxExpansionHeight; 3649 if (mQs != null) { 3650 float previousMin = mQsMinExpansionHeight; 3651 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); 3652 if (mQsExpansionHeight == previousMin) { 3653 mQsExpansionHeight = mQsMinExpansionHeight; 3654 } 3655 mQsMaxExpansionHeight = mQs.getDesiredHeight(); 3656 mNotificationStackScroller.setMaxTopPadding( 3657 mQsMaxExpansionHeight + mQsNotificationTopPadding); 3658 } 3659 positionClockAndNotifications(); 3660 if (mQsExpanded && mQsFullyExpanded) { 3661 mQsExpansionHeight = mQsMaxExpansionHeight; 3662 requestScrollerTopPaddingUpdate(false /* animate */); 3663 requestPanelHeightUpdate(); 3664 3665 // Size has changed, start an animation. 3666 if (mQsMaxExpansionHeight != oldMaxHeight) { 3667 startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); 3668 } 3669 } else if (!mQsExpanded) { 3670 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); 3671 } 3672 updateExpandedHeight(getExpandedHeight()); 3673 updateHeader(); 3674 3675 // If we are running a size change animation, the animation takes care of the height of 3676 // the container. However, if we are not animating, we always need to make the QS 3677 // container 3678 // the desired height so when closing the QS detail, it stays smaller after the size 3679 // change 3680 // animation is finished but the detail view is still being animated away (this 3681 // animation 3682 // takes longer than the size change animation). 3683 if (mQsSizeChangeAnimator == null && mQs != null) { 3684 mQs.setHeightOverride(mQs.getDesiredHeight()); 3685 } 3686 updateMaxHeadsUpTranslation(); 3687 updateGestureExclusionRect(); 3688 if (mExpandAfterLayoutRunnable != null) { 3689 mExpandAfterLayoutRunnable.run(); 3690 mExpandAfterLayoutRunnable = null; 3691 } 3692 DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout"); 3693 } 3694 } 3695 3696 private class DebugDrawable extends Drawable { 3697 3698 @Override 3699 public void draw(Canvas canvas) { 3700 Paint p = new Paint(); 3701 p.setColor(Color.RED); 3702 p.setStrokeWidth(2); 3703 p.setStyle(Paint.Style.STROKE); 3704 canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p); 3705 p.setColor(Color.BLUE); 3706 canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p); 3707 p.setColor(Color.GREEN); 3708 canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(), 3709 calculatePanelHeightQsExpanded(), p); 3710 p.setColor(Color.YELLOW); 3711 canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(), 3712 calculatePanelHeightShade(), p); 3713 p.setColor(Color.MAGENTA); 3714 canvas.drawLine( 3715 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p); 3716 p.setColor(Color.CYAN); 3717 canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), 3718 mNotificationStackScroller.getTopPadding(), p); 3719 p.setColor(Color.GRAY); 3720 canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), 3721 mClockPositionResult.clockY, p); 3722 } 3723 3724 @Override 3725 public void setAlpha(int alpha) { 3726 3727 } 3728 3729 @Override 3730 public void setColorFilter(ColorFilter colorFilter) { 3731 3732 } 3733 3734 @Override 3735 public int getOpacity() { 3736 return 0; 3737 } 3738 } 3739 3740 private class OnConfigurationChangedListener extends 3741 PanelViewController.OnConfigurationChangedListener { 3742 @Override 3743 public void onConfigurationChanged(Configuration newConfig) { 3744 super.onConfigurationChanged(newConfig); 3745 mAffordanceHelper.onConfigurationChanged(); 3746 if (newConfig.orientation != mLastOrientation) { 3747 resetHorizontalPanelPosition(); 3748 } 3749 mLastOrientation = newConfig.orientation; 3750 } 3751 } 3752 3753 private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { 3754 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { 3755 mNavigationBarBottomHeight = insets.getStableInsetBottom(); 3756 updateMaxHeadsUpTranslation(); 3757 return insets; 3758 } 3759 } 3760 } 3761