blob: b4e47731f61ede764caedbb3d8ff27fa78c18d47 [file] [log] [blame]
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001/*
2 * Copyright (C) 2011 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.policy;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.database.ContentObserver;
22import android.os.Handler;
23import android.os.SystemClock;
24import android.provider.Settings;
25import android.util.ArrayMap;
26import android.util.Log;
27import android.util.Pools;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070028import android.view.ViewTreeObserver;
29import android.view.accessibility.AccessibilityEvent;
30
31import com.android.systemui.R;
Selim Cineka59ecc32015-04-07 10:51:49 -070032import com.android.systemui.statusbar.ExpandableNotificationRow;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070033import com.android.systemui.statusbar.NotificationData;
34import com.android.systemui.statusbar.phone.PhoneStatusBar;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
Selim Cineka59ecc32015-04-07 10:51:49 -070038import java.util.HashMap;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070039import java.util.HashSet;
40import java.util.Stack;
Selim Cineka59ecc32015-04-07 10:51:49 -070041import java.util.TreeSet;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070042
Selim Cineka59ecc32015-04-07 10:51:49 -070043public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
Selim Cinekb8f09cf2015-03-16 17:09:28 -070044 private static final String TAG = "HeadsUpManager";
45 private static final boolean DEBUG = false;
46 private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
47
48 private final int mHeadsUpNotificationDecay;
49 private final int mMinimumDisplayTime;
50
51 private final int mTouchSensitivityDelay;
52 private final ArrayMap<String, Long> mSnoozedPackages;
53 private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
54 private final int mDefaultSnoozeLengthMs;
55 private final Handler mHandler = new Handler();
56 private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
57
58 private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
59
60 @Override
61 public HeadsUpEntry acquire() {
62 if (!mPoolObjects.isEmpty()) {
63 return mPoolObjects.pop();
64 }
65 return new HeadsUpEntry();
66 }
67
68 @Override
69 public boolean release(HeadsUpEntry instance) {
70 instance.removeAutoCancelCallbacks();
71 mPoolObjects.push(instance);
72 return true;
73 }
74 };
75
76
77 private PhoneStatusBar mBar;
78 private int mSnoozeLengthMs;
79 private ContentObserver mSettingsObserver;
Selim Cineka59ecc32015-04-07 10:51:49 -070080 private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
81 private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -070082 private HashSet<String> mSwipedOutKeys = new HashSet<>();
83 private int mUser;
84 private Clock mClock;
85 private boolean mReleaseOnExpandFinish;
86 private boolean mTrackingHeadsUp;
87 private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
88 private boolean mIsExpanded;
89 private boolean mHasPinnedHeadsUp;
Selim Cineka59ecc32015-04-07 10:51:49 -070090 private int[] mTmpTwoArray = new int[2];
Selim Cinekb8f09cf2015-03-16 17:09:28 -070091
Selim Cineka59ecc32015-04-07 10:51:49 -070092 public HeadsUpManager(final Context context, ViewTreeObserver observer) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -070093 Resources resources = context.getResources();
94 mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
95 if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
96 mSnoozedPackages = new ArrayMap<>();
97 mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
98 mSnoozeLengthMs = mDefaultSnoozeLengthMs;
99 mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
Selim Cineke53e6bb2015-04-13 16:14:26 -0700100 mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700101 mClock = new Clock();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700102
103 mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
104 SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
105 mSettingsObserver = new ContentObserver(mHandler) {
106 @Override
107 public void onChange(boolean selfChange) {
108 final int packageSnoozeLengthMs = Settings.Global.getInt(
109 context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
110 if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
111 mSnoozeLengthMs = packageSnoozeLengthMs;
112 if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
113 }
114 }
115 };
116 context.getContentResolver().registerContentObserver(
117 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
118 mSettingsObserver);
119 if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
Selim Cineka59ecc32015-04-07 10:51:49 -0700120 observer.addOnComputeInternalInsetsListener(this);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700121 }
122
123 public void setBar(PhoneStatusBar bar) {
124 mBar = bar;
125 }
126
127 public void addListener(OnHeadsUpChangedListener listener) {
128 mListeners.add(listener);
129 }
130
131 public PhoneStatusBar getBar() {
132 return mBar;
133 }
134
135 /**
136 * Called when posting a new notification to the heads up.
137 */
138 public void showNotification(NotificationData.Entry headsUp) {
139 if (DEBUG) Log.v(TAG, "showNotification");
140 addHeadsUpEntry(headsUp);
141 updateNotification(headsUp, true);
142 headsUp.setInterruption();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700143 }
144
145 /**
146 * Called when updating or posting a notification to the heads up.
147 */
148 public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
149 if (DEBUG) Log.v(TAG, "updateNotification");
150
151 headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
152 headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
153
154 if (alert) {
155 HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
156 headsUpEntry.updateEntry();
Selim Cinekaac93252015-04-14 20:04:12 -0700157 setEntryToShade(headsUpEntry, mIsExpanded, false /* justAdded */, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700158 }
159 }
160
161 private void addHeadsUpEntry(NotificationData.Entry entry) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700162 HeadsUpEntry headsUpEntry = mEntryPool.acquire();
Selim Cineka59ecc32015-04-07 10:51:49 -0700163
164 // This will also add the entry to the sortedList
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700165 headsUpEntry.setEntry(entry);
166 mHeadsUpEntries.put(entry.key, headsUpEntry);
Selim Cineka59ecc32015-04-07 10:51:49 -0700167 entry.row.setHeadsUp(true);
Selim Cinekaac93252015-04-14 20:04:12 -0700168 setEntryToShade(headsUpEntry, mIsExpanded /* inShade */, true /* justAdded */, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700169 for (OnHeadsUpChangedListener listener : mListeners) {
170 listener.OnHeadsUpStateChanged(entry, true);
171 }
172 entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700173 }
174
Selim Cinekaac93252015-04-14 20:04:12 -0700175 private void setEntryToShade(HeadsUpEntry headsUpEntry, boolean inShade, boolean justAdded,
176 boolean forceImmediate) {
Selim Cinek1f3f5442015-04-10 17:54:46 -0700177 ExpandableNotificationRow row = headsUpEntry.entry.row;
Selim Cinekaac93252015-04-14 20:04:12 -0700178 if (row.isInShade() != inShade || justAdded) {
Selim Cinek1f3f5442015-04-10 17:54:46 -0700179 row.setInShade(inShade);
Selim Cinekaac93252015-04-14 20:04:12 -0700180 if (!justAdded || !inShade) {
181 updatePinnedHeadsUpState(forceImmediate);
182 for (OnHeadsUpChangedListener listener : mListeners) {
183 listener.OnHeadsUpPinnedChanged(row, !inShade);
Selim Cinek1f3f5442015-04-10 17:54:46 -0700184 }
185 }
186 }
187 }
188
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700189 private void removeHeadsUpEntry(NotificationData.Entry entry) {
190 HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
Selim Cineka59ecc32015-04-07 10:51:49 -0700191 mSortedEntries.remove(remove);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700192 mEntryPool.release(remove);
193 entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
194 entry.row.setHeadsUp(false);
Selim Cinekaac93252015-04-14 20:04:12 -0700195 setEntryToShade(remove, true /* inShade */, false /* justAdded */,
196 false /* forceImmediate */);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700197 for (OnHeadsUpChangedListener listener : mListeners) {
198 listener.OnHeadsUpStateChanged(entry, false);
199 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700200 }
201
202 private void updatePinnedHeadsUpState(boolean forceImmediate) {
203 boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal();
204 if (hasPinnedHeadsUp == mHasPinnedHeadsUp) {
205 return;
206 }
207 mHasPinnedHeadsUp = hasPinnedHeadsUp;
208 for (OnHeadsUpChangedListener listener :mListeners) {
209 listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate);
210 }
211 }
212
213 /**
214 * React to the removal of the notification in the heads up.
215 *
216 * @return true if the notification was removed and false if it still needs to be kept around
217 * for a bit since it wasn't shown long enough
218 */
219 public boolean removeNotification(String key) {
220 if (DEBUG) Log.v(TAG, "remove");
221 if (wasShownLongEnough(key)) {
222 releaseImmediately(key);
223 return true;
224 } else {
225 getHeadsUpEntry(key).hideAsSoonAsPossible();
226 return false;
227 }
228 }
229
230 private boolean wasShownLongEnough(String key) {
231 HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
232 HeadsUpEntry topEntry = getTopEntry();
233 if (mSwipedOutKeys.contains(key)) {
234 // We always instantly dismiss views being manually swiped out.
235 mSwipedOutKeys.remove(key);
236 return true;
237 }
238 if (headsUpEntry != topEntry) {
239 return true;
240 }
241 return headsUpEntry.wasShownLongEnough();
242 }
243
244 public boolean isHeadsUp(String key) {
245 return mHeadsUpEntries.containsKey(key);
246 }
247
248
249 /**
250 * Push any current Heads Up notification down into the shade.
251 */
252 public void releaseAllImmediately() {
253 if (DEBUG) Log.v(TAG, "releaseAllImmediately");
Selim Cineka59ecc32015-04-07 10:51:49 -0700254 HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet());
255 for (String key: keys) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700256 releaseImmediately(key);
257 }
258 }
259
260 public void releaseImmediately(String key) {
261 HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
262 if (headsUpEntry == null) {
263 return;
264 }
265 NotificationData.Entry shadeEntry = headsUpEntry.entry;
266 removeHeadsUpEntry(shadeEntry);
267 }
268
269 public boolean isSnoozed(String packageName) {
270 final String key = snoozeKey(packageName, mUser);
271 Long snoozedUntil = mSnoozedPackages.get(key);
272 if (snoozedUntil != null) {
273 if (snoozedUntil > SystemClock.elapsedRealtime()) {
274 if (DEBUG) Log.v(TAG, key + " snoozed");
275 return true;
276 }
277 mSnoozedPackages.remove(packageName);
278 }
279 return false;
280 }
281
282 public void snooze() {
283 for (String key: mHeadsUpEntries.keySet()) {
284 HeadsUpEntry entry = mHeadsUpEntries.get(key);
285 String packageName = entry.entry.notification.getPackageName();
286 mSnoozedPackages.put(snoozeKey(packageName, mUser),
287 SystemClock.elapsedRealtime() + mSnoozeLengthMs);
288 }
289 mReleaseOnExpandFinish = true;
290 }
291
292 private static String snoozeKey(String packageName, int user) {
293 return user + "," + packageName;
294 }
295
296 private HeadsUpEntry getHeadsUpEntry(String key) {
297 return mHeadsUpEntries.get(key);
298 }
299
300 public NotificationData.Entry getEntry(String key) {
301 return mHeadsUpEntries.get(key).entry;
302 }
303
Selim Cineka59ecc32015-04-07 10:51:49 -0700304 public TreeSet<HeadsUpEntry> getSortedEntries() {
305 return mSortedEntries;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700306 }
307
308 public HeadsUpEntry getTopEntry() {
Selim Cineka59ecc32015-04-07 10:51:49 -0700309 return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700310 }
311
312 /**
313 * @param key the key of the touched notification
314 * @return whether the touch is valid and should not be discarded
315 */
316 public boolean shouldSwallowClick(String key) {
Selim Cinek2f6b3fb2015-04-20 13:04:47 -0700317 HeadsUpEntry entry = mHeadsUpEntries.get(key);
318 if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700319 return true;
320 }
321 return false;
322 }
323
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700324 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
Selim Cineka59ecc32015-04-07 10:51:49 -0700325 if (!mIsExpanded && mHasPinnedHeadsUp) {
326 int minX = Integer.MAX_VALUE;
327 int maxX = 0;
328 int minY = Integer.MAX_VALUE;
329 int maxY = 0;
330 for (HeadsUpEntry entry: mSortedEntries) {
331 ExpandableNotificationRow row = entry.entry.row;
332 if (!row.isInShade()) {
333 row.getLocationOnScreen(mTmpTwoArray);
334 minX = Math.min(minX, mTmpTwoArray[0]);
335 minY = Math.min(minY, 0);
336 maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
337 maxY = Math.max(maxY, row.getHeadsUpHeight());
338 }
339 }
340
341 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
342 info.touchableRegion.set(minX, minY, maxX, maxY);
343 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700344 }
345
346 public void setUser(int user) {
347 mUser = user;
348 }
349
350 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
351 pw.println("HeadsUpManager state:");
352 pw.print(" mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
353 pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
354 pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
355 pw.print(" mUser="); pw.println(mUser);
Selim Cineka59ecc32015-04-07 10:51:49 -0700356 for (HeadsUpEntry entry: mSortedEntries) {
357 pw.print(" HeadsUpEntry="); pw.println(entry.entry);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700358 }
359 int N = mSnoozedPackages.size();
360 pw.println(" snoozed packages: " + N);
361 for (int i = 0; i < N; i++) {
362 pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
363 pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
364 }
365 }
366
367 public boolean hasPinnedHeadsUp() {
368 return mHasPinnedHeadsUp;
369 }
370
371 private boolean hasPinnedHeadsUpInternal() {
372 for (String key: mHeadsUpEntries.keySet()) {
373 HeadsUpEntry entry = mHeadsUpEntries.get(key);
374 if (!entry.entry.row.isInShade()) {
375 return true;
376 }
377 }
378 return false;
379 }
380
381 public void addSwipedOutKey(String key) {
382 mSwipedOutKeys.add(key);
383 }
384
385 public float getHighestPinnedHeadsUp() {
386 float max = 0;
Selim Cineka59ecc32015-04-07 10:51:49 -0700387 for (HeadsUpEntry entry: mSortedEntries) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700388 if (!entry.entry.row.isInShade()) {
389 max = Math.max(max, entry.entry.row.getActualHeight());
390 }
391 }
392 return max;
393 }
394
395 public void releaseAllToShade() {
396 for (String key: mHeadsUpEntries.keySet()) {
397 HeadsUpEntry entry = mHeadsUpEntries.get(key);
Selim Cinekaac93252015-04-14 20:04:12 -0700398 setEntryToShade(entry, true /* toShade */, false /* justAdded */,
399 true /* forceImmediate */);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700400 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700401 }
402
403 public void onExpandingFinished() {
404 if (mReleaseOnExpandFinish) {
405 releaseAllImmediately();
406 mReleaseOnExpandFinish = false;
407 } else {
408 for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
409 removeHeadsUpEntry(entry);
410 }
411 mEntriesToRemoveAfterExpand.clear();
412 }
413 }
414
415 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
416 mTrackingHeadsUp = trackingHeadsUp;
417 }
418
419 public void setIsExpanded(boolean isExpanded) {
Selim Cinek1f3f5442015-04-10 17:54:46 -0700420 if (isExpanded != mIsExpanded) {
421 mIsExpanded = isExpanded;
422 if (isExpanded) {
423 releaseAllToShade();
424 }
425 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700426 }
427
428 public int getTopHeadsUpHeight() {
429 HeadsUpEntry topEntry = getTopEntry();
430 return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
431 }
432
Selim Cinekfbe9a442015-04-13 16:09:49 -0700433 public int compare(NotificationData.Entry a, NotificationData.Entry b) {
434 HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
435 HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
436 if (aEntry == null || bEntry == null) {
437 return aEntry == null ? 1 : -1;
438 }
439 return aEntry.compareTo(bEntry);
440 }
441
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700442 public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
443 public NotificationData.Entry entry;
444 public long postTime;
445 public long earliestRemovaltime;
446 private Runnable mRemoveHeadsUpRunnable;
447
448 public void setEntry(final NotificationData.Entry entry) {
449 this.entry = entry;
450
451 // The actual post time will be just after the heads-up really slided in
452 postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
453 mRemoveHeadsUpRunnable = new Runnable() {
454 @Override
455 public void run() {
456 if (!mTrackingHeadsUp) {
457 removeHeadsUpEntry(entry);
458 } else {
459 mEntriesToRemoveAfterExpand.add(entry);
460 }
461 }
462 };
463 updateEntry();
464 }
465
466 public void updateEntry() {
467 long currentTime = mClock.currentTimeMillis();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700468 earliestRemovaltime = currentTime + mMinimumDisplayTime;
Selim Cinek31d9ef72015-04-15 19:29:49 -0700469 postTime = Math.max(postTime, currentTime);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700470 removeAutoCancelCallbacks();
Selim Cinek31d9ef72015-04-15 19:29:49 -0700471 if (canEntryDecay()) {
472 long finishTime = postTime + mHeadsUpNotificationDecay;
473 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
474 mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
475 }
Selim Cineka59ecc32015-04-07 10:51:49 -0700476 updateSortOrder(HeadsUpEntry.this);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700477 }
478
Selim Cinek31d9ef72015-04-15 19:29:49 -0700479 private boolean canEntryDecay() {
480 return entry.notification.getNotification().fullScreenIntent == null;
481 }
482
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700483 @Override
484 public int compareTo(HeadsUpEntry o) {
Selim Cineka59ecc32015-04-07 10:51:49 -0700485 return postTime < o.postTime ? 1
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700486 : postTime == o.postTime ? 0
Selim Cineka59ecc32015-04-07 10:51:49 -0700487 : -1;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700488 }
489
490 public void removeAutoCancelCallbacks() {
491 mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
492 }
493
494 public boolean wasShownLongEnough() {
495 return earliestRemovaltime < mClock.currentTimeMillis();
496 }
497
498 public void hideAsSoonAsPossible() {
499 removeAutoCancelCallbacks();
500 mHandler.postDelayed(mRemoveHeadsUpRunnable,
501 earliestRemovaltime - mClock.currentTimeMillis());
502 }
503 }
504
Selim Cineka59ecc32015-04-07 10:51:49 -0700505 /**
506 * Update the sorted heads up order.
507 *
508 * @param headsUpEntry the headsUp that changed
509 */
510 private void updateSortOrder(HeadsUpEntry headsUpEntry) {
511 mSortedEntries.remove(headsUpEntry);
512 mSortedEntries.add(headsUpEntry);
513 }
514
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700515 public static class Clock {
516 public long currentTimeMillis() {
517 return SystemClock.elapsedRealtime();
518 }
519 }
520
521 public interface OnHeadsUpChangedListener {
522 void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
Selim Cinekaac93252015-04-14 20:04:12 -0700523 void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700524 void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
525 }
526}