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