blob: dccf2e21fa2cdfda860ec19914864ad738af2643 [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) {
317 if (mClock.currentTimeMillis() < mHeadsUpEntries.get(key).postTime) {
318 return true;
319 }
320 return false;
321 }
322
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700323 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
Selim Cineka59ecc32015-04-07 10:51:49 -0700324 if (!mIsExpanded && mHasPinnedHeadsUp) {
325 int minX = Integer.MAX_VALUE;
326 int maxX = 0;
327 int minY = Integer.MAX_VALUE;
328 int maxY = 0;
329 for (HeadsUpEntry entry: mSortedEntries) {
330 ExpandableNotificationRow row = entry.entry.row;
331 if (!row.isInShade()) {
332 row.getLocationOnScreen(mTmpTwoArray);
333 minX = Math.min(minX, mTmpTwoArray[0]);
334 minY = Math.min(minY, 0);
335 maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
336 maxY = Math.max(maxY, row.getHeadsUpHeight());
337 }
338 }
339
340 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
341 info.touchableRegion.set(minX, minY, maxX, maxY);
342 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700343 }
344
345 public void setUser(int user) {
346 mUser = user;
347 }
348
349 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
350 pw.println("HeadsUpManager state:");
351 pw.print(" mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
352 pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
353 pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
354 pw.print(" mUser="); pw.println(mUser);
Selim Cineka59ecc32015-04-07 10:51:49 -0700355 for (HeadsUpEntry entry: mSortedEntries) {
356 pw.print(" HeadsUpEntry="); pw.println(entry.entry);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700357 }
358 int N = mSnoozedPackages.size();
359 pw.println(" snoozed packages: " + N);
360 for (int i = 0; i < N; i++) {
361 pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
362 pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
363 }
364 }
365
366 public boolean hasPinnedHeadsUp() {
367 return mHasPinnedHeadsUp;
368 }
369
370 private boolean hasPinnedHeadsUpInternal() {
371 for (String key: mHeadsUpEntries.keySet()) {
372 HeadsUpEntry entry = mHeadsUpEntries.get(key);
373 if (!entry.entry.row.isInShade()) {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 public void addSwipedOutKey(String key) {
381 mSwipedOutKeys.add(key);
382 }
383
384 public float getHighestPinnedHeadsUp() {
385 float max = 0;
Selim Cineka59ecc32015-04-07 10:51:49 -0700386 for (HeadsUpEntry entry: mSortedEntries) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700387 if (!entry.entry.row.isInShade()) {
388 max = Math.max(max, entry.entry.row.getActualHeight());
389 }
390 }
391 return max;
392 }
393
394 public void releaseAllToShade() {
395 for (String key: mHeadsUpEntries.keySet()) {
396 HeadsUpEntry entry = mHeadsUpEntries.get(key);
Selim Cinekaac93252015-04-14 20:04:12 -0700397 setEntryToShade(entry, true /* toShade */, false /* justAdded */,
398 true /* forceImmediate */);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700399 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700400 }
401
402 public void onExpandingFinished() {
403 if (mReleaseOnExpandFinish) {
404 releaseAllImmediately();
405 mReleaseOnExpandFinish = false;
406 } else {
407 for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
408 removeHeadsUpEntry(entry);
409 }
410 mEntriesToRemoveAfterExpand.clear();
411 }
412 }
413
414 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
415 mTrackingHeadsUp = trackingHeadsUp;
416 }
417
418 public void setIsExpanded(boolean isExpanded) {
Selim Cinek1f3f5442015-04-10 17:54:46 -0700419 if (isExpanded != mIsExpanded) {
420 mIsExpanded = isExpanded;
421 if (isExpanded) {
422 releaseAllToShade();
423 }
424 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700425 }
426
427 public int getTopHeadsUpHeight() {
428 HeadsUpEntry topEntry = getTopEntry();
429 return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
430 }
431
Selim Cinekfbe9a442015-04-13 16:09:49 -0700432 public int compare(NotificationData.Entry a, NotificationData.Entry b) {
433 HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
434 HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
435 if (aEntry == null || bEntry == null) {
436 return aEntry == null ? 1 : -1;
437 }
438 return aEntry.compareTo(bEntry);
439 }
440
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700441 public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
442 public NotificationData.Entry entry;
443 public long postTime;
444 public long earliestRemovaltime;
445 private Runnable mRemoveHeadsUpRunnable;
446
447 public void setEntry(final NotificationData.Entry entry) {
448 this.entry = entry;
449
450 // The actual post time will be just after the heads-up really slided in
451 postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
452 mRemoveHeadsUpRunnable = new Runnable() {
453 @Override
454 public void run() {
455 if (!mTrackingHeadsUp) {
456 removeHeadsUpEntry(entry);
457 } else {
458 mEntriesToRemoveAfterExpand.add(entry);
459 }
460 }
461 };
462 updateEntry();
463 }
464
465 public void updateEntry() {
466 long currentTime = mClock.currentTimeMillis();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700467 earliestRemovaltime = currentTime + mMinimumDisplayTime;
Selim Cinek31d9ef72015-04-15 19:29:49 -0700468 postTime = Math.max(postTime, currentTime);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700469 removeAutoCancelCallbacks();
Selim Cinek31d9ef72015-04-15 19:29:49 -0700470 if (canEntryDecay()) {
471 long finishTime = postTime + mHeadsUpNotificationDecay;
472 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
473 mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
474 }
Selim Cineka59ecc32015-04-07 10:51:49 -0700475 updateSortOrder(HeadsUpEntry.this);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700476 }
477
Selim Cinek31d9ef72015-04-15 19:29:49 -0700478 private boolean canEntryDecay() {
479 return entry.notification.getNotification().fullScreenIntent == null;
480 }
481
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700482 @Override
483 public int compareTo(HeadsUpEntry o) {
Selim Cineka59ecc32015-04-07 10:51:49 -0700484 return postTime < o.postTime ? 1
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700485 : postTime == o.postTime ? 0
Selim Cineka59ecc32015-04-07 10:51:49 -0700486 : -1;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700487 }
488
489 public void removeAutoCancelCallbacks() {
490 mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
491 }
492
493 public boolean wasShownLongEnough() {
494 return earliestRemovaltime < mClock.currentTimeMillis();
495 }
496
497 public void hideAsSoonAsPossible() {
498 removeAutoCancelCallbacks();
499 mHandler.postDelayed(mRemoveHeadsUpRunnable,
500 earliestRemovaltime - mClock.currentTimeMillis());
501 }
502 }
503
Selim Cineka59ecc32015-04-07 10:51:49 -0700504 /**
505 * Update the sorted heads up order.
506 *
507 * @param headsUpEntry the headsUp that changed
508 */
509 private void updateSortOrder(HeadsUpEntry headsUpEntry) {
510 mSortedEntries.remove(headsUpEntry);
511 mSortedEntries.add(headsUpEntry);
512 }
513
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700514 public static class Clock {
515 public long currentTimeMillis() {
516 return SystemClock.elapsedRealtime();
517 }
518 }
519
520 public interface OnHeadsUpChangedListener {
521 void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
Selim Cinekaac93252015-04-14 20:04:12 -0700522 void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700523 void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
524 }
525}