blob: 7ac59954cb576ac00e6beba09e43d71cf8de81fa [file] [log] [blame]
Selim Cineka7d4f822016-12-06 14:34:47 -08001/*
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
17package com.android.systemui.statusbar.notification;
18
Ned Burns9512e0c2019-05-30 19:36:04 -040019import android.os.Handler;
20import android.os.SystemClock;
Selim Cineka7d4f822016-12-06 14:34:47 -080021import android.view.View;
22
Gus Prevasab336792018-11-14 13:52:20 -050023import androidx.collection.ArraySet;
24
Ned Burns9512e0c2019-05-30 19:36:04 -040025import com.android.systemui.Dumpable;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050026import com.android.systemui.dagger.qualifiers.Main;
Gus Prevas5b9098dc2018-12-21 17:07:15 -050027import com.android.systemui.statusbar.NotificationPresenter;
Ned Burnsf81c4c42019-01-07 14:10:43 -050028import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010029import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
Rohan Shah20790b82018-07-02 17:21:04 -070030import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Selim Cineka7d4f822016-12-06 14:34:47 -080031import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
32
Ned Burns9512e0c2019-05-30 19:36:04 -040033import java.io.FileDescriptor;
34import java.io.PrintWriter;
Selim Cineka7d4f822016-12-06 14:34:47 -080035import java.util.ArrayList;
36
37/**
38 * A manager that ensures that notifications are visually stable. It will suppress reorderings
39 * and reorder at the right time when they are out of view.
40 */
Ned Burns9512e0c2019-05-30 19:36:04 -040041public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
42
43 private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
Selim Cineka7d4f822016-12-06 14:34:47 -080044
Selim Cinekba069ae2020-04-01 19:45:16 -070045 private final ArrayList<Callback> mReorderingAllowedCallbacks = new ArrayList<>();
46 private final ArrayList<Callback> mGroupChangesAllowedCallbacks = new ArrayList<>();
Ned Burns9512e0c2019-05-30 19:36:04 -040047 private final Handler mHandler;
Selim Cineka7d4f822016-12-06 14:34:47 -080048
49 private boolean mPanelExpanded;
50 private boolean mScreenOn;
51 private boolean mReorderingAllowed;
Selim Cinekba069ae2020-04-01 19:45:16 -070052 private boolean mGroupChangedAllowed;
Ned Burns9512e0c2019-05-30 19:36:04 -040053 private boolean mIsTemporaryReorderingAllowed;
54 private long mTemporaryReorderingStart;
Selim Cineka7d4f822016-12-06 14:34:47 -080055 private VisibilityLocationProvider mVisibilityLocationProvider;
56 private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
Ned Burnsa13b6f12019-02-11 20:42:58 -050057 private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
Selim Cineka7d4f822016-12-06 14:34:47 -080058 private ArraySet<View> mAddedChildren = new ArraySet<>();
Adrian Roos4b820e42017-01-13 17:40:43 -080059 private boolean mPulsing;
Selim Cineka7d4f822016-12-06 14:34:47 -080060
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010061 /**
62 * Injected constructor. See {@link NotificationsModule}.
63 */
Ned Burns9512e0c2019-05-30 19:36:04 -040064 public VisualStabilityManager(
Beverly95a0802ac2020-02-10 15:27:40 -050065 NotificationEntryManager notificationEntryManager,
66 @Main Handler handler) {
Ned Burns9512e0c2019-05-30 19:36:04 -040067
68 mHandler = handler;
69
Gus Prevas5b9098dc2018-12-21 17:07:15 -050070 notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
71 @Override
Ned Burnsa13b6f12019-02-11 20:42:58 -050072 public void onPreEntryUpdated(NotificationEntry entry) {
Beverlye018f0d2019-11-06 14:20:29 -050073 final boolean ambientStateHasChanged =
Ned Burns60e94592019-09-06 14:47:25 -040074 entry.isAmbient() != entry.getRow().isLowPriority();
Beverlye018f0d2019-11-06 14:20:29 -050075 if (ambientStateHasChanged) {
76 // note: entries are removed in onReorderingFinished
Ned Burnsa13b6f12019-02-11 20:42:58 -050077 mLowPriorityReorderingViews.add(entry);
Gus Prevas5b9098dc2018-12-21 17:07:15 -050078 }
79 }
80 });
81 }
82
83 public void setUpWithPresenter(NotificationPresenter presenter) {
Jason Monk27d01a622018-12-10 15:57:09 -050084 }
85
Selim Cineka7d4f822016-12-06 14:34:47 -080086 /**
87 * Add a callback to invoke when reordering is allowed again.
Selim Cineka7d4f822016-12-06 14:34:47 -080088 */
89 public void addReorderingAllowedCallback(Callback callback) {
Selim Cinekba069ae2020-04-01 19:45:16 -070090 if (mReorderingAllowedCallbacks.contains(callback)) {
Selim Cineka7d4f822016-12-06 14:34:47 -080091 return;
92 }
Selim Cinekba069ae2020-04-01 19:45:16 -070093 mReorderingAllowedCallbacks.add(callback);
94 }
95
96 /**
97 * Add a callback to invoke when group changes are allowed again.
98 */
99 public void addGroupChangesAllowedCallback(Callback callback) {
100 if (mGroupChangesAllowedCallbacks.contains(callback)) {
101 return;
102 }
103 mGroupChangesAllowedCallbacks.add(callback);
Selim Cineka7d4f822016-12-06 14:34:47 -0800104 }
105
106 /**
107 * Set the panel to be expanded.
108 */
109 public void setPanelExpanded(boolean expanded) {
110 mPanelExpanded = expanded;
Selim Cinekba069ae2020-04-01 19:45:16 -0700111 updateAllowedStates();
Selim Cineka7d4f822016-12-06 14:34:47 -0800112 }
113
114 /**
115 * @param screenOn whether the screen is on
116 */
117 public void setScreenOn(boolean screenOn) {
118 mScreenOn = screenOn;
Selim Cinekba069ae2020-04-01 19:45:16 -0700119 updateAllowedStates();
Selim Cineka7d4f822016-12-06 14:34:47 -0800120 }
121
Adrian Roos4b820e42017-01-13 17:40:43 -0800122 /**
123 * @param pulsing whether we are currently pulsing for ambient display.
124 */
125 public void setPulsing(boolean pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +0100126 if (mPulsing == pulsing) {
127 return;
128 }
Adrian Roos4b820e42017-01-13 17:40:43 -0800129 mPulsing = pulsing;
Selim Cinekba069ae2020-04-01 19:45:16 -0700130 updateAllowedStates();
Adrian Roos4b820e42017-01-13 17:40:43 -0800131 }
132
Selim Cinekba069ae2020-04-01 19:45:16 -0700133 private void updateAllowedStates() {
Ned Burns9512e0c2019-05-30 19:36:04 -0400134 boolean reorderingAllowed =
135 (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing;
136 boolean changedToTrue = reorderingAllowed && !mReorderingAllowed;
Selim Cineka7d4f822016-12-06 14:34:47 -0800137 mReorderingAllowed = reorderingAllowed;
Ned Burns9512e0c2019-05-30 19:36:04 -0400138 if (changedToTrue) {
Selim Cinekba069ae2020-04-01 19:45:16 -0700139 notifyChangeAllowed(mReorderingAllowedCallbacks);
140 }
141 boolean groupChangesAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
142 changedToTrue = groupChangesAllowed && !mGroupChangedAllowed;
143 mGroupChangedAllowed = groupChangesAllowed;
144 if (changedToTrue) {
145 notifyChangeAllowed(mGroupChangesAllowedCallbacks);
Selim Cineka7d4f822016-12-06 14:34:47 -0800146 }
147 }
148
Selim Cinekba069ae2020-04-01 19:45:16 -0700149 private void notifyChangeAllowed(ArrayList<Callback> callbacks) {
150 for (int i = 0; i < callbacks.size(); i++) {
151 callbacks.get(i).onChangeAllowed();
Selim Cineka7d4f822016-12-06 14:34:47 -0800152 }
Selim Cinekba069ae2020-04-01 19:45:16 -0700153 callbacks.clear();
Selim Cineka7d4f822016-12-06 14:34:47 -0800154 }
155
156 /**
157 * @return whether reordering is currently allowed in general.
158 */
159 public boolean isReorderingAllowed() {
160 return mReorderingAllowed;
161 }
162
163 /**
Selim Cinekba069ae2020-04-01 19:45:16 -0700164 * @return whether changes in the grouping should be allowed right now.
165 */
166 public boolean areGroupChangesAllowed() {
167 return mGroupChangedAllowed;
168 }
169
170 /**
Selim Cineka7d4f822016-12-06 14:34:47 -0800171 * @return whether a specific notification is allowed to reorder. Certain notifications are
172 * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added
173 * notifications or heads-up notifications that are out of view.
174 */
175 public boolean canReorderNotification(ExpandableNotificationRow row) {
176 if (mReorderingAllowed) {
177 return true;
178 }
179 if (mAddedChildren.contains(row)) {
180 return true;
181 }
Ned Burnsa13b6f12019-02-11 20:42:58 -0500182 if (mLowPriorityReorderingViews.contains(row.getEntry())) {
Selim Cinek55a3e732017-05-25 18:30:10 -0700183 return true;
184 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800185 if (mAllowedReorderViews.contains(row)
Evan Laird94492852018-10-25 13:43:01 -0400186 && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800187 return true;
188 }
189 return false;
190 }
191
192 public void setVisibilityLocationProvider(
193 VisibilityLocationProvider visibilityLocationProvider) {
194 mVisibilityLocationProvider = visibilityLocationProvider;
195 }
196
197 public void onReorderingFinished() {
198 mAllowedReorderViews.clear();
199 mAddedChildren.clear();
Selim Cinek55a3e732017-05-25 18:30:10 -0700200 mLowPriorityReorderingViews.clear();
Selim Cineka7d4f822016-12-06 14:34:47 -0800201 }
202
203 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500204 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800205 if (isHeadsUp) {
206 // Heads up notifications should in general be allowed to reorder if they are out of
207 // view and stay at the current location if they aren't.
Evan Laird94492852018-10-25 13:43:01 -0400208 mAllowedReorderViews.add(entry.getRow());
Selim Cineka7d4f822016-12-06 14:34:47 -0800209 }
210 }
211
Selim Cineka7d4f822016-12-06 14:34:47 -0800212 /**
Ned Burns9512e0c2019-05-30 19:36:04 -0400213 * Temporarily allows reordering of the entire shade for a period of 1000ms. Subsequent calls
214 * to this method will extend the timer.
215 */
216 public void temporarilyAllowReordering() {
217 mHandler.removeCallbacks(mOnTemporaryReorderingExpired);
218 mHandler.postDelayed(mOnTemporaryReorderingExpired, TEMPORARY_REORDERING_ALLOWED_DURATION);
219 if (!mIsTemporaryReorderingAllowed) {
220 mTemporaryReorderingStart = SystemClock.elapsedRealtime();
221 }
222 mIsTemporaryReorderingAllowed = true;
Selim Cinekba069ae2020-04-01 19:45:16 -0700223 updateAllowedStates();
Ned Burns9512e0c2019-05-30 19:36:04 -0400224 }
225
226 private final Runnable mOnTemporaryReorderingExpired = () -> {
227 mIsTemporaryReorderingAllowed = false;
Selim Cinekba069ae2020-04-01 19:45:16 -0700228 updateAllowedStates();
Ned Burns9512e0c2019-05-30 19:36:04 -0400229 };
230
231 /**
Selim Cineka7d4f822016-12-06 14:34:47 -0800232 * Notify the visual stability manager that a new view was added and should be allowed to
233 * reorder next time.
234 */
235 public void notifyViewAddition(View view) {
236 mAddedChildren.add(view);
237 }
238
Ned Burns9512e0c2019-05-30 19:36:04 -0400239 @Override
240 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
241 pw.println("VisualStabilityManager state:");
242 pw.print(" mIsTemporaryReorderingAllowed="); pw.println(mIsTemporaryReorderingAllowed);
243 pw.print(" mTemporaryReorderingStart="); pw.println(mTemporaryReorderingStart);
244
245 long now = SystemClock.elapsedRealtime();
246 pw.print(" Temporary reordering window has been open for ");
247 pw.print(now - (mIsTemporaryReorderingAllowed ? mTemporaryReorderingStart : now));
248 pw.println("ms");
249
250 pw.println();
251 }
252
Selim Cineka7d4f822016-12-06 14:34:47 -0800253 public interface Callback {
254 /**
Selim Cinekba069ae2020-04-01 19:45:16 -0700255 * Called when changing is allowed again.
Selim Cineka7d4f822016-12-06 14:34:47 -0800256 */
Selim Cinekba069ae2020-04-01 19:45:16 -0700257 void onChangeAllowed();
Selim Cineka7d4f822016-12-06 14:34:47 -0800258 }
259
260}