blob: 41af837a1dbc4a37f820afd2cdd503665650c76b [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;
Chris Wrenab41eec2016-01-04 18:01:27 -050020import android.annotation.SystemApi;
Chris Wrenab41eec2016-01-04 18:01:27 -050021import android.content.ComponentName;
22import android.content.Context;
Julia Reynolds7eba5932015-12-11 16:40:39 -050023import android.content.Intent;
Chris Wren9fa689f2015-11-20 16:44:53 -050024import android.net.Uri;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080025import android.os.Handler;
Chris Wren51017d02015-12-15 15:34:46 -050026import android.os.IBinder;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080027import android.os.Looper;
28import android.os.Message;
Chris Wren51017d02015-12-15 15:34:46 -050029import android.os.RemoteException;
30import android.util.Log;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080031import com.android.internal.os.SomeArgs;
Chris Wren9fa689f2015-11-20 16:44:53 -050032
33/**
34 * A service that helps the user manage notifications by modifying the
35 * relative importance of notifications.
36 * <p>To extend this class, you must declare the service in your manifest file with
37 * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission
38 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
39 * <pre>
40 * &lt;service android:name=".NotificationAssistant"
41 * android:label="&#64;string/service_name"
42 * android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE">
43 * &lt;intent-filter>
44 * &lt;action android:name="android.service.notification.NotificationAssistantService" />
45 * &lt;/intent-filter>
46 * &lt;/service></pre>
Chris Wren10c63d82016-02-05 14:55:35 -050047 * @hide
Chris Wren9fa689f2015-11-20 16:44:53 -050048 */
Chris Wren10c63d82016-02-05 14:55:35 -050049@SystemApi
Chris Wren9fa689f2015-11-20 16:44:53 -050050public abstract class NotificationAssistantService extends NotificationListenerService {
Chris Wren51017d02015-12-15 15:34:46 -050051 private static final String TAG = "NotificationAssistant";
52
Julia Reynolds7eba5932015-12-11 16:40:39 -050053 /**
54 * The {@link Intent} that must be declared as handled by the service.
55 */
56 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
57 public static final String SERVICE_INTERFACE
58 = "android.service.notification.NotificationAssistantService";
59
Chris Wren9fa689f2015-11-20 16:44:53 -050060 /** Notification was canceled by the status bar reporting a click. */
61 public static final int REASON_DELEGATE_CLICK = 1;
62
63 /** Notification was canceled by the status bar reporting a user dismissal. */
64 public static final int REASON_DELEGATE_CANCEL = 2;
65
66 /** Notification was canceled by the status bar reporting a user dismiss all. */
67 public static final int REASON_DELEGATE_CANCEL_ALL = 3;
68
69 /** Notification was canceled by the status bar reporting an inflation error. */
70 public static final int REASON_DELEGATE_ERROR = 4;
71
72 /** Notification was canceled by the package manager modifying the package. */
73 public static final int REASON_PACKAGE_CHANGED = 5;
74
75 /** Notification was canceled by the owning user context being stopped. */
76 public static final int REASON_USER_STOPPED = 6;
77
78 /** Notification was canceled by the user banning the package. */
79 public static final int REASON_PACKAGE_BANNED = 7;
80
81 /** Notification was canceled by the app canceling this specific notification. */
82 public static final int REASON_APP_CANCEL = 8;
83
84 /** Notification was canceled by the app cancelling all its notifications. */
85 public static final int REASON_APP_CANCEL_ALL = 9;
86
87 /** Notification was canceled by a listener reporting a user dismissal. */
88 public static final int REASON_LISTENER_CANCEL = 10;
89
90 /** Notification was canceled by a listener reporting a user dismiss all. */
91 public static final int REASON_LISTENER_CANCEL_ALL = 11;
92
93 /** Notification was canceled because it was a member of a canceled group. */
94 public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
95
96 /** Notification was canceled because it was an invisible member of a group. */
97 public static final int REASON_GROUP_OPTIMIZATION = 13;
98
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +000099 /** Notification was canceled by the device administrator suspending the package. */
Julia Reynoldsef37f282016-02-12 09:11:27 -0500100 public static final int REASON_PACKAGE_SUSPENDED = 14;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000101
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000102 /** Notification was canceled by the owning managed profile being turned off. */
Julia Reynoldsef37f282016-02-12 09:11:27 -0500103 public static final int REASON_PROFILE_TURNED_OFF = 15;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000104
Chris Wren51017d02015-12-15 15:34:46 -0500105 public class Adjustment {
106 int mImportance;
107 CharSequence mExplanation;
108 Uri mReference;
109
110 /**
111 * Create a notification importance adjustment.
112 *
113 * @param importance The final importance of the notification.
114 * @param explanation A human-readable justification for the adjustment.
115 * @param reference A reference to an external object that augments the
116 * explanation, such as a
117 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
118 * or null.
119 */
120 public Adjustment(int importance, CharSequence explanation, Uri reference) {
121 mImportance = importance;
122 mExplanation = explanation;
123 mReference = reference;
124 }
125 }
126
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800127 private Handler mHandler;
128
129 /** @hide */
Chris Wren51017d02015-12-15 15:34:46 -0500130 @Override
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800131 public void registerAsSystemService(Context context, ComponentName componentName,
132 int currentUser) throws RemoteException {
133 super.registerAsSystemService(context, componentName, currentUser);
134 mHandler = new MyHandler(getContext().getMainLooper());
135 }
136
137 @Override
138 protected void attachBaseContext(Context base) {
139 super.attachBaseContext(base);
140 mHandler = new MyHandler(getContext().getMainLooper());
141 }
142
143 @Override
144 public final IBinder onBind(Intent intent) {
Chris Wren51017d02015-12-15 15:34:46 -0500145 if (mWrapper == null) {
146 mWrapper = new NotificationAssistantWrapper();
147 }
148 return mWrapper;
149 }
150
Chris Wren9fa689f2015-11-20 16:44:53 -0500151 /**
152 * A notification was posted by an app. Called before alert.
153 *
154 * @param sbn the new notification
155 * @param importance the initial importance of the notification.
156 * @param user true if the initial importance reflects an explicit user preference.
157 * @return an adjustment or null to take no action, within 100ms.
158 */
Chris Wren51017d02015-12-15 15:34:46 -0500159 abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
Chris Wren9fa689f2015-11-20 16:44:53 -0500160 int importance, boolean user);
161
162 /**
163 * The visibility of a notification has changed.
164 *
165 * @param key the notification key
166 * @param time milliseconds since midnight, January 1, 1970 UTC.
167 * @param visible true if the notification became visible, false if hidden.
168 */
169 public void onNotificationVisibilityChanged(String key, long time, boolean visible)
170 {
171 // Do nothing, Override this to collect visibility statistics.
172 }
173
174 /**
175 * The user clicked on a notification.
176 *
177 * @param key the notification key
178 * @param time milliseconds since midnight, January 1, 1970 UTC.
179 */
180 public void onNotificationClick(String key, long time)
181 {
182 // Do nothing, Override this to collect click statistics
183 }
184
185 /**
186 * The user clicked on a notification action.
187 *
188 * @param key the notification key
189 * @param time milliseconds since midnight, January 1, 1970 UTC.
190 * @param actionIndex the index of the action button that was pressed.
191 */
192 public void onNotificationActionClick(String key, long time, int actionIndex)
193 {
194 // Do nothing, Override this to collect action button click statistics
195 }
196
197 /**
198 * A notification was removed.
199
200 * @param key the notification key
201 * @param time milliseconds since midnight, January 1, 1970 UTC.
202 * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
203 */
204 public void onNotificationRemoved(String key, long time, int reason) {
205 // Do nothing, Override this to collect dismissal statistics
206 }
207
208 /**
209 * Change the importance of an existing notification. N.B. this won’t cause
210 * an existing notification to alert, but might allow a future update to
211 * this notification to alert.
212 *
213 * @param key the notification key
214 * @param adjustment the new importance with an explanation
215 */
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800216 public final void adjustImportance(String key, Adjustment adjustment) {
Chris Wren51017d02015-12-15 15:34:46 -0500217 if (!isBound()) return;
218 try {
219 getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
220 adjustment.mImportance, adjustment.mExplanation);
221 } catch (android.os.RemoteException ex) {
222 Log.v(TAG, "Unable to contact notification manager", ex);
223 }
Chris Wren9fa689f2015-11-20 16:44:53 -0500224 }
225
Chris Wren51017d02015-12-15 15:34:46 -0500226 private class NotificationAssistantWrapper extends NotificationListenerWrapper {
227 @Override
228 public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800229 int importance, boolean user) {
Chris Wren51017d02015-12-15 15:34:46 -0500230 StatusBarNotification sbn;
231 try {
232 sbn = sbnHolder.get();
233 } catch (RemoteException e) {
234 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
235 return;
236 }
237
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800238 SomeArgs args = SomeArgs.obtain();
239 args.arg1 = sbn;
240 args.argi1 = importance;
241 args.argi2 = user ? 1 : 0;
242 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
243 args).sendToTarget();
Chris Wren51017d02015-12-15 15:34:46 -0500244 }
245
246 @Override
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800247 public void onNotificationVisibilityChanged(String key, long time, boolean visible) {
248 SomeArgs args = SomeArgs.obtain();
249 args.arg1 = key;
250 args.arg2 = time;
251 args.argi1 = visible ? 1 : 0;
252 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED,
253 args).sendToTarget();
Chris Wren51017d02015-12-15 15:34:46 -0500254 }
255
256 @Override
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800257 public void onNotificationClick(String key, long time) {
258 SomeArgs args = SomeArgs.obtain();
259 args.arg1 = key;
260 args.arg2 = time;
261 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICK,
262 args).sendToTarget();
Chris Wren51017d02015-12-15 15:34:46 -0500263 }
264
265 @Override
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800266 public void onNotificationActionClick(String key, long time, int actionIndex) {
267 SomeArgs args = SomeArgs.obtain();
268 args.arg1 = key;
269 args.arg2 = time;
270 args.argi1 = actionIndex;
271 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ACTION_CLICK,
272 args).sendToTarget();
Chris Wren51017d02015-12-15 15:34:46 -0500273 }
274
275 @Override
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800276 public void onNotificationRemovedReason(String key, long time, int reason) {
277 SomeArgs args = SomeArgs.obtain();
278 args.arg1 = key;
279 args.arg2 = time;
280 args.argi1 = reason;
281 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED_REASON,
282 args).sendToTarget();
283 }
284 }
285
286 private final class MyHandler extends Handler {
287 public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
288 public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 2;
289 public static final int MSG_ON_NOTIFICATION_CLICK = 3;
290 public static final int MSG_ON_NOTIFICATION_ACTION_CLICK = 4;
291 public static final int MSG_ON_NOTIFICATION_REMOVED_REASON = 5;
292
293 public MyHandler(Looper looper) {
294 super(looper, null, false);
295 }
296
297 @Override
298 public void handleMessage(Message msg) {
299 switch (msg.what) {
300 case MSG_ON_NOTIFICATION_ENQUEUED: {
301 SomeArgs args = (SomeArgs) msg.obj;
302 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
303 final int importance = args.argi1;
304 final boolean user = args.argi2 == 1;
305 args.recycle();
306 Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
307 if (adjustment != null) {
308 adjustImportance(sbn.getKey(), adjustment);
309 }
310 } break;
311
312 case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: {
313 SomeArgs args = (SomeArgs) msg.obj;
314 final String key = (String) args.arg1;
315 final long time = (long) args.arg2;
316 final boolean visible = args.argi1 == 1;
317 args.recycle();
318 onNotificationVisibilityChanged(key, time, visible);
319 } break;
320
321 case MSG_ON_NOTIFICATION_CLICK: {
322 SomeArgs args = (SomeArgs) msg.obj;
323 final String key = (String) args.arg1;
324 final long time = (long) args.arg2;
325 args.recycle();
326 onNotificationClick(key, time);
327 } break;
328
329 case MSG_ON_NOTIFICATION_ACTION_CLICK: {
330 SomeArgs args = (SomeArgs) msg.obj;
331 final String key = (String) args.arg1;
332 final long time = (long) args.arg2;
333 final int actionIndex = args.argi1;
334 args.recycle();
335 onNotificationActionClick(key, time, actionIndex);
336 } break;
337
338 case MSG_ON_NOTIFICATION_REMOVED_REASON: {
339 SomeArgs args = (SomeArgs) msg.obj;
340 final String key = (String) args.arg1;
341 final long time = (long) args.arg2;
342 final int reason = args.argi1;
343 args.recycle();
344 onNotificationRemoved(key, time, reason);
345 } break;
Chris Wren51017d02015-12-15 15:34:46 -0500346 }
347 }
348 }
Chris Wren9fa689f2015-11-20 16:44:53 -0500349}