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 android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.animation.ValueAnimator; 23 import android.app.ActivityManager; 24 import android.app.StatusBarManager; 25 import android.content.Context; 26 import android.content.pm.ResolveInfo; 27 import android.content.res.Configuration; 28 import android.graphics.Canvas; 29 import android.graphics.Color; 30 import android.graphics.Paint; 31 import android.graphics.Rect; 32 import android.util.AttributeSet; 33 import android.util.MathUtils; 34 import android.view.MotionEvent; 35 import android.view.VelocityTracker; 36 import android.view.View; 37 import android.view.ViewTreeObserver; 38 import android.view.WindowInsets; 39 import android.view.accessibility.AccessibilityEvent; 40 import android.widget.FrameLayout; 41 import android.widget.TextView; 42 43 import com.android.internal.logging.MetricsLogger; 44 import com.android.keyguard.KeyguardStatusView; 45 import com.android.systemui.AutoReinflateContainer; 46 import com.android.systemui.AutoReinflateContainer.InflateListener; 47 import com.android.systemui.DejankUtils; 48 import com.android.systemui.EventLogConstants; 49 import com.android.systemui.EventLogTags; 50 import com.android.systemui.Interpolators; 51 import com.android.systemui.R; 52 import com.android.systemui.classifier.FalsingManager; 53 import com.android.systemui.qs.QSContainer; 54 import com.android.systemui.statusbar.ExpandableNotificationRow; 55 import com.android.systemui.statusbar.ExpandableView; 56 import com.android.systemui.statusbar.FlingAnimationUtils; 57 import com.android.systemui.statusbar.GestureRecorder; 58 import com.android.systemui.statusbar.KeyguardAffordanceView; 59 import com.android.systemui.statusbar.NotificationData; 60 import com.android.systemui.statusbar.StatusBarState; 61 import com.android.systemui.statusbar.notification.NotificationUtils; 62 import com.android.systemui.statusbar.policy.HeadsUpManager; 63 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 64 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 65 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 66 import com.android.systemui.statusbar.stack.StackStateAnimator; 67 68 import java.util.List; 69 70 public class NotificationPanelView extends PanelView implements 71 ExpandableView.OnHeightChangedListener, 72 View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener, 73 KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener, 74 OnHeadsUpChangedListener { 75 76 private static final boolean DEBUG = false; 77 78 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is 79 // changed. 80 private static final int CAP_HEIGHT = 1456; 81 private static final int FONT_HEIGHT = 2163; 82 83 private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f; 84 85 static final String COUNTER_PANEL_OPEN = "panel_open"; 86 static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; 87 private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; 88 89 private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1); 90 91 public static final long DOZE_ANIMATION_DURATION = 700; 92 93 private KeyguardAffordanceHelper mAfforanceHelper; 94 private KeyguardUserSwitcher mKeyguardUserSwitcher; 95 private KeyguardStatusBarView mKeyguardStatusBar; 96 protected QSContainer mQsContainer; 97 private AutoReinflateContainer mQsAutoReinflateContainer; 98 private KeyguardStatusView mKeyguardStatusView; 99 private TextView mClockView; 100 private View mReserveNotificationSpace; 101 private View mQsNavbarScrim; 102 protected NotificationsQuickSettingsContainer mNotificationContainerParent; 103 protected NotificationStackScrollLayout mNotificationStackScroller; 104 private boolean mAnimateNextTopPaddingChange; 105 106 private int mTrackingPointer; 107 private VelocityTracker mVelocityTracker; 108 private boolean mQsTracking; 109 110 /** 111 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and 112 * the expansion for quick settings. 113 */ 114 private boolean mConflictingQsExpansionGesture; 115 116 /** 117 * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't 118 * intercepted yet. 119 */ 120 private boolean mIntercepting; 121 private boolean mPanelExpanded; 122 private boolean mQsExpanded; 123 private boolean mQsExpandedWhenExpandingStarted; 124 private boolean mQsFullyExpanded; 125 private boolean mKeyguardShowing; 126 private boolean mDozing; 127 private boolean mDozingOnDown; 128 private int mStatusBarState; 129 private float mInitialHeightOnTouch; 130 private float mInitialTouchX; 131 private float mInitialTouchY; 132 private float mLastTouchX; 133 private float mLastTouchY; 134 protected float mQsExpansionHeight; 135 protected int mQsMinExpansionHeight; 136 protected int mQsMaxExpansionHeight; 137 private int mQsPeekHeight; 138 private boolean mStackScrollerOverscrolling; 139 private boolean mQsExpansionFromOverscroll; 140 private float mLastOverscroll; 141 protected boolean mQsExpansionEnabled = true; 142 private ValueAnimator mQsExpansionAnimator; 143 private FlingAnimationUtils mFlingAnimationUtils; 144 private int mStatusBarMinHeight; 145 private boolean mUnlockIconActive; 146 private int mNotificationsHeaderCollideDistance; 147 private int mUnlockMoveDistance; 148 private float mEmptyDragAmount; 149 150 private ObjectAnimator mClockAnimator; 151 private int mClockAnimationTarget = -1; 152 private int mTopPaddingAdjustment; 153 private KeyguardClockPositionAlgorithm mClockPositionAlgorithm = 154 new KeyguardClockPositionAlgorithm(); 155 private KeyguardClockPositionAlgorithm.Result mClockPositionResult = 156 new KeyguardClockPositionAlgorithm.Result(); 157 private boolean mIsExpanding; 158 159 private boolean mBlockTouches; 160 private int mNotificationScrimWaitDistance; 161 // Used for two finger gesture as well as accessibility shortcut to QS. 162 private boolean mQsExpandImmediate; 163 private boolean mTwoFingerQsExpandPossible; 164 165 /** 166 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still 167 * need to take this into account in our panel height calculation. 168 */ 169 private boolean mQsAnimatorExpand; 170 private boolean mIsLaunchTransitionFinished; 171 private boolean mIsLaunchTransitionRunning; 172 private Runnable mLaunchAnimationEndRunnable; 173 private boolean mOnlyAffordanceInThisMotion; 174 private boolean mKeyguardStatusViewAnimating; 175 private ValueAnimator mQsSizeChangeAnimator; 176 177 private boolean mShadeEmpty; 178 179 private boolean mQsScrimEnabled = true; 180 private boolean mLastAnnouncementWasQuickSettings; 181 private boolean mQsTouchAboveFalsingThreshold; 182 private int mQsFalsingThreshold; 183 184 private float mKeyguardStatusBarAnimateAlpha = 1f; 185 private int mOldLayoutDirection; 186 private HeadsUpTouchHelper mHeadsUpTouchHelper; 187 private boolean mIsExpansionFromHeadsUp; 188 private boolean mListenForHeadsUp; 189 private int mNavigationBarBottomHeight; 190 private boolean mExpandingFromHeadsUp; 191 private boolean mCollapsedOnDown; 192 private int mPositionMinSideMargin; 193 private int mMaxFadeoutHeight; 194 private int mLastOrientation = -1; 195 private boolean mClosingWithAlphaFadeOut; 196 private boolean mHeadsUpAnimatingAway; 197 private boolean mLaunchingAffordance; 198 private FalsingManager mFalsingManager; 199 private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 200 201 private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { 202 @Override 203 public void run() { 204 mHeadsUpAnimatingAway = false; 205 notifyBarPanelExpansionChanged(); 206 } 207 }; 208 private NotificationGroupManager mGroupManager; 209 NotificationPanelView(Context context, AttributeSet attrs)210 public NotificationPanelView(Context context, AttributeSet attrs) { 211 super(context, attrs); 212 setWillNotDraw(!DEBUG); 213 mFalsingManager = FalsingManager.getInstance(context); 214 } 215 setStatusBar(PhoneStatusBar bar)216 public void setStatusBar(PhoneStatusBar bar) { 217 mStatusBar = bar; 218 } 219 220 @Override onFinishInflate()221 protected void onFinishInflate() { 222 super.onFinishInflate(); 223 mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header); 224 mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view); 225 mClockView = (TextView) findViewById(R.id.clock_view); 226 227 mNotificationContainerParent = (NotificationsQuickSettingsContainer) 228 findViewById(R.id.notification_container_parent); 229 mNotificationStackScroller = (NotificationStackScrollLayout) 230 findViewById(R.id.notification_stack_scroller); 231 mNotificationStackScroller.setOnHeightChangedListener(this); 232 mNotificationStackScroller.setOverscrollTopChangedListener(this); 233 mNotificationStackScroller.setOnEmptySpaceClickListener(this); 234 mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); 235 mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); 236 mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); 237 mKeyguardBottomArea.setAffordanceHelper(mAfforanceHelper); 238 mLastOrientation = getResources().getConfiguration().orientation; 239 240 mQsAutoReinflateContainer = 241 (AutoReinflateContainer) findViewById(R.id.qs_auto_reinflate_container); 242 mQsAutoReinflateContainer.addInflateListener(new InflateListener() { 243 @Override 244 public void onInflated(View v) { 245 mQsContainer = (QSContainer) v.findViewById(R.id.quick_settings_container); 246 mQsContainer.setPanelView(NotificationPanelView.this); 247 mQsContainer.getHeader().findViewById(R.id.expand_indicator) 248 .setOnClickListener(NotificationPanelView.this); 249 250 // recompute internal state when qspanel height changes 251 mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() { 252 @Override 253 public void onLayoutChange(View v, int left, int top, int right, int bottom, 254 int oldLeft, int oldTop, int oldRight, int oldBottom) { 255 final int height = bottom - top; 256 final int oldHeight = oldBottom - oldTop; 257 if (height != oldHeight) { 258 onQsHeightChanged(); 259 } 260 } 261 }); 262 mNotificationStackScroller.setQsContainer(mQsContainer); 263 } 264 }); 265 } 266 267 @Override loadDimens()268 protected void loadDimens() { 269 super.loadDimens(); 270 mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f); 271 mStatusBarMinHeight = getResources().getDimensionPixelSize( 272 com.android.internal.R.dimen.status_bar_height); 273 mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height); 274 mNotificationsHeaderCollideDistance = 275 getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance); 276 mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance); 277 mClockPositionAlgorithm.loadDimens(getResources()); 278 mNotificationScrimWaitDistance = 279 getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance); 280 mQsFalsingThreshold = getResources().getDimensionPixelSize( 281 R.dimen.qs_falsing_threshold); 282 mPositionMinSideMargin = getResources().getDimensionPixelSize( 283 R.dimen.notification_panel_min_side_margin); 284 mMaxFadeoutHeight = getResources().getDimensionPixelSize( 285 R.dimen.max_notification_fadeout_height); 286 } 287 updateResources()288 public void updateResources() { 289 int panelWidth = getResources().getDimensionPixelSize(R.dimen.notification_panel_width); 290 int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity); 291 FrameLayout.LayoutParams lp = 292 (FrameLayout.LayoutParams) mQsAutoReinflateContainer.getLayoutParams(); 293 if (lp.width != panelWidth) { 294 lp.width = panelWidth; 295 lp.gravity = panelGravity; 296 mQsAutoReinflateContainer.setLayoutParams(lp); 297 mQsContainer.post(mUpdateHeader); 298 } 299 300 lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); 301 if (lp.width != panelWidth) { 302 lp.width = panelWidth; 303 lp.gravity = panelGravity; 304 mNotificationStackScroller.setLayoutParams(lp); 305 } 306 } 307 308 @Override onLayout(boolean changed, int left, int top, int right, int bottom)309 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 310 super.onLayout(changed, left, top, right, bottom); 311 312 // Update Clock Pivot 313 mKeyguardStatusView.setPivotX(getWidth() / 2); 314 mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getTextSize()); 315 316 // Calculate quick setting heights. 317 int oldMaxHeight = mQsMaxExpansionHeight; 318 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getQsMinExpansionHeight(); 319 mQsMaxExpansionHeight = mQsContainer.getDesiredHeight(); 320 positionClockAndNotifications(); 321 if (mQsExpanded && mQsFullyExpanded) { 322 mQsExpansionHeight = mQsMaxExpansionHeight; 323 requestScrollerTopPaddingUpdate(false /* animate */); 324 requestPanelHeightUpdate(); 325 326 // Size has changed, start an animation. 327 if (mQsMaxExpansionHeight != oldMaxHeight) { 328 startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); 329 } 330 } else if (!mQsExpanded) { 331 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); 332 } 333 updateExpandedHeight(getExpandedHeight()); 334 updateHeader(); 335 336 // If we are running a size change animation, the animation takes care of the height of 337 // the container. However, if we are not animating, we always need to make the QS container 338 // the desired height so when closing the QS detail, it stays smaller after the size change 339 // animation is finished but the detail view is still being animated away (this animation 340 // takes longer than the size change animation). 341 if (mQsSizeChangeAnimator == null) { 342 mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight()); 343 } 344 updateMaxHeadsUpTranslation(); 345 } 346 startQsSizeChangeAnimation(int oldHeight, final int newHeight)347 private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { 348 if (mQsSizeChangeAnimator != null) { 349 oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 350 mQsSizeChangeAnimator.cancel(); 351 } 352 mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); 353 mQsSizeChangeAnimator.setDuration(300); 354 mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 355 mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 356 @Override 357 public void onAnimationUpdate(ValueAnimator animation) { 358 requestScrollerTopPaddingUpdate(false /* animate */); 359 requestPanelHeightUpdate(); 360 int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); 361 mQsContainer.setHeightOverride(height); 362 } 363 }); 364 mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { 365 @Override 366 public void onAnimationEnd(Animator animation) { 367 mQsSizeChangeAnimator = null; 368 } 369 }); 370 mQsSizeChangeAnimator.start(); 371 } 372 373 /** 374 * Positions the clock and notifications dynamically depending on how many notifications are 375 * showing. 376 */ positionClockAndNotifications()377 private void positionClockAndNotifications() { 378 boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); 379 int stackScrollerPadding; 380 if (mStatusBarState != StatusBarState.KEYGUARD) { 381 stackScrollerPadding = mQsContainer.getHeader().getHeight() + mQsPeekHeight; 382 mTopPaddingAdjustment = 0; 383 } else { 384 mClockPositionAlgorithm.setup( 385 mStatusBar.getMaxKeyguardNotifications(), 386 getMaxPanelHeight(), 387 getExpandedHeight(), 388 mNotificationStackScroller.getNotGoneChildCount(), 389 getHeight(), 390 mKeyguardStatusView.getHeight(), 391 mEmptyDragAmount); 392 mClockPositionAlgorithm.run(mClockPositionResult); 393 if (animate || mClockAnimator != null) { 394 startClockAnimation(mClockPositionResult.clockY); 395 } else { 396 mKeyguardStatusView.setY(mClockPositionResult.clockY); 397 } 398 updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale); 399 stackScrollerPadding = mClockPositionResult.stackScrollerPadding; 400 mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment; 401 } 402 mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); 403 requestScrollerTopPaddingUpdate(animate); 404 } 405 406 /** 407 * @param maximum the maximum to return at most 408 * @return the maximum keyguard notifications that can fit on the screen 409 */ computeMaxKeyguardNotifications(int maximum)410 public int computeMaxKeyguardNotifications(int maximum) { 411 float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(), 412 mKeyguardStatusView.getHeight()); 413 int notificationPadding = Math.max(1, getResources().getDimensionPixelSize( 414 R.dimen.notification_divider_height)); 415 final int overflowheight = getResources().getDimensionPixelSize( 416 R.dimen.notification_summary_height); 417 float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize(); 418 float availableSpace = mNotificationStackScroller.getHeight() - minPadding - overflowheight 419 - bottomStackSize; 420 int count = 0; 421 for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { 422 ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); 423 if (!(child instanceof ExpandableNotificationRow)) { 424 continue; 425 } 426 ExpandableNotificationRow row = (ExpandableNotificationRow) child; 427 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( 428 row.getStatusBarNotification()); 429 if (suppressedSummary) { 430 continue; 431 } 432 if (!mStatusBar.shouldShowOnKeyguard(row.getStatusBarNotification())) { 433 continue; 434 } 435 if (row.isRemoved()) { 436 continue; 437 } 438 availableSpace -= child.getMinHeight() + notificationPadding; 439 if (availableSpace >= 0 && count < maximum) { 440 count++; 441 } else { 442 return count; 443 } 444 } 445 return count; 446 } 447 startClockAnimation(int y)448 private void startClockAnimation(int y) { 449 if (mClockAnimationTarget == y) { 450 return; 451 } 452 mClockAnimationTarget = y; 453 getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 454 @Override 455 public boolean onPreDraw() { 456 getViewTreeObserver().removeOnPreDrawListener(this); 457 if (mClockAnimator != null) { 458 mClockAnimator.removeAllListeners(); 459 mClockAnimator.cancel(); 460 } 461 mClockAnimator = ObjectAnimator 462 .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget); 463 mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 464 mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 465 mClockAnimator.addListener(new AnimatorListenerAdapter() { 466 @Override 467 public void onAnimationEnd(Animator animation) { 468 mClockAnimator = null; 469 mClockAnimationTarget = -1; 470 } 471 }); 472 mClockAnimator.start(); 473 return true; 474 } 475 }); 476 } 477 updateClock(float alpha, float scale)478 private void updateClock(float alpha, float scale) { 479 if (!mKeyguardStatusViewAnimating) { 480 mKeyguardStatusView.setAlpha(alpha); 481 } 482 mKeyguardStatusView.setScaleX(scale); 483 mKeyguardStatusView.setScaleY(scale); 484 } 485 animateToFullShade(long delay)486 public void animateToFullShade(long delay) { 487 mAnimateNextTopPaddingChange = true; 488 mNotificationStackScroller.goToFullShade(delay); 489 requestLayout(); 490 } 491 setQsExpansionEnabled(boolean qsExpansionEnabled)492 public void setQsExpansionEnabled(boolean qsExpansionEnabled) { 493 mQsExpansionEnabled = qsExpansionEnabled; 494 mQsContainer.setHeaderClickable(qsExpansionEnabled); 495 } 496 497 @Override resetViews()498 public void resetViews() { 499 mIsLaunchTransitionFinished = false; 500 mBlockTouches = false; 501 mUnlockIconActive = false; 502 if (!mLaunchingAffordance) { 503 mAfforanceHelper.reset(false); 504 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 505 } 506 closeQs(); 507 mStatusBar.dismissPopups(); 508 mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */, 509 true /* cancelAnimators */); 510 mNotificationStackScroller.resetScrollPosition(); 511 } 512 closeQs()513 public void closeQs() { 514 cancelQsAnimation(); 515 setQsExpansion(mQsMinExpansionHeight); 516 } 517 animateCloseQs()518 public void animateCloseQs() { 519 if (mQsExpansionAnimator != null) { 520 if (!mQsAnimatorExpand) { 521 return; 522 } 523 float height = mQsExpansionHeight; 524 mQsExpansionAnimator.cancel(); 525 setQsExpansion(height); 526 } 527 flingSettings(0 /* vel */, false); 528 } 529 openQs()530 public void openQs() { 531 cancelQsAnimation(); 532 if (mQsExpansionEnabled) { 533 setQsExpansion(mQsMaxExpansionHeight); 534 } 535 } 536 expandWithQs()537 public void expandWithQs() { 538 if (mQsExpansionEnabled) { 539 mQsExpandImmediate = true; 540 } 541 expand(true /* animate */); 542 } 543 544 @Override fling(float vel, boolean expand)545 public void fling(float vel, boolean expand) { 546 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); 547 if (gr != null) { 548 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); 549 } 550 super.fling(vel, expand); 551 } 552 553 @Override flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)554 protected void flingToHeight(float vel, boolean expand, float target, 555 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { 556 mHeadsUpTouchHelper.notifyFling(!expand); 557 setClosingWithAlphaFadeout(!expand 558 && mNotificationStackScroller.getFirstChildIntrinsicHeight() <= mMaxFadeoutHeight 559 && getFadeoutAlpha() == 1.0f); 560 super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); 561 } 562 563 @Override dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)564 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 565 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 566 event.getText().add(getKeyguardOrLockScreenString()); 567 mLastAnnouncementWasQuickSettings = false; 568 return true; 569 } 570 return super.dispatchPopulateAccessibilityEventInternal(event); 571 } 572 573 @Override onInterceptTouchEvent(MotionEvent event)574 public boolean onInterceptTouchEvent(MotionEvent event) { 575 if (mBlockTouches || mQsContainer.isCustomizing()) { 576 return false; 577 } 578 initDownStates(event); 579 if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 580 mIsExpansionFromHeadsUp = true; 581 MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); 582 MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); 583 return true; 584 } 585 if (!isFullyCollapsed() && onQsIntercept(event)) { 586 return true; 587 } 588 return super.onInterceptTouchEvent(event); 589 } 590 onQsIntercept(MotionEvent event)591 private boolean onQsIntercept(MotionEvent event) { 592 int pointerIndex = event.findPointerIndex(mTrackingPointer); 593 if (pointerIndex < 0) { 594 pointerIndex = 0; 595 mTrackingPointer = event.getPointerId(pointerIndex); 596 } 597 final float x = event.getX(pointerIndex); 598 final float y = event.getY(pointerIndex); 599 600 switch (event.getActionMasked()) { 601 case MotionEvent.ACTION_DOWN: 602 mIntercepting = true; 603 mInitialTouchY = y; 604 mInitialTouchX = x; 605 initVelocityTracker(); 606 trackMovement(event); 607 if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { 608 getParent().requestDisallowInterceptTouchEvent(true); 609 } 610 if (mQsExpansionAnimator != null) { 611 onQsExpansionStarted(); 612 mInitialHeightOnTouch = mQsExpansionHeight; 613 mQsTracking = true; 614 mIntercepting = false; 615 mNotificationStackScroller.removeLongPressCallback(); 616 } 617 break; 618 case MotionEvent.ACTION_POINTER_UP: 619 final int upPointer = event.getPointerId(event.getActionIndex()); 620 if (mTrackingPointer == upPointer) { 621 // gesture is ongoing, find a new pointer to track 622 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 623 mTrackingPointer = event.getPointerId(newIndex); 624 mInitialTouchX = event.getX(newIndex); 625 mInitialTouchY = event.getY(newIndex); 626 } 627 break; 628 629 case MotionEvent.ACTION_MOVE: 630 final float h = y - mInitialTouchY; 631 trackMovement(event); 632 if (mQsTracking) { 633 634 // Already tracking because onOverscrolled was called. We need to update here 635 // so we don't stop for a frame until the next touch event gets handled in 636 // onTouchEvent. 637 setQsExpansion(h + mInitialHeightOnTouch); 638 trackMovement(event); 639 mIntercepting = false; 640 return true; 641 } 642 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) 643 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { 644 mQsTracking = true; 645 onQsExpansionStarted(); 646 notifyExpandingFinished(); 647 mInitialHeightOnTouch = mQsExpansionHeight; 648 mInitialTouchY = y; 649 mInitialTouchX = x; 650 mIntercepting = false; 651 mNotificationStackScroller.removeLongPressCallback(); 652 return true; 653 } 654 break; 655 656 case MotionEvent.ACTION_CANCEL: 657 case MotionEvent.ACTION_UP: 658 trackMovement(event); 659 if (mQsTracking) { 660 flingQsWithCurrentVelocity(y, 661 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 662 mQsTracking = false; 663 } 664 mIntercepting = false; 665 break; 666 } 667 return false; 668 } 669 670 @Override isInContentBounds(float x, float y)671 protected boolean isInContentBounds(float x, float y) { 672 float stackScrollerX = mNotificationStackScroller.getX(); 673 return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) 674 && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); 675 } 676 initDownStates(MotionEvent event)677 private void initDownStates(MotionEvent event) { 678 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 679 mOnlyAffordanceInThisMotion = false; 680 mQsTouchAboveFalsingThreshold = mQsFullyExpanded; 681 mDozingOnDown = isDozing(); 682 mCollapsedOnDown = isFullyCollapsed(); 683 mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); 684 } 685 } 686 flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)687 private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { 688 float vel = getCurrentVelocity(); 689 final boolean expandsQs = flingExpandsQs(vel); 690 if (expandsQs) { 691 logQsSwipeDown(y); 692 } 693 flingSettings(vel, expandsQs && !isCancelMotionEvent); 694 } 695 logQsSwipeDown(float y)696 private void logQsSwipeDown(float y) { 697 float vel = getCurrentVelocity(); 698 final int gesture = mStatusBarState == StatusBarState.KEYGUARD 699 ? EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS 700 : EventLogConstants.SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS; 701 EventLogTags.writeSysuiLockscreenGesture( 702 gesture, 703 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), 704 (int) (vel / mStatusBar.getDisplayDensity())); 705 } 706 flingExpandsQs(float vel)707 private boolean flingExpandsQs(float vel) { 708 if (isFalseTouch()) { 709 return false; 710 } 711 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 712 return getQsExpansionFraction() > 0.5f; 713 } else { 714 return vel > 0; 715 } 716 } 717 isFalseTouch()718 private boolean isFalseTouch() { 719 if (!needsAntiFalsing()) { 720 return false; 721 } 722 if (mFalsingManager.isClassiferEnabled()) { 723 return mFalsingManager.isFalseTouch(); 724 } 725 return !mQsTouchAboveFalsingThreshold; 726 } 727 getQsExpansionFraction()728 private float getQsExpansionFraction() { 729 return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight) 730 / (getTempQsMaxExpansion() - mQsMinExpansionHeight)); 731 } 732 733 @Override onTouchEvent(MotionEvent event)734 public boolean onTouchEvent(MotionEvent event) { 735 if (mBlockTouches || mQsContainer.isCustomizing()) { 736 return false; 737 } 738 initDownStates(event); 739 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() 740 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 741 mIsExpansionFromHeadsUp = true; 742 MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); 743 } 744 if ((!mIsExpanding || mHintAnimationRunning) 745 && !mQsExpanded 746 && mStatusBar.getBarState() != StatusBarState.SHADE) { 747 mAfforanceHelper.onTouchEvent(event); 748 } 749 if (mOnlyAffordanceInThisMotion) { 750 return true; 751 } 752 mHeadsUpTouchHelper.onTouchEvent(event); 753 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { 754 return true; 755 } 756 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { 757 MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); 758 updateVerticalPanelPosition(event.getX()); 759 } 760 super.onTouchEvent(event); 761 return true; 762 } 763 handleQsTouch(MotionEvent event)764 private boolean handleQsTouch(MotionEvent event) { 765 final int action = event.getActionMasked(); 766 if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f 767 && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded 768 && mQsExpansionEnabled) { 769 770 // Down in the empty area while fully expanded - go to QS. 771 mQsTracking = true; 772 mConflictingQsExpansionGesture = true; 773 onQsExpansionStarted(); 774 mInitialHeightOnTouch = mQsExpansionHeight; 775 mInitialTouchY = event.getX(); 776 mInitialTouchX = event.getY(); 777 } 778 if (!isFullyCollapsed()) { 779 handleQsDown(event); 780 } 781 if (!mQsExpandImmediate && mQsTracking) { 782 onQsTouch(event); 783 if (!mConflictingQsExpansionGesture) { 784 return true; 785 } 786 } 787 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 788 mConflictingQsExpansionGesture = false; 789 } 790 if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() 791 && mQsExpansionEnabled) { 792 mTwoFingerQsExpandPossible = true; 793 } 794 if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) 795 && event.getY(event.getActionIndex()) < mStatusBarMinHeight) { 796 MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1); 797 mQsExpandImmediate = true; 798 requestPanelHeightUpdate(); 799 800 // Normally, we start listening when the panel is expanded, but here we need to start 801 // earlier so the state is already up to date when dragging down. 802 setListening(true); 803 } 804 return false; 805 } 806 isInQsArea(float x, float y)807 private boolean isInQsArea(float x, float y) { 808 return (x >= mQsAutoReinflateContainer.getX() 809 && x <= mQsAutoReinflateContainer.getX() + mQsAutoReinflateContainer.getWidth()) 810 && (y <= mNotificationStackScroller.getBottomMostNotificationBottom() 811 || y <= mQsContainer.getY() + mQsContainer.getHeight()); 812 } 813 isOpenQsEvent(MotionEvent event)814 private boolean isOpenQsEvent(MotionEvent event) { 815 final int pointerCount = event.getPointerCount(); 816 final int action = event.getActionMasked(); 817 818 final boolean twoFingerDrag = action == MotionEvent.ACTION_POINTER_DOWN 819 && pointerCount == 2; 820 821 final boolean stylusButtonClickDrag = action == MotionEvent.ACTION_DOWN 822 && (event.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY) 823 || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)); 824 825 final boolean mouseButtonClickDrag = action == MotionEvent.ACTION_DOWN 826 && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY) 827 || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)); 828 829 return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag; 830 } 831 handleQsDown(MotionEvent event)832 private void handleQsDown(MotionEvent event) { 833 if (event.getActionMasked() == MotionEvent.ACTION_DOWN 834 && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { 835 mFalsingManager.onQsDown(); 836 mQsTracking = true; 837 onQsExpansionStarted(); 838 mInitialHeightOnTouch = mQsExpansionHeight; 839 mInitialTouchY = event.getX(); 840 mInitialTouchX = event.getY(); 841 842 // If we interrupt an expansion gesture here, make sure to update the state correctly. 843 notifyExpandingFinished(); 844 } 845 } 846 847 @Override flingExpands(float vel, float vectorVel, float x, float y)848 protected boolean flingExpands(float vel, float vectorVel, float x, float y) { 849 boolean expands = super.flingExpands(vel, vectorVel, x, y); 850 851 // If we are already running a QS expansion, make sure that we keep the panel open. 852 if (mQsExpansionAnimator != null) { 853 expands = true; 854 } 855 return expands; 856 } 857 858 @Override hasConflictingGestures()859 protected boolean hasConflictingGestures() { 860 return mStatusBar.getBarState() != StatusBarState.SHADE; 861 } 862 863 @Override shouldGestureIgnoreXTouchSlop(float x, float y)864 protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { 865 return !mAfforanceHelper.isOnAffordanceIcon(x, y); 866 } 867 onQsTouch(MotionEvent event)868 private void onQsTouch(MotionEvent event) { 869 int pointerIndex = event.findPointerIndex(mTrackingPointer); 870 if (pointerIndex < 0) { 871 pointerIndex = 0; 872 mTrackingPointer = event.getPointerId(pointerIndex); 873 } 874 final float y = event.getY(pointerIndex); 875 final float x = event.getX(pointerIndex); 876 final float h = y - mInitialTouchY; 877 878 switch (event.getActionMasked()) { 879 case MotionEvent.ACTION_DOWN: 880 mQsTracking = true; 881 mInitialTouchY = y; 882 mInitialTouchX = x; 883 onQsExpansionStarted(); 884 mInitialHeightOnTouch = mQsExpansionHeight; 885 initVelocityTracker(); 886 trackMovement(event); 887 break; 888 889 case MotionEvent.ACTION_POINTER_UP: 890 final int upPointer = event.getPointerId(event.getActionIndex()); 891 if (mTrackingPointer == upPointer) { 892 // gesture is ongoing, find a new pointer to track 893 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 894 final float newY = event.getY(newIndex); 895 final float newX = event.getX(newIndex); 896 mTrackingPointer = event.getPointerId(newIndex); 897 mInitialHeightOnTouch = mQsExpansionHeight; 898 mInitialTouchY = newY; 899 mInitialTouchX = newX; 900 } 901 break; 902 903 case MotionEvent.ACTION_MOVE: 904 setQsExpansion(h + mInitialHeightOnTouch); 905 if (h >= getFalsingThreshold()) { 906 mQsTouchAboveFalsingThreshold = true; 907 } 908 trackMovement(event); 909 break; 910 911 case MotionEvent.ACTION_UP: 912 case MotionEvent.ACTION_CANCEL: 913 mQsTracking = false; 914 mTrackingPointer = -1; 915 trackMovement(event); 916 float fraction = getQsExpansionFraction(); 917 if (fraction != 0f || y >= mInitialTouchY) { 918 flingQsWithCurrentVelocity(y, 919 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 920 } 921 if (mVelocityTracker != null) { 922 mVelocityTracker.recycle(); 923 mVelocityTracker = null; 924 } 925 break; 926 } 927 } 928 getFalsingThreshold()929 private int getFalsingThreshold() { 930 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 931 return (int) (mQsFalsingThreshold * factor); 932 } 933 934 @Override onOverscrollTopChanged(float amount, boolean isRubberbanded)935 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { 936 cancelQsAnimation(); 937 if (!mQsExpansionEnabled) { 938 amount = 0f; 939 } 940 float rounded = amount >= 1f ? amount : 0f; 941 setOverScrolling(rounded != 0f && isRubberbanded); 942 mQsExpansionFromOverscroll = rounded != 0f; 943 mLastOverscroll = rounded; 944 updateQsState(); 945 setQsExpansion(mQsMinExpansionHeight + rounded); 946 } 947 948 @Override flingTopOverscroll(float velocity, boolean open)949 public void flingTopOverscroll(float velocity, boolean open) { 950 mLastOverscroll = 0f; 951 mQsExpansionFromOverscroll = false; 952 setQsExpansion(mQsExpansionHeight); 953 flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, open && mQsExpansionEnabled, 954 new Runnable() { 955 @Override 956 public void run() { 957 mStackScrollerOverscrolling = false; 958 setOverScrolling(false); 959 updateQsState(); 960 } 961 }, false /* isClick */); 962 } 963 setOverScrolling(boolean overscrolling)964 private void setOverScrolling(boolean overscrolling) { 965 mStackScrollerOverscrolling = overscrolling; 966 mQsContainer.setOverscrolling(overscrolling); 967 } 968 onQsExpansionStarted()969 private void onQsExpansionStarted() { 970 onQsExpansionStarted(0); 971 } 972 onQsExpansionStarted(int overscrollAmount)973 private void onQsExpansionStarted(int overscrollAmount) { 974 cancelQsAnimation(); 975 cancelHeightAnimator(); 976 977 // Reset scroll position and apply that position to the expanded height. 978 float height = mQsExpansionHeight - overscrollAmount; 979 setQsExpansion(height); 980 requestPanelHeightUpdate(); 981 } 982 setQsExpanded(boolean expanded)983 private void setQsExpanded(boolean expanded) { 984 boolean changed = mQsExpanded != expanded; 985 if (changed) { 986 mQsExpanded = expanded; 987 updateQsState(); 988 requestPanelHeightUpdate(); 989 mFalsingManager.setQsExpanded(expanded); 990 mStatusBar.setQsExpanded(expanded); 991 mNotificationContainerParent.setQsExpanded(expanded); 992 } 993 } 994 setBarState(int statusBarState, boolean keyguardFadingAway, boolean goingToFullShade)995 public void setBarState(int statusBarState, boolean keyguardFadingAway, 996 boolean goingToFullShade) { 997 int oldState = mStatusBarState; 998 boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD; 999 setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); 1000 setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); 1001 1002 mStatusBarState = statusBarState; 1003 mKeyguardShowing = keyguardShowing; 1004 mQsContainer.setKeyguardShowing(mKeyguardShowing); 1005 1006 if (oldState == StatusBarState.KEYGUARD 1007 && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) { 1008 animateKeyguardStatusBarOut(); 1009 long delay = mStatusBarState == StatusBarState.SHADE_LOCKED 1010 ? 0 : mStatusBar.calculateGoingToFullShadeDelay(); 1011 mQsContainer.animateHeaderSlidingIn(delay); 1012 } else if (oldState == StatusBarState.SHADE_LOCKED 1013 && statusBarState == StatusBarState.KEYGUARD) { 1014 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); 1015 mQsContainer.animateHeaderSlidingOut(); 1016 } else { 1017 mKeyguardStatusBar.setAlpha(1f); 1018 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); 1019 if (keyguardShowing && oldState != mStatusBarState) { 1020 mKeyguardBottomArea.onKeyguardShowingChanged(); 1021 mQsContainer.hideImmediately(); 1022 } 1023 } 1024 if (keyguardShowing) { 1025 updateDozingVisibilities(false /* animate */); 1026 } 1027 resetVerticalPanelPosition(); 1028 updateQsState(); 1029 } 1030 1031 private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() { 1032 @Override 1033 public void run() { 1034 mKeyguardStatusViewAnimating = false; 1035 mKeyguardStatusView.setVisibility(View.GONE); 1036 } 1037 }; 1038 1039 private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() { 1040 @Override 1041 public void run() { 1042 mKeyguardStatusViewAnimating = false; 1043 } 1044 }; 1045 1046 private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { 1047 @Override 1048 public void run() { 1049 mKeyguardStatusBar.setVisibility(View.INVISIBLE); 1050 mKeyguardStatusBar.setAlpha(1f); 1051 mKeyguardStatusBarAnimateAlpha = 1f; 1052 } 1053 }; 1054 animateKeyguardStatusBarOut()1055 private void animateKeyguardStatusBarOut() { 1056 ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); 1057 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 1058 anim.setStartDelay(mStatusBar.isKeyguardFadingAway() 1059 ? mStatusBar.getKeyguardFadingAwayDelay() 1060 : 0); 1061 anim.setDuration(mStatusBar.isKeyguardFadingAway() 1062 ? mStatusBar.getKeyguardFadingAwayDuration() / 2 1063 : StackStateAnimator.ANIMATION_DURATION_STANDARD); 1064 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 1065 anim.addListener(new AnimatorListenerAdapter() { 1066 @Override 1067 public void onAnimationEnd(Animator animation) { 1068 mAnimateKeyguardStatusBarInvisibleEndRunnable.run(); 1069 } 1070 }); 1071 anim.start(); 1072 } 1073 1074 private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener = 1075 new ValueAnimator.AnimatorUpdateListener() { 1076 @Override 1077 public void onAnimationUpdate(ValueAnimator animation) { 1078 mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue(); 1079 updateHeaderKeyguardAlpha(); 1080 } 1081 }; 1082 animateKeyguardStatusBarIn(long duration)1083 private void animateKeyguardStatusBarIn(long duration) { 1084 mKeyguardStatusBar.setVisibility(View.VISIBLE); 1085 mKeyguardStatusBar.setAlpha(0f); 1086 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1087 anim.addUpdateListener(mStatusBarAnimateAlphaListener); 1088 anim.setDuration(duration); 1089 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 1090 anim.start(); 1091 } 1092 1093 private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { 1094 @Override 1095 public void run() { 1096 mKeyguardBottomArea.setVisibility(View.GONE); 1097 } 1098 }; 1099 setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)1100 private void setKeyguardBottomAreaVisibility(int statusBarState, 1101 boolean goingToFullShade) { 1102 mKeyguardBottomArea.animate().cancel(); 1103 if (goingToFullShade) { 1104 mKeyguardBottomArea.animate() 1105 .alpha(0f) 1106 .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay()) 1107 .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2) 1108 .setInterpolator(Interpolators.ALPHA_OUT) 1109 .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable) 1110 .start(); 1111 } else if (statusBarState == StatusBarState.KEYGUARD 1112 || statusBarState == StatusBarState.SHADE_LOCKED) { 1113 if (!mDozing) { 1114 mKeyguardBottomArea.setVisibility(View.VISIBLE); 1115 } 1116 mKeyguardBottomArea.setAlpha(1f); 1117 } else { 1118 mKeyguardBottomArea.setVisibility(View.GONE); 1119 mKeyguardBottomArea.setAlpha(1f); 1120 } 1121 } 1122 setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, boolean goingToFullShade)1123 private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, 1124 boolean goingToFullShade) { 1125 if ((!keyguardFadingAway && mStatusBarState == StatusBarState.KEYGUARD 1126 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) { 1127 mKeyguardStatusView.animate().cancel(); 1128 mKeyguardStatusViewAnimating = true; 1129 mKeyguardStatusView.animate() 1130 .alpha(0f) 1131 .setStartDelay(0) 1132 .setDuration(160) 1133 .setInterpolator(Interpolators.ALPHA_OUT) 1134 .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable); 1135 if (keyguardFadingAway) { 1136 mKeyguardStatusView.animate() 1137 .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay()) 1138 .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2) 1139 .start(); 1140 } 1141 } else if (mStatusBarState == StatusBarState.SHADE_LOCKED 1142 && statusBarState == StatusBarState.KEYGUARD) { 1143 mKeyguardStatusView.animate().cancel(); 1144 mKeyguardStatusView.setVisibility(View.VISIBLE); 1145 mKeyguardStatusViewAnimating = true; 1146 mKeyguardStatusView.setAlpha(0f); 1147 mKeyguardStatusView.animate() 1148 .alpha(1f) 1149 .setStartDelay(0) 1150 .setDuration(320) 1151 .setInterpolator(Interpolators.ALPHA_IN) 1152 .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); 1153 } else if (statusBarState == StatusBarState.KEYGUARD) { 1154 mKeyguardStatusView.animate().cancel(); 1155 mKeyguardStatusViewAnimating = false; 1156 mKeyguardStatusView.setVisibility(View.VISIBLE); 1157 mKeyguardStatusView.setAlpha(1f); 1158 } else { 1159 mKeyguardStatusView.animate().cancel(); 1160 mKeyguardStatusViewAnimating = false; 1161 mKeyguardStatusView.setVisibility(View.GONE); 1162 mKeyguardStatusView.setAlpha(1f); 1163 } 1164 } 1165 updateQsState()1166 private void updateQsState() { 1167 mQsContainer.setExpanded(mQsExpanded); 1168 mNotificationStackScroller.setQsExpanded(mQsExpanded); 1169 mNotificationStackScroller.setScrollingEnabled( 1170 mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded 1171 || mQsExpansionFromOverscroll)); 1172 updateEmptyShadeView(); 1173 mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded 1174 && !mStackScrollerOverscrolling && mQsScrimEnabled 1175 ? View.VISIBLE 1176 : View.INVISIBLE); 1177 if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { 1178 mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); 1179 } 1180 } 1181 setQsExpansion(float height)1182 private void setQsExpansion(float height) { 1183 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); 1184 mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; 1185 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling) { 1186 setQsExpanded(true); 1187 } else if (height <= mQsMinExpansionHeight && mQsExpanded) { 1188 setQsExpanded(false); 1189 if (mLastAnnouncementWasQuickSettings && !mTracking && !isCollapsing()) { 1190 announceForAccessibility(getKeyguardOrLockScreenString()); 1191 mLastAnnouncementWasQuickSettings = false; 1192 } 1193 } 1194 mQsExpansionHeight = height; 1195 updateQsExpansion(); 1196 requestScrollerTopPaddingUpdate(false /* animate */); 1197 if (mKeyguardShowing) { 1198 updateHeaderKeyguardAlpha(); 1199 } 1200 if (mStatusBarState == StatusBarState.SHADE_LOCKED 1201 || mStatusBarState == StatusBarState.KEYGUARD) { 1202 updateKeyguardBottomAreaAlpha(); 1203 } 1204 if (mStatusBarState == StatusBarState.SHADE && mQsExpanded 1205 && !mStackScrollerOverscrolling && mQsScrimEnabled) { 1206 mQsNavbarScrim.setAlpha(getQsExpansionFraction()); 1207 } 1208 1209 // Upon initialisation when we are not layouted yet we don't want to announce that we are 1210 // fully expanded, hence the != 0.0f check. 1211 if (height != 0.0f && mQsFullyExpanded && !mLastAnnouncementWasQuickSettings) { 1212 announceForAccessibility(getContext().getString( 1213 R.string.accessibility_desc_quick_settings)); 1214 mLastAnnouncementWasQuickSettings = true; 1215 } 1216 if (mQsFullyExpanded && mFalsingManager.shouldEnforceBouncer()) { 1217 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, 1218 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); 1219 } 1220 if (DEBUG) { 1221 invalidate(); 1222 } 1223 } 1224 updateQsExpansion()1225 protected void updateQsExpansion() { 1226 mQsContainer.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation()); 1227 } 1228 getKeyguardOrLockScreenString()1229 private String getKeyguardOrLockScreenString() { 1230 if (mQsContainer.isCustomizing()) { 1231 return getContext().getString(R.string.accessibility_desc_quick_settings_edit); 1232 } else if (mStatusBarState == StatusBarState.KEYGUARD) { 1233 return getContext().getString(R.string.accessibility_desc_lock_screen); 1234 } else { 1235 return getContext().getString(R.string.accessibility_desc_notification_shade); 1236 } 1237 } 1238 calculateQsTopPadding()1239 private float calculateQsTopPadding() { 1240 if (mKeyguardShowing 1241 && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) { 1242 1243 // Either QS pushes the notifications down when fully expanded, or QS is fully above the 1244 // notifications (mostly on tablets). maxNotifications denotes the normal top padding 1245 // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to 1246 // take the maximum and linearly interpolate with the panel expansion for a nice motion. 1247 int maxNotifications = mClockPositionResult.stackScrollerPadding 1248 - mClockPositionResult.stackScrollerPaddingAdjustment; 1249 int maxQs = getTempQsMaxExpansion(); 1250 int max = mStatusBarState == StatusBarState.KEYGUARD 1251 ? Math.max(maxNotifications, maxQs) 1252 : maxQs; 1253 return (int) interpolate(getExpandedFraction(), 1254 mQsMinExpansionHeight, max); 1255 } else if (mQsSizeChangeAnimator != null) { 1256 return (int) mQsSizeChangeAnimator.getAnimatedValue(); 1257 } else if (mKeyguardShowing) { 1258 1259 // We can only do the smoother transition on Keyguard when we also are not collapsing 1260 // from a scrolled quick settings. 1261 return interpolate(getQsExpansionFraction(), 1262 mNotificationStackScroller.getIntrinsicPadding(), 1263 mQsMaxExpansionHeight); 1264 } else { 1265 return mQsExpansionHeight; 1266 } 1267 } 1268 requestScrollerTopPaddingUpdate(boolean animate)1269 protected void requestScrollerTopPaddingUpdate(boolean animate) { 1270 mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), 1271 mAnimateNextTopPaddingChange || animate, 1272 mKeyguardShowing 1273 && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)); 1274 mAnimateNextTopPaddingChange = false; 1275 } 1276 trackMovement(MotionEvent event)1277 private void trackMovement(MotionEvent event) { 1278 if (mVelocityTracker != null) mVelocityTracker.addMovement(event); 1279 mLastTouchX = event.getX(); 1280 mLastTouchY = event.getY(); 1281 } 1282 initVelocityTracker()1283 private void initVelocityTracker() { 1284 if (mVelocityTracker != null) { 1285 mVelocityTracker.recycle(); 1286 } 1287 mVelocityTracker = VelocityTracker.obtain(); 1288 } 1289 getCurrentVelocity()1290 private float getCurrentVelocity() { 1291 if (mVelocityTracker == null) { 1292 return 0; 1293 } 1294 mVelocityTracker.computeCurrentVelocity(1000); 1295 return mVelocityTracker.getYVelocity(); 1296 } 1297 cancelQsAnimation()1298 private void cancelQsAnimation() { 1299 if (mQsExpansionAnimator != null) { 1300 mQsExpansionAnimator.cancel(); 1301 } 1302 } 1303 flingSettings(float vel, boolean expand)1304 public void flingSettings(float vel, boolean expand) { 1305 flingSettings(vel, expand, null, false /* isClick */); 1306 } 1307 flingSettings(float vel, boolean expand, final Runnable onFinishRunnable, boolean isClick)1308 private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable, 1309 boolean isClick) { 1310 float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight; 1311 if (target == mQsExpansionHeight) { 1312 if (onFinishRunnable != null) { 1313 onFinishRunnable.run(); 1314 } 1315 return; 1316 } 1317 boolean belowFalsingThreshold = isFalseTouch(); 1318 if (belowFalsingThreshold) { 1319 vel = 0; 1320 } 1321 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); 1322 if (isClick) { 1323 animator.setInterpolator(Interpolators.TOUCH_RESPONSE); 1324 animator.setDuration(368); 1325 } else { 1326 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); 1327 } 1328 if (belowFalsingThreshold) { 1329 animator.setDuration(350); 1330 } 1331 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 1332 @Override 1333 public void onAnimationUpdate(ValueAnimator animation) { 1334 setQsExpansion((Float) animation.getAnimatedValue()); 1335 } 1336 }); 1337 animator.addListener(new AnimatorListenerAdapter() { 1338 @Override 1339 public void onAnimationEnd(Animator animation) { 1340 mQsExpansionAnimator = null; 1341 if (onFinishRunnable != null) { 1342 onFinishRunnable.run(); 1343 } 1344 } 1345 }); 1346 animator.start(); 1347 mQsExpansionAnimator = animator; 1348 mQsAnimatorExpand = expand; 1349 } 1350 1351 /** 1352 * @return Whether we should intercept a gesture to open Quick Settings. 1353 */ shouldQuickSettingsIntercept(float x, float y, float yDiff)1354 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { 1355 if (!mQsExpansionEnabled || mCollapsedOnDown) { 1356 return false; 1357 } 1358 View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer.getHeader(); 1359 boolean onHeader = x >= mQsAutoReinflateContainer.getX() 1360 && x <= mQsAutoReinflateContainer.getX() + mQsAutoReinflateContainer.getWidth() 1361 && y >= header.getTop() && y <= header.getBottom(); 1362 if (mQsExpanded) { 1363 return onHeader || (yDiff < 0 && isInQsArea(x, y)); 1364 } else { 1365 return onHeader; 1366 } 1367 } 1368 1369 @Override isScrolledToBottom()1370 protected boolean isScrolledToBottom() { 1371 if (!isInSettings()) { 1372 return mStatusBar.getBarState() == StatusBarState.KEYGUARD 1373 || mNotificationStackScroller.isScrolledToBottom(); 1374 } else { 1375 return true; 1376 } 1377 } 1378 1379 @Override getMaxPanelHeight()1380 protected int getMaxPanelHeight() { 1381 int min = mStatusBarMinHeight; 1382 if (mStatusBar.getBarState() != StatusBarState.KEYGUARD 1383 && mNotificationStackScroller.getNotGoneChildCount() == 0) { 1384 int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); 1385 min = Math.max(min, minHeight); 1386 } 1387 int maxHeight; 1388 if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) { 1389 maxHeight = calculatePanelHeightQsExpanded(); 1390 } else { 1391 maxHeight = calculatePanelHeightShade(); 1392 } 1393 maxHeight = Math.max(maxHeight, min); 1394 return maxHeight; 1395 } 1396 isInSettings()1397 public boolean isInSettings() { 1398 return mQsExpanded; 1399 } 1400 isExpanding()1401 public boolean isExpanding() { 1402 return mIsExpanding; 1403 } 1404 1405 @Override onHeightUpdated(float expandedHeight)1406 protected void onHeightUpdated(float expandedHeight) { 1407 if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { 1408 positionClockAndNotifications(); 1409 } 1410 if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null 1411 && !mQsExpansionFromOverscroll) { 1412 float t; 1413 if (mKeyguardShowing) { 1414 1415 // On Keyguard, interpolate the QS expansion linearly to the panel expansion 1416 t = expandedHeight / getMaxPanelHeight(); 1417 } else { 1418 1419 // In Shade, interpolate linearly such that QS is closed whenever panel height is 1420 // minimum QS expansion + minStackHeight 1421 float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding() 1422 + mNotificationStackScroller.getLayoutMinHeight(); 1423 float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); 1424 t = (expandedHeight - panelHeightQsCollapsed) 1425 / (panelHeightQsExpanded - panelHeightQsCollapsed); 1426 } 1427 setQsExpansion(mQsMinExpansionHeight 1428 + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight)); 1429 } 1430 updateExpandedHeight(expandedHeight); 1431 updateHeader(); 1432 updateUnlockIcon(); 1433 updateNotificationTranslucency(); 1434 updatePanelExpanded(); 1435 mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed()); 1436 if (DEBUG) { 1437 invalidate(); 1438 } 1439 } 1440 updatePanelExpanded()1441 private void updatePanelExpanded() { 1442 boolean isExpanded = !isFullyCollapsed(); 1443 if (mPanelExpanded != isExpanded) { 1444 mHeadsUpManager.setIsExpanded(isExpanded); 1445 mStatusBar.setPanelExpanded(isExpanded); 1446 mPanelExpanded = isExpanded; 1447 } 1448 } 1449 1450 /** 1451 * @return a temporary override of {@link #mQsMaxExpansionHeight}, which is needed when 1452 * collapsing QS / the panel when QS was scrolled 1453 */ getTempQsMaxExpansion()1454 private int getTempQsMaxExpansion() { 1455 return mQsMaxExpansionHeight; 1456 } 1457 calculatePanelHeightShade()1458 private int calculatePanelHeightShade() { 1459 int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); 1460 int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin 1461 - mTopPaddingAdjustment; 1462 maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); 1463 return maxHeight; 1464 } 1465 calculatePanelHeightQsExpanded()1466 private int calculatePanelHeightQsExpanded() { 1467 float notificationHeight = mNotificationStackScroller.getHeight() 1468 - mNotificationStackScroller.getEmptyBottomMargin() 1469 - mNotificationStackScroller.getTopPadding(); 1470 1471 // When only empty shade view is visible in QS collapsed state, simulate that we would have 1472 // it in expanded QS state as well so we don't run into troubles when fading the view in/out 1473 // and expanding/collapsing the whole panel from/to quick settings. 1474 if (mNotificationStackScroller.getNotGoneChildCount() == 0 1475 && mShadeEmpty) { 1476 notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight() 1477 + mNotificationStackScroller.getBottomStackPeekSize() 1478 + mNotificationStackScroller.getBottomStackSlowDownHeight(); 1479 } 1480 int maxQsHeight = mQsMaxExpansionHeight; 1481 1482 // If an animation is changing the size of the QS panel, take the animated value. 1483 if (mQsSizeChangeAnimator != null) { 1484 maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 1485 } 1486 float totalHeight = Math.max( 1487 maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD 1488 ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment 1489 : 0) 1490 + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow(); 1491 if (totalHeight > mNotificationStackScroller.getHeight()) { 1492 float fullyCollapsedHeight = maxQsHeight 1493 + mNotificationStackScroller.getLayoutMinHeight(); 1494 totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); 1495 } 1496 return (int) totalHeight; 1497 } 1498 updateNotificationTranslucency()1499 private void updateNotificationTranslucency() { 1500 float alpha = 1f; 1501 if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) { 1502 alpha = getFadeoutAlpha(); 1503 } 1504 mNotificationStackScroller.setAlpha(alpha); 1505 } 1506 getFadeoutAlpha()1507 private float getFadeoutAlpha() { 1508 float alpha = (getNotificationsTopY() + mNotificationStackScroller.getFirstItemMinHeight()) 1509 / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize() 1510 - mNotificationStackScroller.getBottomStackSlowDownHeight()); 1511 alpha = Math.max(0, Math.min(alpha, 1)); 1512 alpha = (float) Math.pow(alpha, 0.75); 1513 return alpha; 1514 } 1515 1516 @Override getOverExpansionAmount()1517 protected float getOverExpansionAmount() { 1518 return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); 1519 } 1520 1521 @Override getOverExpansionPixels()1522 protected float getOverExpansionPixels() { 1523 return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); 1524 } 1525 updateUnlockIcon()1526 private void updateUnlockIcon() { 1527 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD 1528 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) { 1529 boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance; 1530 KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); 1531 if (active && !mUnlockIconActive && mTracking) { 1532 lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null); 1533 lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150, 1534 Interpolators.FAST_OUT_LINEAR_IN); 1535 } else if (!active && mUnlockIconActive && mTracking) { 1536 lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */, 1537 150, Interpolators.FAST_OUT_LINEAR_IN, null); 1538 lockIcon.setImageScale(1.0f, true, 150, 1539 Interpolators.FAST_OUT_LINEAR_IN); 1540 } 1541 mUnlockIconActive = active; 1542 } 1543 } 1544 1545 /** 1546 * Hides the header when notifications are colliding with it. 1547 */ updateHeader()1548 private void updateHeader() { 1549 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) { 1550 updateHeaderKeyguardAlpha(); 1551 } 1552 updateQsExpansion(); 1553 } 1554 getHeaderTranslation()1555 protected float getHeaderTranslation() { 1556 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) { 1557 return 0; 1558 } 1559 float translation = NotificationUtils.interpolate(-mQsMinExpansionHeight, 0, 1560 mNotificationStackScroller.getAppearFraction(mExpandedHeight)); 1561 return Math.min(0, translation); 1562 } 1563 1564 /** 1565 * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area) 1566 * during swiping up 1567 */ getKeyguardContentsAlpha()1568 private float getKeyguardContentsAlpha() { 1569 float alpha; 1570 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) { 1571 1572 // When on Keyguard, we hide the header as soon as the top card of the notification 1573 // stack scroller is close enough (collision distance) to the bottom of the header. 1574 alpha = getNotificationsTopY() 1575 / 1576 (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance); 1577 } else { 1578 1579 // In SHADE_LOCKED, the top card is already really close to the header. Hide it as 1580 // soon as we start translating the stack. 1581 alpha = getNotificationsTopY() / mKeyguardStatusBar.getHeight(); 1582 } 1583 alpha = MathUtils.constrain(alpha, 0, 1); 1584 alpha = (float) Math.pow(alpha, 0.75); 1585 return alpha; 1586 } 1587 updateHeaderKeyguardAlpha()1588 private void updateHeaderKeyguardAlpha() { 1589 float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); 1590 mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) 1591 * mKeyguardStatusBarAnimateAlpha); 1592 mKeyguardStatusBar.setVisibility(mKeyguardStatusBar.getAlpha() != 0f 1593 && !mDozing ? VISIBLE : INVISIBLE); 1594 } 1595 updateKeyguardBottomAreaAlpha()1596 private void updateKeyguardBottomAreaAlpha() { 1597 float alpha = Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction()); 1598 mKeyguardBottomArea.setAlpha(alpha); 1599 mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f 1600 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 1601 : IMPORTANT_FOR_ACCESSIBILITY_AUTO); 1602 } 1603 getNotificationsTopY()1604 private float getNotificationsTopY() { 1605 if (mNotificationStackScroller.getNotGoneChildCount() == 0) { 1606 return getExpandedHeight(); 1607 } 1608 return mNotificationStackScroller.getNotificationsTopY(); 1609 } 1610 1611 @Override onExpandingStarted()1612 protected void onExpandingStarted() { 1613 super.onExpandingStarted(); 1614 mNotificationStackScroller.onExpansionStarted(); 1615 mIsExpanding = true; 1616 mQsExpandedWhenExpandingStarted = mQsFullyExpanded; 1617 if (mQsExpanded) { 1618 onQsExpansionStarted(); 1619 } 1620 // Since there are QS tiles in the header now, we need to make sure we start listening 1621 // immediately so they can be up to date. 1622 mQsContainer.setHeaderListening(true); 1623 } 1624 1625 @Override onExpandingFinished()1626 protected void onExpandingFinished() { 1627 super.onExpandingFinished(); 1628 mNotificationStackScroller.onExpansionStopped(); 1629 mHeadsUpManager.onExpandingFinished(); 1630 mIsExpanding = false; 1631 if (isFullyCollapsed()) { 1632 DejankUtils.postAfterTraversal(new Runnable() { 1633 @Override 1634 public void run() { 1635 setListening(false); 1636 } 1637 }); 1638 1639 // Workaround b/22639032: Make sure we invalidate something because else RenderThread 1640 // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go 1641 // ahead with rendering and we jank. 1642 postOnAnimation(new Runnable() { 1643 @Override 1644 public void run() { 1645 getParent().invalidateChild(NotificationPanelView.this, mDummyDirtyRect); 1646 } 1647 }); 1648 } else { 1649 setListening(true); 1650 } 1651 mQsExpandImmediate = false; 1652 mTwoFingerQsExpandPossible = false; 1653 mIsExpansionFromHeadsUp = false; 1654 mNotificationStackScroller.setTrackingHeadsUp(false); 1655 mExpandingFromHeadsUp = false; 1656 setPanelScrimMinFraction(0.0f); 1657 } 1658 setListening(boolean listening)1659 private void setListening(boolean listening) { 1660 mQsContainer.setListening(listening); 1661 mKeyguardStatusBar.setListening(listening); 1662 } 1663 1664 @Override expand(boolean animate)1665 public void expand(boolean animate) { 1666 super.expand(animate); 1667 setListening(true); 1668 } 1669 1670 @Override setOverExpansion(float overExpansion, boolean isPixels)1671 protected void setOverExpansion(float overExpansion, boolean isPixels) { 1672 if (mConflictingQsExpansionGesture || mQsExpandImmediate) { 1673 return; 1674 } 1675 if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) { 1676 mNotificationStackScroller.setOnHeightChangedListener(null); 1677 if (isPixels) { 1678 mNotificationStackScroller.setOverScrolledPixels( 1679 overExpansion, true /* onTop */, false /* animate */); 1680 } else { 1681 mNotificationStackScroller.setOverScrollAmount( 1682 overExpansion, true /* onTop */, false /* animate */); 1683 } 1684 mNotificationStackScroller.setOnHeightChangedListener(this); 1685 } 1686 } 1687 1688 @Override onTrackingStarted()1689 protected void onTrackingStarted() { 1690 mFalsingManager.onTrackingStarted(); 1691 super.onTrackingStarted(); 1692 if (mQsFullyExpanded) { 1693 mQsExpandImmediate = true; 1694 } 1695 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD 1696 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) { 1697 mAfforanceHelper.animateHideLeftRightIcon(); 1698 } 1699 mNotificationStackScroller.onPanelTrackingStarted(); 1700 } 1701 1702 @Override onTrackingStopped(boolean expand)1703 protected void onTrackingStopped(boolean expand) { 1704 mFalsingManager.onTrackingStopped(); 1705 super.onTrackingStopped(expand); 1706 if (expand) { 1707 mNotificationStackScroller.setOverScrolledPixels( 1708 0.0f, true /* onTop */, true /* animate */); 1709 } 1710 mNotificationStackScroller.onPanelTrackingStopped(); 1711 if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD 1712 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) { 1713 if (!mHintAnimationRunning) { 1714 mAfforanceHelper.reset(true); 1715 } 1716 } 1717 if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD 1718 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) { 1719 KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); 1720 lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null); 1721 lockIcon.setImageScale(2.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN); 1722 } 1723 } 1724 1725 @Override onHeightChanged(ExpandableView view, boolean needsAnimation)1726 public void onHeightChanged(ExpandableView view, boolean needsAnimation) { 1727 1728 // Block update if we are in quick settings and just the top padding changed 1729 // (i.e. view == null). 1730 if (view == null && mQsExpanded) { 1731 return; 1732 } 1733 ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); 1734 ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow 1735 ? (ExpandableNotificationRow) firstChildNotGone 1736 : null; 1737 if (firstRow != null 1738 && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) { 1739 requestScrollerTopPaddingUpdate(false); 1740 } 1741 requestPanelHeightUpdate(); 1742 } 1743 1744 @Override onReset(ExpandableView view)1745 public void onReset(ExpandableView view) { 1746 } 1747 onQsHeightChanged()1748 public void onQsHeightChanged() { 1749 mQsMaxExpansionHeight = mQsContainer.getDesiredHeight(); 1750 if (mQsExpanded && mQsFullyExpanded) { 1751 mQsExpansionHeight = mQsMaxExpansionHeight; 1752 requestScrollerTopPaddingUpdate(false /* animate */); 1753 requestPanelHeightUpdate(); 1754 } 1755 } 1756 1757 @Override onConfigurationChanged(Configuration newConfig)1758 protected void onConfigurationChanged(Configuration newConfig) { 1759 super.onConfigurationChanged(newConfig); 1760 mAfforanceHelper.onConfigurationChanged(); 1761 if (newConfig.orientation != mLastOrientation) { 1762 resetVerticalPanelPosition(); 1763 } 1764 mLastOrientation = newConfig.orientation; 1765 } 1766 1767 @Override onApplyWindowInsets(WindowInsets insets)1768 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1769 mNavigationBarBottomHeight = insets.getStableInsetBottom(); 1770 updateMaxHeadsUpTranslation(); 1771 return insets; 1772 } 1773 updateMaxHeadsUpTranslation()1774 private void updateMaxHeadsUpTranslation() { 1775 mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); 1776 } 1777 1778 @Override onRtlPropertiesChanged(int layoutDirection)1779 public void onRtlPropertiesChanged(int layoutDirection) { 1780 if (layoutDirection != mOldLayoutDirection) { 1781 mAfforanceHelper.onRtlPropertiesChanged(); 1782 mOldLayoutDirection = layoutDirection; 1783 } 1784 } 1785 1786 @Override onClick(View v)1787 public void onClick(View v) { 1788 if (v.getId() == R.id.expand_indicator) { 1789 onQsExpansionStarted(); 1790 if (mQsExpanded) { 1791 flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */); 1792 } else if (mQsExpansionEnabled) { 1793 EventLogTags.writeSysuiLockscreenGesture( 1794 EventLogConstants.SYSUI_TAP_TO_OPEN_QS, 1795 0, 0); 1796 flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */); 1797 } 1798 } 1799 } 1800 1801 @Override onAnimationToSideStarted(boolean rightPage, float translation, float vel)1802 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { 1803 boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage; 1804 mIsLaunchTransitionRunning = true; 1805 mLaunchAnimationEndRunnable = null; 1806 float displayDensity = mStatusBar.getDisplayDensity(); 1807 int lengthDp = Math.abs((int) (translation / displayDensity)); 1808 int velocityDp = Math.abs((int) (vel / displayDensity)); 1809 if (start) { 1810 EventLogTags.writeSysuiLockscreenGesture( 1811 EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp); 1812 1813 mFalsingManager.onLeftAffordanceOn(); 1814 if (mFalsingManager.shouldEnforceBouncer()) { 1815 mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { 1816 @Override 1817 public void run() { 1818 mKeyguardBottomArea.launchLeftAffordance(); 1819 } 1820 }, null, true /* dismissShade */, false /* afterKeyguardGone */, 1821 true /* deferred */); 1822 } 1823 else { 1824 mKeyguardBottomArea.launchLeftAffordance(); 1825 } 1826 } else { 1827 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( 1828 mLastCameraLaunchSource)) { 1829 EventLogTags.writeSysuiLockscreenGesture( 1830 EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, 1831 lengthDp, velocityDp); 1832 } 1833 mFalsingManager.onCameraOn(); 1834 if (mFalsingManager.shouldEnforceBouncer()) { 1835 mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { 1836 @Override 1837 public void run() { 1838 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); 1839 } 1840 }, null, true /* dismissShade */, false /* afterKeyguardGone */, 1841 true /* deferred */); 1842 } 1843 else { 1844 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); 1845 } 1846 } 1847 mStatusBar.startLaunchTransitionTimeout(); 1848 mBlockTouches = true; 1849 } 1850 1851 @Override onAnimationToSideEnded()1852 public void onAnimationToSideEnded() { 1853 mIsLaunchTransitionRunning = false; 1854 mIsLaunchTransitionFinished = true; 1855 if (mLaunchAnimationEndRunnable != null) { 1856 mLaunchAnimationEndRunnable.run(); 1857 mLaunchAnimationEndRunnable = null; 1858 } 1859 } 1860 1861 @Override startUnlockHintAnimation()1862 protected void startUnlockHintAnimation() { 1863 super.startUnlockHintAnimation(); 1864 startHighlightIconAnimation(getCenterIcon()); 1865 } 1866 1867 /** 1868 * Starts the highlight (making it fully opaque) animation on an icon. 1869 */ startHighlightIconAnimation(final KeyguardAffordanceView icon)1870 private void startHighlightIconAnimation(final KeyguardAffordanceView icon) { 1871 icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, 1872 Interpolators.FAST_OUT_SLOW_IN, new Runnable() { 1873 @Override 1874 public void run() { 1875 icon.setImageAlpha(icon.getRestingAlpha(), 1876 true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, 1877 Interpolators.FAST_OUT_SLOW_IN, null); 1878 } 1879 }); 1880 } 1881 1882 @Override getMaxTranslationDistance()1883 public float getMaxTranslationDistance() { 1884 return (float) Math.hypot(getWidth(), getHeight()); 1885 } 1886 1887 @Override onSwipingStarted(boolean rightIcon)1888 public void onSwipingStarted(boolean rightIcon) { 1889 mFalsingManager.onAffordanceSwipingStarted(rightIcon); 1890 boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon 1891 : rightIcon; 1892 if (camera) { 1893 mKeyguardBottomArea.bindCameraPrewarmService(); 1894 } 1895 requestDisallowInterceptTouchEvent(true); 1896 mOnlyAffordanceInThisMotion = true; 1897 mQsTracking = false; 1898 } 1899 1900 @Override onSwipingAborted()1901 public void onSwipingAborted() { 1902 mFalsingManager.onAffordanceSwipingAborted(); 1903 mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); 1904 } 1905 1906 @Override onIconClicked(boolean rightIcon)1907 public void onIconClicked(boolean rightIcon) { 1908 if (mHintAnimationRunning) { 1909 return; 1910 } 1911 mHintAnimationRunning = true; 1912 mAfforanceHelper.startHintAnimation(rightIcon, new Runnable() { 1913 @Override 1914 public void run() { 1915 mHintAnimationRunning = false; 1916 mStatusBar.onHintFinished(); 1917 } 1918 }); 1919 rightIcon = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon; 1920 if (rightIcon) { 1921 mStatusBar.onCameraHintStarted(); 1922 } else { 1923 if (mKeyguardBottomArea.isLeftVoiceAssist()) { 1924 mStatusBar.onVoiceAssistHintStarted(); 1925 } else { 1926 mStatusBar.onPhoneHintStarted(); 1927 } 1928 } 1929 } 1930 1931 @Override getLeftIcon()1932 public KeyguardAffordanceView getLeftIcon() { 1933 return getLayoutDirection() == LAYOUT_DIRECTION_RTL 1934 ? mKeyguardBottomArea.getRightView() 1935 : mKeyguardBottomArea.getLeftView(); 1936 } 1937 1938 @Override getCenterIcon()1939 public KeyguardAffordanceView getCenterIcon() { 1940 return mKeyguardBottomArea.getLockIcon(); 1941 } 1942 1943 @Override getRightIcon()1944 public KeyguardAffordanceView getRightIcon() { 1945 return getLayoutDirection() == LAYOUT_DIRECTION_RTL 1946 ? mKeyguardBottomArea.getLeftView() 1947 : mKeyguardBottomArea.getRightView(); 1948 } 1949 1950 @Override getLeftPreview()1951 public View getLeftPreview() { 1952 return getLayoutDirection() == LAYOUT_DIRECTION_RTL 1953 ? mKeyguardBottomArea.getRightPreview() 1954 : mKeyguardBottomArea.getLeftPreview(); 1955 } 1956 1957 @Override getRightPreview()1958 public View getRightPreview() { 1959 return getLayoutDirection() == LAYOUT_DIRECTION_RTL 1960 ? mKeyguardBottomArea.getLeftPreview() 1961 : mKeyguardBottomArea.getRightPreview(); 1962 } 1963 1964 @Override getAffordanceFalsingFactor()1965 public float getAffordanceFalsingFactor() { 1966 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 1967 } 1968 1969 @Override needsAntiFalsing()1970 public boolean needsAntiFalsing() { 1971 return mStatusBarState == StatusBarState.KEYGUARD; 1972 } 1973 1974 @Override getPeekHeight()1975 protected float getPeekHeight() { 1976 if (mNotificationStackScroller.getNotGoneChildCount() > 0) { 1977 return mNotificationStackScroller.getPeekHeight(); 1978 } else { 1979 return mQsMinExpansionHeight; 1980 } 1981 } 1982 1983 @Override getCannedFlingDurationFactor()1984 protected float getCannedFlingDurationFactor() { 1985 if (mQsExpanded) { 1986 return 0.7f; 1987 } else { 1988 return 0.6f; 1989 } 1990 } 1991 1992 @Override fullyExpandedClearAllVisible()1993 protected boolean fullyExpandedClearAllVisible() { 1994 return mNotificationStackScroller.isDismissViewNotGone() 1995 && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; 1996 } 1997 1998 @Override isClearAllVisible()1999 protected boolean isClearAllVisible() { 2000 return mNotificationStackScroller.isDismissViewVisible(); 2001 } 2002 2003 @Override getClearAllHeight()2004 protected int getClearAllHeight() { 2005 return mNotificationStackScroller.getDismissViewHeight(); 2006 } 2007 2008 @Override isTrackingBlocked()2009 protected boolean isTrackingBlocked() { 2010 return mConflictingQsExpansionGesture && mQsExpanded; 2011 } 2012 isQsExpanded()2013 public boolean isQsExpanded() { 2014 return mQsExpanded; 2015 } 2016 isQsDetailShowing()2017 public boolean isQsDetailShowing() { 2018 return mQsContainer.isShowingDetail(); 2019 } 2020 closeQsDetail()2021 public void closeQsDetail() { 2022 mQsContainer.getQsPanel().closeDetail(); 2023 } 2024 2025 @Override shouldDelayChildPressedState()2026 public boolean shouldDelayChildPressedState() { 2027 return true; 2028 } 2029 isLaunchTransitionFinished()2030 public boolean isLaunchTransitionFinished() { 2031 return mIsLaunchTransitionFinished; 2032 } 2033 isLaunchTransitionRunning()2034 public boolean isLaunchTransitionRunning() { 2035 return mIsLaunchTransitionRunning; 2036 } 2037 setLaunchTransitionEndRunnable(Runnable r)2038 public void setLaunchTransitionEndRunnable(Runnable r) { 2039 mLaunchAnimationEndRunnable = r; 2040 } 2041 setEmptyDragAmount(float amount)2042 public void setEmptyDragAmount(float amount) { 2043 float factor = 0.8f; 2044 if (mNotificationStackScroller.getNotGoneChildCount() > 0) { 2045 factor = 0.4f; 2046 } else if (!mStatusBar.hasActiveNotifications()) { 2047 factor = 0.4f; 2048 } 2049 mEmptyDragAmount = amount * factor; 2050 positionClockAndNotifications(); 2051 } 2052 interpolate(float t, float start, float end)2053 private static float interpolate(float t, float start, float end) { 2054 return (1 - t) * start + t * end; 2055 } 2056 setDozing(boolean dozing, boolean animate)2057 public void setDozing(boolean dozing, boolean animate) { 2058 if (dozing == mDozing) return; 2059 mDozing = dozing; 2060 if (mStatusBarState == StatusBarState.KEYGUARD) { 2061 updateDozingVisibilities(animate); 2062 } 2063 } 2064 updateDozingVisibilities(boolean animate)2065 private void updateDozingVisibilities(boolean animate) { 2066 if (mDozing) { 2067 mKeyguardStatusBar.setVisibility(View.INVISIBLE); 2068 mKeyguardBottomArea.setVisibility(View.INVISIBLE); 2069 } else { 2070 mKeyguardBottomArea.setVisibility(View.VISIBLE); 2071 mKeyguardStatusBar.setVisibility(View.VISIBLE); 2072 if (animate) { 2073 animateKeyguardStatusBarIn(DOZE_ANIMATION_DURATION); 2074 mKeyguardBottomArea.startFinishDozeAnimation(); 2075 } 2076 } 2077 } 2078 2079 @Override isDozing()2080 public boolean isDozing() { 2081 return mDozing; 2082 } 2083 setShadeEmpty(boolean shadeEmpty)2084 public void setShadeEmpty(boolean shadeEmpty) { 2085 mShadeEmpty = shadeEmpty; 2086 updateEmptyShadeView(); 2087 } 2088 updateEmptyShadeView()2089 private void updateEmptyShadeView() { 2090 2091 // Hide "No notifications" in QS. 2092 mNotificationStackScroller.updateEmptyShadeView(mShadeEmpty && !mQsExpanded); 2093 } 2094 setQsScrimEnabled(boolean qsScrimEnabled)2095 public void setQsScrimEnabled(boolean qsScrimEnabled) { 2096 boolean changed = mQsScrimEnabled != qsScrimEnabled; 2097 mQsScrimEnabled = qsScrimEnabled; 2098 if (changed) { 2099 updateQsState(); 2100 } 2101 } 2102 setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher)2103 public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { 2104 mKeyguardUserSwitcher = keyguardUserSwitcher; 2105 } 2106 2107 private final Runnable mUpdateHeader = new Runnable() { 2108 @Override 2109 public void run() { 2110 mQsContainer.getHeader().updateEverything(); 2111 } 2112 }; 2113 onScreenTurningOn()2114 public void onScreenTurningOn() { 2115 mKeyguardStatusView.refreshTime(); 2116 } 2117 2118 @Override onEmptySpaceClicked(float x, float y)2119 public void onEmptySpaceClicked(float x, float y) { 2120 onEmptySpaceClick(x); 2121 } 2122 2123 @Override onMiddleClicked()2124 protected boolean onMiddleClicked() { 2125 switch (mStatusBar.getBarState()) { 2126 case StatusBarState.KEYGUARD: 2127 if (!mDozingOnDown) { 2128 EventLogTags.writeSysuiLockscreenGesture( 2129 EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT, 2130 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 2131 startUnlockHintAnimation(); 2132 } 2133 return true; 2134 case StatusBarState.SHADE_LOCKED: 2135 if (!mQsExpanded) { 2136 mStatusBar.goToKeyguard(); 2137 } 2138 return true; 2139 case StatusBarState.SHADE: 2140 2141 // This gets called in the middle of the touch handling, where the state is still 2142 // that we are tracking the panel. Collapse the panel after this is done. 2143 post(mPostCollapseRunnable); 2144 return false; 2145 default: 2146 return true; 2147 } 2148 } 2149 2150 @Override dispatchDraw(Canvas canvas)2151 protected void dispatchDraw(Canvas canvas) { 2152 super.dispatchDraw(canvas); 2153 if (DEBUG) { 2154 Paint p = new Paint(); 2155 p.setColor(Color.RED); 2156 p.setStrokeWidth(2); 2157 p.setStyle(Paint.Style.STROKE); 2158 canvas.drawLine(0, getMaxPanelHeight(), getWidth(), getMaxPanelHeight(), p); 2159 p.setColor(Color.BLUE); 2160 canvas.drawLine(0, getExpandedHeight(), getWidth(), getExpandedHeight(), p); 2161 p.setColor(Color.GREEN); 2162 canvas.drawLine(0, calculatePanelHeightQsExpanded(), getWidth(), 2163 calculatePanelHeightQsExpanded(), p); 2164 p.setColor(Color.YELLOW); 2165 canvas.drawLine(0, calculatePanelHeightShade(), getWidth(), 2166 calculatePanelHeightShade(), p); 2167 p.setColor(Color.MAGENTA); 2168 canvas.drawLine(0, calculateQsTopPadding(), getWidth(), 2169 calculateQsTopPadding(), p); 2170 p.setColor(Color.CYAN); 2171 canvas.drawLine(0, mNotificationStackScroller.getTopPadding(), getWidth(), 2172 mNotificationStackScroller.getTopPadding(), p); 2173 } 2174 } 2175 2176 @Override onHeadsUpPinnedModeChanged(final boolean inPinnedMode)2177 public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { 2178 if (inPinnedMode) { 2179 mHeadsUpExistenceChangedRunnable.run(); 2180 updateNotificationTranslucency(); 2181 } else { 2182 mHeadsUpAnimatingAway = true; 2183 mNotificationStackScroller.runAfterAnimationFinished( 2184 mHeadsUpExistenceChangedRunnable); 2185 } 2186 } 2187 2188 @Override onHeadsUpPinned(ExpandableNotificationRow headsUp)2189 public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { 2190 mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true); 2191 } 2192 2193 @Override onHeadsUpUnPinned(ExpandableNotificationRow headsUp)2194 public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { 2195 } 2196 2197 @Override onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp)2198 public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 2199 mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp); 2200 } 2201 2202 @Override setHeadsUpManager(HeadsUpManager headsUpManager)2203 public void setHeadsUpManager(HeadsUpManager headsUpManager) { 2204 super.setHeadsUpManager(headsUpManager); 2205 mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller, 2206 this); 2207 } 2208 setTrackingHeadsUp(boolean tracking)2209 public void setTrackingHeadsUp(boolean tracking) { 2210 if (tracking) { 2211 mNotificationStackScroller.setTrackingHeadsUp(true); 2212 mExpandingFromHeadsUp = true; 2213 } 2214 // otherwise we update the state when the expansion is finished 2215 } 2216 2217 @Override onClosingFinished()2218 protected void onClosingFinished() { 2219 super.onClosingFinished(); 2220 resetVerticalPanelPosition(); 2221 setClosingWithAlphaFadeout(false); 2222 } 2223 setClosingWithAlphaFadeout(boolean closing)2224 private void setClosingWithAlphaFadeout(boolean closing) { 2225 mClosingWithAlphaFadeOut = closing; 2226 mNotificationStackScroller.forceNoOverlappingRendering(closing); 2227 } 2228 2229 /** 2230 * Updates the vertical position of the panel so it is positioned closer to the touch 2231 * responsible for opening the panel. 2232 * 2233 * @param x the x-coordinate the touch event 2234 */ updateVerticalPanelPosition(float x)2235 protected void updateVerticalPanelPosition(float x) { 2236 if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) { 2237 resetVerticalPanelPosition(); 2238 return; 2239 } 2240 float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; 2241 float rightMost = getWidth() - mPositionMinSideMargin 2242 - mNotificationStackScroller.getWidth() / 2; 2243 if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { 2244 x = getWidth() / 2; 2245 } 2246 x = Math.min(rightMost, Math.max(leftMost, x)); 2247 setVerticalPanelTranslation(x - 2248 (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2)); 2249 } 2250 resetVerticalPanelPosition()2251 private void resetVerticalPanelPosition() { 2252 setVerticalPanelTranslation(0f); 2253 } 2254 setVerticalPanelTranslation(float translation)2255 protected void setVerticalPanelTranslation(float translation) { 2256 mNotificationStackScroller.setTranslationX(translation); 2257 mQsAutoReinflateContainer.setTranslationX(translation); 2258 } 2259 updateExpandedHeight(float expandedHeight)2260 protected void updateExpandedHeight(float expandedHeight) { 2261 mNotificationStackScroller.setExpandedHeight(expandedHeight); 2262 updateKeyguardBottomAreaAlpha(); 2263 } 2264 setPanelScrimMinFraction(float minFraction)2265 public void setPanelScrimMinFraction(float minFraction) { 2266 mBar.panelScrimMinFractionChanged(minFraction); 2267 } 2268 clearNotificationEffects()2269 public void clearNotificationEffects() { 2270 mStatusBar.clearNotificationEffects(); 2271 } 2272 2273 @Override isPanelVisibleBecauseOfHeadsUp()2274 protected boolean isPanelVisibleBecauseOfHeadsUp() { 2275 return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway; 2276 } 2277 2278 @Override hasOverlappingRendering()2279 public boolean hasOverlappingRendering() { 2280 return !mDozing; 2281 } 2282 launchCamera(boolean animate, int source)2283 public void launchCamera(boolean animate, int source) { 2284 if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { 2285 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; 2286 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { 2287 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; 2288 } else { 2289 2290 // Default. 2291 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 2292 } 2293 2294 // If we are launching it when we are occluded already we don't want it to animate, 2295 // nor setting these flags, since the occluded state doesn't change anymore, hence it's 2296 // never reset. 2297 if (!isFullyCollapsed()) { 2298 mLaunchingAffordance = true; 2299 setLaunchingAffordance(true); 2300 } else { 2301 animate = false; 2302 } 2303 mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL); 2304 } 2305 onAffordanceLaunchEnded()2306 public void onAffordanceLaunchEnded() { 2307 mLaunchingAffordance = false; 2308 setLaunchingAffordance(false); 2309 } 2310 2311 @Override setAlpha(float alpha)2312 public void setAlpha(float alpha) { 2313 super.setAlpha(alpha); 2314 mNotificationStackScroller.setParentFadingOut(alpha != 1.0f); 2315 } 2316 2317 /** 2318 * Set whether we are currently launching an affordance. This is currently only set when 2319 * launched via a camera gesture. 2320 */ setLaunchingAffordance(boolean launchingAffordance)2321 private void setLaunchingAffordance(boolean launchingAffordance) { 2322 getLeftIcon().setLaunchingAffordance(launchingAffordance); 2323 getRightIcon().setLaunchingAffordance(launchingAffordance); 2324 getCenterIcon().setLaunchingAffordance(launchingAffordance); 2325 } 2326 2327 /** 2328 * Whether the camera application can be launched for the camera launch gesture. 2329 * 2330 * @param keyguardIsShowing whether keyguard is being shown 2331 */ canCameraGestureBeLaunched(boolean keyguardIsShowing)2332 public boolean canCameraGestureBeLaunched(boolean keyguardIsShowing) { 2333 ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); 2334 String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null) 2335 ? null : resolveInfo.activityInfo.packageName; 2336 return packageToLaunch != null && 2337 (keyguardIsShowing || !isForegroundApp(packageToLaunch)) && 2338 !mAfforanceHelper.isSwipingInProgress(); 2339 } 2340 2341 /** 2342 * Return true if the applications with the package name is running in foreground. 2343 * 2344 * @param pkgName application package name. 2345 */ isForegroundApp(String pkgName)2346 private boolean isForegroundApp(String pkgName) { 2347 ActivityManager am = getContext().getSystemService(ActivityManager.class); 2348 List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); 2349 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 2350 } 2351 setGroupManager(NotificationGroupManager groupManager)2352 public void setGroupManager(NotificationGroupManager groupManager) { 2353 mGroupManager = groupManager; 2354 } 2355 } 2356