Home | History | Annotate | Download | only in notification
      1 /*
      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 
     17 package android.service.notification;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.SdkConstant;
     22 import android.annotation.SystemApi;
     23 import android.annotation.TestApi;
     24 import android.app.NotificationChannel;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.os.Handler;
     28 import android.os.IBinder;
     29 import android.os.Looper;
     30 import android.os.Message;
     31 import android.os.RemoteException;
     32 import android.util.Log;
     33 import com.android.internal.os.SomeArgs;
     34 
     35 import java.util.ArrayList;
     36 import java.util.List;
     37 
     38 /**
     39  * A service that helps the user manage notifications.
     40  * @hide
     41  */
     42 @SystemApi
     43 @TestApi
     44 public abstract class NotificationAssistantService extends NotificationListenerService {
     45     private static final String TAG = "NotificationAssistants";
     46 
     47     /**
     48      * The {@link Intent} that must be declared as handled by the service.
     49      */
     50     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     51     public static final String SERVICE_INTERFACE
     52             = "android.service.notification.NotificationAssistantService";
     53 
     54     private Handler mHandler;
     55 
     56     @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) {
     64         if (mWrapper == null) {
     65             mWrapper = new NotificationAssistantServiceWrapper();
     66         }
     67         return mWrapper;
     68     }
     69 
     70     /**
     71      * 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     /**
     82      * A notification was posted by an app. Called before alert.
     83      *
     84      * @param sbn the new notification
     85      * @return an adjustment or null to take no action, within 100ms.
     86      */
     87     abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
     88 
     89     /**
     90      * Updates a notification.  N.B. this wont cause
     91      * an existing notification to alert, but might allow a future update to
     92      * this notification to alert.
     93      *
     94      * @param adjustment the adjustment with an explanation
     95      */
     96     public final void adjustNotification(Adjustment adjustment) {
     97         if (!isBound()) return;
     98         try {
     99             getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment);
    100         } catch (android.os.RemoteException ex) {
    101             Log.v(TAG, "Unable to contact notification manager", ex);
    102             throw ex.rethrowFromSystemServer();
    103         }
    104     }
    105 
    106     /**
    107      * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
    108      * N.B. this wont cause an existing notification to alert, but might allow a future update to
    109      * these notifications to alert.
    110      *
    111      * @param adjustments a list of adjustments with explanations
    112      */
    113     public final void adjustNotifications(List<Adjustment> adjustments) {
    114         if (!isBound()) return;
    115         try {
    116             getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments);
    117         } catch (android.os.RemoteException ex) {
    118             Log.v(TAG, "Unable to contact notification manager", ex);
    119             throw ex.rethrowFromSystemServer();
    120         }
    121     }
    122 
    123     /**
    124      * Inform the notification manager about un-snoozing a specific notification.
    125      * <p>
    126      * This should only be used for notifications snoozed by this listener using
    127      * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
    128      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
    129      * notification.
    130      * @param key The key of the notification to snooze
    131      */
    132     public final void unsnoozeNotification(String key) {
    133         if (!isBound()) return;
    134         try {
    135             getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key);
    136         } catch (android.os.RemoteException ex) {
    137             Log.v(TAG, "Unable to contact notification manager", ex);
    138         }
    139     }
    140 
    141     private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
    142         @Override
    143         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
    144             StatusBarNotification sbn;
    145             try {
    146                 sbn = sbnHolder.get();
    147             } catch (RemoteException e) {
    148                 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
    149                 return;
    150             }
    151 
    152             SomeArgs args = SomeArgs.obtain();
    153             args.arg1 = sbn;
    154             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
    155                     args).sendToTarget();
    156         }
    157 
    158         @Override
    159         public void onNotificationSnoozedUntilContext(
    160                 IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId)
    161                 throws RemoteException {
    162             StatusBarNotification sbn;
    163             try {
    164                 sbn = sbnHolder.get();
    165             } catch (RemoteException e) {
    166                 Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification", e);
    167                 return;
    168             }
    169 
    170             SomeArgs args = SomeArgs.obtain();
    171             args.arg1 = sbn;
    172             args.arg2 = snoozeCriterionId;
    173             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_SNOOZED,
    174                     args).sendToTarget();
    175         }
    176     }
    177 
    178     private final class MyHandler extends Handler {
    179         public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
    180         public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
    181 
    182         public MyHandler(Looper looper) {
    183             super(looper, null, false);
    184         }
    185 
    186         @Override
    187         public void handleMessage(Message msg) {
    188             switch (msg.what) {
    189                 case MSG_ON_NOTIFICATION_ENQUEUED: {
    190                     SomeArgs args = (SomeArgs) msg.obj;
    191                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
    192                     args.recycle();
    193                     Adjustment adjustment = onNotificationEnqueued(sbn);
    194                     if (adjustment != null) {
    195                         if (!isBound()) return;
    196                         try {
    197                             getNotificationInterface().applyEnqueuedAdjustmentFromAssistant(
    198                                     mWrapper, adjustment);
    199                         } catch (android.os.RemoteException ex) {
    200                             Log.v(TAG, "Unable to contact notification manager", ex);
    201                             throw ex.rethrowFromSystemServer();
    202                         }
    203                     }
    204                     break;
    205                 }
    206                 case MSG_ON_NOTIFICATION_SNOOZED: {
    207                     SomeArgs args = (SomeArgs) msg.obj;
    208                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
    209                     String snoozeCriterionId = (String) args.arg2;
    210                     args.recycle();
    211                     onNotificationSnoozedUntilContext(sbn, snoozeCriterionId);
    212                     break;
    213                 }
    214             }
    215         }
    216     }
    217 }
    218