blob: 4c0092144da921f4a9ddd5d2ebda75728da706af [file] [log] [blame]
/*
* Copyright (C) 2014 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 com.android.server.notification;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telecom.TelecomManager;
import com.android.internal.util.NotificationMessagingUtil;
import java.util.Comparator;
import java.util.Objects;
/**
* Sorts notifications individually into attention-relevant order.
*/
public class NotificationComparator
implements Comparator<NotificationRecord> {
private final Context mContext;
private final NotificationMessagingUtil mMessagingUtil;
private String mDefaultPhoneApp;
public NotificationComparator(Context context) {
mContext = context;
mContext.registerReceiver(mPhoneAppBroadcastReceiver,
new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
mMessagingUtil = new NotificationMessagingUtil(mContext);
}
@Override
public int compare(NotificationRecord left, NotificationRecord right) {
// first all colorized notifications
boolean leftImportantColorized = isImportantColorized(left);
boolean rightImportantColorized = isImportantColorized(right);
if (leftImportantColorized != rightImportantColorized) {
return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
}
// sufficiently important ongoing notifications of certain categories
boolean leftImportantOngoing = isImportantOngoing(left);
boolean rightImportantOngoing = isImportantOngoing(right);
if (leftImportantOngoing != rightImportantOngoing) {
// by ongoing, ongoing higher than non-ongoing
return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
}
boolean leftMessaging = isImportantMessaging(left);
boolean rightMessaging = isImportantMessaging(right);
if (leftMessaging != rightMessaging) {
return -1 * Boolean.compare(leftMessaging, rightMessaging);
}
// Next: sufficiently import person to person communication
boolean leftPeople = isImportantPeople(left);
boolean rightPeople = isImportantPeople(right);
final int contactAffinityComparison =
Float.compare(left.getContactAffinity(), right.getContactAffinity());
if (leftPeople && rightPeople){
// by contact proximity, close to far. if same proximity, check further fields.
if (contactAffinityComparison != 0) {
return -1 * contactAffinityComparison;
}
} else if (leftPeople != rightPeople) {
// People, messaging higher than non-messaging
return -1 * Boolean.compare(leftPeople, rightPeople);
}
final int leftImportance = left.getImportance();
final int rightImportance = right.getImportance();
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
}
// by contact proximity, close to far. if same proximity, check further fields.
if (contactAffinityComparison != 0) {
return -1 * contactAffinityComparison;
}
// Whether or not the notification can bypass DND.
final int leftPackagePriority = left.getPackagePriority();
final int rightPackagePriority = right.getPackagePriority();
if (leftPackagePriority != rightPackagePriority) {
// by priority, high to low
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
final int leftPriority = left.sbn.getNotification().priority;
final int rightPriority = right.sbn.getNotification().priority;
if (leftPriority != rightPriority) {
// by priority, high to low
return -1 * Integer.compare(leftPriority, rightPriority);
}
// then break ties by time, most recent first
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
private boolean isImportantColorized(NotificationRecord record) {
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
return record.getNotification().isColorized();
}
private boolean isImportantOngoing(NotificationRecord record) {
if (!isOngoing(record)) {
return false;
}
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
return isCall(record) || isMediaNotification(record);
}
protected boolean isImportantPeople(NotificationRecord record) {
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
return true;
}
return false;
}
protected boolean isImportantMessaging(NotificationRecord record) {
return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
}
private boolean isOngoing(NotificationRecord record) {
final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE;
return (record.getNotification().flags & ongoingFlags) != 0;
}
private boolean isMediaNotification(NotificationRecord record) {
return record.getNotification().hasMediaSession();
}
private boolean isCall(NotificationRecord record) {
return record.getNotification().category == Notification.CATEGORY_CALL
&& isDefaultPhoneApp(record.sbn.getPackageName());
}
private boolean isDefaultPhoneApp(String pkg) {
if (mDefaultPhoneApp == null) {
final TelecomManager telecomm =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultDialerPackage() : null;
}
return Objects.equals(pkg, mDefaultPhoneApp);
}
private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mDefaultPhoneApp =
intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
}
};
}