Loading api/system-current.txt +0 −2 Original line number Diff line number Diff line Loading @@ -37556,8 +37556,6 @@ package android.service.notification { method public int getUser(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override"; field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed"; } public final class Condition implements android.os.Parcelable { core/java/android/app/NotificationChannel.java +4 −4 Original line number Diff line number Diff line Loading @@ -134,11 +134,11 @@ public final class NotificationChannel implements Parcelable { this.mLockscreenVisibility = lockscreenVisibility; } // Modifiable by apps. // Modifiable by apps on channel creation. /** * Sets the ringtone that should be played for notifications posted to this channel if * the notifications don't supply a ringtone. * the notifications don't supply a ringtone. Only modifiable on channel creation. */ public void setDefaultRingtone(Uri defaultRingtone) { this.mRingtone = defaultRingtone; Loading @@ -146,7 +146,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notifications posted to this channel should display notification lights, * on devices that support that feature. * on devices that support that feature. Only modifiable on channel creation. */ public void setLights(boolean lights) { this.mLights = lights; Loading @@ -154,7 +154,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notification posted to this channel should vibrate, even if individual * notifications are marked as having vibration. * notifications are marked as having vibration only modifiable on channel creation. */ public void setVibration(boolean vibration) { this.mVibration = vibration; Loading core/java/android/service/notification/Adjustment.java +0 −3 Original line number Diff line number Diff line Loading @@ -36,9 +36,6 @@ public final class Adjustment implements Parcelable { private final Bundle mSignals; private final int mUser; public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override"; public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed"; /** * Create a notification adjustment. * Loading packages/ExtServices/src/android/ext/services/notification/Ranker.java +1 −166 Original line number Diff line number Diff line Loading @@ -35,180 +35,15 @@ import java.util.Map; import android.ext.services.R; /** * Class that provides an updatable ranker module for the notification manager.. * Class that provides an updatable ranker module for the notification manager. */ public final class Ranker extends NotificationRankerService { private static final String TAG = "RocketRanker"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int AUTOBUNDLE_AT_COUNT = 4; private static final String AUTOBUNDLE_KEY = "ranker_bundle"; // Map of user : <Map of package : notification keys>. Only contains notifications that are not // bundled by the app (aka no group or sort key). Map<Integer, Map<String, LinkedHashSet<String>>> mUnbundledNotifications; @Override public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance, boolean user) { if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey()); return null; } @Override public void onNotificationPosted(StatusBarNotification sbn) { if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); try { List<String> notificationsToBundle = new ArrayList<>(); if (!sbn.isAppGroup()) { // Not grouped by the app, add to the list of notifications for the app; // send bundling update if app exceeds the autobundling limit. synchronized (mUnbundledNotifications) { Map<String, LinkedHashSet<String>> unbundledNotificationsByUser = mUnbundledNotifications.get(sbn.getUserId()); if (unbundledNotificationsByUser == null) { unbundledNotificationsByUser = new HashMap<>(); } mUnbundledNotifications.put(sbn.getUserId(), unbundledNotificationsByUser); LinkedHashSet<String> notificationsForPackage = unbundledNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null) { notificationsForPackage = new LinkedHashSet<>(); } notificationsForPackage.add(sbn.getKey()); unbundledNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) { for (String key : notificationsForPackage) { notificationsToBundle.add(key); } } } if (notificationsToBundle.size() > 0) { adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0), true, sbn.getUserId()); adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true, sbn.getUserId()); } } else { // Grouped, but not by us. Send updates to unautobundle, if we bundled it. maybeUnbundle(sbn, false, sbn.getUserId()); } } catch (Exception e) { Slog.e(TAG, "Failure processing new notification", e); } } @Override public void onNotificationRemoved(StatusBarNotification sbn) { try { maybeUnbundle(sbn, true, sbn.getUserId()); } catch (Exception e) { Slog.e(TAG, "Error processing canceled notification", e); } } /** * Un-autobundles notifications that are now grouped by the app. Additionally cancels * autobundling if the status change of this notification resulted in the loose notification * count being under the limit. */ private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone, int user) { List<String> notificationsToUnAutobundle = new ArrayList<>(); boolean removeSummary = false; synchronized (mUnbundledNotifications) { Map<String, LinkedHashSet<String>> unbundledNotificationsByUser = mUnbundledNotifications.get(sbn.getUserId()); if (unbundledNotificationsByUser == null || unbundledNotificationsByUser.size() == 0) { return; } LinkedHashSet<String> notificationsForPackage = unbundledNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null || notificationsForPackage.size() == 0) { return; } if (notificationsForPackage.remove(sbn.getKey())) { if (!notificationGone) { // Add the current notification to the unbundling list if it still exists. notificationsToUnAutobundle.add(sbn.getKey()); } // If the status change of this notification has brought the number of loose // notifications back below the limit, remove the summary and un-autobundle. if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) { removeSummary = true; for (String key : notificationsForPackage) { notificationsToUnAutobundle.add(key); } } } } if (notificationsToUnAutobundle.size() > 0) { if (removeSummary) { adjustAutobundlingSummary(sbn.getPackageName(), null, false, user); } adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false, user); } } @Override public void onListenerConnected() { if (DEBUG) Log.i(TAG, "CONNECTED"); mUnbundledNotifications = new HashMap<>(); for (StatusBarNotification sbn : getActiveNotifications()) { onNotificationPosted(sbn); } } private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded, int user) { Bundle signals = new Bundle(); if (summaryNeeded) { signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true); signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY); } else { signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false); } Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals, getContext().getString(R.string.notification_ranker_autobundle_explanation), null, user); if (DEBUG) { Log.i(TAG, "Summary update for: " + packageName + " " + (summaryNeeded ? "adding" : "removing")); } try { adjustNotification(adjustment); } catch (Exception e) { Slog.e(TAG, "Adjustment failed", e); } } private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle, int user) { List<Adjustment> adjustments = new ArrayList<>(); for (String key : keys) { adjustments.add(createBundlingAdjustment(packageName, key, bundle, user)); if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key); } try { adjustNotifications(adjustments); } catch (Exception e) { Slog.e(TAG, "Adjustments failed", e); } } private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle, int user) { Bundle signals = new Bundle(); if (bundle) { signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY); } else { signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null); } return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals, getContext().getString(R.string.notification_ranker_autobundle_explanation), null, user); } } No newline at end of file services/core/java/com/android/server/notification/GroupHelper.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * 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 android.service.notification.StatusBarNotification; import android.util.Log; import android.util.Slog; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; /** * NotificationManagerService helper for auto-grouping notifications. */ public class GroupHelper { private static final String TAG = "GroupHelper"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); protected static final int AUTOGROUP_AT_COUNT = 4; protected static final String AUTOGROUP_KEY = "ranker_group"; private final Callback mCallback; // Map of user : <Map of package : notification keys>. Only contains notifications that are not // groupd by the app (aka no group or sort key). Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>(); public GroupHelper(Callback callback) {; mCallback = callback; } public void onNotificationPosted(StatusBarNotification sbn) { if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); try { List<String> notificationsToGroup = new ArrayList<>(); if (!sbn.isAppGroup()) { // Not grouped by the app, add to the list of notifications for the app; // send grouping update if app exceeds the autogrouping limit. synchronized (mUngroupedNotifications) { Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser = mUngroupedNotifications.get(sbn.getUserId()); if (ungroupedNotificationsByUser == null) { ungroupedNotificationsByUser = new HashMap<>(); } mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser); LinkedHashSet<String> notificationsForPackage = ungroupedNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null) { notificationsForPackage = new LinkedHashSet<>(); } notificationsForPackage.add(sbn.getKey()); ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT) { notificationsToGroup.addAll(notificationsForPackage); } } if (notificationsToGroup.size() > 0) { adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), notificationsToGroup.get(0), true); adjustNotificationBundling(notificationsToGroup, true); } } else { // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. maybeUngroup(sbn, false, sbn.getUserId()); } } catch (Exception e) { Slog.e(TAG, "Failure processing new notification", e); } } public void onNotificationRemoved(StatusBarNotification sbn) { try { maybeUngroup(sbn, true, sbn.getUserId()); } catch (Exception e) { Slog.e(TAG, "Error processing canceled notification", e); } } /** * Un-autogroups notifications that are now grouped by the app. Additionally cancels * autogrouping if the status change of this notification resulted in the loose notification * count being under the limit. */ private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { List<String> notificationsToUnAutogroup = new ArrayList<>(); boolean removeSummary = false; synchronized (mUngroupedNotifications) { Map<String, LinkedHashSet<String>> ungroupdNotificationsByUser = mUngroupedNotifications.get(sbn.getUserId()); if (ungroupdNotificationsByUser == null || ungroupdNotificationsByUser.size() == 0) { return; } LinkedHashSet<String> notificationsForPackage = ungroupdNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null || notificationsForPackage.size() == 0) { return; } if (notificationsForPackage.remove(sbn.getKey())) { if (!notificationGone) { // Add the current notification to the ungrouping list if it still exists. notificationsToUnAutogroup.add(sbn.getKey()); } // If the status change of this notification has brought the number of loose // notifications back below the limit, remove the summary and un-autogroup. if (notificationsForPackage.size() == AUTOGROUP_AT_COUNT - 1) { removeSummary = true; for (String key : notificationsForPackage) { notificationsToUnAutogroup.add(key); } } } } if (notificationsToUnAutogroup.size() > 0) { if (removeSummary) { adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); } adjustNotificationBundling(notificationsToUnAutogroup, false); } } private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, boolean summaryNeeded) { if (summaryNeeded) { mCallback.addAutoGroupSummary(userId, packageName, triggeringKey); } else { mCallback.removeAutoGroupSummary(userId, packageName); } } private void adjustNotificationBundling(List<String> keys, boolean group) { for (String key : keys) { if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); if (group) { mCallback.addAutoGroup(key); } else { mCallback.removeAutoGroup(key); } } } protected interface Callback { void addAutoGroup(String key); void removeAutoGroup(String key); void addAutoGroupSummary(int userId, String pkg, String triggeringKey); void removeAutoGroupSummary(int user, String pkg); } } Loading
api/system-current.txt +0 −2 Original line number Diff line number Diff line Loading @@ -37556,8 +37556,6 @@ package android.service.notification { method public int getUser(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override"; field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed"; } public final class Condition implements android.os.Parcelable {
core/java/android/app/NotificationChannel.java +4 −4 Original line number Diff line number Diff line Loading @@ -134,11 +134,11 @@ public final class NotificationChannel implements Parcelable { this.mLockscreenVisibility = lockscreenVisibility; } // Modifiable by apps. // Modifiable by apps on channel creation. /** * Sets the ringtone that should be played for notifications posted to this channel if * the notifications don't supply a ringtone. * the notifications don't supply a ringtone. Only modifiable on channel creation. */ public void setDefaultRingtone(Uri defaultRingtone) { this.mRingtone = defaultRingtone; Loading @@ -146,7 +146,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notifications posted to this channel should display notification lights, * on devices that support that feature. * on devices that support that feature. Only modifiable on channel creation. */ public void setLights(boolean lights) { this.mLights = lights; Loading @@ -154,7 +154,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notification posted to this channel should vibrate, even if individual * notifications are marked as having vibration. * notifications are marked as having vibration only modifiable on channel creation. */ public void setVibration(boolean vibration) { this.mVibration = vibration; Loading
core/java/android/service/notification/Adjustment.java +0 −3 Original line number Diff line number Diff line Loading @@ -36,9 +36,6 @@ public final class Adjustment implements Parcelable { private final Bundle mSignals; private final int mUser; public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override"; public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed"; /** * Create a notification adjustment. * Loading
packages/ExtServices/src/android/ext/services/notification/Ranker.java +1 −166 Original line number Diff line number Diff line Loading @@ -35,180 +35,15 @@ import java.util.Map; import android.ext.services.R; /** * Class that provides an updatable ranker module for the notification manager.. * Class that provides an updatable ranker module for the notification manager. */ public final class Ranker extends NotificationRankerService { private static final String TAG = "RocketRanker"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int AUTOBUNDLE_AT_COUNT = 4; private static final String AUTOBUNDLE_KEY = "ranker_bundle"; // Map of user : <Map of package : notification keys>. Only contains notifications that are not // bundled by the app (aka no group or sort key). Map<Integer, Map<String, LinkedHashSet<String>>> mUnbundledNotifications; @Override public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance, boolean user) { if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey()); return null; } @Override public void onNotificationPosted(StatusBarNotification sbn) { if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); try { List<String> notificationsToBundle = new ArrayList<>(); if (!sbn.isAppGroup()) { // Not grouped by the app, add to the list of notifications for the app; // send bundling update if app exceeds the autobundling limit. synchronized (mUnbundledNotifications) { Map<String, LinkedHashSet<String>> unbundledNotificationsByUser = mUnbundledNotifications.get(sbn.getUserId()); if (unbundledNotificationsByUser == null) { unbundledNotificationsByUser = new HashMap<>(); } mUnbundledNotifications.put(sbn.getUserId(), unbundledNotificationsByUser); LinkedHashSet<String> notificationsForPackage = unbundledNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null) { notificationsForPackage = new LinkedHashSet<>(); } notificationsForPackage.add(sbn.getKey()); unbundledNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) { for (String key : notificationsForPackage) { notificationsToBundle.add(key); } } } if (notificationsToBundle.size() > 0) { adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0), true, sbn.getUserId()); adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true, sbn.getUserId()); } } else { // Grouped, but not by us. Send updates to unautobundle, if we bundled it. maybeUnbundle(sbn, false, sbn.getUserId()); } } catch (Exception e) { Slog.e(TAG, "Failure processing new notification", e); } } @Override public void onNotificationRemoved(StatusBarNotification sbn) { try { maybeUnbundle(sbn, true, sbn.getUserId()); } catch (Exception e) { Slog.e(TAG, "Error processing canceled notification", e); } } /** * Un-autobundles notifications that are now grouped by the app. Additionally cancels * autobundling if the status change of this notification resulted in the loose notification * count being under the limit. */ private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone, int user) { List<String> notificationsToUnAutobundle = new ArrayList<>(); boolean removeSummary = false; synchronized (mUnbundledNotifications) { Map<String, LinkedHashSet<String>> unbundledNotificationsByUser = mUnbundledNotifications.get(sbn.getUserId()); if (unbundledNotificationsByUser == null || unbundledNotificationsByUser.size() == 0) { return; } LinkedHashSet<String> notificationsForPackage = unbundledNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null || notificationsForPackage.size() == 0) { return; } if (notificationsForPackage.remove(sbn.getKey())) { if (!notificationGone) { // Add the current notification to the unbundling list if it still exists. notificationsToUnAutobundle.add(sbn.getKey()); } // If the status change of this notification has brought the number of loose // notifications back below the limit, remove the summary and un-autobundle. if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) { removeSummary = true; for (String key : notificationsForPackage) { notificationsToUnAutobundle.add(key); } } } } if (notificationsToUnAutobundle.size() > 0) { if (removeSummary) { adjustAutobundlingSummary(sbn.getPackageName(), null, false, user); } adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false, user); } } @Override public void onListenerConnected() { if (DEBUG) Log.i(TAG, "CONNECTED"); mUnbundledNotifications = new HashMap<>(); for (StatusBarNotification sbn : getActiveNotifications()) { onNotificationPosted(sbn); } } private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded, int user) { Bundle signals = new Bundle(); if (summaryNeeded) { signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true); signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY); } else { signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false); } Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals, getContext().getString(R.string.notification_ranker_autobundle_explanation), null, user); if (DEBUG) { Log.i(TAG, "Summary update for: " + packageName + " " + (summaryNeeded ? "adding" : "removing")); } try { adjustNotification(adjustment); } catch (Exception e) { Slog.e(TAG, "Adjustment failed", e); } } private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle, int user) { List<Adjustment> adjustments = new ArrayList<>(); for (String key : keys) { adjustments.add(createBundlingAdjustment(packageName, key, bundle, user)); if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key); } try { adjustNotifications(adjustments); } catch (Exception e) { Slog.e(TAG, "Adjustments failed", e); } } private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle, int user) { Bundle signals = new Bundle(); if (bundle) { signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY); } else { signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null); } return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals, getContext().getString(R.string.notification_ranker_autobundle_explanation), null, user); } } No newline at end of file
services/core/java/com/android/server/notification/GroupHelper.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * 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 android.service.notification.StatusBarNotification; import android.util.Log; import android.util.Slog; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; /** * NotificationManagerService helper for auto-grouping notifications. */ public class GroupHelper { private static final String TAG = "GroupHelper"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); protected static final int AUTOGROUP_AT_COUNT = 4; protected static final String AUTOGROUP_KEY = "ranker_group"; private final Callback mCallback; // Map of user : <Map of package : notification keys>. Only contains notifications that are not // groupd by the app (aka no group or sort key). Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>(); public GroupHelper(Callback callback) {; mCallback = callback; } public void onNotificationPosted(StatusBarNotification sbn) { if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); try { List<String> notificationsToGroup = new ArrayList<>(); if (!sbn.isAppGroup()) { // Not grouped by the app, add to the list of notifications for the app; // send grouping update if app exceeds the autogrouping limit. synchronized (mUngroupedNotifications) { Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser = mUngroupedNotifications.get(sbn.getUserId()); if (ungroupedNotificationsByUser == null) { ungroupedNotificationsByUser = new HashMap<>(); } mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser); LinkedHashSet<String> notificationsForPackage = ungroupedNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null) { notificationsForPackage = new LinkedHashSet<>(); } notificationsForPackage.add(sbn.getKey()); ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT) { notificationsToGroup.addAll(notificationsForPackage); } } if (notificationsToGroup.size() > 0) { adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), notificationsToGroup.get(0), true); adjustNotificationBundling(notificationsToGroup, true); } } else { // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. maybeUngroup(sbn, false, sbn.getUserId()); } } catch (Exception e) { Slog.e(TAG, "Failure processing new notification", e); } } public void onNotificationRemoved(StatusBarNotification sbn) { try { maybeUngroup(sbn, true, sbn.getUserId()); } catch (Exception e) { Slog.e(TAG, "Error processing canceled notification", e); } } /** * Un-autogroups notifications that are now grouped by the app. Additionally cancels * autogrouping if the status change of this notification resulted in the loose notification * count being under the limit. */ private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { List<String> notificationsToUnAutogroup = new ArrayList<>(); boolean removeSummary = false; synchronized (mUngroupedNotifications) { Map<String, LinkedHashSet<String>> ungroupdNotificationsByUser = mUngroupedNotifications.get(sbn.getUserId()); if (ungroupdNotificationsByUser == null || ungroupdNotificationsByUser.size() == 0) { return; } LinkedHashSet<String> notificationsForPackage = ungroupdNotificationsByUser.get(sbn.getPackageName()); if (notificationsForPackage == null || notificationsForPackage.size() == 0) { return; } if (notificationsForPackage.remove(sbn.getKey())) { if (!notificationGone) { // Add the current notification to the ungrouping list if it still exists. notificationsToUnAutogroup.add(sbn.getKey()); } // If the status change of this notification has brought the number of loose // notifications back below the limit, remove the summary and un-autogroup. if (notificationsForPackage.size() == AUTOGROUP_AT_COUNT - 1) { removeSummary = true; for (String key : notificationsForPackage) { notificationsToUnAutogroup.add(key); } } } } if (notificationsToUnAutogroup.size() > 0) { if (removeSummary) { adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); } adjustNotificationBundling(notificationsToUnAutogroup, false); } } private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, boolean summaryNeeded) { if (summaryNeeded) { mCallback.addAutoGroupSummary(userId, packageName, triggeringKey); } else { mCallback.removeAutoGroupSummary(userId, packageName); } } private void adjustNotificationBundling(List<String> keys, boolean group) { for (String key : keys) { if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); if (group) { mCallback.addAutoGroup(key); } else { mCallback.removeAutoGroup(key); } } } protected interface Callback { void addAutoGroup(String key); void removeAutoGroup(String key); void addAutoGroupSummary(int userId, String pkg, String triggeringKey); void removeAutoGroupSummary(int user, String pkg); } }