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