Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 1 | /* |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 2 | * Copyright (C) 2015 The Android Open Source Project |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 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 | |
| 17 | package com.android.systemui.statusbar.policy; |
| 18 | |
Ned Burns | 1a5e22f | 2019-02-14 15:11:52 -0500 | [diff] [blame] | 19 | import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; |
Kevin | d5022f9 | 2018-10-08 18:30:26 -0700 | [diff] [blame] | 20 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 21 | import android.annotation.NonNull; |
| 22 | import android.annotation.Nullable; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 23 | import android.content.Context; |
| 24 | import android.content.res.Resources; |
| 25 | import android.database.ContentObserver; |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 26 | import android.provider.Settings; |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 27 | import android.util.ArrayMap; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 28 | import android.util.Log; |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 29 | import android.view.accessibility.AccessibilityManager; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 30 | |
Chris Wren | b659c4f | 2015-06-25 17:12:27 -0400 | [diff] [blame] | 31 | import com.android.internal.logging.MetricsLogger; |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 32 | import com.android.systemui.Dependency; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 33 | import com.android.systemui.R; |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 34 | import com.android.systemui.statusbar.AlertingNotificationManager; |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 35 | import com.android.systemui.statusbar.notification.collection.NotificationEntry; |
Ned Burns | 1a5e22f | 2019-02-14 15:11:52 -0500 | [diff] [blame] | 36 | import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 37 | |
| 38 | import java.io.FileDescriptor; |
| 39 | import java.io.PrintWriter; |
| 40 | import java.util.HashSet; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 41 | |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 42 | /** |
| 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 | */ |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 46 | public abstract class HeadsUpManager extends AlertingNotificationManager { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 47 | private static final String TAG = "HeadsUpManager"; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 48 | private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; |
| 49 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 50 | protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 51 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 52 | protected final Context mContext; |
| 53 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 54 | protected int mTouchAcceptanceDelay; |
| 55 | protected int mSnoozeLengthMs; |
| 56 | protected boolean mHasPinnedNotification; |
| 57 | protected int mUser; |
| 58 | |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 59 | private final ArrayMap<String, Long> mSnoozedPackages; |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 60 | private final AccessibilityManagerWrapper mAccessibilityMgr; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 61 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 62 | public HeadsUpManager(@NonNull final Context context) { |
Chris Wren | b659c4f | 2015-06-25 17:12:27 -0400 | [diff] [blame] | 63 | mContext = context; |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 64 | mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class); |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 65 | Resources resources = context.getResources(); |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 66 | mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 67 | mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 68 | 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 Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 72 | |
| 73 | mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 74 | SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs); |
| 75 | ContentObserver settingsObserver = new ContentObserver(mHandler) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 76 | @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; |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 82 | if (Log.isLoggable(TAG, Log.VERBOSE)) { |
| 83 | Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs); |
| 84 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 85 | } |
| 86 | } |
| 87 | }; |
| 88 | context.getContentResolver().registerContentObserver( |
| 89 | Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 90 | settingsObserver); |
Selim Cinek | 737bff3 | 2015-05-08 16:08:35 -0700 | [diff] [blame] | 91 | } |
| 92 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 93 | /** |
| 94 | * Adds an OnHeadUpChangedListener to observe events. |
| 95 | */ |
| 96 | public void addListener(@NonNull OnHeadsUpChangedListener listener) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 97 | mListeners.add(listener); |
| 98 | } |
| 99 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 100 | /** |
| 101 | * Removes the OnHeadUpChangedListener from the observer list. |
| 102 | */ |
| 103 | public void removeListener(@NonNull OnHeadsUpChangedListener listener) { |
Jason Monk | 7ebba0b | 2017-04-20 14:10:20 -0400 | [diff] [blame] | 104 | mListeners.remove(listener); |
| 105 | } |
| 106 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 107 | 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 Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 115 | protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 116 | return hasFullScreenIntent(entry); |
Selim Cinek | 131c1e2 | 2015-05-11 19:04:49 -0700 | [diff] [blame] | 117 | } |
| 118 | |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 119 | protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) { |
Selim Cinek | 131c1e2 | 2015-05-11 19:04:49 -0700 | [diff] [blame] | 120 | return entry.notification.getNotification().fullScreenIntent != null; |
| 121 | } |
| 122 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 123 | protected void setEntryPinned( |
| 124 | @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 125 | if (Log.isLoggable(TAG, Log.VERBOSE)) { |
| 126 | Log.v(TAG, "setEntryPinned: " + isPinned); |
| 127 | } |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 128 | NotificationEntry entry = headsUpEntry.mEntry; |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 129 | if (entry.isRowPinned() != isPinned) { |
| 130 | entry.setRowPinned(isPinned); |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 131 | updatePinnedMode(); |
| 132 | for (OnHeadsUpChangedListener listener : mListeners) { |
| 133 | if (isPinned) { |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 134 | listener.onHeadsUpPinned(entry); |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 135 | } else { |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 136 | listener.onHeadsUpUnPinned(entry); |
Selim Cinek | 1f3f544 | 2015-04-10 17:54:46 -0700 | [diff] [blame] | 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
Kevin | 01a53cb | 2018-11-09 18:19:54 -0800 | [diff] [blame] | 142 | public @InflationFlag int getContentFlag() { |
| 143 | return FLAG_CONTENT_VIEW_HEADS_UP; |
| 144 | } |
| 145 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 146 | @Override |
| 147 | protected void onAlertEntryAdded(AlertEntry alertEntry) { |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 148 | NotificationEntry entry = alertEntry.mEntry; |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 149 | entry.setHeadsUp(true); |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 150 | setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry)); |
| 151 | for (OnHeadsUpChangedListener listener : mListeners) { |
| 152 | listener.onHeadsUpStateChanged(entry, true); |
| 153 | } |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 154 | } |
| 155 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 156 | @Override |
| 157 | protected void onAlertEntryRemoved(AlertEntry alertEntry) { |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 158 | NotificationEntry entry = alertEntry.mEntry; |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 159 | entry.setHeadsUp(false); |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 160 | setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 161 | for (OnHeadsUpChangedListener listener : mListeners) { |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 162 | listener.onHeadsUpStateChanged(entry, false); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 163 | } |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 164 | entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 165 | } |
| 166 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 167 | protected void updatePinnedMode() { |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 168 | boolean hasPinnedNotification = hasPinnedNotificationInternal(); |
| 169 | if (hasPinnedNotification == mHasPinnedNotification) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 170 | return; |
| 171 | } |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 172 | if (Log.isLoggable(TAG, Log.VERBOSE)) { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 173 | Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " + |
| 174 | hasPinnedNotification); |
| 175 | } |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 176 | mHasPinnedNotification = hasPinnedNotification; |
Chris Wren | 063926b | 2015-10-16 16:24:20 -0400 | [diff] [blame] | 177 | if (mHasPinnedNotification) { |
| 178 | MetricsLogger.count(mContext, "note_peek", 1); |
| 179 | } |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 180 | for (OnHeadsUpChangedListener listener : mListeners) { |
John Spurlock | b349af57 | 2015-04-29 12:24:19 -0400 | [diff] [blame] | 181 | listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 182 | } |
| 183 | } |
| 184 | |
| 185 | /** |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 186 | * Returns if the given notification is snoozed or not. |
| 187 | */ |
| 188 | public boolean isSnoozed(@NonNull String packageName) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 189 | final String key = snoozeKey(packageName, mUser); |
| 190 | Long snoozedUntil = mSnoozedPackages.get(key); |
| 191 | if (snoozedUntil != null) { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 192 | if (snoozedUntil > mClock.currentTimeMillis()) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 193 | if (Log.isLoggable(TAG, Log.VERBOSE)) { |
| 194 | Log.v(TAG, key + " snoozed"); |
| 195 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 196 | return true; |
| 197 | } |
| 198 | mSnoozedPackages.remove(packageName); |
| 199 | } |
| 200 | return false; |
| 201 | } |
| 202 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 203 | /** |
| 204 | * Snoozes all current Heads Up Notifications. |
| 205 | */ |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 206 | public void snooze() { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 207 | for (String key : mAlertEntries.keySet()) { |
| 208 | AlertEntry entry = getHeadsUpEntry(key); |
| 209 | String packageName = entry.mEntry.notification.getPackageName(); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 210 | mSnoozedPackages.put(snoozeKey(packageName, mUser), |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 211 | mClock.currentTimeMillis() + mSnoozeLengthMs); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 212 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 213 | } |
| 214 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 215 | @NonNull |
| 216 | private static String snoozeKey(@NonNull String packageName, int user) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 217 | return user + "," + packageName; |
| 218 | } |
| 219 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 220 | @Nullable |
| 221 | protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 222 | return (HeadsUpEntry) mAlertEntries.get(key); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 223 | } |
| 224 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 225 | /** |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 226 | * Returns the top Heads Up Notification, which appears to show at first. |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 227 | */ |
| 228 | @Nullable |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 229 | public NotificationEntry getTopEntry() { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 230 | HeadsUpEntry topEntry = getTopHeadsUpEntry(); |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 231 | return (topEntry != null) ? topEntry.mEntry : null; |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | @Nullable |
| 235 | protected HeadsUpEntry getTopHeadsUpEntry() { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 236 | if (mAlertEntries.isEmpty()) { |
Selim Cinek | 3362c13 | 2016-02-11 15:43:03 -0800 | [diff] [blame] | 237 | return null; |
| 238 | } |
| 239 | HeadsUpEntry topEntry = null; |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 240 | for (AlertEntry entry: mAlertEntries.values()) { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 241 | if (topEntry == null || entry.compareTo(topEntry) < 0) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 242 | topEntry = (HeadsUpEntry) entry; |
Selim Cinek | 3362c13 | 2016-02-11 15:43:03 -0800 | [diff] [blame] | 243 | } |
| 244 | } |
| 245 | return topEntry; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | /** |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 249 | * Sets the current user. |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 250 | */ |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 251 | public void setUser(int user) { |
| 252 | mUser = user; |
| 253 | } |
| 254 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 255 | public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 256 | pw.println("HeadsUpManager state:"); |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 257 | dumpInternal(fd, pw, args); |
| 258 | } |
| 259 | |
| 260 | protected void dumpInternal( |
| 261 | @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 262 | pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 263 | pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 264 | pw.print(" now="); pw.println(mClock.currentTimeMillis()); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 265 | pw.print(" mUser="); pw.println(mUser); |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 266 | for (AlertEntry entry: mAlertEntries.values()) { |
| 267 | pw.print(" HeadsUpEntry="); pw.println(entry.mEntry); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 268 | } |
| 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 iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 277 | /** |
| 278 | * Returns if there are any pinned Heads Up Notifications or not. |
| 279 | */ |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 280 | public boolean hasPinnedHeadsUp() { |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 281 | return mHasPinnedNotification; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 282 | } |
| 283 | |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 284 | private boolean hasPinnedNotificationInternal() { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 285 | for (String key : mAlertEntries.keySet()) { |
| 286 | AlertEntry entry = getHeadsUpEntry(key); |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 287 | if (entry.mEntry.isRowPinned()) { |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 288 | return true; |
| 289 | } |
| 290 | } |
| 291 | return false; |
| 292 | } |
| 293 | |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 294 | /** |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 295 | * Unpins all pinned Heads Up Notifications. |
felkachang | 3e99b0a | 2018-07-09 11:27:00 +0800 | [diff] [blame] | 296 | * @param userUnPinned The unpinned action is trigger by user real operation. |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 297 | */ |
felkachang | 3e99b0a | 2018-07-09 11:27:00 +0800 | [diff] [blame] | 298 | public void unpinAll(boolean userUnPinned) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 299 | for (String key : mAlertEntries.keySet()) { |
| 300 | HeadsUpEntry entry = getHeadsUpEntry(key); |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 301 | setEntryPinned(entry, false /* isPinned */); |
Selim Cinek | 31aada4 | 2015-12-18 17:51:15 -0800 | [diff] [blame] | 302 | // maybe it got un sticky |
| 303 | entry.updateEntry(false /* updatePostTime */); |
felkachang | 3e99b0a | 2018-07-09 11:27:00 +0800 | [diff] [blame] | 304 | |
| 305 | // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay |
| 306 | // on the screen. |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 307 | if (userUnPinned && entry.mEntry != null) { |
| 308 | if (entry.mEntry.mustStayOnScreen()) { |
| 309 | entry.mEntry.setHeadsUpIsVisible(); |
felkachang | 3e99b0a | 2018-07-09 11:27:00 +0800 | [diff] [blame] | 310 | } |
| 311 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 312 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 313 | } |
| 314 | |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 315 | /** |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 316 | * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as |
| 317 | * well. |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 318 | */ |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 319 | public boolean isTrackingHeadsUp() { |
| 320 | // Might be implemented in subclass. |
| 321 | return false; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 322 | } |
| 323 | |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 324 | /** |
| 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 Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 330 | public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 331 | AlertEntry aEntry = getHeadsUpEntry(a.key); |
| 332 | AlertEntry bEntry = getHeadsUpEntry(b.key); |
Selim Cinek | fbe9a44 | 2015-04-13 16:09:49 -0700 | [diff] [blame] | 333 | if (aEntry == null || bEntry == null) { |
| 334 | return aEntry == null ? 1 : -1; |
| 335 | } |
| 336 | return aEntry.compareTo(bEntry); |
| 337 | } |
| 338 | |
Selim Cinek | 737bff3 | 2015-05-08 16:08:35 -0700 | [diff] [blame] | 339 | /** |
Selim Cinek | 31aada4 | 2015-12-18 17:51:15 -0800 | [diff] [blame] | 340 | * 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 Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 343 | public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 344 | HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key); |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 345 | if (headsUpEntry != null && entry.isRowPinned()) { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 346 | headsUpEntry.setExpanded(expanded); |
Selim Cinek | 31aada4 | 2015-12-18 17:51:15 -0800 | [diff] [blame] | 347 | } |
| 348 | } |
| 349 | |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 350 | @NonNull |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 351 | @Override |
| 352 | protected HeadsUpEntry createAlertEntry() { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 353 | return new HeadsUpEntry(); |
Selim Cinek | a7d4f82 | 2016-12-06 14:34:47 -0800 | [diff] [blame] | 354 | } |
| 355 | |
Selim Cinek | aa9db1f | 2018-02-27 17:35:47 -0800 | [diff] [blame] | 356 | public void onDensityOrFontScaleChanged() { |
| 357 | } |
| 358 | |
Selim Cinek | e3c6e46 | 2019-06-24 19:37:06 -0700 | [diff] [blame] | 359 | public boolean isEntryAutoHeadsUpped(String key) { |
| 360 | return false; |
| 361 | } |
| 362 | |
Selim Cinek | 31aada4 | 2015-12-18 17:51:15 -0800 | [diff] [blame] | 363 | /** |
Selim Cinek | 684a442 | 2015-04-15 16:18:39 -0700 | [diff] [blame] | 364 | * This represents a notification and how long it is in a heads up mode. It also manages its |
| 365 | * lifecycle automatically when created. |
| 366 | */ |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 367 | protected class HeadsUpEntry extends AlertEntry { |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 368 | public boolean remoteInputActive; |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 369 | protected boolean expanded; |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 370 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 371 | @Override |
| 372 | protected boolean isSticky() { |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 373 | return (mEntry.isRowPinned() && expanded) |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 374 | || remoteInputActive || hasFullScreenIntent(mEntry); |
Selim Cinek | 31aada4 | 2015-12-18 17:51:15 -0800 | [diff] [blame] | 375 | } |
| 376 | |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 377 | @Override |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 378 | public int compareTo(@NonNull AlertEntry alertEntry) { |
| 379 | HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry; |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 380 | boolean isPinned = mEntry.isRowPinned(); |
| 381 | boolean otherPinned = headsUpEntry.mEntry.isRowPinned(); |
Selim Cinek | 3362c13 | 2016-02-11 15:43:03 -0800 | [diff] [blame] | 382 | if (isPinned && !otherPinned) { |
| 383 | return -1; |
| 384 | } else if (!isPinned && otherPinned) { |
| 385 | return 1; |
| 386 | } |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 387 | boolean selfFullscreen = hasFullScreenIntent(mEntry); |
| 388 | boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry); |
Selim Cinek | 2ae7107 | 2015-10-20 16:07:12 -0700 | [diff] [blame] | 389 | if (selfFullscreen && !otherFullscreen) { |
| 390 | return -1; |
| 391 | } else if (!selfFullscreen && otherFullscreen) { |
| 392 | return 1; |
| 393 | } |
Adrian Roos | e211f57 | 2016-04-08 14:24:20 -0700 | [diff] [blame] | 394 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 395 | if (remoteInputActive && !headsUpEntry.remoteInputActive) { |
Adrian Roos | e211f57 | 2016-04-08 14:24:20 -0700 | [diff] [blame] | 396 | return -1; |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 397 | } else if (!remoteInputActive && headsUpEntry.remoteInputActive) { |
Adrian Roos | e211f57 | 2016-04-08 14:24:20 -0700 | [diff] [blame] | 398 | return 1; |
| 399 | } |
| 400 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 401 | return super.compareTo(headsUpEntry); |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 402 | } |
| 403 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 404 | public void setExpanded(boolean expanded) { |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 405 | this.expanded = expanded; |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 406 | } |
| 407 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 408 | @Override |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 409 | public void reset() { |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 410 | super.reset(); |
yoshiki iguchi | 0adf6a6 | 2018-02-01 13:46:26 +0900 | [diff] [blame] | 411 | expanded = false; |
| 412 | remoteInputActive = false; |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 413 | } |
| 414 | |
Kevin | d4f66a4 | 2018-08-03 13:12:51 -0700 | [diff] [blame] | 415 | @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 Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 419 | } |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 420 | |
| 421 | @Override |
| 422 | protected long calculateFinishTime() { |
Selim Cinek | e3c6e46 | 2019-06-24 19:37:06 -0700 | [diff] [blame] | 423 | return mPostTime + getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay); |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 424 | } |
| 425 | |
| 426 | /** |
| 427 | * Get user-preferred or default timeout duration. The larger one will be returned. |
| 428 | * @return milliseconds before auto-dismiss |
Selim Cinek | e3c6e46 | 2019-06-24 19:37:06 -0700 | [diff] [blame] | 429 | * @param requestedTimeout |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 430 | */ |
Selim Cinek | e3c6e46 | 2019-06-24 19:37:06 -0700 | [diff] [blame] | 431 | protected int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) { |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 432 | return mAccessibilityMgr.getRecommendedTimeoutMillis( |
Selim Cinek | e3c6e46 | 2019-06-24 19:37:06 -0700 | [diff] [blame] | 433 | requestedTimeout, |
Dieter Hsu | bb0fdbc | 2019-03-08 20:56:28 +0800 | [diff] [blame] | 434 | AccessibilityManager.FLAG_CONTENT_CONTROLS |
| 435 | | AccessibilityManager.FLAG_CONTENT_ICONS |
| 436 | | AccessibilityManager.FLAG_CONTENT_TEXT); |
| 437 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 438 | } |
Selim Cinek | b8f09cf | 2015-03-16 17:09:28 -0700 | [diff] [blame] | 439 | } |