blob: 3dcf7ed674c7fc3114b4b3b2fb803ffb0f6cad2d [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);
Selim Cinek0260c752020-05-11 16:03:52 -0700442 mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this,
443 false /* persistent */);
Selim Cineke3c6e462019-06-24 19:37:06 -0700444 } else if (mTrackingHeadsUp) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900445 mEntriesToRemoveAfterExpand.add(entry);
Selim Cineke3c6e462019-06-24 19:37:06 -0700446 } else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400447 mKeysToRemoveWhenLeavingKeyguard.add(entry.getKey());
Selim Cineke3c6e462019-06-24 19:37:06 -0700448 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400449 removeAlertEntry(entry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900450 }
451 };
452
Kevind4f66a42018-08-03 13:12:51 -0700453 setEntry(entry, removeHeadsUpRunnable);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900454 }
455
456 @Override
457 public void updateEntry(boolean updatePostTime) {
Selim Cineke3c6e462019-06-24 19:37:06 -0700458 mIsAutoHeadsUp = mEntry.isAutoHeadsUp();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900459 super.updateEntry(updatePostTime);
460
Kevind4f66a42018-08-03 13:12:51 -0700461 if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
462 mEntriesToRemoveAfterExpand.remove(mEntry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900463 }
Kevind4f66a42018-08-03 13:12:51 -0700464 if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
465 mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900466 }
Ned Burns00b4b2d2019-10-17 22:09:27 -0400467 mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.getKey());
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900468 }
469
470 @Override
Kevind4f66a42018-08-03 13:12:51 -0700471 public void setExpanded(boolean expanded) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900472 if (this.expanded == expanded) {
473 return;
474 }
475
476 this.expanded = expanded;
477 if (expanded) {
478 removeAutoRemovalCallbacks();
479 } else {
480 updateEntry(false /* updatePostTime */);
481 }
482 }
Gus Prevas211181532018-12-13 14:49:33 -0500483
484 public void setMenuShownPinned(boolean menuShownPinned) {
485 if (mMenuShownPinned == menuShownPinned) {
486 return;
487 }
488
489 mMenuShownPinned = menuShownPinned;
490 if (menuShownPinned) {
491 removeAutoRemovalCallbacks();
492 } else {
493 updateEntry(false /* updatePostTime */);
494 }
495 }
496
497 @Override
498 public void reset() {
499 super.reset();
500 mMenuShownPinned = false;
Selim Cinekc3fec682019-06-06 18:11:07 -0700501 extended = false;
Selim Cineke3c6e462019-06-24 19:37:06 -0700502 mIsAutoHeadsUp = false;
Selim Cinekc3fec682019-06-06 18:11:07 -0700503 }
504
505 private void extendPulse() {
506 if (!extended) {
507 extended = true;
508 updateEntry(false);
509 }
510 }
511
512 @Override
Selim Cineke3c6e462019-06-24 19:37:06 -0700513 public int compareTo(AlertEntry alertEntry) {
514 HeadsUpEntryPhone headsUpEntry = (HeadsUpEntryPhone) alertEntry;
515 boolean autoShown = isAutoHeadsUp();
516 boolean otherAutoShown = headsUpEntry.isAutoHeadsUp();
517 if (autoShown && !otherAutoShown) {
518 return 1;
519 } else if (!autoShown && otherAutoShown) {
520 return -1;
521 }
522 return super.compareTo(alertEntry);
523 }
524
525 @Override
Selim Cinekc3fec682019-06-06 18:11:07 -0700526 protected long calculateFinishTime() {
527 return mPostTime + getDecayDuration() + (extended ? mExtensionTime : 0);
528 }
529
530 private int getDecayDuration() {
Lucas Dupind534c122019-07-16 18:48:59 -0700531 if (isAutoHeadsUp()) {
Selim Cineke3c6e462019-06-24 19:37:06 -0700532 return getRecommendedHeadsUpTimeoutMs(mAutoHeadsUpNotificationDecay);
533 } else {
534 return getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
535 }
536 }
537
538 private boolean isAutoHeadsUp() {
539 return mIsAutoHeadsUp;
Gus Prevas211181532018-12-13 14:49:33 -0500540 }
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900541 }
Jason Monke59dc402018-08-16 12:05:01 -0400542
543 public interface AnimationStateHandler {
544 void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed);
545 }
Beverly95a0802ac2020-02-10 15:27:40 -0500546
547 /**
548 * Listener to register for HeadsUpNotification Phone changes.
549 */
550 public interface OnHeadsUpPhoneListenerChange {
551 /**
552 * Called when a heads up notification is 'going away' or no longer 'going away'.
553 * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}.
554 */
555 void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway);
556 }
557
558 private final StateListener mStatusBarStateListener = new StateListener() {
559 @Override
560 public void onStateChanged(int newState) {
561 boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
562 boolean isKeyguard = newState == StatusBarState.KEYGUARD;
563 mStatusBarState = newState;
564 if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
565 String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
566 for (String key : keys) {
567 removeAlertEntry(key);
568 }
569 mKeysToRemoveWhenLeavingKeyguard.clear();
570 }
571 }
572
573 @Override
574 public void onDozingChanged(boolean isDozing) {
575 if (!isDozing) {
576 // Let's make sure all huns we got while dozing time out within the normal timeout
577 // duration. Otherwise they could get stuck for a very long time
578 for (AlertEntry entry : mAlertEntries.values()) {
579 entry.updateEntry(true /* updatePostTime */);
580 }
581 }
582 }
583 };
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900584}