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