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 import android.view.View; 24 25 import com.android.systemui.R; 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.SectionProvider; 33 import com.android.systemui.statusbar.policy.HeadsUpManager; 34 35 import java.util.ArrayList; 36 37 /** 38 * A global state to track all input states for the algorithm. 39 */ 40 public class AmbientState { 41 42 private static final float MAX_PULSE_HEIGHT = 100000f; 43 44 private final SectionProvider mSectionProvider; 45 private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>(); 46 private int mScrollY; 47 private int mAnchorViewIndex; 48 private int mAnchorViewY; 49 private boolean mDimmed; 50 private ActivatableNotificationView mActivatedChild; 51 private float mOverScrollTopAmount; 52 private float mOverScrollBottomAmount; 53 private int mSpeedBumpIndex = -1; 54 private boolean mDozing; 55 private boolean mHideSensitive; 56 private float mStackTranslation; 57 private int mLayoutHeight; 58 private int mTopPadding; 59 private boolean mShadeExpanded; 60 private float mMaxHeadsUpTranslation; 61 private boolean mDismissAllInProgress; 62 private int mLayoutMinHeight; 63 private NotificationShelf mShelf; 64 private int mZDistanceBetweenElements; 65 private int mBaseZHeight; 66 private int mMaxLayoutHeight; 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 boolean mQsCustomizerShowing; 77 private int mIntrinsicPadding; 78 private int mExpandAnimationTopChange; 79 private ExpandableNotificationRow mExpandingNotification; 80 private float mHideAmount; 81 private boolean mAppearing; 82 private float mPulseHeight = MAX_PULSE_HEIGHT; 83 private float mDozeAmount = 0.0f; 84 private HeadsUpManager mHeadUpManager; 85 private Runnable mOnPulseHeightChangedListener; 86 private ExpandableNotificationRow mTrackedHeadsUpRow; 87 private float mAppearFraction; 88 AmbientState( Context context, @NonNull SectionProvider sectionProvider, HeadsUpManager headsUpManager)89 public AmbientState( 90 Context context, 91 @NonNull SectionProvider sectionProvider, 92 HeadsUpManager headsUpManager) { 93 mSectionProvider = sectionProvider; 94 mHeadUpManager = headsUpManager; 95 reload(context); 96 } 97 98 /** 99 * Reload the dimens e.g. if the density changed. 100 */ reload(Context context)101 public void reload(Context context) { 102 mZDistanceBetweenElements = getZDistanceBetweenElements(context); 103 mBaseZHeight = getBaseHeight(mZDistanceBetweenElements); 104 } 105 getZDistanceBetweenElements(Context context)106 private static int getZDistanceBetweenElements(Context context) { 107 return Math.max(1, context.getResources() 108 .getDimensionPixelSize(R.dimen.z_distance_between_notifications)); 109 } 110 getBaseHeight(int zdistanceBetweenElements)111 private static int getBaseHeight(int zdistanceBetweenElements) { 112 return 4 * zdistanceBetweenElements; 113 } 114 115 /** 116 * @return the launch height for notifications that are launched 117 */ getNotificationLaunchHeight(Context context)118 public static int getNotificationLaunchHeight(Context context) { 119 int zDistance = getZDistanceBetweenElements(context); 120 return getBaseHeight(zDistance) * 2; 121 } 122 123 /** 124 * @return the basic Z height on which notifications remain. 125 */ getBaseZHeight()126 public int getBaseZHeight() { 127 return mBaseZHeight; 128 } 129 130 /** 131 * @return the distance in Z between two overlaying notifications. 132 */ getZDistanceBetweenElements()133 public int getZDistanceBetweenElements() { 134 return mZDistanceBetweenElements; 135 } 136 getScrollY()137 public int getScrollY() { 138 return mScrollY; 139 } 140 setScrollY(int scrollY)141 public void setScrollY(int scrollY) { 142 this.mScrollY = scrollY; 143 } 144 145 /** 146 * Index of the child view whose Y position on screen is returned by {@link #getAnchorViewY()}. 147 * Other views are laid out outwards from this view in both directions. 148 */ getAnchorViewIndex()149 public int getAnchorViewIndex() { 150 return mAnchorViewIndex; 151 } 152 setAnchorViewIndex(int anchorViewIndex)153 public void setAnchorViewIndex(int anchorViewIndex) { 154 mAnchorViewIndex = anchorViewIndex; 155 } 156 157 /** Current Y position of the view at {@link #getAnchorViewIndex()}. */ getAnchorViewY()158 public int getAnchorViewY() { 159 return mAnchorViewY; 160 } 161 setAnchorViewY(int anchorViewY)162 public void setAnchorViewY(int anchorViewY) { 163 mAnchorViewY = anchorViewY; 164 } 165 166 /** Call when dragging begins. */ onBeginDrag(ExpandableView view)167 public void onBeginDrag(ExpandableView view) { 168 mDraggedViews.add(view); 169 } 170 onDragFinished(View view)171 public void onDragFinished(View view) { 172 mDraggedViews.remove(view); 173 } 174 getDraggedViews()175 public ArrayList<ExpandableView> getDraggedViews() { 176 return mDraggedViews; 177 } 178 179 /** 180 * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are 181 * translucent and everything is scaled back a bit. 182 */ setDimmed(boolean dimmed)183 public void setDimmed(boolean dimmed) { 184 mDimmed = dimmed; 185 } 186 187 /** While dozing, we draw as little as possible, assuming a black background */ setDozing(boolean dozing)188 public void setDozing(boolean dozing) { 189 mDozing = dozing; 190 } 191 192 /** Hide ratio of the status bar **/ setHideAmount(float hidemount)193 public void setHideAmount(float hidemount) { 194 if (hidemount == 1.0f && mHideAmount != hidemount) { 195 // Whenever we are fully hidden, let's reset the pulseHeight again 196 setPulseHeight(MAX_PULSE_HEIGHT); 197 } 198 mHideAmount = hidemount; 199 } 200 201 /** Returns the hide ratio of the status bar */ getHideAmount()202 public float getHideAmount() { 203 return mHideAmount; 204 } 205 setHideSensitive(boolean hideSensitive)206 public void setHideSensitive(boolean hideSensitive) { 207 mHideSensitive = hideSensitive; 208 } 209 210 /** 211 * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap 212 * interaction. This child is then scaled normally and its background is fully opaque. 213 */ setActivatedChild(ActivatableNotificationView activatedChild)214 public void setActivatedChild(ActivatableNotificationView activatedChild) { 215 mActivatedChild = activatedChild; 216 } 217 isDimmed()218 public boolean isDimmed() { 219 // While we are expanding from pulse, we want the notifications not to be dimmed, otherwise 220 // you'd see the difference to the pulsing notification 221 return mDimmed && !(isPulseExpanding() && mDozeAmount == 1.0f); 222 } 223 isDozing()224 public boolean isDozing() { 225 return mDozing; 226 } 227 isHideSensitive()228 public boolean isHideSensitive() { 229 return mHideSensitive; 230 } 231 getActivatedChild()232 public ActivatableNotificationView getActivatedChild() { 233 return mActivatedChild; 234 } 235 setOverScrollAmount(float amount, boolean onTop)236 public void setOverScrollAmount(float amount, boolean onTop) { 237 if (onTop) { 238 mOverScrollTopAmount = amount; 239 } else { 240 mOverScrollBottomAmount = amount; 241 } 242 } 243 getOverScrollAmount(boolean top)244 public float getOverScrollAmount(boolean top) { 245 return top ? mOverScrollTopAmount : mOverScrollBottomAmount; 246 } 247 getSpeedBumpIndex()248 public int getSpeedBumpIndex() { 249 return mSpeedBumpIndex; 250 } 251 setSpeedBumpIndex(int shelfIndex)252 public void setSpeedBumpIndex(int shelfIndex) { 253 mSpeedBumpIndex = shelfIndex; 254 } 255 getSectionProvider()256 public SectionProvider getSectionProvider() { 257 return mSectionProvider; 258 } 259 getStackTranslation()260 public float getStackTranslation() { 261 return mStackTranslation; 262 } 263 setStackTranslation(float stackTranslation)264 public void setStackTranslation(float stackTranslation) { 265 mStackTranslation = stackTranslation; 266 } 267 setLayoutHeight(int layoutHeight)268 public void setLayoutHeight(int layoutHeight) { 269 mLayoutHeight = layoutHeight; 270 } 271 getTopPadding()272 public float getTopPadding() { 273 return mTopPadding; 274 } 275 setTopPadding(int topPadding)276 public void setTopPadding(int topPadding) { 277 mTopPadding = topPadding; 278 } 279 getInnerHeight()280 public int getInnerHeight() { 281 return getInnerHeight(false /* ignorePulseHeight */); 282 } 283 284 /** 285 * @param ignorePulseHeight ignore the pulse height for this request 286 * @return the inner height of the algorithm. 287 */ getInnerHeight(boolean ignorePulseHeight)288 public int getInnerHeight(boolean ignorePulseHeight) { 289 if (mDozeAmount == 1.0f && !isPulseExpanding()) { 290 return mShelf.getHeight(); 291 } 292 int height = Math.max(mLayoutMinHeight, 293 Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding); 294 if (ignorePulseHeight) { 295 return height; 296 } 297 float pulseHeight = Math.min(mPulseHeight, (float) height); 298 return (int) MathUtils.lerp(height, pulseHeight, mDozeAmount); 299 } 300 isPulseExpanding()301 public boolean isPulseExpanding() { 302 return mPulseHeight != MAX_PULSE_HEIGHT && mDozeAmount != 0.0f && mHideAmount != 1.0f; 303 } 304 isShadeExpanded()305 public boolean isShadeExpanded() { 306 return mShadeExpanded; 307 } 308 setShadeExpanded(boolean shadeExpanded)309 public void setShadeExpanded(boolean shadeExpanded) { 310 mShadeExpanded = shadeExpanded; 311 } 312 setMaxHeadsUpTranslation(float maxHeadsUpTranslation)313 public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) { 314 mMaxHeadsUpTranslation = maxHeadsUpTranslation; 315 } 316 getMaxHeadsUpTranslation()317 public float getMaxHeadsUpTranslation() { 318 return mMaxHeadsUpTranslation; 319 } 320 setDismissAllInProgress(boolean dismissAllInProgress)321 public void setDismissAllInProgress(boolean dismissAllInProgress) { 322 mDismissAllInProgress = dismissAllInProgress; 323 } 324 isDismissAllInProgress()325 public boolean isDismissAllInProgress() { 326 return mDismissAllInProgress; 327 } 328 setLayoutMinHeight(int layoutMinHeight)329 public void setLayoutMinHeight(int layoutMinHeight) { 330 mLayoutMinHeight = layoutMinHeight; 331 } 332 setShelf(NotificationShelf shelf)333 public void setShelf(NotificationShelf shelf) { 334 mShelf = shelf; 335 } 336 337 @Nullable getShelf()338 public NotificationShelf getShelf() { 339 return mShelf; 340 } 341 setLayoutMaxHeight(int maxLayoutHeight)342 public void setLayoutMaxHeight(int maxLayoutHeight) { 343 mMaxLayoutHeight = maxLayoutHeight; 344 } 345 346 /** 347 * Sets the last visible view of the host layout, that has a background, i.e the very last 348 * view in the shade, without the clear all button. 349 */ setLastVisibleBackgroundChild( ExpandableView lastVisibleBackgroundChild)350 public void setLastVisibleBackgroundChild( 351 ExpandableView lastVisibleBackgroundChild) { 352 mLastVisibleBackgroundChild = lastVisibleBackgroundChild; 353 } 354 getLastVisibleBackgroundChild()355 public ExpandableView getLastVisibleBackgroundChild() { 356 return mLastVisibleBackgroundChild; 357 } 358 setCurrentScrollVelocity(float currentScrollVelocity)359 public void setCurrentScrollVelocity(float currentScrollVelocity) { 360 mCurrentScrollVelocity = currentScrollVelocity; 361 } 362 getCurrentScrollVelocity()363 public float getCurrentScrollVelocity() { 364 return mCurrentScrollVelocity; 365 } 366 isOnKeyguard()367 public boolean isOnKeyguard() { 368 return mStatusBarState == StatusBarState.KEYGUARD; 369 } 370 setStatusBarState(int statusBarState)371 public void setStatusBarState(int statusBarState) { 372 mStatusBarState = statusBarState; 373 } 374 setExpandingVelocity(float expandingVelocity)375 public void setExpandingVelocity(float expandingVelocity) { 376 mExpandingVelocity = expandingVelocity; 377 } 378 setExpansionChanging(boolean expansionChanging)379 public void setExpansionChanging(boolean expansionChanging) { 380 mExpansionChanging = expansionChanging; 381 } 382 isExpansionChanging()383 public boolean isExpansionChanging() { 384 return mExpansionChanging; 385 } 386 getExpandingVelocity()387 public float getExpandingVelocity() { 388 return mExpandingVelocity; 389 } 390 setPanelTracking(boolean panelTracking)391 public void setPanelTracking(boolean panelTracking) { 392 mPanelTracking = panelTracking; 393 } 394 hasPulsingNotifications()395 public boolean hasPulsingNotifications() { 396 return mPulsing && mHeadUpManager != null && mHeadUpManager.hasNotifications(); 397 } 398 setPulsing(boolean hasPulsing)399 public void setPulsing(boolean hasPulsing) { 400 mPulsing = hasPulsing; 401 } 402 403 /** 404 * @return if we're pulsing in general 405 */ isPulsing()406 public boolean isPulsing() { 407 return mPulsing; 408 } 409 isPulsing(NotificationEntry entry)410 public boolean isPulsing(NotificationEntry entry) { 411 if (!mPulsing || mHeadUpManager == null) { 412 return false; 413 } 414 return mHeadUpManager.isAlerting(entry.getKey()); 415 } 416 isPanelTracking()417 public boolean isPanelTracking() { 418 return mPanelTracking; 419 } 420 isPanelFullWidth()421 public boolean isPanelFullWidth() { 422 return mPanelFullWidth; 423 } 424 setPanelFullWidth(boolean panelFullWidth)425 public void setPanelFullWidth(boolean panelFullWidth) { 426 mPanelFullWidth = panelFullWidth; 427 } 428 setUnlockHintRunning(boolean unlockHintRunning)429 public void setUnlockHintRunning(boolean unlockHintRunning) { 430 mUnlockHintRunning = unlockHintRunning; 431 } 432 isUnlockHintRunning()433 public boolean isUnlockHintRunning() { 434 return mUnlockHintRunning; 435 } 436 isQsCustomizerShowing()437 public boolean isQsCustomizerShowing() { 438 return mQsCustomizerShowing; 439 } 440 setQsCustomizerShowing(boolean qsCustomizerShowing)441 public void setQsCustomizerShowing(boolean qsCustomizerShowing) { 442 mQsCustomizerShowing = qsCustomizerShowing; 443 } 444 setIntrinsicPadding(int intrinsicPadding)445 public void setIntrinsicPadding(int intrinsicPadding) { 446 mIntrinsicPadding = intrinsicPadding; 447 } 448 getIntrinsicPadding()449 public int getIntrinsicPadding() { 450 return mIntrinsicPadding; 451 } 452 453 /** 454 * @return whether a view is dozing and not pulsing right now 455 */ isDozingAndNotPulsing(ExpandableView view)456 public boolean isDozingAndNotPulsing(ExpandableView view) { 457 if (view instanceof ExpandableNotificationRow) { 458 return isDozingAndNotPulsing((ExpandableNotificationRow) view); 459 } 460 return false; 461 } 462 463 /** 464 * @return whether a row is dozing and not pulsing right now 465 */ isDozingAndNotPulsing(ExpandableNotificationRow row)466 public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) { 467 return isDozing() && !isPulsing(row.getEntry()); 468 } 469 setExpandAnimationTopChange(int expandAnimationTopChange)470 public void setExpandAnimationTopChange(int expandAnimationTopChange) { 471 mExpandAnimationTopChange = expandAnimationTopChange; 472 } 473 setExpandingNotification(ExpandableNotificationRow row)474 public void setExpandingNotification(ExpandableNotificationRow row) { 475 mExpandingNotification = row; 476 } 477 getExpandingNotification()478 public ExpandableNotificationRow getExpandingNotification() { 479 return mExpandingNotification; 480 } 481 getExpandAnimationTopChange()482 public int getExpandAnimationTopChange() { 483 return mExpandAnimationTopChange; 484 } 485 486 /** 487 * @return {@code true } when shade is completely hidden: in AOD, ambient display or when 488 * bypassing. 489 */ isFullyHidden()490 public boolean isFullyHidden() { 491 return mHideAmount == 1; 492 } 493 isHiddenAtAll()494 public boolean isHiddenAtAll() { 495 return mHideAmount != 0; 496 } 497 setAppearing(boolean appearing)498 public void setAppearing(boolean appearing) { 499 mAppearing = appearing; 500 } 501 isAppearing()502 public boolean isAppearing() { 503 return mAppearing; 504 } 505 setPulseHeight(float height)506 public void setPulseHeight(float height) { 507 if (height != mPulseHeight) { 508 mPulseHeight = height; 509 if (mOnPulseHeightChangedListener != null) { 510 mOnPulseHeightChangedListener.run(); 511 } 512 } 513 } 514 getPulseHeight()515 public float getPulseHeight() { 516 if (mPulseHeight == MAX_PULSE_HEIGHT) { 517 // If we're not pulse expanding, the height should be 0 518 return 0; 519 } 520 return mPulseHeight; 521 } 522 setDozeAmount(float dozeAmount)523 public void setDozeAmount(float dozeAmount) { 524 if (dozeAmount != mDozeAmount) { 525 mDozeAmount = dozeAmount; 526 if (dozeAmount == 0.0f || dozeAmount == 1.0f) { 527 // We woke all the way up, let's reset the pulse height 528 setPulseHeight(MAX_PULSE_HEIGHT); 529 } 530 } 531 } 532 533 /** 534 * Is the device fully awake, which is different from not tark at all when there are pulsing 535 * notifications. 536 */ isFullyAwake()537 public boolean isFullyAwake() { 538 return mDozeAmount == 0.0f; 539 } 540 setOnPulseHeightChangedListener(Runnable onPulseHeightChangedListener)541 public void setOnPulseHeightChangedListener(Runnable onPulseHeightChangedListener) { 542 mOnPulseHeightChangedListener = onPulseHeightChangedListener; 543 } 544 getOnPulseHeightChangedListener()545 public Runnable getOnPulseHeightChangedListener() { 546 return mOnPulseHeightChangedListener; 547 } 548 setTrackedHeadsUpRow(ExpandableNotificationRow row)549 public void setTrackedHeadsUpRow(ExpandableNotificationRow row) { 550 mTrackedHeadsUpRow = row; 551 } 552 553 /** 554 * Returns the currently tracked heads up row, if there is one and it is currently above the 555 * shelf (still appearing). 556 */ getTrackedHeadsUpRow()557 public ExpandableNotificationRow getTrackedHeadsUpRow() { 558 if (mTrackedHeadsUpRow == null || !mTrackedHeadsUpRow.isAboveShelf()) { 559 return null; 560 } 561 return mTrackedHeadsUpRow; 562 } 563 setAppearFraction(float appearFraction)564 public void setAppearFraction(float appearFraction) { 565 mAppearFraction = appearFraction; 566 } 567 getAppearFraction()568 public float getAppearFraction() { 569 return mAppearFraction; 570 } 571 } 572