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