Change notification shade ordering.
Certain ongoing and people centric notifications can now (mostly) trump importance-based ordering. Bug: 30374279 Test: runtest systemui-notification Change-Id: Ieab6015174f9595c08057dc408233202035aae3e
This commit is contained in:
@@ -5141,6 +5141,7 @@ package android.app {
|
||||
method public java.lang.String getChannel();
|
||||
method public java.lang.String getGroup();
|
||||
method public android.graphics.drawable.Icon getLargeIcon();
|
||||
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
|
||||
method public android.graphics.drawable.Icon getSmallIcon();
|
||||
method public java.lang.String getSortKey();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
|
||||
@@ -3985,19 +3985,6 @@ public class Notification implements Parcelable
|
||||
return new Builder(builderContext, n);
|
||||
}
|
||||
|
||||
private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
|
||||
Class<? extends Style>[] classes = new Class[] {
|
||||
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
|
||||
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
|
||||
MessagingStyle.class };
|
||||
for (Class<? extends Style> innerClass : classes) {
|
||||
if (templateClass.equals(innerClass.getName())) {
|
||||
return innerClass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #build()} instead.
|
||||
*/
|
||||
@@ -4174,6 +4161,23 @@ public class Notification implements Parcelable
|
||||
return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
|
||||
Class<? extends Style>[] classes = new Class[] {
|
||||
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
|
||||
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
|
||||
MessagingStyle.class };
|
||||
for (Class<? extends Style> innerClass : classes) {
|
||||
if (templateClass.equals(innerClass.getName())) {
|
||||
return innerClass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that can apply a rich notification style to a {@link Notification.Builder}
|
||||
* object.
|
||||
|
||||
@@ -15,7 +15,25 @@
|
||||
*/
|
||||
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.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Sorts notifications individually into attention-relevant order.
|
||||
@@ -23,8 +41,45 @@ import java.util.Comparator;
|
||||
public class NotificationComparator
|
||||
implements Comparator<NotificationRecord> {
|
||||
|
||||
private final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
|
||||
|
||||
private final Context mContext;
|
||||
private String mDefaultPhoneApp;
|
||||
private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
|
||||
|
||||
public NotificationComparator(Context context) {
|
||||
mContext = context;
|
||||
mContext.registerReceiver(mPhoneAppBroadcastReceiver,
|
||||
new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING), false, mSmsContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(NotificationRecord left, NotificationRecord right) {
|
||||
// First up: 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);
|
||||
}
|
||||
|
||||
// Next: sufficiently import person to person communication
|
||||
boolean leftPeople = isImportantMessaging(left);
|
||||
boolean rightPeople = isImportantMessaging(right);
|
||||
|
||||
if (leftPeople && rightPeople){
|
||||
// by contact proximity, close to far. if same proximity, check further fields.
|
||||
if (Float.compare(left.getContactAffinity(), right.getContactAffinity()) != 0) {
|
||||
return -1 * Float.compare(left.getContactAffinity(), right.getContactAffinity());
|
||||
}
|
||||
} 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) {
|
||||
@@ -47,14 +102,112 @@ public class NotificationComparator
|
||||
return -1 * Integer.compare(leftPriority, rightPriority);
|
||||
}
|
||||
|
||||
final float leftPeople = left.getContactAffinity();
|
||||
final float rightPeople = right.getContactAffinity();
|
||||
if (leftPeople != rightPeople) {
|
||||
// by contact proximity, close to far
|
||||
return -1 * Float.compare(leftPeople, rightPeople);
|
||||
}
|
||||
|
||||
// then break ties by time, most recent first
|
||||
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
|
||||
}
|
||||
|
||||
private boolean isImportantOngoing(NotificationRecord record) {
|
||||
if (!isOngoing(record)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: add whitelist
|
||||
|
||||
return isCall(record) || isMediaNotification(record);
|
||||
}
|
||||
|
||||
protected boolean isImportantMessaging(NotificationRecord record) {
|
||||
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<? extends Notification.Style> style = getNotificationStyle(record);
|
||||
if (Notification.MessagingStyle.class.equals(style)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (record.getNotification().category == Notification.CATEGORY_MESSAGE
|
||||
&& isDefaultMessagingApp(record)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isOngoing(NotificationRecord record) {
|
||||
final int ongoingFlags =
|
||||
Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_ONGOING_EVENT;
|
||||
return (record.getNotification().flags & ongoingFlags) != 0;
|
||||
}
|
||||
|
||||
|
||||
private Class<? extends Notification.Style> getNotificationStyle(NotificationRecord record) {
|
||||
String templateClass =
|
||||
record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE);
|
||||
|
||||
if (!TextUtils.isEmpty(templateClass)) {
|
||||
return Notification.getNotificationStyleClass(templateClass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMediaNotification(NotificationRecord record) {
|
||||
return record.getNotification().extras.getParcelable(
|
||||
Notification.EXTRA_MEDIA_SESSION) != null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isDefaultMessagingApp(NotificationRecord record) {
|
||||
final int userId = record.getUserId();
|
||||
if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false;
|
||||
if (mDefaultSmsApp.get(userId) == null) {
|
||||
mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
|
||||
}
|
||||
return Objects.equals(mDefaultSmsApp.get(userId), record.sbn.getPackageName());
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mDefaultPhoneApp =
|
||||
intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
|
||||
}
|
||||
};
|
||||
|
||||
private final ContentObserver mSmsContentObserver = new ContentObserver(
|
||||
new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri, int userId) {
|
||||
if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) {
|
||||
mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class RankingHelper implements RankingConfig {
|
||||
private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
|
||||
private final NotificationSignalExtractor[] mSignalExtractors;
|
||||
private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
|
||||
private final NotificationComparator mPreliminaryComparator;
|
||||
private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
|
||||
|
||||
private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
|
||||
@@ -90,6 +90,8 @@ public class RankingHelper implements RankingConfig {
|
||||
mRankingHandler = rankingHandler;
|
||||
mPm = pm;
|
||||
|
||||
mPreliminaryComparator = new NotificationComparator(mContext);
|
||||
|
||||
final int N = extractorNames.length;
|
||||
mSignalExtractors = new NotificationSignalExtractor[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.session.MediaSession;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class NotificationComparatorTest {
|
||||
@Mock Context mContext;
|
||||
@Mock TelecomManager mTm;
|
||||
@Mock RankingHandler handler;
|
||||
@Mock PackageManager mPm;
|
||||
|
||||
private final String callPkg = "com.android.server.notification";
|
||||
private final int callUid = 10;
|
||||
private String smsPkg;
|
||||
private final int smsUid = 11;
|
||||
private final String pkg2 = "pkg2";
|
||||
private final int uid2 = 1111111;
|
||||
|
||||
private NotificationRecord mRecordMinCall;
|
||||
private NotificationRecord mRecordHighCall;
|
||||
private NotificationRecord mRecordDefaultMedia;
|
||||
private NotificationRecord mRecordEmail;
|
||||
private NotificationRecord mRecordInlineReply;
|
||||
private NotificationRecord mRecordSms;
|
||||
private NotificationRecord mRecordStarredContact;
|
||||
private NotificationRecord mRecordContact;
|
||||
private NotificationRecord mRecordUrgent;
|
||||
private NotificationRecord mRecordCheater;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
int userId = UserHandle.myUserId();
|
||||
|
||||
when(mContext.getResources()).thenReturn(
|
||||
InstrumentationRegistry.getTargetContext().getResources());
|
||||
when(mContext.getContentResolver()).thenReturn(
|
||||
InstrumentationRegistry.getTargetContext().getContentResolver());
|
||||
when(mContext.getPackageManager()).thenReturn(mPm);
|
||||
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
|
||||
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
|
||||
final ApplicationInfo legacy = new ApplicationInfo();
|
||||
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
|
||||
try {
|
||||
when(mPm.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(legacy);
|
||||
when(mContext.getApplicationInfo()).thenReturn(legacy);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// let's hope not
|
||||
}
|
||||
|
||||
smsPkg = Settings.Secure.getString(mContext.getContentResolver(),
|
||||
Settings.Secure.SMS_DEFAULT_APPLICATION);
|
||||
|
||||
Notification n1 = new Notification.Builder(mContext)
|
||||
.setCategory(Notification.CATEGORY_CALL)
|
||||
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
|
||||
.build();
|
||||
mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
|
||||
callPkg, getDefaultChannel(), 1, "minCall", callUid, callUid, n1,
|
||||
new UserHandle(userId), "", 2000));
|
||||
mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
|
||||
|
||||
Notification n2 = new Notification.Builder(mContext)
|
||||
.setCategory(Notification.CATEGORY_CALL)
|
||||
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
|
||||
.build();
|
||||
mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
|
||||
callPkg, getDefaultChannel(), 1, "highcall", callUid, callUid, n2,
|
||||
new UserHandle(userId), "", 1999));
|
||||
mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
Notification n3 = new Notification.Builder(mContext)
|
||||
.setStyle(new Notification.MediaStyle()
|
||||
.setMediaSession(new MediaSession.Token(null)))
|
||||
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
|
||||
.build();
|
||||
mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "media", uid2, uid2, n3, new UserHandle(userId),
|
||||
"", 1499));
|
||||
mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
Notification n4 = new Notification.Builder(mContext)
|
||||
.setStyle(new Notification.MessagingStyle("sender!")).build();
|
||||
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
|
||||
"", 1599));
|
||||
mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
|
||||
|
||||
Notification n5 = new Notification.Builder(mContext)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE).build();
|
||||
mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
|
||||
smsPkg, getDefaultChannel(), 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
|
||||
"", 1299));
|
||||
mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
Notification n6 = new Notification.Builder(mContext).build();
|
||||
mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "starred", uid2, uid2, n6, new UserHandle(userId),
|
||||
"", 1259));
|
||||
mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
|
||||
mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
Notification n7 = new Notification.Builder(mContext).build();
|
||||
mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "contact", uid2, uid2, n7, new UserHandle(userId),
|
||||
"", 1259));
|
||||
mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
|
||||
mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
Notification n8 = new Notification.Builder(mContext).build();
|
||||
mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
|
||||
"", 1258));
|
||||
mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
Notification n9 = new Notification.Builder(mContext)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setFlag(Notification.FLAG_ONGOING_EVENT
|
||||
|Notification.FLAG_FOREGROUND_SERVICE, true)
|
||||
.build();
|
||||
mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
|
||||
"", 9258));
|
||||
mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
|
||||
Notification n10 = new Notification.Builder(mContext)
|
||||
.setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
|
||||
mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, getDefaultChannel(), 1, "email", uid2, uid2, n10, new UserHandle(userId),
|
||||
"", 1599));
|
||||
mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrdering() throws Exception {
|
||||
final List<NotificationRecord> expected = new ArrayList<>();
|
||||
expected.add(mRecordHighCall);
|
||||
expected.add(mRecordDefaultMedia);
|
||||
expected.add(mRecordStarredContact);
|
||||
expected.add(mRecordContact);
|
||||
expected.add(mRecordInlineReply);
|
||||
expected.add(mRecordSms);
|
||||
expected.add(mRecordEmail);
|
||||
expected.add(mRecordUrgent);
|
||||
expected.add(mRecordCheater);
|
||||
expected.add(mRecordMinCall);
|
||||
|
||||
List<NotificationRecord> actual = new ArrayList<>();
|
||||
actual.addAll(expected);
|
||||
Collections.shuffle(actual);
|
||||
|
||||
Collections.sort(actual, new NotificationComparator(mContext));
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessaging() throws Exception {
|
||||
NotificationComparator comp = new NotificationComparator(mContext);
|
||||
assertTrue(comp.isImportantMessaging(mRecordStarredContact));
|
||||
assertTrue(comp.isImportantMessaging(mRecordContact));
|
||||
assertTrue(comp.isImportantMessaging(mRecordInlineReply));
|
||||
assertTrue(comp.isImportantMessaging(mRecordSms));
|
||||
assertFalse(comp.isImportantMessaging(mRecordEmail));
|
||||
assertFalse(comp.isImportantMessaging(mRecordCheater));
|
||||
}
|
||||
|
||||
private NotificationChannel getDefaultChannel() {
|
||||
return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user