/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.service.notification;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.SomeArgs;
import java.util.List;
A service that helps the user manage notifications.
@hide
/**
* A service that helps the user manage notifications.
* @hide
*/
@SystemApi
@TestApi
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistants";
The Intent
that must be declared as handled by the service. /**
* The {@link Intent} that must be declared as handled by the service.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE
= "android.service.notification.NotificationAssistantService";
@hide
/**
* @hide
*/
protected Handler mHandler;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
mHandler = new MyHandler(getContext().getMainLooper());
}
@Override
public final IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new NotificationAssistantServiceWrapper();
}
return mWrapper;
}
A notification was snoozed until a context. For use with Adjustment.KEY_SNOOZE_CRITERIA
. When the device reaches the given context, the assistant should restore the notification with unsnoozeNotification(String)
. Params: - sbn – the notification to snooze
- snoozeCriterionId – the
SnoozeCriterion.getId()
representing a device context.
/**
* A notification was snoozed until a context. For use with
* {@link Adjustment#KEY_SNOOZE_CRITERIA}. When the device reaches the given context, the
* assistant should restore the notification with {@link #unsnoozeNotification(String)}.
*
* @param sbn the notification to snooze
* @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
*/
abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
String snoozeCriterionId);
A notification was posted by an app. Called before post.
Params: - sbn – the new notification
Returns: an adjustment or null to take no action, within 100ms.
/**
* A notification was posted by an app. Called before post.
*
* @param sbn the new notification
* @return an adjustment or null to take no action, within 100ms.
*/
abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
Implement this method to learn when notifications are removed, how they were interacted with
before removal, and why they were removed.
This might occur because the user has dismissed the notification using system UI (or another
notification listener) or because the app has withdrawn the notification.
NOTE: The StatusBarNotification
object you receive will be "light"; that is, the result from StatusBarNotification.getNotification
may be missing some heavyweight fields such as Notification.contentView
and Notification.largeIcon
. However, all other fields on StatusBarNotification
, sufficient to match this call with a prior call to NotificationListenerService.onNotificationPosted(StatusBarNotification)
, will be intact.
Params: - sbn – A data structure encapsulating at least the original information (tag and id) and source (package name) used to post the
Notification
that was just removed. - rankingMap – The current ranking map that can be used to retrieve ranking information
for active notifications.
- stats – Stats about how the user interacted with the notification before it was removed.
- reason – see
NotificationListenerService.REASON_LISTENER_CANCEL
, etc.
/**
* Implement this method to learn when notifications are removed, how they were interacted with
* before removal, and why they were removed.
* <p>
* This might occur because the user has dismissed the notification using system UI (or another
* notification listener) or because the app has withdrawn the notification.
* <p>
* NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
* result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
* fields such as {@link android.app.Notification#contentView} and
* {@link android.app.Notification#largeIcon}. However, all other fields on
* {@link StatusBarNotification}, sufficient to match this call with a prior call to
* {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
*
** @param sbn A data structure encapsulating at least the original information (tag and id)
* and source (package name) used to post the {@link android.app.Notification} that
* was just removed.
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications.
* @param stats Stats about how the user interacted with the notification before it was removed.
* @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
*/
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
NotificationStats stats, int reason) {
onNotificationRemoved(sbn, rankingMap, reason);
}
Updates a notification. N.B. this won’t cause
an existing notification to alert, but might allow a future update to
this notification to alert.
Params: - adjustment – the adjustment with an explanation
/**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
*
* @param adjustment the adjustment with an explanation
*/
public final void adjustNotification(Adjustment adjustment) {
if (!isBound()) return;
try {
getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
}
}
Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
N.B. this won’t cause an existing notification to alert, but might allow a future update to
these notifications to alert.
Params: - adjustments – a list of adjustments with explanations
/**
* Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
* N.B. this won’t cause an existing notification to alert, but might allow a future update to
* these notifications to alert.
*
* @param adjustments a list of adjustments with explanations
*/
public final void adjustNotifications(List<Adjustment> adjustments) {
if (!isBound()) return;
try {
getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
}
}
Inform the notification manager about un-snoozing a specific notification.
This should only be used for notifications snoozed by this listener using NotificationListenerService.snoozeNotification(String, String)
. Once un-snoozed, you will get a NotificationListenerService.onNotificationPosted(StatusBarNotification, RankingMap)
callback for the notification.
Params: - key – The key of the notification to snooze
/**
* Inform the notification manager about un-snoozing a specific notification.
* <p>
* This should only be used for notifications snoozed by this listener using
* {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
* {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
* notification.
* @param key The key of the notification to snooze
*/
public final void unsnoozeNotification(String key) {
if (!isBound()) return;
try {
getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
@Override
public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
} catch (RemoteException e) {
Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
args).sendToTarget();
}
@Override
public void onNotificationSnoozedUntilContext(
IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId)
throws RemoteException {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
} catch (RemoteException e) {
Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification", e);
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
args.arg2 = snoozeCriterionId;
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_SNOOZED,
args).sendToTarget();
}
}
private final class MyHandler extends Handler {
public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
public MyHandler(Looper looper) {
super(looper, null, false);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_NOTIFICATION_ENQUEUED: {
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
args.recycle();
Adjustment adjustment = onNotificationEnqueued(sbn);
if (adjustment != null) {
if (!isBound()) return;
try {
getNotificationInterface().applyEnqueuedAdjustmentFromAssistant(
mWrapper, adjustment);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
}
}
break;
}
case MSG_ON_NOTIFICATION_SNOOZED: {
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
String snoozeCriterionId = (String) args.arg2;
args.recycle();
onNotificationSnoozedUntilContext(sbn, snoozeCriterionId);
break;
}
}
}
}
}