blob: 18e0ab00a78b8f627f0e3ddc94a23e731d20f3c1 [file] [log] [blame]
Chris Wren9fa689f2015-11-20 16:44:53 -05001/*
2 * Copyright (C) 2015 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 android.service.notification;
18
Julia Reynolds7eba5932015-12-11 16:40:39 -050019import android.annotation.SdkConstant;
Julia Reynolds1327d3c2017-02-17 09:26:45 -050020import android.annotation.SystemApi;
21import android.annotation.TestApi;
Chris Wrenab41eec2016-01-04 18:01:27 -050022import android.content.Context;
Julia Reynolds7eba5932015-12-11 16:40:39 -050023import android.content.Intent;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080024import android.os.Handler;
Chris Wren51017d02015-12-15 15:34:46 -050025import android.os.IBinder;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080026import android.os.Looper;
27import android.os.Message;
Chris Wren51017d02015-12-15 15:34:46 -050028import android.os.RemoteException;
29import android.util.Log;
Julia Reynolds503ed942017-10-04 16:04:56 -040030
Svet Ganovb8f53ee2016-02-18 08:38:56 -080031import com.android.internal.os.SomeArgs;
Chris Wren9fa689f2015-11-20 16:44:53 -050032
Julia Reynoldse46bb372016-03-17 11:05:58 -040033import java.util.List;
34
Chris Wren9fa689f2015-11-20 16:44:53 -050035/**
Julia Reynolds77b2cc92016-11-08 14:41:09 -050036 * A service that helps the user manage notifications.
Julia Reynolds1327d3c2017-02-17 09:26:45 -050037 * @hide
Chris Wren9fa689f2015-11-20 16:44:53 -050038 */
Julia Reynolds1327d3c2017-02-17 09:26:45 -050039@SystemApi
40@TestApi
Julia Reynolds77b2cc92016-11-08 14:41:09 -050041public abstract class NotificationAssistantService extends NotificationListenerService {
42 private static final String TAG = "NotificationAssistants";
Chris Wren51017d02015-12-15 15:34:46 -050043
Julia Reynolds7eba5932015-12-11 16:40:39 -050044 /**
45 * The {@link Intent} that must be declared as handled by the service.
46 */
47 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
48 public static final String SERVICE_INTERFACE
Julia Reynolds77b2cc92016-11-08 14:41:09 -050049 = "android.service.notification.NotificationAssistantService";
Julia Reynolds7eba5932015-12-11 16:40:39 -050050
Rohan Shah0350dab2018-05-04 13:42:18 -070051 /**
52 * @hide
53 */
54 protected Handler mHandler;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080055
Svet Ganovb8f53ee2016-02-18 08:38:56 -080056 @Override
57 protected void attachBaseContext(Context base) {
58 super.attachBaseContext(base);
59 mHandler = new MyHandler(getContext().getMainLooper());
60 }
61
62 @Override
63 public final IBinder onBind(Intent intent) {
Chris Wren51017d02015-12-15 15:34:46 -050064 if (mWrapper == null) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -050065 mWrapper = new NotificationAssistantServiceWrapper();
Chris Wren51017d02015-12-15 15:34:46 -050066 }
67 return mWrapper;
68 }
69
Chris Wren9fa689f2015-11-20 16:44:53 -050070 /**
Julia Reynolds79672302017-01-12 08:30:16 -050071 * A notification was snoozed until a context. For use with
72 * {@link Adjustment#KEY_SNOOZE_CRITERIA}. When the device reaches the given context, the
73 * assistant should restore the notification with {@link #unsnoozeNotification(String)}.
74 *
75 * @param sbn the notification to snooze
76 * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
77 */
78 abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
79 String snoozeCriterionId);
80
81 /**
Julia Reynolds503ed942017-10-04 16:04:56 -040082 * A notification was posted by an app. Called before post.
Chris Wren9fa689f2015-11-20 16:44:53 -050083 *
84 * @param sbn the new notification
Chris Wren9fa689f2015-11-20 16:44:53 -050085 * @return an adjustment or null to take no action, within 100ms.
86 */
Julia Reynoldsceecfcf2017-01-31 09:44:26 -050087 abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
Chris Wren9fa689f2015-11-20 16:44:53 -050088
89 /**
Julia Reynolds503ed942017-10-04 16:04:56 -040090 * Implement this method to learn when notifications are removed, how they were interacted with
91 * before removal, and why they were removed.
92 * <p>
93 * This might occur because the user has dismissed the notification using system UI (or another
94 * notification listener) or because the app has withdrawn the notification.
95 * <p>
96 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
97 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
98 * fields such as {@link android.app.Notification#contentView} and
99 * {@link android.app.Notification#largeIcon}. However, all other fields on
100 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
101 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
102 *
103 ** @param sbn A data structure encapsulating at least the original information (tag and id)
104 * and source (package name) used to post the {@link android.app.Notification} that
105 * was just removed.
106 * @param rankingMap The current ranking map that can be used to retrieve ranking information
107 * for active notifications.
108 * @param stats Stats about how the user interacted with the notification before it was removed.
109 * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
110 */
111 @Override
112 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
113 NotificationStats stats, int reason) {
114 onNotificationRemoved(sbn, rankingMap, reason);
115 }
116
117 /**
Julia Reynoldse46bb372016-03-17 11:05:58 -0400118 * Updates a notification. N.B. this won’t cause
Chris Wren9fa689f2015-11-20 16:44:53 -0500119 * an existing notification to alert, but might allow a future update to
120 * this notification to alert.
121 *
Julia Reynoldse46bb372016-03-17 11:05:58 -0400122 * @param adjustment the adjustment with an explanation
Chris Wren9fa689f2015-11-20 16:44:53 -0500123 */
Julia Reynoldse46bb372016-03-17 11:05:58 -0400124 public final void adjustNotification(Adjustment adjustment) {
Chris Wren51017d02015-12-15 15:34:46 -0500125 if (!isBound()) return;
126 try {
Julia Reynolds52e64d02016-12-09 15:36:12 -0500127 getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment);
Julia Reynoldse46bb372016-03-17 11:05:58 -0400128 } catch (android.os.RemoteException ex) {
129 Log.v(TAG, "Unable to contact notification manager", ex);
Julia Reynolds52e64d02016-12-09 15:36:12 -0500130 throw ex.rethrowFromSystemServer();
Julia Reynoldse46bb372016-03-17 11:05:58 -0400131 }
132 }
133
134 /**
135 * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
136 * N.B. this won’t cause an existing notification to alert, but might allow a future update to
137 * these notifications to alert.
138 *
139 * @param adjustments a list of adjustments with explanations
140 */
141 public final void adjustNotifications(List<Adjustment> adjustments) {
142 if (!isBound()) return;
143 try {
Julia Reynolds52e64d02016-12-09 15:36:12 -0500144 getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments);
Chris Wren51017d02015-12-15 15:34:46 -0500145 } catch (android.os.RemoteException ex) {
146 Log.v(TAG, "Unable to contact notification manager", ex);
Julia Reynolds52e64d02016-12-09 15:36:12 -0500147 throw ex.rethrowFromSystemServer();
Chris Wren51017d02015-12-15 15:34:46 -0500148 }
Chris Wren9fa689f2015-11-20 16:44:53 -0500149 }
150
Julia Reynolds52e64d02016-12-09 15:36:12 -0500151 /**
Julia Reynoldscf63ff12017-01-24 13:55:48 -0500152 * Inform the notification manager about un-snoozing a specific notification.
153 * <p>
154 * This should only be used for notifications snoozed by this listener using
155 * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
156 * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
157 * notification.
158 * @param key The key of the notification to snooze
159 */
160 public final void unsnoozeNotification(String key) {
161 if (!isBound()) return;
162 try {
163 getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key);
164 } catch (android.os.RemoteException ex) {
165 Log.v(TAG, "Unable to contact notification manager", ex);
166 }
167 }
168
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500169 private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
Chris Wren51017d02015-12-15 15:34:46 -0500170 @Override
Julia Reynoldsceecfcf2017-01-31 09:44:26 -0500171 public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
Chris Wren51017d02015-12-15 15:34:46 -0500172 StatusBarNotification sbn;
173 try {
174 sbn = sbnHolder.get();
175 } catch (RemoteException e) {
176 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
177 return;
178 }
179
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800180 SomeArgs args = SomeArgs.obtain();
181 args.arg1 = sbn;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800182 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
183 args).sendToTarget();
Chris Wren51017d02015-12-15 15:34:46 -0500184 }
Julia Reynolds79672302017-01-12 08:30:16 -0500185
186 @Override
187 public void onNotificationSnoozedUntilContext(
188 IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId)
189 throws RemoteException {
190 StatusBarNotification sbn;
191 try {
192 sbn = sbnHolder.get();
193 } catch (RemoteException e) {
194 Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification", e);
195 return;
196 }
197
198 SomeArgs args = SomeArgs.obtain();
199 args.arg1 = sbn;
200 args.arg2 = snoozeCriterionId;
201 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_SNOOZED,
202 args).sendToTarget();
203 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800204 }
205
206 private final class MyHandler extends Handler {
207 public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
Julia Reynolds79672302017-01-12 08:30:16 -0500208 public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800209
210 public MyHandler(Looper looper) {
211 super(looper, null, false);
212 }
213
214 @Override
215 public void handleMessage(Message msg) {
216 switch (msg.what) {
217 case MSG_ON_NOTIFICATION_ENQUEUED: {
218 SomeArgs args = (SomeArgs) msg.obj;
219 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800220 args.recycle();
Julia Reynoldsceecfcf2017-01-31 09:44:26 -0500221 Adjustment adjustment = onNotificationEnqueued(sbn);
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800222 if (adjustment != null) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500223 if (!isBound()) return;
224 try {
225 getNotificationInterface().applyEnqueuedAdjustmentFromAssistant(
226 mWrapper, adjustment);
227 } catch (android.os.RemoteException ex) {
228 Log.v(TAG, "Unable to contact notification manager", ex);
229 throw ex.rethrowFromSystemServer();
230 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800231 }
Julia Reynolds79672302017-01-12 08:30:16 -0500232 break;
233 }
234 case MSG_ON_NOTIFICATION_SNOOZED: {
235 SomeArgs args = (SomeArgs) msg.obj;
236 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
237 String snoozeCriterionId = (String) args.arg2;
238 args.recycle();
239 onNotificationSnoozedUntilContext(sbn, snoozeCriterionId);
240 break;
241 }
Chris Wren51017d02015-12-15 15:34:46 -0500242 }
243 }
244 }
Chris Wren9fa689f2015-11-20 16:44:53 -0500245}