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