blob: 9b9f4de7a18f1082531c678af30e343bd46da63e [file] [log] [blame]
Chris Wrenf9536642014-04-17 10:01:54 -04001/*
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 */
16package com.android.server.notification;
17
Gus Prevas2c963d22018-12-03 15:49:06 -050018import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
19
Julia Reynolds4a02afb2016-12-13 13:39:52 -050020import android.app.Notification;
21import android.app.NotificationManager;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
Gus Prevas2c963d22018-12-03 15:49:06 -050026import android.provider.Settings;
Julia Reynolds4a02afb2016-12-13 13:39:52 -050027import android.telecom.TelecomManager;
Julia Reynolds4a02afb2016-12-13 13:39:52 -050028
Selim Cinek7d1009b2017-01-25 15:28:28 -080029import com.android.internal.util.NotificationMessagingUtil;
30
Chris Wrenf9536642014-04-17 10:01:54 -040031import java.util.Comparator;
Julia Reynolds4a02afb2016-12-13 13:39:52 -050032import java.util.Objects;
Chris Wrenf9536642014-04-17 10:01:54 -040033
34/**
Julia Reynolds0421e6d2016-01-08 09:51:24 -050035 * Sorts notifications individually into attention-relevant order.
Chris Wrenf9536642014-04-17 10:01:54 -040036 */
37public class NotificationComparator
Chris Wren333a61c2014-05-28 16:40:57 -040038 implements Comparator<NotificationRecord> {
Chris Wrenf9536642014-04-17 10:01:54 -040039
Julia Reynolds4a02afb2016-12-13 13:39:52 -050040 private final Context mContext;
Selim Cinek7d1009b2017-01-25 15:28:28 -080041 private final NotificationMessagingUtil mMessagingUtil;
Julia Reynolds4a02afb2016-12-13 13:39:52 -050042 private String mDefaultPhoneApp;
Julia Reynolds4a02afb2016-12-13 13:39:52 -050043
44 public NotificationComparator(Context context) {
45 mContext = context;
46 mContext.registerReceiver(mPhoneAppBroadcastReceiver,
47 new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
Selim Cinek7d1009b2017-01-25 15:28:28 -080048 mMessagingUtil = new NotificationMessagingUtil(mContext);
Julia Reynolds4a02afb2016-12-13 13:39:52 -050049 }
50
Chris Wrenf9536642014-04-17 10:01:54 -040051 @Override
Chris Wren1031c972014-07-23 13:11:45 +000052 public int compare(NotificationRecord left, NotificationRecord right) {
Gus Prevas2c963d22018-12-03 15:49:06 -050053 final int leftImportance = left.getImportance();
54 final int rightImportance = right.getImportance();
55 final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
56 final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
57
58 // With new interruption model, prefer importance bucket above all other criteria
59 // (to ensure buckets are contiguous)
60 if (Settings.Secure.getInt(mContext.getContentResolver(),
61 Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
62 if (isLeftHighImportance != isRightHighImportance) {
63 // by importance bucket, high importance higher than low importance
64 return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
65 }
66 }
67
Selim Cinek7b9605b2017-01-19 17:36:00 -080068 // first all colorized notifications
Selim Cinekc6c639cb2017-05-05 19:49:22 -070069 boolean leftImportantColorized = isImportantColorized(left);
70 boolean rightImportantColorized = isImportantColorized(right);
Selim Cinek7b9605b2017-01-19 17:36:00 -080071
72 if (leftImportantColorized != rightImportantColorized) {
73 return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
74 }
75
76 // sufficiently important ongoing notifications of certain categories
Julia Reynolds4a02afb2016-12-13 13:39:52 -050077 boolean leftImportantOngoing = isImportantOngoing(left);
78 boolean rightImportantOngoing = isImportantOngoing(right);
79
80 if (leftImportantOngoing != rightImportantOngoing) {
81 // by ongoing, ongoing higher than non-ongoing
82 return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
83 }
84
Selim Cinek7d1009b2017-01-25 15:28:28 -080085 boolean leftMessaging = isImportantMessaging(left);
86 boolean rightMessaging = isImportantMessaging(right);
87 if (leftMessaging != rightMessaging) {
88 return -1 * Boolean.compare(leftMessaging, rightMessaging);
89 }
90
Julia Reynolds4a02afb2016-12-13 13:39:52 -050091 // Next: sufficiently import person to person communication
Selim Cinek7d1009b2017-01-25 15:28:28 -080092 boolean leftPeople = isImportantPeople(left);
93 boolean rightPeople = isImportantPeople(right);
Julia Reynolds24450372017-05-02 15:04:34 -040094 final int contactAffinityComparison =
95 Float.compare(left.getContactAffinity(), right.getContactAffinity());
Julia Reynolds4a02afb2016-12-13 13:39:52 -050096
97 if (leftPeople && rightPeople){
98 // by contact proximity, close to far. if same proximity, check further fields.
Julia Reynolds24450372017-05-02 15:04:34 -040099 if (contactAffinityComparison != 0) {
100 return -1 * contactAffinityComparison;
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500101 }
102 } else if (leftPeople != rightPeople) {
103 // People, messaging higher than non-messaging
104 return -1 * Boolean.compare(leftPeople, rightPeople);
105 }
106
Chris Wrenbdf33762015-12-04 15:50:51 -0500107 if (leftImportance != rightImportance) {
Julia Reynoldsdbc114f2016-03-03 11:22:12 -0500108 // by importance, high to low
Chris Wrenbdf33762015-12-04 15:50:51 -0500109 return -1 * Integer.compare(leftImportance, rightImportance);
Chris Wrenf9536642014-04-17 10:01:54 -0400110 }
Chris Wren1031c972014-07-23 13:11:45 +0000111
Julia Reynolds24450372017-05-02 15:04:34 -0400112 // by contact proximity, close to far. if same proximity, check further fields.
113 if (contactAffinityComparison != 0) {
114 return -1 * contactAffinityComparison;
115 }
116
Julia Reynoldsdbc114f2016-03-03 11:22:12 -0500117 // Whether or not the notification can bypass DND.
Julia Reynolds0421e6d2016-01-08 09:51:24 -0500118 final int leftPackagePriority = left.getPackagePriority();
119 final int rightPackagePriority = right.getPackagePriority();
120 if (leftPackagePriority != rightPackagePriority) {
121 // by priority, high to low
122 return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
123 }
124
Julia Reynoldsdbc114f2016-03-03 11:22:12 -0500125 final int leftPriority = left.sbn.getNotification().priority;
126 final int rightPriority = right.sbn.getNotification().priority;
127 if (leftPriority != rightPriority) {
128 // by priority, high to low
129 return -1 * Integer.compare(leftPriority, rightPriority);
130 }
131
Chris Wrenf9536642014-04-17 10:01:54 -0400132 // then break ties by time, most recent first
Chris Wren1031c972014-07-23 13:11:45 +0000133 return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
Chris Wrenf9536642014-04-17 10:01:54 -0400134 }
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500135
Selim Cinekc6c639cb2017-05-05 19:49:22 -0700136 private boolean isImportantColorized(NotificationRecord record) {
Selim Cinek7b9605b2017-01-19 17:36:00 -0800137 if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
138 return false;
139 }
140 return record.getNotification().isColorized();
141 }
142
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500143 private boolean isImportantOngoing(NotificationRecord record) {
144 if (!isOngoing(record)) {
145 return false;
146 }
147
148 if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
149 return false;
150 }
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500151
152 return isCall(record) || isMediaNotification(record);
153 }
154
Selim Cinek7d1009b2017-01-25 15:28:28 -0800155 protected boolean isImportantPeople(NotificationRecord record) {
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500156 if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
157 return false;
158 }
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500159 if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
160 return true;
161 }
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500162 return false;
163 }
164
Selim Cinek7d1009b2017-01-25 15:28:28 -0800165 protected boolean isImportantMessaging(NotificationRecord record) {
166 return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
167 }
168
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500169 private boolean isOngoing(NotificationRecord record) {
Julia Reynolds889280c2017-04-12 12:27:00 -0400170 final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE;
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500171 return (record.getNotification().flags & ongoingFlags) != 0;
172 }
173
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500174 private boolean isMediaNotification(NotificationRecord record) {
Selim Cinek99104832017-01-25 14:47:33 -0800175 return record.getNotification().hasMediaSession();
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500176 }
177
178 private boolean isCall(NotificationRecord record) {
Julia Reynolds472f01c2018-01-18 09:25:44 -0500179 return record.isCategory(Notification.CATEGORY_CALL)
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500180 && isDefaultPhoneApp(record.sbn.getPackageName());
181 }
182
183 private boolean isDefaultPhoneApp(String pkg) {
184 if (mDefaultPhoneApp == null) {
185 final TelecomManager telecomm =
186 (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
187 mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultDialerPackage() : null;
188 }
189 return Objects.equals(pkg, mDefaultPhoneApp);
190 }
191
Julia Reynolds4a02afb2016-12-13 13:39:52 -0500192 private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
193 @Override
194 public void onReceive(Context context, Intent intent) {
195 mDefaultPhoneApp =
196 intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
197 }
198 };
Chris Wrenf9536642014-04-17 10:01:54 -0400199}