Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2014 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 package com.android.server.notification;
     17 
     18 import android.app.Notification;
     19 import android.app.NotificationManager;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.telecom.TelecomManager;
     25 
     26 import com.android.internal.util.NotificationMessagingUtil;
     27 
     28 import java.util.Comparator;
     29 import java.util.Objects;
     30 
     31 /**
     32  * Sorts notifications individually into attention-relevant order.
     33  */
     34 public class NotificationComparator
     35         implements Comparator<NotificationRecord> {
     36 
     37     private final Context mContext;
     38     private final NotificationMessagingUtil mMessagingUtil;
     39     private String mDefaultPhoneApp;
     40 
     41     public NotificationComparator(Context context) {
     42         mContext = context;
     43         mContext.registerReceiver(mPhoneAppBroadcastReceiver,
     44                 new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
     45         mMessagingUtil = new NotificationMessagingUtil(mContext);
     46     }
     47 
     48     @Override
     49     public int compare(NotificationRecord left, NotificationRecord right) {
     50         // first all colorized notifications
     51         boolean leftImportantColorized = isImportantColorized(left);
     52         boolean rightImportantColorized = isImportantColorized(right);
     53 
     54         if (leftImportantColorized != rightImportantColorized) {
     55             return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
     56         }
     57 
     58         // sufficiently important ongoing notifications of certain categories
     59         boolean leftImportantOngoing = isImportantOngoing(left);
     60         boolean rightImportantOngoing = isImportantOngoing(right);
     61 
     62         if (leftImportantOngoing != rightImportantOngoing) {
     63             // by ongoing, ongoing higher than non-ongoing
     64             return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
     65         }
     66 
     67         boolean leftMessaging = isImportantMessaging(left);
     68         boolean rightMessaging = isImportantMessaging(right);
     69         if (leftMessaging != rightMessaging) {
     70             return -1 * Boolean.compare(leftMessaging, rightMessaging);
     71         }
     72 
     73         // Next: sufficiently import person to person communication
     74         boolean leftPeople = isImportantPeople(left);
     75         boolean rightPeople = isImportantPeople(right);
     76         final int contactAffinityComparison =
     77                 Float.compare(left.getContactAffinity(), right.getContactAffinity());
     78 
     79         if (leftPeople && rightPeople){
     80             // by contact proximity, close to far. if same proximity, check further fields.
     81             if (contactAffinityComparison != 0) {
     82                 return -1 * contactAffinityComparison;
     83             }
     84         } else if (leftPeople != rightPeople) {
     85             // People, messaging higher than non-messaging
     86             return -1 * Boolean.compare(leftPeople, rightPeople);
     87         }
     88 
     89         final int leftImportance = left.getImportance();
     90         final int rightImportance = right.getImportance();
     91         if (leftImportance != rightImportance) {
     92             // by importance, high to low
     93             return -1 * Integer.compare(leftImportance, rightImportance);
     94         }
     95 
     96         // by contact proximity, close to far. if same proximity, check further fields.
     97         if (contactAffinityComparison != 0) {
     98             return -1 * contactAffinityComparison;
     99         }
    100 
    101         // Whether or not the notification can bypass DND.
    102         final int leftPackagePriority = left.getPackagePriority();
    103         final int rightPackagePriority = right.getPackagePriority();
    104         if (leftPackagePriority != rightPackagePriority) {
    105             // by priority, high to low
    106             return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
    107         }
    108 
    109         final int leftPriority = left.sbn.getNotification().priority;
    110         final int rightPriority = right.sbn.getNotification().priority;
    111         if (leftPriority != rightPriority) {
    112             // by priority, high to low
    113             return -1 * Integer.compare(leftPriority, rightPriority);
    114         }
    115 
    116         // then break ties by time, most recent first
    117         return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
    118     }
    119 
    120     private boolean isImportantColorized(NotificationRecord record) {
    121         if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
    122             return false;
    123         }
    124         return record.getNotification().isColorized();
    125     }
    126 
    127     private boolean isImportantOngoing(NotificationRecord record) {
    128         if (!isOngoing(record)) {
    129             return false;
    130         }
    131 
    132         if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
    133             return false;
    134         }
    135 
    136         return isCall(record) || isMediaNotification(record);
    137     }
    138 
    139     protected boolean isImportantPeople(NotificationRecord record) {
    140         if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
    141             return false;
    142         }
    143         if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
    144             return true;
    145         }
    146         return false;
    147     }
    148 
    149     protected boolean isImportantMessaging(NotificationRecord record) {
    150         return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
    151     }
    152 
    153     private boolean isOngoing(NotificationRecord record) {
    154         final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE;
    155         return (record.getNotification().flags & ongoingFlags) != 0;
    156     }
    157 
    158     private boolean isMediaNotification(NotificationRecord record) {
    159         return record.getNotification().hasMediaSession();
    160     }
    161 
    162     private boolean isCall(NotificationRecord record) {
    163         return record.isCategory(Notification.CATEGORY_CALL)
    164                 && isDefaultPhoneApp(record.sbn.getPackageName());
    165     }
    166 
    167     private boolean isDefaultPhoneApp(String pkg) {
    168         if (mDefaultPhoneApp == null) {
    169             final TelecomManager telecomm =
    170                     (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
    171             mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultDialerPackage() : null;
    172         }
    173         return Objects.equals(pkg, mDefaultPhoneApp);
    174     }
    175 
    176     private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
    177         @Override
    178         public void onReceive(Context context, Intent intent) {
    179             mDefaultPhoneApp =
    180                     intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
    181         }
    182     };
    183 }
    184