blob: b84dc476dd6f72309b855169dba7631ddcbafb2f [file] [log] [blame]
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001/*
Selim Cinek684a4422015-04-15 16:18:39 -07002 * Copyright (C) 2015 The Android Open Source Project
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003 *
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
Ned Burns1a5e22f2019-02-14 15:11:52 -050019import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
Kevind5022f92018-10-08 18:30:26 -070020
yoshiki iguchi4e30e762018-02-06 12:09:23 +090021import android.annotation.NonNull;
22import android.annotation.Nullable;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070023import android.content.Context;
24import android.content.res.Resources;
25import android.database.ContentObserver;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090026import android.provider.Settings;
Gus Prevasab336792018-11-14 13:52:20 -050027import android.util.ArrayMap;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070028import android.util.Log;
Dieter Hsubb0fdbc2019-03-08 20:56:28 +080029import android.view.accessibility.AccessibilityManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070030
Chris Wrenb659c4f2015-06-25 17:12:27 -040031import com.android.internal.logging.MetricsLogger;
Dieter Hsubb0fdbc2019-03-08 20:56:28 +080032import com.android.systemui.Dependency;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070033import com.android.systemui.R;
Kevind4f66a42018-08-03 13:12:51 -070034import com.android.systemui.statusbar.AlertingNotificationManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050035import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Ned Burns1a5e22f2019-02-14 15:11:52 -050036import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070037
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40import java.util.HashSet;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070041
Selim Cinek684a4422015-04-15 16:18:39 -070042/**
43 * A manager which handles heads up notifications which is a special mode where
44 * they simply peek from the top of the screen.
45 */
Kevind4f66a42018-08-03 13:12:51 -070046public abstract class HeadsUpManager extends AlertingNotificationManager {
Selim Cinekb8f09cf2015-03-16 17:09:28 -070047 private static final String TAG = "HeadsUpManager";
Selim Cinekb8f09cf2015-03-16 17:09:28 -070048 private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
49
yoshiki iguchi4e30e762018-02-06 12:09:23 +090050 protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -070051
yoshiki iguchi4e30e762018-02-06 12:09:23 +090052 protected final Context mContext;
53
yoshiki iguchi4e30e762018-02-06 12:09:23 +090054 protected int mTouchAcceptanceDelay;
55 protected int mSnoozeLengthMs;
56 protected boolean mHasPinnedNotification;
57 protected int mUser;
58
Selim Cinekb8f09cf2015-03-16 17:09:28 -070059 private final ArrayMap<String, Long> mSnoozedPackages;
Dieter Hsubb0fdbc2019-03-08 20:56:28 +080060 private final AccessibilityManagerWrapper mAccessibilityMgr;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070061
yoshiki iguchi4e30e762018-02-06 12:09:23 +090062 public HeadsUpManager(@NonNull final Context context) {
Chris Wrenb659c4f2015-06-25 17:12:27 -040063 mContext = context;
Dieter Hsubb0fdbc2019-03-08 20:56:28 +080064 mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
yoshiki iguchi4e30e762018-02-06 12:09:23 +090065 Resources resources = context.getResources();
yoshiki iguchi0adf6a62018-02-01 13:46:26 +090066 mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
Kevind4f66a42018-08-03 13:12:51 -070067 mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
yoshiki iguchi4e30e762018-02-06 12:09:23 +090068 mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
69 mSnoozedPackages = new ArrayMap<>();
70 int defaultSnoozeLengthMs =
71 resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
Selim Cinekb8f09cf2015-03-16 17:09:28 -070072
73 mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +090074 SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
75 ContentObserver settingsObserver = new ContentObserver(mHandler) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -070076 @Override
77 public void onChange(boolean selfChange) {
78 final int packageSnoozeLengthMs = Settings.Global.getInt(
79 context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
80 if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
81 mSnoozeLengthMs = packageSnoozeLengthMs;
Kevind4f66a42018-08-03 13:12:51 -070082 if (Log.isLoggable(TAG, Log.VERBOSE)) {
83 Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
84 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -070085 }
86 }
87 };
88 context.getContentResolver().registerContentObserver(
89 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
yoshiki iguchi4e30e762018-02-06 12:09:23 +090090 settingsObserver);
Selim Cinek737bff32015-05-08 16:08:35 -070091 }
92
yoshiki iguchi4e30e762018-02-06 12:09:23 +090093 /**
94 * Adds an OnHeadUpChangedListener to observe events.
95 */
96 public void addListener(@NonNull OnHeadsUpChangedListener listener) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -070097 mListeners.add(listener);
98 }
99
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900100 /**
101 * Removes the OnHeadUpChangedListener from the observer list.
102 */
103 public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
Jason Monk7ebba0b2017-04-20 14:10:20 -0400104 mListeners.remove(listener);
105 }
106
Kevind4f66a42018-08-03 13:12:51 -0700107 public void updateNotification(@NonNull String key, boolean alert) {
108 super.updateNotification(key, alert);
109 AlertEntry alertEntry = getHeadsUpEntry(key);
110 if (alert && alertEntry != null) {
111 setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700112 }
113 }
114
Ned Burnsf81c4c42019-01-07 14:10:43 -0500115 protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900116 return hasFullScreenIntent(entry);
Selim Cinek131c1e22015-05-11 19:04:49 -0700117 }
118
Ned Burnsf81c4c42019-01-07 14:10:43 -0500119 protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
Selim Cinek131c1e22015-05-11 19:04:49 -0700120 return entry.notification.getNotification().fullScreenIntent != null;
121 }
122
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900123 protected void setEntryPinned(
124 @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
Kevind4f66a42018-08-03 13:12:51 -0700125 if (Log.isLoggable(TAG, Log.VERBOSE)) {
126 Log.v(TAG, "setEntryPinned: " + isPinned);
127 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500128 NotificationEntry entry = headsUpEntry.mEntry;
Evan Laird94492852018-10-25 13:43:01 -0400129 if (entry.isRowPinned() != isPinned) {
130 entry.setRowPinned(isPinned);
Selim Cinek684a4422015-04-15 16:18:39 -0700131 updatePinnedMode();
132 for (OnHeadsUpChangedListener listener : mListeners) {
133 if (isPinned) {
Evan Laird94492852018-10-25 13:43:01 -0400134 listener.onHeadsUpPinned(entry);
Selim Cinek684a4422015-04-15 16:18:39 -0700135 } else {
Evan Laird94492852018-10-25 13:43:01 -0400136 listener.onHeadsUpUnPinned(entry);
Selim Cinek1f3f5442015-04-10 17:54:46 -0700137 }
138 }
139 }
140 }
141
Kevin01a53cb2018-11-09 18:19:54 -0800142 public @InflationFlag int getContentFlag() {
143 return FLAG_CONTENT_VIEW_HEADS_UP;
144 }
145
Kevind4f66a42018-08-03 13:12:51 -0700146 @Override
147 protected void onAlertEntryAdded(AlertEntry alertEntry) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500148 NotificationEntry entry = alertEntry.mEntry;
Evan Laird94492852018-10-25 13:43:01 -0400149 entry.setHeadsUp(true);
Kevind4f66a42018-08-03 13:12:51 -0700150 setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
151 for (OnHeadsUpChangedListener listener : mListeners) {
152 listener.onHeadsUpStateChanged(entry, true);
153 }
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900154 }
155
Kevind4f66a42018-08-03 13:12:51 -0700156 @Override
157 protected void onAlertEntryRemoved(AlertEntry alertEntry) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500158 NotificationEntry entry = alertEntry.mEntry;
Evan Laird94492852018-10-25 13:43:01 -0400159 entry.setHeadsUp(false);
Kevind4f66a42018-08-03 13:12:51 -0700160 setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700161 for (OnHeadsUpChangedListener listener : mListeners) {
Selim Cinek684a4422015-04-15 16:18:39 -0700162 listener.onHeadsUpStateChanged(entry, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700163 }
Evan Laird94492852018-10-25 13:43:01 -0400164 entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700165 }
166
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900167 protected void updatePinnedMode() {
Selim Cinek684a4422015-04-15 16:18:39 -0700168 boolean hasPinnedNotification = hasPinnedNotificationInternal();
169 if (hasPinnedNotification == mHasPinnedNotification) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700170 return;
171 }
Kevind4f66a42018-08-03 13:12:51 -0700172 if (Log.isLoggable(TAG, Log.VERBOSE)) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900173 Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
174 hasPinnedNotification);
175 }
Selim Cinek684a4422015-04-15 16:18:39 -0700176 mHasPinnedNotification = hasPinnedNotification;
Chris Wren063926b2015-10-16 16:24:20 -0400177 if (mHasPinnedNotification) {
178 MetricsLogger.count(mContext, "note_peek", 1);
179 }
Selim Cinek684a4422015-04-15 16:18:39 -0700180 for (OnHeadsUpChangedListener listener : mListeners) {
John Spurlockb349af572015-04-29 12:24:19 -0400181 listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700182 }
183 }
184
185 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900186 * Returns if the given notification is snoozed or not.
187 */
188 public boolean isSnoozed(@NonNull String packageName) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700189 final String key = snoozeKey(packageName, mUser);
190 Long snoozedUntil = mSnoozedPackages.get(key);
191 if (snoozedUntil != null) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900192 if (snoozedUntil > mClock.currentTimeMillis()) {
Kevind4f66a42018-08-03 13:12:51 -0700193 if (Log.isLoggable(TAG, Log.VERBOSE)) {
194 Log.v(TAG, key + " snoozed");
195 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700196 return true;
197 }
198 mSnoozedPackages.remove(packageName);
199 }
200 return false;
201 }
202
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900203 /**
204 * Snoozes all current Heads Up Notifications.
205 */
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700206 public void snooze() {
Kevind4f66a42018-08-03 13:12:51 -0700207 for (String key : mAlertEntries.keySet()) {
208 AlertEntry entry = getHeadsUpEntry(key);
209 String packageName = entry.mEntry.notification.getPackageName();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700210 mSnoozedPackages.put(snoozeKey(packageName, mUser),
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900211 mClock.currentTimeMillis() + mSnoozeLengthMs);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700212 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700213 }
214
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900215 @NonNull
216 private static String snoozeKey(@NonNull String packageName, int user) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700217 return user + "," + packageName;
218 }
219
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900220 @Nullable
221 protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
Kevind4f66a42018-08-03 13:12:51 -0700222 return (HeadsUpEntry) mAlertEntries.get(key);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700223 }
224
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900225 /**
Kevind4f66a42018-08-03 13:12:51 -0700226 * Returns the top Heads Up Notification, which appears to show at first.
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900227 */
228 @Nullable
Ned Burnsf81c4c42019-01-07 14:10:43 -0500229 public NotificationEntry getTopEntry() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900230 HeadsUpEntry topEntry = getTopHeadsUpEntry();
Kevind4f66a42018-08-03 13:12:51 -0700231 return (topEntry != null) ? topEntry.mEntry : null;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900232 }
233
234 @Nullable
235 protected HeadsUpEntry getTopHeadsUpEntry() {
Kevind4f66a42018-08-03 13:12:51 -0700236 if (mAlertEntries.isEmpty()) {
Selim Cinek3362c132016-02-11 15:43:03 -0800237 return null;
238 }
239 HeadsUpEntry topEntry = null;
Kevind4f66a42018-08-03 13:12:51 -0700240 for (AlertEntry entry: mAlertEntries.values()) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900241 if (topEntry == null || entry.compareTo(topEntry) < 0) {
Kevind4f66a42018-08-03 13:12:51 -0700242 topEntry = (HeadsUpEntry) entry;
Selim Cinek3362c132016-02-11 15:43:03 -0800243 }
244 }
245 return topEntry;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700246 }
247
248 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900249 * Sets the current user.
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700250 */
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700251 public void setUser(int user) {
252 mUser = user;
253 }
254
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900255 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700256 pw.println("HeadsUpManager state:");
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900257 dumpInternal(fd, pw, args);
258 }
259
260 protected void dumpInternal(
261 @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
Selim Cinek684a4422015-04-15 16:18:39 -0700262 pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700263 pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900264 pw.print(" now="); pw.println(mClock.currentTimeMillis());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700265 pw.print(" mUser="); pw.println(mUser);
Kevind4f66a42018-08-03 13:12:51 -0700266 for (AlertEntry entry: mAlertEntries.values()) {
267 pw.print(" HeadsUpEntry="); pw.println(entry.mEntry);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700268 }
269 int N = mSnoozedPackages.size();
270 pw.println(" snoozed packages: " + N);
271 for (int i = 0; i < N; i++) {
272 pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
273 pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
274 }
275 }
276
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900277 /**
278 * Returns if there are any pinned Heads Up Notifications or not.
279 */
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700280 public boolean hasPinnedHeadsUp() {
Selim Cinek684a4422015-04-15 16:18:39 -0700281 return mHasPinnedNotification;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700282 }
283
Selim Cinek684a4422015-04-15 16:18:39 -0700284 private boolean hasPinnedNotificationInternal() {
Kevind4f66a42018-08-03 13:12:51 -0700285 for (String key : mAlertEntries.keySet()) {
286 AlertEntry entry = getHeadsUpEntry(key);
Evan Laird94492852018-10-25 13:43:01 -0400287 if (entry.mEntry.isRowPinned()) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700288 return true;
289 }
290 }
291 return false;
292 }
293
Selim Cinek684a4422015-04-15 16:18:39 -0700294 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900295 * Unpins all pinned Heads Up Notifications.
felkachang3e99b0a2018-07-09 11:27:00 +0800296 * @param userUnPinned The unpinned action is trigger by user real operation.
Selim Cinek684a4422015-04-15 16:18:39 -0700297 */
felkachang3e99b0a2018-07-09 11:27:00 +0800298 public void unpinAll(boolean userUnPinned) {
Kevind4f66a42018-08-03 13:12:51 -0700299 for (String key : mAlertEntries.keySet()) {
300 HeadsUpEntry entry = getHeadsUpEntry(key);
Selim Cinek684a4422015-04-15 16:18:39 -0700301 setEntryPinned(entry, false /* isPinned */);
Selim Cinek31aada42015-12-18 17:51:15 -0800302 // maybe it got un sticky
303 entry.updateEntry(false /* updatePostTime */);
felkachang3e99b0a2018-07-09 11:27:00 +0800304
305 // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
306 // on the screen.
Evan Laird94492852018-10-25 13:43:01 -0400307 if (userUnPinned && entry.mEntry != null) {
308 if (entry.mEntry.mustStayOnScreen()) {
309 entry.mEntry.setHeadsUpIsVisible();
felkachang3e99b0a2018-07-09 11:27:00 +0800310 }
311 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700312 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700313 }
314
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900315 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900316 * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
317 * well.
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900318 */
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900319 public boolean isTrackingHeadsUp() {
320 // Might be implemented in subclass.
321 return false;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700322 }
323
Selim Cinek684a4422015-04-15 16:18:39 -0700324 /**
325 * Compare two entries and decide how they should be ranked.
326 *
327 * @return -1 if the first argument should be ranked higher than the second, 1 if the second
328 * one should be ranked higher and 0 if they are equal.
329 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500330 public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
Kevind4f66a42018-08-03 13:12:51 -0700331 AlertEntry aEntry = getHeadsUpEntry(a.key);
332 AlertEntry bEntry = getHeadsUpEntry(b.key);
Selim Cinekfbe9a442015-04-13 16:09:49 -0700333 if (aEntry == null || bEntry == null) {
334 return aEntry == null ? 1 : -1;
335 }
336 return aEntry.compareTo(bEntry);
337 }
338
Selim Cinek737bff32015-05-08 16:08:35 -0700339 /**
Selim Cinek31aada42015-12-18 17:51:15 -0800340 * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
341 * until it's collapsed again.
342 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500343 public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
Kevind4f66a42018-08-03 13:12:51 -0700344 HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
Evan Laird94492852018-10-25 13:43:01 -0400345 if (headsUpEntry != null && entry.isRowPinned()) {
Kevind4f66a42018-08-03 13:12:51 -0700346 headsUpEntry.setExpanded(expanded);
Selim Cinek31aada42015-12-18 17:51:15 -0800347 }
348 }
349
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900350 @NonNull
Kevind4f66a42018-08-03 13:12:51 -0700351 @Override
352 protected HeadsUpEntry createAlertEntry() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900353 return new HeadsUpEntry();
Selim Cineka7d4f822016-12-06 14:34:47 -0800354 }
355
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800356 public void onDensityOrFontScaleChanged() {
357 }
358
Selim Cineke3c6e462019-06-24 19:37:06 -0700359 public boolean isEntryAutoHeadsUpped(String key) {
360 return false;
361 }
362
Selim Cinek31aada42015-12-18 17:51:15 -0800363 /**
Selim Cinek684a4422015-04-15 16:18:39 -0700364 * This represents a notification and how long it is in a heads up mode. It also manages its
365 * lifecycle automatically when created.
366 */
Kevind4f66a42018-08-03 13:12:51 -0700367 protected class HeadsUpEntry extends AlertEntry {
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900368 public boolean remoteInputActive;
Kevind4f66a42018-08-03 13:12:51 -0700369 protected boolean expanded;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700370
Kevind4f66a42018-08-03 13:12:51 -0700371 @Override
372 protected boolean isSticky() {
Evan Laird94492852018-10-25 13:43:01 -0400373 return (mEntry.isRowPinned() && expanded)
Kevind4f66a42018-08-03 13:12:51 -0700374 || remoteInputActive || hasFullScreenIntent(mEntry);
Selim Cinek31aada42015-12-18 17:51:15 -0800375 }
376
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700377 @Override
Kevind4f66a42018-08-03 13:12:51 -0700378 public int compareTo(@NonNull AlertEntry alertEntry) {
379 HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
Evan Laird94492852018-10-25 13:43:01 -0400380 boolean isPinned = mEntry.isRowPinned();
381 boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
Selim Cinek3362c132016-02-11 15:43:03 -0800382 if (isPinned && !otherPinned) {
383 return -1;
384 } else if (!isPinned && otherPinned) {
385 return 1;
386 }
Kevind4f66a42018-08-03 13:12:51 -0700387 boolean selfFullscreen = hasFullScreenIntent(mEntry);
388 boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry);
Selim Cinek2ae71072015-10-20 16:07:12 -0700389 if (selfFullscreen && !otherFullscreen) {
390 return -1;
391 } else if (!selfFullscreen && otherFullscreen) {
392 return 1;
393 }
Adrian Roose211f572016-04-08 14:24:20 -0700394
Kevind4f66a42018-08-03 13:12:51 -0700395 if (remoteInputActive && !headsUpEntry.remoteInputActive) {
Adrian Roose211f572016-04-08 14:24:20 -0700396 return -1;
Kevind4f66a42018-08-03 13:12:51 -0700397 } else if (!remoteInputActive && headsUpEntry.remoteInputActive) {
Adrian Roose211f572016-04-08 14:24:20 -0700398 return 1;
399 }
400
Kevind4f66a42018-08-03 13:12:51 -0700401 return super.compareTo(headsUpEntry);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700402 }
403
Kevind4f66a42018-08-03 13:12:51 -0700404 public void setExpanded(boolean expanded) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900405 this.expanded = expanded;
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900406 }
407
Kevind4f66a42018-08-03 13:12:51 -0700408 @Override
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900409 public void reset() {
Kevind4f66a42018-08-03 13:12:51 -0700410 super.reset();
yoshiki iguchi0adf6a62018-02-01 13:46:26 +0900411 expanded = false;
412 remoteInputActive = false;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900413 }
414
Kevind4f66a42018-08-03 13:12:51 -0700415 @Override
416 protected long calculatePostTime() {
417 // The actual post time will be just after the heads-up really slided in
418 return super.calculatePostTime() + mTouchAcceptanceDelay;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700419 }
Dieter Hsubb0fdbc2019-03-08 20:56:28 +0800420
421 @Override
422 protected long calculateFinishTime() {
Selim Cineke3c6e462019-06-24 19:37:06 -0700423 return mPostTime + getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
Dieter Hsubb0fdbc2019-03-08 20:56:28 +0800424 }
425
426 /**
427 * Get user-preferred or default timeout duration. The larger one will be returned.
428 * @return milliseconds before auto-dismiss
Selim Cineke3c6e462019-06-24 19:37:06 -0700429 * @param requestedTimeout
Dieter Hsubb0fdbc2019-03-08 20:56:28 +0800430 */
Selim Cineke3c6e462019-06-24 19:37:06 -0700431 protected int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
Dieter Hsubb0fdbc2019-03-08 20:56:28 +0800432 return mAccessibilityMgr.getRecommendedTimeoutMillis(
Selim Cineke3c6e462019-06-24 19:37:06 -0700433 requestedTimeout,
Dieter Hsubb0fdbc2019-03-08 20:56:28 +0800434 AccessibilityManager.FLAG_CONTENT_CONTROLS
435 | AccessibilityManager.FLAG_CONTENT_ICONS
436 | AccessibilityManager.FLAG_CONTENT_TEXT);
437 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700438 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700439}