diff options
Diffstat (limited to 'java/com')
13 files changed, 411 insertions, 186 deletions
diff --git a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java index c64e03e4e..584f07fe3 100644 --- a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java @@ -18,7 +18,6 @@ package com.android.dialer.app.calllog; import android.annotation.TargetApi; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.os.Build.VERSION_CODES; @@ -35,6 +34,7 @@ import com.android.dialer.app.R; import com.android.dialer.calllogutils.PhoneAccountUtils; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelManager; /** Shows a notification in the status bar for legacy vociemail. */ @@ -77,9 +77,7 @@ public final class LegacyVoicemailNotifier { callVoicemailIntent, voicemailSettingsIntent, isRefresh); - context - .getSystemService(NotificationManager.class) - .notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification); + DialerNotificationManager.notify(context, NOTIFICATION_TAG, NOTIFICATION_ID, notification); } @NonNull @@ -151,8 +149,7 @@ public final class LegacyVoicemailNotifier { public static void cancelNotification(@NonNull Context context) { LogUtil.enterBlock("LegacyVoicemailNotifier.cancelNotification"); Assert.checkArgument(BuildCompat.isAtLeastO()); - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + DialerNotificationManager.cancel(context, NOTIFICATION_TAG, NOTIFICATION_ID); } private LegacyVoicemailNotifier() {} diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java index e0e3fdf3f..b363b5ab6 100644 --- a/java/com/android/dialer/app/calllog/MissedCallNotifier.java +++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java @@ -17,7 +17,6 @@ package com.android.dialer.app.calllog; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -48,7 +47,9 @@ import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; +import com.android.dialer.notification.NotificationManagerUtils; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.util.DialerUtils; @@ -59,9 +60,17 @@ import java.util.Set; /** Creates a notification for calls that the user missed (neither answered nor rejected). */ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { - static final String NOTIFICATION_TAG_PREFIX = "MissedCall_"; - static final String NOTIFICATION_GROUP = "MissedCall"; + /** Prefix used to generate a unique tag for each missed call notification. */ + private static final String NOTIFICATION_TAG_PREFIX = "MissedCall_"; + /** Common ID for all missed call notifications. */ private static final int NOTIFICATION_ID = 1; + /** Tag for the group summary notification. */ + private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_MissedCall"; + /** + * Key used to associate all missed call notifications and the summary as belonging to a single + * group. + */ + private static final String GROUP_KEY = "MissedCallGroup"; private final Context context; private final CallLogNotificationsQueryHelper callLogNotificationsQueryHelper; @@ -202,33 +211,29 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { configureLedOnNotification(notification); LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification"); - getNotificationMgr().notify(getNotificationTagForGroupSummary(), NOTIFICATION_ID, notification); + DialerNotificationManager.notify( + context, GROUP_SUMMARY_NOTIFICATION_TAG, NOTIFICATION_ID, notification); if (useCallList) { // Do not repost active notifications to prevent erasing post call notes. - NotificationManager manager = getNotificationMgr(); Set<String> activeTags = new ArraySet<>(); - for (StatusBarNotification activeNotification : manager.getActiveNotifications()) { + for (StatusBarNotification activeNotification : + DialerNotificationManager.getActiveNotifications(context)) { activeTags.add(activeNotification.getTag()); } for (NewCall call : newCalls) { String callTag = getNotificationTagForCall(call); if (!activeTags.contains(callTag)) { - manager.notify(callTag, NOTIFICATION_ID, getNotificationForCall(call, null)); + DialerNotificationManager.notify( + context, callTag, NOTIFICATION_ID, getNotificationForCall(call, null)); } } } } public static void cancelAllMissedCallNotifications(@NonNull Context context) { - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String tag = notification.getTag(); - if (tag != null && tag.startsWith(NOTIFICATION_TAG_PREFIX)) { - notificationManager.cancel(tag, notification.getId()); - } - } + NotificationManagerUtils.cancelAllInGroup(context, GROUP_KEY); } public static void cancelSingleMissedCallNotification( @@ -239,31 +244,9 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { "unable to cancel notification, uri is null"); return; } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - String callTag = getNotificationTagForCallUri(callUri); - String summaryTag = getNotificationTagForGroupSummary(); - int notificationCount = 0; - - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String currentTag = notification.getTag(); - if (currentTag == null) { - continue; - } - if (currentTag.equals(callTag)) { - notificationManager.cancel(notification.getTag(), notification.getId()); - } else if (currentTag.startsWith(NOTIFICATION_TAG_PREFIX) && !currentTag.equals(summaryTag)) { - notificationCount++; - } - } - - if (notificationCount == 0) { - // There are no more missed call notifications. Remove the summary notification too. - notificationManager.cancel(summaryTag, NOTIFICATION_ID); - } - } - - private static String getNotificationTagForGroupSummary() { - return NOTIFICATION_TAG_PREFIX + "GroupSummary"; + // This will also dismiss the group summary if there are no more missed call notifications. + DialerNotificationManager.cancel( + context, getNotificationTagForCallUri(callUri), NOTIFICATION_ID); } private static String getNotificationTagForCall(@NonNull NewCall call) { @@ -280,11 +263,11 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { for (NewCall call : newCalls) { if (call.number.equals(number.replace("tel:", ""))) { // Update the first notification that matches our post call note sender. - getNotificationMgr() - .notify( - getNotificationTagForCall(call), - NOTIFICATION_ID, - getNotificationForCall(call, note)); + DialerNotificationManager.notify( + context, + getNotificationTagForCall(call), + NOTIFICATION_ID, + getNotificationForCall(call, note)); break; } } @@ -366,7 +349,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { private Notification.Builder createNotificationBuilder() { return new Notification.Builder(context) - .setGroup(NOTIFICATION_GROUP) + .setGroup(GROUP_KEY) .setSmallIcon(android.R.drawable.stat_notify_missed_call) .setColor(context.getResources().getColor(R.color.dialer_theme_color, null)) .setAutoCancel(true) @@ -466,8 +449,4 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { private void closeSystemDialogs(Context context) { context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } - - private NotificationManager getNotificationMgr() { - return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - } } diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java index 99fe466d8..cbadfd317 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java @@ -17,7 +17,6 @@ package com.android.dialer.app.calllog; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -26,7 +25,6 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; -import android.service.notification.StatusBarNotification; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.os.BuildCompat; @@ -43,7 +41,9 @@ import com.android.dialer.app.list.DialtactsPagerAdapter; import com.android.dialer.common.LogUtil; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelManager; +import com.android.dialer.notification.NotificationManagerUtils; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.telecom.TelecomUtil; import java.util.List; @@ -51,9 +51,17 @@ import java.util.Map; /** Shows a notification in the status bar for visual voicemail. */ final class VisualVoicemailNotifier { + /** Prefix used to generate a unique tag for each voicemail notification. */ private static final String NOTIFICATION_TAG_PREFIX = "VisualVoicemail_"; - private static final String NOTIFICATION_GROUP = "VisualVoicemail"; + /** Common ID for all voicemail notifications. */ private static final int NOTIFICATION_ID = 1; + /** Tag for the group summary notification. */ + private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_VisualVoicemail"; + /** + * Key used to associate all voicemail notifications and the summary as belonging to a single + * group. + */ + private static final String GROUP_KEY = "VisualVoicemailGroup"; public static void showNotifications( @NonNull Context context, @@ -82,12 +90,12 @@ final class VisualVoicemailNotifier { groupSummary.setChannelId(NotificationChannelManager.getVoicemailChannelId(context, handle)); } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.notify( - getNotificationTagForGroupSummary(), NOTIFICATION_ID, groupSummary.build()); + DialerNotificationManager.notify( + context, GROUP_SUMMARY_NOTIFICATION_TAG, NOTIFICATION_ID, groupSummary.build()); for (NewCall voicemail : newCalls) { - notificationManager.notify( + DialerNotificationManager.notify( + context, getNotificationTagForVoicemail(voicemail), NOTIFICATION_ID, createNotificationForVoicemail(context, voicemail, contactInfos)); @@ -96,13 +104,7 @@ final class VisualVoicemailNotifier { public static void cancelAllVoicemailNotifications(@NonNull Context context) { LogUtil.enterBlock("VisualVoicemailNotifier.cancelAllVoicemailNotifications"); - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String tag = notification.getTag(); - if (tag != null && tag.startsWith(NOTIFICATION_TAG_PREFIX)) { - notificationManager.cancel(tag, notification.getId()); - } - } + NotificationManagerUtils.cancelAllInGroup(context, GROUP_KEY); } public static void cancelSingleVoicemailNotification( @@ -112,27 +114,9 @@ final class VisualVoicemailNotifier { LogUtil.e("VisualVoicemailNotifier.cancelSingleVoicemailNotification", "uri is null"); return; } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - String voicemailTag = getNotificationTagForUri(voicemailUri); - String summaryTag = getNotificationTagForGroupSummary(); - int notificationCount = 0; - - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String currentTag = notification.getTag(); - if (currentTag == null) { - continue; - } - if (currentTag.equals(voicemailTag)) { - notificationManager.cancel(notification.getTag(), notification.getId()); - } else if (currentTag.startsWith(NOTIFICATION_TAG_PREFIX) && !currentTag.equals(summaryTag)) { - notificationCount++; - } - } - - if (notificationCount == 0) { - // There are no more visual voicemail notifications. Remove the summary notification too. - notificationManager.cancel(summaryTag, NOTIFICATION_ID); - } + // This will also dismiss the group summary if there are no more voicemail notifications. + DialerNotificationManager.cancel( + context, getNotificationTagForUri(voicemailUri), NOTIFICATION_ID); } private static String getNotificationTagForVoicemail(@NonNull NewCall voicemail) { @@ -143,15 +127,11 @@ final class VisualVoicemailNotifier { return NOTIFICATION_TAG_PREFIX + voicemailUri; } - private static String getNotificationTagForGroupSummary() { - return NOTIFICATION_TAG_PREFIX + "GroupSummary"; - } - private static Notification.Builder createNotificationBuilder(@NonNull Context context) { return new Notification.Builder(context) .setSmallIcon(android.R.drawable.stat_notify_voicemail) .setColor(context.getColor(R.color.dialer_theme_color)) - .setGroup(NOTIFICATION_GROUP) + .setGroup(GROUP_KEY) .setOnlyAlertOnce(true) .setAutoCancel(true); } diff --git a/java/com/android/dialer/blocking/FilteredNumbersUtil.java b/java/com/android/dialer/blocking/FilteredNumbersUtil.java index 9e0c23c80..fba3b86db 100644 --- a/java/com/android/dialer/blocking/FilteredNumbersUtil.java +++ b/java/com/android/dialer/blocking/FilteredNumbersUtil.java @@ -16,7 +16,6 @@ package com.android.dialer.blocking; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ContentValues; import android.content.Context; @@ -36,6 +35,7 @@ import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnHasBlockedN import com.android.dialer.common.LogUtil; import com.android.dialer.logging.InteractionEvent; import com.android.dialer.logging.Logger; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.PermissionsUtil; @@ -242,8 +242,6 @@ public class FilteredNumbersUtil { return; } - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); Notification.Builder builder = new Notification.Builder(context) .setSmallIcon(R.drawable.quantum_ic_block_white_24) @@ -263,7 +261,8 @@ public class FilteredNumbersUtil { FilteredNumberCompat.createManageBlockedNumbersIntent(context), PendingIntent.FLAG_UPDATE_CURRENT)); - notificationManager.notify( + DialerNotificationManager.notify( + context, CALL_BLOCKING_NOTIFICATION_TAG, CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID, builder.build()); diff --git a/java/com/android/dialer/notification/DialerNotificationManager.java b/java/com/android/dialer/notification/DialerNotificationManager.java new file mode 100644 index 000000000..0e3420169 --- /dev/null +++ b/java/com/android/dialer/notification/DialerNotificationManager.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 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.dialer.notification; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.service.notification.StatusBarNotification; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.os.BuildCompat; +import android.text.TextUtils; +import android.util.Pair; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; + +/** + * Wrapper around the notification manager APIs. The wrapper ensures that channels are set and that + * notifications are limited to 10 per group. + */ +public final class DialerNotificationManager { + public static void notify(@NonNull Context context, int id, @NonNull Notification notification) { + Assert.isNotNull(context); + Assert.isNotNull(notification); + throw Assert.createUnsupportedOperationFailException("all notifications must have tags"); + } + + public static void notify( + @NonNull Context context, @NonNull String tag, int id, @NonNull Notification notification) { + Assert.isNotNull(context); + Assert.isNotNull(notification); + Assert.checkArgument(!TextUtils.isEmpty(tag)); + + if (BuildCompat.isAtLeastO()) { + Assert.checkArgument(!TextUtils.isEmpty(notification.getChannelId())); + } + + getNotificationManager(context).notify(tag, id, notification); + NotificationThrottler.throttle(context, notification); + } + + public static void cancel(@NonNull Context context, int id) { + Assert.isNotNull(context); + throw Assert.createUnsupportedOperationFailException( + "notification IDs are not unique across the app, a tag must be specified"); + } + + public static void cancel(@NonNull Context context, @NonNull String tag, int id) { + Assert.isNotNull(context); + Assert.checkArgument(!TextUtils.isEmpty(tag)); + + NotificationManager notificationManager = getNotificationManager(context); + StatusBarNotification[] notifications = notificationManager.getActiveNotifications(); + + String groupKey = findGroupKey(notifications, tag, id); + if (!TextUtils.isEmpty(groupKey)) { + Pair<StatusBarNotification, Integer> groupSummaryAndCount = + getGroupSummaryAndCount(notifications, groupKey); + if (groupSummaryAndCount.first != null && groupSummaryAndCount.second <= 1) { + LogUtil.i( + "DialerNotificationManager.cancel", + "last notification in group (%s) removed, also removing group summary", + groupKey); + notificationManager.cancel( + groupSummaryAndCount.first.getTag(), groupSummaryAndCount.first.getId()); + } + } + + notificationManager.cancel(tag, id); + } + + public static StatusBarNotification[] getActiveNotifications(@NonNull Context context) { + Assert.isNotNull(context); + return getNotificationManager(context).getActiveNotifications(); + } + + @Nullable + private static String findGroupKey( + @NonNull StatusBarNotification[] notifications, @NonNull String tag, int id) { + for (StatusBarNotification notification : notifications) { + if (TextUtils.equals(tag, notification.getTag()) && id == notification.getId()) { + return notification.getNotification().getGroup(); + } + } + return null; + } + + @NonNull + private static Pair<StatusBarNotification, Integer> getGroupSummaryAndCount( + @NonNull StatusBarNotification[] notifications, @NonNull String groupKey) { + StatusBarNotification groupSummaryNotification = null; + int groupCount = 0; + for (StatusBarNotification notification : notifications) { + if (TextUtils.equals(groupKey, notification.getNotification().getGroup())) { + if ((notification.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0) { + groupSummaryNotification = notification; + } else { + groupCount++; + } + } + } + return new Pair<>(groupSummaryNotification, groupCount); + } + + @NonNull + private static NotificationManager getNotificationManager(@NonNull Context context) { + return context.getSystemService(NotificationManager.class); + } + + private DialerNotificationManager() {} +} diff --git a/java/com/android/dialer/notification/NotificationManagerUtils.java b/java/com/android/dialer/notification/NotificationManagerUtils.java new file mode 100644 index 000000000..d99a9036d --- /dev/null +++ b/java/com/android/dialer/notification/NotificationManagerUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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.dialer.notification; + +import android.app.NotificationManager; +import android.content.Context; +import android.service.notification.StatusBarNotification; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import com.android.dialer.common.Assert; + +/** Utilities to manage notifications. */ +public final class NotificationManagerUtils { + public static void cancelAllInGroup(@NonNull Context context, @NonNull String groupKey) { + Assert.isNotNull(context); + Assert.checkArgument(!TextUtils.isEmpty(groupKey)); + + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { + if (TextUtils.equals(groupKey, notification.getNotification().getGroup())) { + notificationManager.cancel(notification.getTag(), notification.getId()); + } + } + } + + private NotificationManagerUtils() {} +} diff --git a/java/com/android/dialer/notification/NotificationThrottler.java b/java/com/android/dialer/notification/NotificationThrottler.java new file mode 100644 index 000000000..7c2428737 --- /dev/null +++ b/java/com/android/dialer/notification/NotificationThrottler.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 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.dialer.notification; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.service.notification.StatusBarNotification; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Utility to ensure that only a certain number of notifications are shown for a particular + * notification type. Once the limit is reached, older notifications are cancelled. + */ +/* package */ class NotificationThrottler { + private static final int MAX_NOTIFICATIONS_PER_TAG = 10; + + /* package */ static void throttle(@NonNull Context context, @NonNull Notification notification) { + Assert.isNotNull(context); + Assert.isNotNull(notification); + + // No limiting for non-grouped notifications. + String groupKey = notification.getGroup(); + if (TextUtils.isEmpty(groupKey)) { + return; + } + + int count = 0; + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + for (StatusBarNotification currentNotification : notificationManager.getActiveNotifications()) { + if (isNotificationInGroup(currentNotification, groupKey)) { + count++; + } + } + + if (count > MAX_NOTIFICATIONS_PER_TAG) { + LogUtil.i( + "NotificationThrottler.throttle", + "groupKey: %s is over limit, count: %d, limit: %d", + groupKey, + count, + MAX_NOTIFICATIONS_PER_TAG); + List<StatusBarNotification> notifications = getSortedMatchingNotifications(context, groupKey); + for (int i = 0; i < (count - MAX_NOTIFICATIONS_PER_TAG); i++) { + notificationManager.cancel(notifications.get(i).getTag(), notifications.get(i).getId()); + } + } + } + + private static List<StatusBarNotification> getSortedMatchingNotifications( + @NonNull Context context, @NonNull String groupKey) { + List<StatusBarNotification> notifications = new ArrayList<>(); + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { + if (isNotificationInGroup(notification, groupKey)) { + notifications.add(notification); + } + } + Collections.sort( + notifications, + new Comparator<StatusBarNotification>() { + @Override + public int compare(StatusBarNotification left, StatusBarNotification right) { + return Long.compare(left.getPostTime(), right.getPostTime()); + } + }); + return notifications; + } + + private static boolean isNotificationInGroup( + @NonNull StatusBarNotification notification, @NonNull String groupKey) { + // Don't include group summaries. + if ((notification.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0) { + return false; + } + + return TextUtils.equals(groupKey, notification.getNotification().getGroup()); + } + + private NotificationThrottler() {} +} diff --git a/java/com/android/incallui/ExternalCallNotifier.java b/java/com/android/incallui/ExternalCallNotifier.java index ed3c91897..9e7805236 100644 --- a/java/com/android/incallui/ExternalCallNotifier.java +++ b/java/com/android/incallui/ExternalCallNotifier.java @@ -18,7 +18,6 @@ package com.android.incallui; import android.annotation.TargetApi; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -41,7 +40,9 @@ import com.android.contacts.common.ContactsUtils; import com.android.contacts.common.compat.CallCompat; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.util.ContactDisplayUtils; +import com.android.dialer.common.Assert; import com.android.dialer.contactphoto.BitmapUtil; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; import com.android.incallui.call.DialerCall; import com.android.incallui.call.DialerCallDelegate; @@ -58,17 +59,25 @@ import java.util.Map; */ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListener { - /** Tag used with the notification manager to uniquely identify external call notifications. */ + /** + * Common tag for all external call notifications. Unlike other grouped notifications in Dialer, + * external call notifications are uniquely identified by ID. + */ private static final String NOTIFICATION_TAG = "EXTERNAL_CALL"; - private static final int NOTIFICATION_SUMMARY_ID = -1; + private static final int GROUP_SUMMARY_NOTIFICATION_ID = -1; + private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_ExternalCall"; + /** + * Key used to associate all external call notifications and the summary as belonging to a single + * group. + */ + private static final String GROUP_KEY = "ExternalCallGroup"; private final Context mContext; private final ContactInfoCache mContactInfoCache; private Map<Call, NotificationInfo> mNotifications = new ArrayMap<>(); private int mNextUniqueNotificationId; private ContactsPreferences mContactsPreferences; - private boolean mShowingSummary; /** Initializes a new instance of the external call notifier. */ public ExternalCallNotifier( @@ -85,9 +94,7 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen @Override public void onExternalCallAdded(android.telecom.Call call) { Log.i(this, "onExternalCallAdded " + call); - if (mNotifications.containsKey(call)) { - throw new IllegalArgumentException(); - } + Assert.checkArgument(!mNotifications.containsKey(call)); NotificationInfo info = new NotificationInfo(call, mNextUniqueNotificationId++); mNotifications.put(call, info); @@ -108,9 +115,7 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen /** Handles updates to an external call. */ @Override public void onExternalCallUpdated(Call call) { - if (!mNotifications.containsKey(call)) { - throw new IllegalArgumentException(); - } + Assert.checkArgument(mNotifications.containsKey(call)); postNotification(mNotifications.get(call)); } @@ -183,28 +188,13 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen /** Dismisses a notification for an external call. */ private void dismissNotification(Call call) { - if (!mNotifications.containsKey(call)) { - throw new IllegalArgumentException(); - } + Assert.checkArgument(mNotifications.containsKey(call)); - NotificationManager notificationManager = - (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(NOTIFICATION_TAG, mNotifications.get(call).getNotificationId()); + // This will also dismiss the group summary if there are no more external call notifications. + DialerNotificationManager.cancel( + mContext, NOTIFICATION_TAG, mNotifications.get(call).getNotificationId()); mNotifications.remove(call); - - if (mShowingSummary && mNotifications.size() <= 1) { - // Where a summary notification is showing and there is now not enough notifications to - // necessitate a summary, cancel the summary. - notificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_SUMMARY_ID); - mShowingSummary = false; - - // If there is still a single call requiring a notification, re-post the notification as a - // standalone notification without a summary notification. - if (mNotifications.size() == 1) { - postNotification(mNotifications.values().iterator().next()); - } - } } /** @@ -237,7 +227,7 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen builder.setOngoing(true); // Make the notification prioritized over the other normal notifications. builder.setPriority(Notification.PRIORITY_HIGH); - builder.setGroup(NOTIFICATION_TAG); + builder.setGroup(GROUP_KEY); boolean isVideoCall = VideoProfile.isVideo(info.getCall().getDetails().getVideoState()); // Set the content ("Ongoing call on another device") @@ -293,28 +283,10 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen builder.setPublicVersion(publicBuilder.build()); Notification notification = builder.build(); - NotificationManager notificationManager = - (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.notify(NOTIFICATION_TAG, info.getNotificationId(), notification); - - if (!mShowingSummary && mNotifications.size() > 1) { - // If the number of notifications shown is > 1, and we're not already showing a group summary, - // build one now. This will ensure the like notifications are grouped together. - - Notification.Builder summary = new Notification.Builder(mContext); - // Set notification as ongoing since calls are long-running versus a point-in-time notice. - summary.setOngoing(true); - // Make the notification prioritized over the other normal notifications. - summary.setPriority(Notification.PRIORITY_HIGH); - summary.setGroup(NOTIFICATION_TAG); - summary.setGroupSummary(true); - summary.setSmallIcon(R.drawable.quantum_ic_call_white_24); - if (BuildCompat.isAtLeastO()) { - summary.setChannelId(NotificationChannelId.DEFAULT); - } - notificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_SUMMARY_ID, summary.build()); - mShowingSummary = true; - } + DialerNotificationManager.notify( + mContext, NOTIFICATION_TAG, info.getNotificationId(), notification); + + showGroupSummaryNotification(mContext); } /** @@ -475,4 +447,20 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen mPersonReference = personReference; } } + + private static void showGroupSummaryNotification(@NonNull Context context) { + Notification.Builder summary = new Notification.Builder(context); + // Set notification as ongoing since calls are long-running versus a point-in-time notice. + summary.setOngoing(true); + // Make the notification prioritized over the other normal notifications. + summary.setPriority(Notification.PRIORITY_HIGH); + summary.setGroup(GROUP_KEY); + summary.setGroupSummary(true); + summary.setSmallIcon(R.drawable.quantum_ic_call_white_24); + if (BuildCompat.isAtLeastO()) { + summary.setChannelId(NotificationChannelId.DEFAULT); + } + DialerNotificationManager.notify( + context, GROUP_SUMMARY_NOTIFICATION_TAG, GROUP_SUMMARY_NOTIFICATION_ID, summary.build()); + } } diff --git a/java/com/android/incallui/StatusBarNotifier.java b/java/com/android/incallui/StatusBarNotifier.java index 07f179edc..d20c62f4b 100644 --- a/java/com/android/incallui/StatusBarNotifier.java +++ b/java/com/android/incallui/StatusBarNotifier.java @@ -28,7 +28,6 @@ import static com.android.incallui.NotificationBroadcastReceiver.ACTION_HANG_UP_ import android.Manifest; import android.app.ActivityManager; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -62,6 +61,7 @@ import com.android.contacts.common.ContactsUtils; import com.android.contacts.common.ContactsUtils.UserType; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.util.ContactDisplayUtils; +import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.contactphoto.BitmapUtil; @@ -70,6 +70,7 @@ import com.android.dialer.enrichedcall.Session; import com.android.dialer.lettertile.LetterTileDrawable; import com.android.dialer.lettertile.LetterTileDrawable.ContactType; import com.android.dialer.multimedia.MultimediaData; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; import com.android.dialer.oem.MotorolaUtils; import com.android.dialer.util.DrawableConverter; @@ -113,7 +114,6 @@ public class StatusBarNotifier private final Context mContext; private final ContactInfoCache mContactInfoCache; - private final NotificationManager mNotificationManager; private final DialerRingtoneManager mDialerRingtoneManager; @Nullable private ContactsPreferences mContactsPreferences; private int mCurrentNotification = NOTIFICATION_NONE; @@ -126,11 +126,9 @@ public class StatusBarNotifier private StatusBarCallListener mStatusBarCallListener; public StatusBarNotifier(@NonNull Context context, @NonNull ContactInfoCache contactInfoCache) { - Objects.requireNonNull(context); - mContext = context; + mContext = Assert.isNotNull(context); mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext); mContactInfoCache = contactInfoCache; - mNotificationManager = context.getSystemService(NotificationManager.class); mDialerRingtoneManager = new DialerRingtoneManager( new InCallTonePlayer(new ToneGeneratorFactory(), new PausableExecutorImpl()), @@ -142,14 +140,12 @@ public class StatusBarNotifier * Should only be called from a irrecoverable state where it is necessary to dismiss all * notifications. */ - static void clearAllCallNotifications(Context backupContext) { - LogUtil.i( + static void clearAllCallNotifications(Context context) { + LogUtil.e( "StatusBarNotifier.clearAllCallNotifications", "something terrible happened, clear all InCall notifications"); - NotificationManager notificationManager = - backupContext.getSystemService(NotificationManager.class); - notificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + DialerNotificationManager.cancel(context, NOTIFICATION_TAG, NOTIFICATION_ID); } private static int getWorkStringFromPersonalString(int resId) { @@ -218,7 +214,7 @@ public class StatusBarNotifier } if (mCurrentNotification != NOTIFICATION_NONE) { LogUtil.i("StatusBarNotifier.cancelNotification", "cancel"); - mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + DialerNotificationManager.cancel(mContext, NOTIFICATION_TAG, NOTIFICATION_ID); } mCurrentNotification = NOTIFICATION_NONE; } @@ -379,7 +375,7 @@ public class StatusBarNotifier "Canceling old notification so this one can be noisy"); // Moving from a non-interuptive notification (or none) to a noisy one. Cancel the old // notification (if there is one) so the fullScreenIntent or HUN will show - mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + DialerNotificationManager.cancel(mContext, NOTIFICATION_TAG, NOTIFICATION_ID); } break; case NOTIFICATION_INCOMING_CALL_QUIET: @@ -438,7 +434,7 @@ public class StatusBarNotifier "displaying notification for " + notificationType); try { - mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification); + DialerNotificationManager.notify(mContext, NOTIFICATION_TAG, NOTIFICATION_ID, notification); } catch (RuntimeException e) { // TODO(b/34744003): Move the memory stats into silent feedback PSD. ActivityManager activityManager = mContext.getSystemService(ActivityManager.class); diff --git a/java/com/android/incallui/call/ExternalCallList.java b/java/com/android/incallui/call/ExternalCallList.java index 52a7a304b..f104dfe9f 100644 --- a/java/com/android/incallui/call/ExternalCallList.java +++ b/java/com/android/incallui/call/ExternalCallList.java @@ -22,6 +22,7 @@ import android.support.annotation.NonNull; import android.telecom.Call; import android.util.ArraySet; import com.android.contacts.common.compat.CallCompat; +import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import java.util.Collections; import java.util.Set; @@ -48,9 +49,8 @@ public class ExternalCallList { /** Begins tracking an external call and notifies listeners of the new call. */ public void onCallAdded(Call telecomCall) { - if (!telecomCall.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) { - throw new IllegalArgumentException(); - } + Assert.checkArgument( + telecomCall.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)); mExternalCalls.add(telecomCall); telecomCall.registerCallback(mTelecomCallCallback, new Handler(Looper.getMainLooper())); notifyExternalCallAdded(telecomCall); diff --git a/java/com/android/incallui/spam/SpamCallListListener.java b/java/com/android/incallui/spam/SpamCallListListener.java index 7ca3fbba6..997d4d6bc 100644 --- a/java/com/android/incallui/spam/SpamCallListListener.java +++ b/java/com/android/incallui/spam/SpamCallListListener.java @@ -19,7 +19,6 @@ package com.android.incallui.spam; import android.annotation.TargetApi; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -46,6 +45,7 @@ import com.android.dialer.location.GeoUtil; import com.android.dialer.logging.ContactLookupResult; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; import com.android.dialer.spam.Spam; import com.android.dialer.telecom.TelecomUtil; @@ -62,7 +62,16 @@ import java.util.Random; * etc). */ public class SpamCallListListener implements CallList.Listener { + /** Common ID for all spam notifications. */ static final int NOTIFICATION_ID = 1; + /** Prefix used to generate a unique tag for each spam notification. */ + static final String NOTIFICATION_TAG_PREFIX = "SpamCall_"; + /** + * Key used to associate all spam notifications into a single group. Note, unlike other group + * notifications in Dialer, spam notifications don't have a top level group summary notification. + * The group is still useful for things like rate limiting on a per group basis. + */ + private static final String GROUP_KEY = "SpamCallGroup"; private final Context context; private final Random random; @@ -76,8 +85,7 @@ public class SpamCallListListener implements CallList.Listener { Context context, Random rand, @NonNull DialerExecutorFactory factory) { this.context = context; this.random = rand; - Assert.isNotNull(factory); - this.dialerExecutorFactory = factory; + this.dialerExecutorFactory = Assert.isNotNull(factory); } /** Checks if the number is in the call history. */ @@ -254,7 +262,8 @@ public class SpamCallListListener implements CallList.Listener { .setCategory(Notification.CATEGORY_STATUS) .setPriority(Notification.PRIORITY_DEFAULT) .setColor(context.getColor(R.color.dialer_theme_color)) - .setSmallIcon(R.drawable.ic_call_end_white_24dp); + .setSmallIcon(R.drawable.ic_call_end_white_24dp) + .setGroup(GROUP_KEY); if (BuildCompat.isAtLeastO()) { builder.setChannelId(NotificationChannelId.DEFAULT); } @@ -294,8 +303,8 @@ public class SpamCallListListener implements CallList.Listener { .build()) .setContentTitle( context.getString(R.string.non_spam_notification_title, getDisplayNumber(call))); - ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) - .notify(call.getNumber(), NOTIFICATION_ID, notificationBuilder.build()); + DialerNotificationManager.notify( + context, getNotificationTagForCall(call), NOTIFICATION_ID, notificationBuilder.build()); } private boolean shouldThrottleSpamNotification() { @@ -405,8 +414,8 @@ public class SpamCallListListener implements CallList.Listener { .build()) .setContentTitle( context.getString(R.string.spam_notification_title, getDisplayNumber(call))); - ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) - .notify(call.getNumber(), NOTIFICATION_ID, notificationBuilder.build()); + DialerNotificationManager.notify( + context, getNotificationTagForCall(call), NOTIFICATION_ID, notificationBuilder.build()); } /** @@ -434,7 +443,8 @@ public class SpamCallListListener implements CallList.Listener { /** Creates a pending intent for {@link SpamNotificationService}. */ private PendingIntent createServicePendingIntent(DialerCall call, String action) { Intent intent = - SpamNotificationService.createServiceIntent(context, call, action, NOTIFICATION_ID); + SpamNotificationService.createServiceIntent( + context, call, action, getNotificationTagForCall(call), NOTIFICATION_ID); return PendingIntent.getService( context, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT); } @@ -442,8 +452,13 @@ public class SpamCallListListener implements CallList.Listener { /** Creates a pending intent for {@link SpamNotificationActivity}. */ private PendingIntent createActivityPendingIntent(DialerCall call, String action) { Intent intent = - SpamNotificationActivity.createActivityIntent(context, call, action, NOTIFICATION_ID); + SpamNotificationActivity.createActivityIntent( + context, call, action, getNotificationTagForCall(call), NOTIFICATION_ID); return PendingIntent.getActivity( context, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT); } + + static String getNotificationTagForCall(@NonNull DialerCall call) { + return NOTIFICATION_TAG_PREFIX + call.getNumber(); + } } diff --git a/java/com/android/incallui/spam/SpamNotificationActivity.java b/java/com/android/incallui/spam/SpamNotificationActivity.java index ece0e4931..160db2622 100644 --- a/java/com/android/incallui/spam/SpamNotificationActivity.java +++ b/java/com/android/incallui/spam/SpamNotificationActivity.java @@ -18,7 +18,6 @@ package com.android.incallui.spam; import android.app.AlertDialog; import android.app.Dialog; -import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -38,6 +37,7 @@ import com.android.dialer.logging.ContactLookupResult; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ReportingLocation; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.spam.Spam; import com.android.incallui.R; import com.android.incallui.call.DialerCall; @@ -57,6 +57,7 @@ public class SpamNotificationActivity extends FragmentActivity { "com.android.incallui.spam.ACTION_MARK_NUMBER_AS_NOT_SPAM"; private static final String TAG = "SpamNotifications"; + private static final String EXTRA_NOTIFICATION_TAG = "notification_tag"; private static final String EXTRA_NOTIFICATION_ID = "notification_id"; private static final String EXTRA_CALL_INFO = "call_info"; @@ -82,12 +83,13 @@ public class SpamNotificationActivity extends FragmentActivity { * @return Intent intent that starts this activity. */ public static Intent createActivityIntent( - Context context, DialerCall call, String action, int notificationId) { + Context context, DialerCall call, String action, String notificationTag, int notificationId) { Intent intent = new Intent(context, SpamNotificationActivity.class); intent.setAction(action); // This ensures only one activity of this kind exists at a time. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_NOTIFICATION_TAG, notificationTag); intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); intent.putExtra(EXTRA_CALL_INFO, newCallInfoBundle(call)); return intent; @@ -294,10 +296,9 @@ public class SpamNotificationActivity extends FragmentActivity { /** Cancels the notification associated with the number. */ private void cancelNotification() { + String notificationTag = getIntent().getStringExtra(EXTRA_NOTIFICATION_TAG); int notificationId = getIntent().getIntExtra(EXTRA_NOTIFICATION_ID, 1); - String number = getCallInfo().getString(CALL_INFO_KEY_PHONE_NUMBER); - ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) - .cancel(number, notificationId); + DialerNotificationManager.cancel(this, notificationTag, notificationId); } private String getCountryIso() { diff --git a/java/com/android/incallui/spam/SpamNotificationService.java b/java/com/android/incallui/spam/SpamNotificationService.java index 7888fc84e..c92d61609 100644 --- a/java/com/android/incallui/spam/SpamNotificationService.java +++ b/java/com/android/incallui/spam/SpamNotificationService.java @@ -16,7 +16,6 @@ package com.android.incallui.spam; -import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -30,6 +29,7 @@ import com.android.dialer.logging.ContactLookupResult; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ReportingLocation; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.spam.Spam; import com.android.incallui.call.DialerCall; @@ -47,17 +47,19 @@ public class SpamNotificationService extends Service { private static final String EXTRA_PHONE_NUMBER = "service_phone_number"; private static final String EXTRA_CALL_ID = "service_call_id"; private static final String EXTRA_CALL_START_TIME_MILLIS = "service_call_start_time_millis"; + private static final String EXTRA_NOTIFICATION_TAG = "service_notification_tag"; private static final String EXTRA_NOTIFICATION_ID = "service_notification_id"; private static final String EXTRA_CONTACT_LOOKUP_RESULT_TYPE = "service_contact_lookup_result_type"; /** Creates an intent to start this service. */ public static Intent createServiceIntent( - Context context, DialerCall call, String action, int notificationId) { + Context context, DialerCall call, String action, String notificationTag, int notificationId) { Intent intent = new Intent(context, SpamNotificationService.class); intent.setAction(action); intent.putExtra(EXTRA_PHONE_NUMBER, call.getNumber()); intent.putExtra(EXTRA_CALL_ID, call.getUniqueCallId()); intent.putExtra(EXTRA_CALL_START_TIME_MILLIS, call.getTimeAddedMs()); + intent.putExtra(EXTRA_NOTIFICATION_TAG, notificationTag); intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); intent.putExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult); return intent; @@ -80,13 +82,13 @@ public class SpamNotificationService extends Service { return START_NOT_STICKY; } String number = intent.getStringExtra(EXTRA_PHONE_NUMBER); + String notificationTag = intent.getStringExtra(EXTRA_NOTIFICATION_TAG); int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 1); String countryIso = GeoUtil.getCurrentCountryIso(this); ContactLookupResult.Type contactLookupResultType = ContactLookupResult.Type.forNumber(intent.getIntExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, 0)); - ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) - .cancel(number, notificationId); + DialerNotificationManager.cancel(this, notificationTag, notificationId); switch (intent.getAction()) { case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM: |