1 /* 2 * Copyright (C) 2014 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.notification.stack; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.util.MathUtils; 23 24 import com.android.systemui.R; 25 import com.android.systemui.dagger.SysUISingleton; 26 import com.android.systemui.statusbar.NotificationShelf; 27 import com.android.systemui.statusbar.StatusBarState; 28 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 29 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 30 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 31 import com.android.systemui.statusbar.notification.row.ExpandableView; 32 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController; 33 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider; 34 35 import javax.inject.Inject; 36 37 /** 38 * A global state to track all input states for the algorithm. 39 */ 40 @SysUISingleton 41 public class AmbientState { 42 43 private static final float MAX_PULSE_HEIGHT = 100000f; 44 private static final boolean NOTIFICATIONS_HAVE_SHADOWS = false; 45 46 private final SectionProvider mSectionProvider; 47 private final BypassController mBypassController; 48 private int mScrollY; 49 private boolean mDimmed; 50 private ActivatableNotificationView mActivatedChild; 51 private float mOverScrollTopAmount; 52 private float mOverScrollBottomAmount; 53 private boolean mDozing; 54 private boolean mHideSensitive; 55 private float mStackTranslation; 56 private int mLayoutHeight; 57 private int mTopPadding; 58 private boolean mShadeExpanded; 59 private float mMaxHeadsUpTranslation; 60 private boolean mDismissAllInProgress; 61 private int mLayoutMinHeight; 62 private NotificationShelf mShelf; 63 private int mZDistanceBetweenElements; 64 private int mBaseZHeight; 65 private int mContentHeight; 66 private ExpandableView mLastVisibleBackgroundChild; 67 private float mCurrentScrollVelocity; 68 private int mStatusBarState; 69 private float mExpandingVelocity; 70 private boolean mPanelTracking; 71 private boolean mExpansionChanging; 72 private boolean mPanelFullWidth; 73 private boolean mPulsing; 74 private boolean mUnlockHintRunning; 75 private boolean mQsCustomizerShowing; 76 private int mIntrinsicPadding; 77 private float mHideAmount; 78 private boolean mAppearing; 79 private float mPulseHeight = MAX_PULSE_HEIGHT; 80 private float mDozeAmount = 0.0f; 81 private Runnable mOnPulseHeightChangedListener; 82 private ExpandableNotificationRow mTrackedHeadsUpRow; 83 private float mAppearFraction; 84 private boolean mIsShadeOpening; 85 private float mOverExpansion; 86 87 /** Distance of top of notifications panel from top of screen. */ 88 private float mStackY = 0; 89 90 /** Height of notifications panel. */ 91 private float mStackHeight = 0; 92 93 /** Fraction of shade expansion. */ 94 private float mExpansionFraction; 95 96 /** Height of the notifications panel without top padding when expansion completes. */ 97 private float mStackEndHeight; 98 private float mTransitionToFullShadeAmount; 99 100 /** 101 * @return Height of the notifications panel without top padding when expansion completes. 102 */ getStackEndHeight()103 public float getStackEndHeight() { 104 return mStackEndHeight; 105 } 106 107 /** 108 * @param stackEndHeight Height of the notifications panel without top padding 109 * when expansion completes. 110 */ setStackEndHeight(float stackEndHeight)111 public void setStackEndHeight(float stackEndHeight) { 112 mStackEndHeight = stackEndHeight; 113 } 114 115 /** 116 * @param stackY Distance of top of notifications panel from top of screen. 117 */ setStackY(float stackY)118 public void setStackY(float stackY) { 119 mStackY = stackY; 120 } 121 122 /** 123 * @return Distance of top of notifications panel from top of screen. 124 */ getStackY()125 public float getStackY() { 126 return mStackY; 127 } 128 129 /** 130 * @param expansionFraction Fraction of shade expansion. 131 */ setExpansionFraction(float expansionFraction)132 public void setExpansionFraction(float expansionFraction) { 133 mExpansionFraction = expansionFraction; 134 } 135 136 /** 137 * @return Fraction of shade expansion. 138 */ getExpansionFraction()139 public float getExpansionFraction() { 140 return mExpansionFraction; 141 } 142 143 /** 144 * @param stackHeight Height of notifications panel. 145 */ setStackHeight(float stackHeight)146 public void setStackHeight(float stackHeight) { 147 mStackHeight = stackHeight; 148 } 149 150 /** 151 * @return Height of notifications panel. 152 */ getStackHeight()153 public float getStackHeight() { 154 return mStackHeight; 155 } 156 157 /** Tracks the state from AlertingNotificationManager#hasNotifications() */ 158 private boolean mHasAlertEntries; 159 160 @Inject AmbientState( Context context, @NonNull SectionProvider sectionProvider, @NonNull BypassController bypassController)161 public AmbientState( 162 Context context, 163 @NonNull SectionProvider sectionProvider, 164 @NonNull BypassController bypassController) { 165 mSectionProvider = sectionProvider; 166 mBypassController = bypassController; 167 reload(context); 168 } 169 170 /** 171 * Reload the dimens e.g. if the density changed. 172 */ reload(Context context)173 public void reload(Context context) { 174 mZDistanceBetweenElements = getZDistanceBetweenElements(context); 175 mBaseZHeight = getBaseHeight(mZDistanceBetweenElements); 176 } 177 setIsShadeOpening(boolean isOpening)178 public void setIsShadeOpening(boolean isOpening) { 179 mIsShadeOpening = isOpening; 180 } 181 isShadeOpening()182 public boolean isShadeOpening() { 183 return mIsShadeOpening; 184 } 185 setOverExpansion(float overExpansion)186 void setOverExpansion(float overExpansion) { 187 mOverExpansion = overExpansion; 188 } 189 getOverExpansion()190 float getOverExpansion() { 191 return mOverExpansion; 192 } 193 getZDistanceBetweenElements(Context context)194 private static int getZDistanceBetweenElements(Context context) { 195 return Math.max(1, context.getResources() 196 .getDimensionPixelSize(R.dimen.z_distance_between_notifications)); 197 } 198 getBaseHeight(int zdistanceBetweenElements)199 private static int getBaseHeight(int zdistanceBetweenElements) { 200 return NOTIFICATIONS_HAVE_SHADOWS ? 4 * zdistanceBetweenElements : 0; 201 } 202 203 /** 204 * @return the launch height for notifications that are launched 205 */ getNotificationLaunchHeight(Context context)206 public static int getNotificationLaunchHeight(Context context) { 207 int zDistance = getZDistanceBetweenElements(context); 208 return NOTIFICATIONS_HAVE_SHADOWS ? 2 * getBaseHeight(zDistance) : 4 * zDistance; 209 } 210 211 /** 212 * @return the basic Z height on which notifications remain. 213 */ getBaseZHeight()214 public int getBaseZHeight() { 215 return mBaseZHeight; 216 } 217 218 /** 219 * @return the distance in Z between two overlaying notifications. 220 */ getZDistanceBetweenElements()221 public int getZDistanceBetweenElements() { 222 return mZDistanceBetweenElements; 223 } 224 getScrollY()225 public int getScrollY() { 226 return mScrollY; 227 } 228 229 /** 230 * Set the new Scroll Y position. 231 */ setScrollY(int scrollY)232 public void setScrollY(int scrollY) { 233 // Because we're dealing with an overscroller, scrollY could sometimes become smaller than 234 // 0. However this is only for internal purposes and the scroll position when read 235 // should never be smaller than 0, otherwise it can lead to flickers. 236 this.mScrollY = Math.max(scrollY, 0); 237 } 238 239 /** 240 * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are 241 * translucent and everything is scaled back a bit. 242 */ setDimmed(boolean dimmed)243 public void setDimmed(boolean dimmed) { 244 mDimmed = dimmed; 245 } 246 247 /** While dozing, we draw as little as possible, assuming a black background */ setDozing(boolean dozing)248 public void setDozing(boolean dozing) { 249 mDozing = dozing; 250 } 251 252 /** Hide ratio of the status bar **/ setHideAmount(float hidemount)253 public void setHideAmount(float hidemount) { 254 if (hidemount == 1.0f && mHideAmount != hidemount) { 255 // Whenever we are fully hidden, let's reset the pulseHeight again 256 setPulseHeight(MAX_PULSE_HEIGHT); 257 } 258 mHideAmount = hidemount; 259 } 260 261 /** Returns the hide ratio of the status bar */ getHideAmount()262 public float getHideAmount() { 263 return mHideAmount; 264 } 265 setHideSensitive(boolean hideSensitive)266 public void setHideSensitive(boolean hideSensitive) { 267 mHideSensitive = hideSensitive; 268 } 269 270 /** 271 * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap 272 * interaction. This child is then scaled normally and its background is fully opaque. 273 */ setActivatedChild(ActivatableNotificationView activatedChild)274 public void setActivatedChild(ActivatableNotificationView activatedChild) { 275 mActivatedChild = activatedChild; 276 } 277 isDimmed()278 public boolean isDimmed() { 279 // While we are expanding from pulse, we want the notifications not to be dimmed, otherwise 280 // you'd see the difference to the pulsing notification 281 return mDimmed && !(isPulseExpanding() && mDozeAmount == 1.0f); 282 } 283 isDozing()284 public boolean isDozing() { 285 return mDozing; 286 } 287 isHideSensitive()288 public boolean isHideSensitive() { 289 return mHideSensitive; 290 } 291 getActivatedChild()292 public ActivatableNotificationView getActivatedChild() { 293 return mActivatedChild; 294 } 295 setOverScrollAmount(float amount, boolean onTop)296 public void setOverScrollAmount(float amount, boolean onTop) { 297 if (onTop) { 298 mOverScrollTopAmount = amount; 299 } else { 300 mOverScrollBottomAmount = amount; 301 } 302 } 303 304 /** 305 * Is bypass currently enabled? 306 */ isBypassEnabled()307 public boolean isBypassEnabled() { 308 return mBypassController.isBypassEnabled(); 309 } 310 getOverScrollAmount(boolean top)311 public float getOverScrollAmount(boolean top) { 312 return top ? mOverScrollTopAmount : mOverScrollBottomAmount; 313 } 314 getSectionProvider()315 public SectionProvider getSectionProvider() { 316 return mSectionProvider; 317 } 318 getStackTranslation()319 public float getStackTranslation() { 320 return mStackTranslation; 321 } 322 setStackTranslation(float stackTranslation)323 public void setStackTranslation(float stackTranslation) { 324 mStackTranslation = stackTranslation; 325 } 326 setLayoutHeight(int layoutHeight)327 public void setLayoutHeight(int layoutHeight) { 328 mLayoutHeight = layoutHeight; 329 } 330 getTopPadding()331 public float getTopPadding() { 332 return mTopPadding; 333 } 334 setTopPadding(int topPadding)335 public void setTopPadding(int topPadding) { 336 mTopPadding = topPadding; 337 } 338 getInnerHeight()339 public int getInnerHeight() { 340 return getInnerHeight(false /* ignorePulseHeight */); 341 } 342 343 /** 344 * @param ignorePulseHeight ignore the pulse height for this request 345 * @return the inner height of the algorithm. 346 */ getInnerHeight(boolean ignorePulseHeight)347 public int getInnerHeight(boolean ignorePulseHeight) { 348 if (mDozeAmount == 1.0f && !isPulseExpanding()) { 349 return mShelf.getHeight(); 350 } 351 int height = (int) Math.max(mLayoutMinHeight, 352 Math.min(mLayoutHeight, mContentHeight) - mTopPadding); 353 if (ignorePulseHeight) { 354 return height; 355 } 356 float pulseHeight = Math.min(mPulseHeight, (float) height); 357 return (int) MathUtils.lerp(height, pulseHeight, mDozeAmount); 358 } 359 isPulseExpanding()360 public boolean isPulseExpanding() { 361 return mPulseHeight != MAX_PULSE_HEIGHT && mDozeAmount != 0.0f && mHideAmount != 1.0f; 362 } 363 isShadeExpanded()364 public boolean isShadeExpanded() { 365 return mShadeExpanded; 366 } 367 setShadeExpanded(boolean shadeExpanded)368 public void setShadeExpanded(boolean shadeExpanded) { 369 mShadeExpanded = shadeExpanded; 370 } 371 setMaxHeadsUpTranslation(float maxHeadsUpTranslation)372 public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) { 373 mMaxHeadsUpTranslation = maxHeadsUpTranslation; 374 } 375 getMaxHeadsUpTranslation()376 public float getMaxHeadsUpTranslation() { 377 return mMaxHeadsUpTranslation; 378 } 379 setDismissAllInProgress(boolean dismissAllInProgress)380 public void setDismissAllInProgress(boolean dismissAllInProgress) { 381 mDismissAllInProgress = dismissAllInProgress; 382 } 383 isDismissAllInProgress()384 public boolean isDismissAllInProgress() { 385 return mDismissAllInProgress; 386 } 387 setLayoutMinHeight(int layoutMinHeight)388 public void setLayoutMinHeight(int layoutMinHeight) { 389 mLayoutMinHeight = layoutMinHeight; 390 } 391 setShelf(NotificationShelf shelf)392 public void setShelf(NotificationShelf shelf) { 393 mShelf = shelf; 394 } 395 396 @Nullable getShelf()397 public NotificationShelf getShelf() { 398 return mShelf; 399 } 400 setContentHeight(int contentHeight)401 public void setContentHeight(int contentHeight) { 402 mContentHeight = contentHeight; 403 } 404 getContentHeight()405 public float getContentHeight() { 406 return mContentHeight; 407 } 408 409 /** 410 * Sets the last visible view of the host layout, that has a background, i.e the very last 411 * view in the shade, without the clear all button. 412 */ setLastVisibleBackgroundChild( ExpandableView lastVisibleBackgroundChild)413 public void setLastVisibleBackgroundChild( 414 ExpandableView lastVisibleBackgroundChild) { 415 mLastVisibleBackgroundChild = lastVisibleBackgroundChild; 416 } 417 getLastVisibleBackgroundChild()418 public ExpandableView getLastVisibleBackgroundChild() { 419 return mLastVisibleBackgroundChild; 420 } 421 setCurrentScrollVelocity(float currentScrollVelocity)422 public void setCurrentScrollVelocity(float currentScrollVelocity) { 423 mCurrentScrollVelocity = currentScrollVelocity; 424 } 425 getCurrentScrollVelocity()426 public float getCurrentScrollVelocity() { 427 return mCurrentScrollVelocity; 428 } 429 isOnKeyguard()430 public boolean isOnKeyguard() { 431 return mStatusBarState == StatusBarState.KEYGUARD; 432 } 433 setStatusBarState(int statusBarState)434 public void setStatusBarState(int statusBarState) { 435 mStatusBarState = statusBarState; 436 } 437 setExpandingVelocity(float expandingVelocity)438 public void setExpandingVelocity(float expandingVelocity) { 439 mExpandingVelocity = expandingVelocity; 440 } 441 setExpansionChanging(boolean expansionChanging)442 public void setExpansionChanging(boolean expansionChanging) { 443 mExpansionChanging = expansionChanging; 444 } 445 isExpansionChanging()446 public boolean isExpansionChanging() { 447 return mExpansionChanging; 448 } 449 getExpandingVelocity()450 public float getExpandingVelocity() { 451 return mExpandingVelocity; 452 } 453 setPanelTracking(boolean panelTracking)454 public void setPanelTracking(boolean panelTracking) { 455 mPanelTracking = panelTracking; 456 } 457 hasPulsingNotifications()458 public boolean hasPulsingNotifications() { 459 return mPulsing && mHasAlertEntries; 460 } 461 setPulsing(boolean hasPulsing)462 public void setPulsing(boolean hasPulsing) { 463 mPulsing = hasPulsing; 464 } 465 466 /** 467 * @return if we're pulsing in general 468 */ isPulsing()469 public boolean isPulsing() { 470 return mPulsing; 471 } 472 isPulsing(NotificationEntry entry)473 public boolean isPulsing(NotificationEntry entry) { 474 return mPulsing && entry.isAlerting(); 475 } 476 isPanelTracking()477 public boolean isPanelTracking() { 478 return mPanelTracking; 479 } 480 isPanelFullWidth()481 public boolean isPanelFullWidth() { 482 return mPanelFullWidth; 483 } 484 setPanelFullWidth(boolean panelFullWidth)485 public void setPanelFullWidth(boolean panelFullWidth) { 486 mPanelFullWidth = panelFullWidth; 487 } 488 setUnlockHintRunning(boolean unlockHintRunning)489 public void setUnlockHintRunning(boolean unlockHintRunning) { 490 mUnlockHintRunning = unlockHintRunning; 491 } 492 isUnlockHintRunning()493 public boolean isUnlockHintRunning() { 494 return mUnlockHintRunning; 495 } 496 isQsCustomizerShowing()497 public boolean isQsCustomizerShowing() { 498 return mQsCustomizerShowing; 499 } 500 setQsCustomizerShowing(boolean qsCustomizerShowing)501 public void setQsCustomizerShowing(boolean qsCustomizerShowing) { 502 mQsCustomizerShowing = qsCustomizerShowing; 503 } 504 setIntrinsicPadding(int intrinsicPadding)505 public void setIntrinsicPadding(int intrinsicPadding) { 506 mIntrinsicPadding = intrinsicPadding; 507 } 508 getIntrinsicPadding()509 public int getIntrinsicPadding() { 510 return mIntrinsicPadding; 511 } 512 513 /** 514 * @return whether a view is dozing and not pulsing right now 515 */ isDozingAndNotPulsing(ExpandableView view)516 public boolean isDozingAndNotPulsing(ExpandableView view) { 517 if (view instanceof ExpandableNotificationRow) { 518 return isDozingAndNotPulsing((ExpandableNotificationRow) view); 519 } 520 return false; 521 } 522 523 /** 524 * @return whether a row is dozing and not pulsing right now 525 */ isDozingAndNotPulsing(ExpandableNotificationRow row)526 public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) { 527 return isDozing() && !isPulsing(row.getEntry()); 528 } 529 530 /** 531 * @return {@code true } when shade is completely hidden: in AOD, ambient display or when 532 * bypassing. 533 */ isFullyHidden()534 public boolean isFullyHidden() { 535 return mHideAmount == 1; 536 } 537 isHiddenAtAll()538 public boolean isHiddenAtAll() { 539 return mHideAmount != 0; 540 } 541 setAppearing(boolean appearing)542 public void setAppearing(boolean appearing) { 543 mAppearing = appearing; 544 } 545 isAppearing()546 public boolean isAppearing() { 547 return mAppearing; 548 } 549 setPulseHeight(float height)550 public void setPulseHeight(float height) { 551 if (height != mPulseHeight) { 552 mPulseHeight = height; 553 if (mOnPulseHeightChangedListener != null) { 554 mOnPulseHeightChangedListener.run(); 555 } 556 } 557 } 558 getPulseHeight()559 public float getPulseHeight() { 560 if (mPulseHeight == MAX_PULSE_HEIGHT) { 561 // If we're not pulse expanding, the height should be 0 562 return 0; 563 } 564 return mPulseHeight; 565 } 566 setDozeAmount(float dozeAmount)567 public void setDozeAmount(float dozeAmount) { 568 if (dozeAmount != mDozeAmount) { 569 mDozeAmount = dozeAmount; 570 if (dozeAmount == 0.0f || dozeAmount == 1.0f) { 571 // We woke all the way up, let's reset the pulse height 572 setPulseHeight(MAX_PULSE_HEIGHT); 573 } 574 } 575 } 576 577 /** 578 * Is the device fully awake, which is different from not tark at all when there are pulsing 579 * notifications. 580 */ isFullyAwake()581 public boolean isFullyAwake() { 582 return mDozeAmount == 0.0f; 583 } 584 setOnPulseHeightChangedListener(Runnable onPulseHeightChangedListener)585 public void setOnPulseHeightChangedListener(Runnable onPulseHeightChangedListener) { 586 mOnPulseHeightChangedListener = onPulseHeightChangedListener; 587 } 588 getOnPulseHeightChangedListener()589 public Runnable getOnPulseHeightChangedListener() { 590 return mOnPulseHeightChangedListener; 591 } 592 setTrackedHeadsUpRow(ExpandableNotificationRow row)593 public void setTrackedHeadsUpRow(ExpandableNotificationRow row) { 594 mTrackedHeadsUpRow = row; 595 } 596 597 /** 598 * Set the amount of pixels we have currently dragged down if we're transitioning to the full 599 * shade. 0.0f means we're not transitioning yet. 600 */ setTransitionToFullShadeAmount(float transitionToFullShadeAmount)601 public void setTransitionToFullShadeAmount(float transitionToFullShadeAmount) { 602 mTransitionToFullShadeAmount = transitionToFullShadeAmount; 603 } 604 605 /** 606 * get 607 */ getTransitionToFullShadeAmount()608 public float getTransitionToFullShadeAmount() { 609 return mTransitionToFullShadeAmount; 610 } 611 612 /** 613 * Returns the currently tracked heads up row, if there is one and it is currently above the 614 * shelf (still appearing). 615 */ getTrackedHeadsUpRow()616 public ExpandableNotificationRow getTrackedHeadsUpRow() { 617 if (mTrackedHeadsUpRow == null || !mTrackedHeadsUpRow.isAboveShelf()) { 618 return null; 619 } 620 return mTrackedHeadsUpRow; 621 } 622 setAppearFraction(float appearFraction)623 public void setAppearFraction(float appearFraction) { 624 mAppearFraction = appearFraction; 625 } 626 getAppearFraction()627 public float getAppearFraction() { 628 return mAppearFraction; 629 } 630 setHasAlertEntries(boolean hasAlertEntries)631 public void setHasAlertEntries(boolean hasAlertEntries) { 632 mHasAlertEntries = hasAlertEntries; 633 } 634 } 635