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