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