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