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