blob: 6b0df95f54dd787f2b27e8f0c6907af95b29b5bd [file] [log] [blame]
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001/*
2 * Copyright (C) 2018 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.phone;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.Context;
22import android.content.res.Resources;
Evan Lairde1822fd2019-07-10 15:55:45 -040023import android.graphics.Region;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090024import android.util.Pools;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090025
Gus Prevasab336792018-11-14 13:52:20 -050026import androidx.collection.ArraySet;
27
Selim Cinekc3fec682019-06-06 18:11:07 -070028import com.android.internal.annotations.VisibleForTesting;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090029import com.android.systemui.Dumpable;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080030import com.android.systemui.R;
Beverly8fdb5332019-02-04 14:29:49 -050031import com.android.systemui.plugins.statusbar.StatusBarStateController;
32import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Jason Monk1fd3fc32018-08-14 17:20:09 -040033import com.android.systemui.statusbar.StatusBarState;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090034import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050035import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Gus Prevasab336792018-11-14 13:52:20 -050036import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Adrian Roos22af6502018-02-22 16:57:08 +010037import com.android.systemui.statusbar.policy.ConfigurationController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090038import com.android.systemui.statusbar.policy.HeadsUpManager;
39import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
40
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
Beverly95a0802ac2020-02-10 15:27:40 -050043import java.util.ArrayList;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090044import java.util.HashSet;
Beverly95a0802ac2020-02-10 15:27:40 -050045import java.util.List;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090046import java.util.Stack;
47
48/**
49 * A implementation of HeadsUpManager for phone and car.
50 */
51public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
Beverly95a0802ac2020-02-10 15:27:40 -050052 VisualStabilityManager.Callback, OnHeadsUpChangedListener {
yoshiki iguchi4e30e762018-02-06 12:09:23 +090053 private static final String TAG = "HeadsUpManagerPhone";
yoshiki iguchi4e30e762018-02-06 12:09:23 +090054
Selim Cinekc3fec682019-06-06 18:11:07 -070055 @VisibleForTesting
56 final int mExtensionTime;
Selim Cinekd21232e2019-06-20 14:15:59 -070057 private final KeyguardBypassController mBypassController;
Beverly95a0802ac2020-02-10 15:27:40 -050058 private final NotificationGroupManager mGroupManager;
59 private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
Selim Cineke3c6e462019-06-24 19:37:06 -070060 private final int mAutoHeadsUpNotificationDecay;
Selim Cinekc3fec682019-06-06 18:11:07 -070061 private VisualStabilityManager mVisualStabilityManager;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090062 private boolean mReleaseOnExpandFinish;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080063
yoshiki iguchi4e30e762018-02-06 12:09:23 +090064 private boolean mTrackingHeadsUp;
65 private HashSet<String> mSwipedOutKeys = new HashSet<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -050066 private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
Selim Cineke3c6e462019-06-24 19:37:06 -070067 private HashSet<String> mKeysToRemoveWhenLeavingKeyguard = new HashSet<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -050068 private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
yoshiki iguchi4e30e762018-02-06 12:09:23 +090069 = new ArraySet<>();
70 private boolean mIsExpanded;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090071 private boolean mHeadsUpGoingAway;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090072 private int mStatusBarState;
Jason Monke59dc402018-08-16 12:05:01 -040073 private AnimationStateHandler mAnimationStateHandler;
Beverly95a0802ac2020-02-10 15:27:40 -050074 private int mHeadsUpInset;
75
76 // Used for determining the region for touch interaction
77 private final Region mTouchableRegion = new Region();
Jason Monk1fd3fc32018-08-14 17:20:09 -040078
yoshiki iguchi4e30e762018-02-06 12:09:23 +090079 private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
80 private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
81
82 @Override
83 public HeadsUpEntryPhone acquire() {
84 if (!mPoolObjects.isEmpty()) {
85 return mPoolObjects.pop();
86 }
87 return new HeadsUpEntryPhone();
88 }
89
90 @Override
91 public boolean release(@NonNull HeadsUpEntryPhone instance) {
92 mPoolObjects.push(instance);
93 return true;
94 }
95 };
96
97 ///////////////////////////////////////////////////////////////////////////////////////////////
98 // Constructor:
99
James O'Learycbea84d2019-03-14 11:18:24 -0400100 public HeadsUpManagerPhone(@NonNull final Context context,
Selim Cinekd21232e2019-06-20 14:15:59 -0700101 StatusBarStateController statusBarStateController,
Beverly95a0802ac2020-02-10 15:27:40 -0500102 KeyguardBypassController bypassController,
103 NotificationGroupManager groupManager,
104 ConfigurationController configurationController) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900105 super(context);
Selim Cinekc3fec682019-06-06 18:11:07 -0700106 Resources resources = mContext.getResources();
Selim Cinekc3fec682019-06-06 18:11:07 -0700107 mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
Selim Cineke3c6e462019-06-24 19:37:06 -0700108 mAutoHeadsUpNotificationDecay = resources.getInteger(
109 R.integer.auto_heads_up_notification_decay);
Beverly95a0802ac2020-02-10 15:27:40 -0500110 statusBarStateController.addCallback(mStatusBarStateListener);
Selim Cinekd21232e2019-06-20 14:15:59 -0700111 mBypassController = bypassController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900112 mGroupManager = groupManager;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900113
Beverly95a0802ac2020-02-10 15:27:40 -0500114 updateResources();
115 configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900116 @Override
Beverly95a0802ac2020-02-10 15:27:40 -0500117 public void onDensityOrFontScaleChanged() {
118 updateResources();
119 }
120
121 @Override
122 public void onOverlayChanged() {
123 updateResources();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900124 }
125 });
Jason Monk1fd3fc32018-08-14 17:20:09 -0400126 }
127
Beverly95a0802ac2020-02-10 15:27:40 -0500128 void setup(VisualStabilityManager visualStabilityManager) {
129 mVisualStabilityManager = visualStabilityManager;
130 }
131
Jason Monke59dc402018-08-16 12:05:01 -0400132 public void setAnimationStateHandler(AnimationStateHandler handler) {
133 mAnimationStateHandler = handler;
134 }
135
Beverly95a0802ac2020-02-10 15:27:40 -0500136 private void updateResources() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800137 Resources resources = mContext.getResources();
Beverly95a0802ac2020-02-10 15:27:40 -0500138 mHeadsUpInset =
139 resources.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height)
140 + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
Jorim Jaggi71aaa402018-06-06 17:22:56 +0200141 }
142
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900143 ///////////////////////////////////////////////////////////////////////////////////////////////
144 // Public methods:
145
146 /**
Beverly95a0802ac2020-02-10 15:27:40 -0500147 * Add a listener to receive callbacks onHeadsUpGoingAway
148 */
149 void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
150 mHeadsUpPhoneListeners.add(listener);
151 }
152
153 /**
154 * Gets the touchable region needed for heads up notifications. Returns null if no touchable
155 * region is required (ie: no heads up notification currently exists).
156 */
157 @Nullable Region getTouchableRegion() {
158 NotificationEntry topEntry = getTopEntry();
159
160 // This call could be made in an inconsistent state while the pinnedMode hasn't been
161 // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
162 // therefore also check if the topEntry is null.
163 if (!hasPinnedHeadsUp() || topEntry == null) {
164 return null;
165 } else {
166 if (topEntry.isChildInGroup()) {
167 final NotificationEntry groupSummary =
168 mGroupManager.getGroupSummary(topEntry.getSbn());
169 if (groupSummary != null) {
170 topEntry = groupSummary;
171 }
172 }
173 ExpandableNotificationRow topRow = topEntry.getRow();
174 int[] tmpArray = new int[2];
175 topRow.getLocationOnScreen(tmpArray);
176 int minX = tmpArray[0];
177 int maxX = tmpArray[0] + topRow.getWidth();
178 int height = topRow.getIntrinsicHeight();
179 mTouchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
180 return mTouchableRegion;
181 }
182 }
183
184 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900185 * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
186 * that a user might have consciously clicked on it.
187 *
188 * @param key the key of the touched notification
189 * @return whether the touch is invalid and should be discarded
190 */
Beverly95a0802ac2020-02-10 15:27:40 -0500191 boolean shouldSwallowClick(@NonNull String key) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900192 HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
Kevind4f66a42018-08-03 13:12:51 -0700193 return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900194 }
195
196 public void onExpandingFinished() {
197 if (mReleaseOnExpandFinish) {
198 releaseAllImmediately();
199 mReleaseOnExpandFinish = false;
200 } else {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500201 for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400202 if (isAlerting(entry.getKey())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900203 // Maybe the heads-up was removed already
Ned Burns00b4b2d2019-10-17 22:09:27 -0400204 removeAlertEntry(entry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900205 }
206 }
207 }
208 mEntriesToRemoveAfterExpand.clear();
209 }
210
211 /**
212 * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
213 * from the list even after a Heads Up Notification is gone.
214 */
215 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
216 mTrackingHeadsUp = trackingHeadsUp;
217 }
218
219 /**
220 * Notify that the status bar panel gets expanded or collapsed.
221 *
222 * @param isExpanded True to notify expanded, false to notify collapsed.
223 */
Beverly95a0802ac2020-02-10 15:27:40 -0500224 void setIsPanelExpanded(boolean isExpanded) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900225 if (isExpanded != mIsExpanded) {
226 mIsExpanded = isExpanded;
227 if (isExpanded) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900228 mHeadsUpGoingAway = false;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900229 }
Selim Cinek91d04662019-06-24 17:13:06 -0700230 }
231 }
232
Selim Cineke3c6e462019-06-24 19:37:06 -0700233 @Override
234 public boolean isEntryAutoHeadsUpped(String key) {
235 HeadsUpEntryPhone headsUpEntryPhone = getHeadsUpEntryPhone(key);
236 if (headsUpEntryPhone == null) {
237 return false;
238 }
239 return headsUpEntryPhone.isAutoHeadsUp();
240 }
241
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900242 /**
243 * Set that we are exiting the headsUp pinned mode, but some notifications might still be
244 * animating out. This is used to keep the touchable regions in a sane state.
245 */
Beverly95a0802ac2020-02-10 15:27:40 -0500246 void setHeadsUpGoingAway(boolean headsUpGoingAway) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900247 if (headsUpGoingAway != mHeadsUpGoingAway) {
248 mHeadsUpGoingAway = headsUpGoingAway;
Beverly95a0802ac2020-02-10 15:27:40 -0500249 for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
250 listener.onHeadsUpGoingAwayStateChanged(headsUpGoingAway);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900251 }
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900252 }
253 }
254
Beverly95a0802ac2020-02-10 15:27:40 -0500255 boolean isHeadsUpGoingAway() {
James O'Learycbea84d2019-03-14 11:18:24 -0400256 return mHeadsUpGoingAway;
257 }
258
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900259 /**
260 * Notifies that a remote input textbox in notification gets active or inactive.
James O'Learycbea84d2019-03-14 11:18:24 -0400261 *
262 * @param entry The entry of the target notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900263 * @param remoteInputActive True to notify active, False to notify inactive.
264 */
265 public void setRemoteInputActive(
Ned Burnsf81c4c42019-01-07 14:10:43 -0500266 @NonNull NotificationEntry entry, boolean remoteInputActive) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400267 HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900268 if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
269 headsUpEntry.remoteInputActive = remoteInputActive;
270 if (remoteInputActive) {
271 headsUpEntry.removeAutoRemovalCallbacks();
272 } else {
273 headsUpEntry.updateEntry(false /* updatePostTime */);
274 }
275 }
276 }
277
Gus Prevas211181532018-12-13 14:49:33 -0500278 /**
279 * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
280 * area if it's pinned until it's hidden again.
281 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500282 public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400283 HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
Gus Prevas211181532018-12-13 14:49:33 -0500284 if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
285 ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
286 }
287 }
288
Selim Cinekc3fec682019-06-06 18:11:07 -0700289 /**
290 * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
291 * longer.
292 */
293 public void extendHeadsUp() {
294 HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
295 if (topEntry == null) {
296 return;
297 }
298 topEntry.extendPulse();
299 }
300
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900301 ///////////////////////////////////////////////////////////////////////////////////////////////
302 // HeadsUpManager public methods overrides:
303
304 @Override
305 public boolean isTrackingHeadsUp() {
306 return mTrackingHeadsUp;
307 }
308
Kevind4f66a42018-08-03 13:12:51 -0700309 @Override
310 public void snooze() {
311 super.snooze();
312 mReleaseOnExpandFinish = true;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900313 }
314
315 public void addSwipedOutNotification(@NonNull String key) {
316 mSwipedOutKeys.add(key);
317 }
318
319 ///////////////////////////////////////////////////////////////////////////////////////////////
320 // Dumpable overrides:
321
322 @Override
323 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
324 pw.println("HeadsUpManagerPhone state:");
325 dumpInternal(fd, pw, args);
326 }
327
Adrian Roos22af6502018-02-22 16:57:08 +0100328 @Override
Selim Cinekbd559092019-01-28 19:38:58 -0800329 public boolean shouldExtendLifetime(NotificationEntry entry) {
330 // We should not defer the removal if reordering isn't allowed since otherwise
331 // these won't disappear until reordering is allowed again, which happens only once
332 // the notification panel is collapsed again.
333 return mVisualStabilityManager.isReorderingAllowed() && super.shouldExtendLifetime(entry);
334 }
335
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900336 ///////////////////////////////////////////////////////////////////////////////////////////////
337 // VisualStabilityManager.Callback overrides:
338
339 @Override
Selim Cinekba069ae2020-04-01 19:45:16 -0700340 public void onChangeAllowed() {
Jason Monke59dc402018-08-16 12:05:01 -0400341 mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500342 for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400343 if (isAlerting(entry.getKey())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900344 // Maybe the heads-up was removed already
Ned Burns00b4b2d2019-10-17 22:09:27 -0400345 removeAlertEntry(entry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900346 }
347 }
348 mEntriesToRemoveWhenReorderingAllowed.clear();
Jason Monke59dc402018-08-16 12:05:01 -0400349 mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900350 }
351
352 ///////////////////////////////////////////////////////////////////////////////////////////////
353 // HeadsUpManager utility (protected) methods overrides:
354
355 @Override
Kevind4f66a42018-08-03 13:12:51 -0700356 protected HeadsUpEntry createAlertEntry() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900357 return mEntryPool.acquire();
358 }
359
360 @Override
Kevind4f66a42018-08-03 13:12:51 -0700361 protected void onAlertEntryRemoved(AlertEntry alertEntry) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400362 mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.getKey());
Kevind4f66a42018-08-03 13:12:51 -0700363 super.onAlertEntryRemoved(alertEntry);
364 mEntryPool.release((HeadsUpEntryPhone) alertEntry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900365 }
366
367 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500368 protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
Selim Cinekd21232e2019-06-20 14:15:59 -0700369 boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsExpanded;
370 if (mBypassController.getBypassEnabled()) {
371 pin |= mStatusBarState == StatusBarState.KEYGUARD;
372 }
373 return pin || super.shouldHeadsUpBecomePinned(entry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900374 }
375
376 @Override
377 protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
378 super.dumpInternal(fd, pw, args);
James O'Learycbea84d2019-03-14 11:18:24 -0400379 pw.print(" mBarState=");
380 pw.println(mStatusBarState);
Evan Lairde1822fd2019-07-10 15:55:45 -0400381 pw.print(" mTouchableRegion=");
382 pw.println(mTouchableRegion);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900383 }
384
385 ///////////////////////////////////////////////////////////////////////////////////////////////
386 // Private utility methods:
387
388 @Nullable
389 private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
Kevind4f66a42018-08-03 13:12:51 -0700390 return (HeadsUpEntryPhone) mAlertEntries.get(key);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900391 }
392
393 @Nullable
394 private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
395 return (HeadsUpEntryPhone) getTopHeadsUpEntry();
396 }
397
Kevina5ff1fa2018-08-21 16:35:48 -0700398 @Override
399 protected boolean canRemoveImmediately(@NonNull String key) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900400 if (mSwipedOutKeys.contains(key)) {
401 // We always instantly dismiss views being manually swiped out.
402 mSwipedOutKeys.remove(key);
403 return true;
404 }
405
406 HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
407 HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
Kevina5ff1fa2018-08-21 16:35:48 -0700408
409 return headsUpEntry == null || headsUpEntry != topEntry || super.canRemoveImmediately(key);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900410 }
411
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900412 ///////////////////////////////////////////////////////////////////////////////////////////////
413 // HeadsUpEntryPhone:
414
415 protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
Gus Prevas211181532018-12-13 14:49:33 -0500416
417 private boolean mMenuShownPinned;
418
Selim Cinekc3fec682019-06-06 18:11:07 -0700419 /**
420 * If the time this entry has been on was extended
421 */
422 private boolean extended;
423
Selim Cineke3c6e462019-06-24 19:37:06 -0700424 /**
425 * Was this entry received while on keyguard
426 */
427 private boolean mIsAutoHeadsUp;
428
Selim Cinekc3fec682019-06-06 18:11:07 -0700429
Gus Prevas211181532018-12-13 14:49:33 -0500430 @Override
431 protected boolean isSticky() {
432 return super.isSticky() || mMenuShownPinned;
433 }
434
Ned Burnsf81c4c42019-01-07 14:10:43 -0500435 public void setEntry(@NonNull final NotificationEntry entry) {
James O'Learycbea84d2019-03-14 11:18:24 -0400436 Runnable removeHeadsUpRunnable = () -> {
Selim Cinekc3fec682019-06-06 18:11:07 -0700437 if (!mVisualStabilityManager.isReorderingAllowed()
438 // We don't want to allow reordering while pulsing, but headsup need to
Selim Cinekb57dd8a2019-06-14 12:27:58 -0700439 // time out anyway
440 && !entry.showingPulsing()) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900441 mEntriesToRemoveWhenReorderingAllowed.add(entry);
Beverly95a0802ac2020-02-10 15:27:40 -0500442 mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this);
Selim Cineke3c6e462019-06-24 19:37:06 -0700443 } else if (mTrackingHeadsUp) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900444 mEntriesToRemoveAfterExpand.add(entry);
Selim Cineke3c6e462019-06-24 19:37:06 -0700445 } else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400446 mKeysToRemoveWhenLeavingKeyguard.add(entry.getKey());
Selim Cineke3c6e462019-06-24 19:37:06 -0700447 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400448 removeAlertEntry(entry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900449 }
450 };
451
Kevind4f66a42018-08-03 13:12:51 -0700452 setEntry(entry, removeHeadsUpRunnable);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900453 }
454
455 @Override
456 public void updateEntry(boolean updatePostTime) {
Selim Cineke3c6e462019-06-24 19:37:06 -0700457 mIsAutoHeadsUp = mEntry.isAutoHeadsUp();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900458 super.updateEntry(updatePostTime);
459
Kevind4f66a42018-08-03 13:12:51 -0700460 if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
461 mEntriesToRemoveAfterExpand.remove(mEntry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900462 }
Kevind4f66a42018-08-03 13:12:51 -0700463 if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
464 mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900465 }
Ned Burns00b4b2d2019-10-17 22:09:27 -0400466 mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900467 }
468
469 @Override
Kevind4f66a42018-08-03 13:12:51 -0700470 public void setExpanded(boolean expanded) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900471 if (this.expanded == expanded) {
472 return;
473 }
474
475 this.expanded = expanded;
476 if (expanded) {
477 removeAutoRemovalCallbacks();
478 } else {
479 updateEntry(false /* updatePostTime */);
480 }
481 }
Gus Prevas211181532018-12-13 14:49:33 -0500482
483 public void setMenuShownPinned(boolean menuShownPinned) {
484 if (mMenuShownPinned == menuShownPinned) {
485 return;
486 }
487
488 mMenuShownPinned = menuShownPinned;
489 if (menuShownPinned) {
490 removeAutoRemovalCallbacks();
491 } else {
492 updateEntry(false /* updatePostTime */);
493 }
494 }
495
496 @Override
497 public void reset() {
498 super.reset();
499 mMenuShownPinned = false;
Selim Cinekc3fec682019-06-06 18:11:07 -0700500 extended = false;
Selim Cineke3c6e462019-06-24 19:37:06 -0700501 mIsAutoHeadsUp = false;
Selim Cinekc3fec682019-06-06 18:11:07 -0700502 }
503
504 private void extendPulse() {
505 if (!extended) {
506 extended = true;
507 updateEntry(false);
508 }
509 }
510
511 @Override
Selim Cineke3c6e462019-06-24 19:37:06 -0700512 public int compareTo(AlertEntry alertEntry) {
513 HeadsUpEntryPhone headsUpEntry = (HeadsUpEntryPhone) alertEntry;
514 boolean autoShown = isAutoHeadsUp();
515 boolean otherAutoShown = headsUpEntry.isAutoHeadsUp();
516 if (autoShown && !otherAutoShown) {
517 return 1;
518 } else if (!autoShown && otherAutoShown) {
519 return -1;
520 }
521 return super.compareTo(alertEntry);
522 }
523
524 @Override
Selim Cinekc3fec682019-06-06 18:11:07 -0700525 protected long calculateFinishTime() {
526 return mPostTime + getDecayDuration() + (extended ? mExtensionTime : 0);
527 }
528
529 private int getDecayDuration() {
Lucas Dupind534c122019-07-16 18:48:59 -0700530 if (isAutoHeadsUp()) {
Selim Cineke3c6e462019-06-24 19:37:06 -0700531 return getRecommendedHeadsUpTimeoutMs(mAutoHeadsUpNotificationDecay);
532 } else {
533 return getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
534 }
535 }
536
537 private boolean isAutoHeadsUp() {
538 return mIsAutoHeadsUp;
Gus Prevas211181532018-12-13 14:49:33 -0500539 }
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900540 }
Jason Monke59dc402018-08-16 12:05:01 -0400541
542 public interface AnimationStateHandler {
543 void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed);
544 }
Beverly95a0802ac2020-02-10 15:27:40 -0500545
546 /**
547 * Listener to register for HeadsUpNotification Phone changes.
548 */
549 public interface OnHeadsUpPhoneListenerChange {
550 /**
551 * Called when a heads up notification is 'going away' or no longer 'going away'.
552 * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}.
553 */
554 void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway);
555 }
556
557 private final StateListener mStatusBarStateListener = new StateListener() {
558 @Override
559 public void onStateChanged(int newState) {
560 boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
561 boolean isKeyguard = newState == StatusBarState.KEYGUARD;
562 mStatusBarState = newState;
563 if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
564 String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
565 for (String key : keys) {
566 removeAlertEntry(key);
567 }
568 mKeysToRemoveWhenLeavingKeyguard.clear();
569 }
570 }
571
572 @Override
573 public void onDozingChanged(boolean isDozing) {
574 if (!isDozing) {
575 // Let's make sure all huns we got while dozing time out within the normal timeout
576 // duration. Otherwise they could get stuck for a very long time
577 for (AlertEntry entry : mAlertEntries.values()) {
578 entry.updateEntry(true /* updatePostTime */);
579 }
580 }
581 }
582 };
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900583}