1 /*
2  * Copyright (C) 2016 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;
18 
19 import android.support.v4.util.ArraySet;
20 import android.view.View;
21 
22 import com.android.systemui.statusbar.ExpandableNotificationRow;
23 import com.android.systemui.statusbar.NotificationData;
24 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
25 
26 import java.util.ArrayList;
27 
28 /**
29  * A manager that ensures that notifications are visually stable. It will suppress reorderings
30  * and reorder at the right time when they are out of view.
31  */
32 public class VisualStabilityManager implements OnHeadsUpChangedListener {
33 
34     private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
35 
36     private boolean mPanelExpanded;
37     private boolean mScreenOn;
38     private boolean mReorderingAllowed;
39     private VisibilityLocationProvider mVisibilityLocationProvider;
40     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
41     private ArraySet<View> mLowPriorityReorderingViews = new ArraySet<>();
42     private ArraySet<View> mAddedChildren = new ArraySet<>();
43     private boolean mPulsing;
44 
45     /**
46      * Add a callback to invoke when reordering is allowed again.
47      * @param callback
48      */
addReorderingAllowedCallback(Callback callback)49     public void addReorderingAllowedCallback(Callback callback) {
50         if (mCallbacks.contains(callback)) {
51             return;
52         }
53         mCallbacks.add(callback);
54     }
55 
56     /**
57      * Set the panel to be expanded.
58      */
setPanelExpanded(boolean expanded)59     public void setPanelExpanded(boolean expanded) {
60         mPanelExpanded = expanded;
61         updateReorderingAllowed();
62     }
63 
64     /**
65      * @param screenOn whether the screen is on
66      */
setScreenOn(boolean screenOn)67     public void setScreenOn(boolean screenOn) {
68         mScreenOn = screenOn;
69         updateReorderingAllowed();
70     }
71 
72     /**
73      * @param pulsing whether we are currently pulsing for ambient display.
74      */
setPulsing(boolean pulsing)75     public void setPulsing(boolean pulsing) {
76         if (mPulsing == pulsing) {
77             return;
78         }
79         mPulsing = pulsing;
80         updateReorderingAllowed();
81     }
82 
updateReorderingAllowed()83     private void updateReorderingAllowed() {
84         boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
85         boolean changed = reorderingAllowed && !mReorderingAllowed;
86         mReorderingAllowed = reorderingAllowed;
87         if (changed) {
88             notifyCallbacks();
89         }
90     }
91 
notifyCallbacks()92     private void notifyCallbacks() {
93         for (int i = 0; i < mCallbacks.size(); i++) {
94             Callback callback = mCallbacks.get(i);
95             callback.onReorderingAllowed();
96         }
97         mCallbacks.clear();
98     }
99 
100     /**
101      * @return whether reordering is currently allowed in general.
102      */
isReorderingAllowed()103     public boolean isReorderingAllowed() {
104         return mReorderingAllowed;
105     }
106 
107     /**
108      * @return whether a specific notification is allowed to reorder. Certain notifications are
109      * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added
110      * notifications or heads-up notifications that are out of view.
111      */
canReorderNotification(ExpandableNotificationRow row)112     public boolean canReorderNotification(ExpandableNotificationRow row) {
113         if (mReorderingAllowed) {
114             return true;
115         }
116         if (mAddedChildren.contains(row)) {
117             return true;
118         }
119         if (mLowPriorityReorderingViews.contains(row)) {
120             return true;
121         }
122         if (mAllowedReorderViews.contains(row)
123                 && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
124             return true;
125         }
126         return false;
127     }
128 
setVisibilityLocationProvider( VisibilityLocationProvider visibilityLocationProvider)129     public void setVisibilityLocationProvider(
130             VisibilityLocationProvider visibilityLocationProvider) {
131         mVisibilityLocationProvider = visibilityLocationProvider;
132     }
133 
onReorderingFinished()134     public void onReorderingFinished() {
135         mAllowedReorderViews.clear();
136         mAddedChildren.clear();
137         mLowPriorityReorderingViews.clear();
138     }
139 
140     @Override
onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp)141     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
142         if (isHeadsUp) {
143             // Heads up notifications should in general be allowed to reorder if they are out of
144             // view and stay at the current location if they aren't.
145             mAllowedReorderViews.add(entry.row);
146         }
147     }
148 
onLowPriorityUpdated(NotificationData.Entry entry)149     public void onLowPriorityUpdated(NotificationData.Entry entry) {
150         mLowPriorityReorderingViews.add(entry.row);
151     }
152 
153     /**
154      * Notify the visual stability manager that a new view was added and should be allowed to
155      * reorder next time.
156      */
notifyViewAddition(View view)157     public void notifyViewAddition(View view) {
158         mAddedChildren.add(view);
159     }
160 
161     public interface Callback {
162         /**
163          * Called when reordering is allowed again.
164          */
onReorderingAllowed()165         void onReorderingAllowed();
166     }
167 
168 }
169