blob: f6d3cdfddb31ca900eba4e5cf726d5bf1b597825 [file] [log] [blame]
Gus Prevasb43dc652018-12-20 13:11:45 -05001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.notification;
18
19import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
Ned Burns1a5e22f2019-02-14 15:11:52 -050020import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
21import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
Gus Prevasb43dc652018-12-20 13:11:45 -050022
23import android.app.Notification;
24import android.service.notification.StatusBarNotification;
25import android.util.Log;
26
Gus Prevasca1b6f72018-12-28 10:53:11 -050027import com.android.internal.statusbar.NotificationVisibility;
Gus Prevasb43dc652018-12-20 13:11:45 -050028import com.android.systemui.statusbar.AlertingNotificationManager;
29import com.android.systemui.statusbar.AmbientPulseManager;
30import com.android.systemui.statusbar.NotificationListener;
31import com.android.systemui.statusbar.NotificationRemoteInputManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050032import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Ned Burns1a5e22f2019-02-14 15:11:52 -050033import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
Gus Prevasb43dc652018-12-20 13:11:45 -050034import com.android.systemui.statusbar.phone.ShadeController;
35import com.android.systemui.statusbar.policy.HeadsUpManager;
36
37import javax.inject.Inject;
38import javax.inject.Singleton;
39
40import dagger.Lazy;
41
42/** Handles heads-up and pulsing behavior driven by notification changes. */
43@Singleton
44public class NotificationAlertingManager {
45
46 private static final String TAG = "NotifAlertManager";
47
48 private final AmbientPulseManager mAmbientPulseManager;
49 private final NotificationRemoteInputManager mRemoteInputManager;
50 private final VisualStabilityManager mVisualStabilityManager;
51 private final Lazy<ShadeController> mShadeController;
52 private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
53 private final NotificationListener mNotificationListener;
54
55 private HeadsUpManager mHeadsUpManager;
56
57 @Inject
58 public NotificationAlertingManager(
59 NotificationEntryManager notificationEntryManager,
60 AmbientPulseManager ambientPulseManager,
61 NotificationRemoteInputManager remoteInputManager,
62 VisualStabilityManager visualStabilityManager,
63 Lazy<ShadeController> shadeController,
64 NotificationInterruptionStateProvider notificationInterruptionStateProvider,
65 NotificationListener notificationListener) {
66 mAmbientPulseManager = ambientPulseManager;
67 mRemoteInputManager = remoteInputManager;
68 mVisualStabilityManager = visualStabilityManager;
69 mShadeController = shadeController;
70 mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
71 mNotificationListener = notificationListener;
72
73 notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
74 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -050075 public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
Gus Prevasb43dc652018-12-20 13:11:45 -050076 showAlertingView(entry, inflatedFlags);
77 }
78
79 @Override
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080080 public void onPostEntryUpdated(NotificationEntry entry) {
Gus Prevasb43dc652018-12-20 13:11:45 -050081 updateAlertState(entry);
82 }
83
84 @Override
Gus Prevasf37435a2018-12-20 15:40:01 -050085 public void onEntryRemoved(
Ned Burnsf81c4c42019-01-07 14:10:43 -050086 NotificationEntry entry,
Gus Prevasca1b6f72018-12-28 10:53:11 -050087 NotificationVisibility visibility,
Gus Prevasdca2be52018-12-21 11:25:10 -050088 boolean removedByUser) {
Ned Burnsef2ef6c2019-01-02 16:48:08 -050089 stopAlerting(entry.key);
Gus Prevasb43dc652018-12-20 13:11:45 -050090 }
91 });
92 }
93
94 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
95 mHeadsUpManager = headsUpManager;
96 }
97
98 /**
99 * Adds the entry to the respective alerting manager if the content view was inflated and
100 * the entry should still alert.
101 *
102 * @param entry entry to add
103 * @param inflatedFlags flags representing content views that were inflated
104 */
Ned Burns1a5e22f2019-02-14 15:11:52 -0500105 private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500106 if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
107 // Possible for shouldHeadsUp to change between the inflation starting and ending.
108 // If it does and we no longer need to heads up, we should free the view.
109 if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
110 mHeadsUpManager.showNotification(entry);
111 // Mark as seen immediately
112 setNotificationShown(entry.notification);
113 } else {
114 entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
115 }
116 }
117 if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
118 if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
119 mAmbientPulseManager.showNotification(entry);
120 } else {
121 entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
122 }
123 }
124 }
125
Ned Burnsf81c4c42019-01-07 14:10:43 -0500126 private void updateAlertState(NotificationEntry entry) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500127 boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
128 AlertingNotificationManager alertManager;
129 boolean shouldAlert;
130 if (mShadeController.get().isDozing()) {
131 alertManager = mAmbientPulseManager;
132 shouldAlert = mNotificationInterruptionStateProvider.shouldPulse(entry);
133 } else {
134 alertManager = mHeadsUpManager;
135 shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
136 }
137 final boolean wasAlerting = alertManager.isAlerting(entry.key);
138 if (wasAlerting) {
139 if (!shouldAlert) {
140 // We don't want this to be interrupting anymore, let's remove it
141 alertManager.removeNotification(entry.key,
142 false /* ignoreEarliestRemovalTime */);
143 } else {
144 alertManager.updateNotification(entry.key, alertAgain);
145 }
146 } else if (shouldAlert && alertAgain) {
147 // This notification was updated to be alerting, show it!
148 alertManager.showNotification(entry);
149 }
150 }
151
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800152 /**
153 * Checks whether an update for a notification warrants an alert for the user.
154 *
155 * @param oldEntry the entry for this notification.
156 * @param newNotification the new notification for this entry.
157 * @return whether this notification should alert the user.
158 */
159 public static boolean alertAgain(
Ned Burnsf81c4c42019-01-07 14:10:43 -0500160 NotificationEntry oldEntry, Notification newNotification) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500161 return oldEntry == null || !oldEntry.hasInterrupted()
162 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
163 }
164
165 private void setNotificationShown(StatusBarNotification n) {
166 try {
167 mNotificationListener.setNotificationsShown(new String[]{n.getKey()});
168 } catch (RuntimeException e) {
169 Log.d(TAG, "failed setNotificationsShown: ", e);
170 }
171 }
172
Gus Prevasf37435a2018-12-20 15:40:01 -0500173 private void stopAlerting(final String key) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500174 // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
175 // Though the remove itself may fail, it lets the manager know to remove as soon as
176 // possible.
Gus Prevasf37435a2018-12-20 15:40:01 -0500177 if (mHeadsUpManager.isAlerting(key)) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500178 // A cancel() in response to a remote input shouldn't be delayed, as it makes the
179 // sending look longer than it takes.
180 // Also we should not defer the removal if reordering isn't allowed since otherwise
181 // some notifications can't disappear before the panel is closed.
182 boolean ignoreEarliestRemovalTime =
Gus Prevasf37435a2018-12-20 15:40:01 -0500183 mRemoteInputManager.getController().isSpinning(key)
Gus Prevasb43dc652018-12-20 13:11:45 -0500184 && !FORCE_REMOTE_INPUT_HISTORY
185 || !mVisualStabilityManager.isReorderingAllowed();
Gus Prevasf37435a2018-12-20 15:40:01 -0500186 mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
Gus Prevasb43dc652018-12-20 13:11:45 -0500187 }
Gus Prevasf37435a2018-12-20 15:40:01 -0500188 if (mAmbientPulseManager.isAlerting(key)) {
189 mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
Gus Prevasb43dc652018-12-20 13:11:45 -0500190 }
191 }
192}