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