Update the autogroup summary icon on notification changes
When child notifications of an auto-group are posted/removed, the summary icon should be updated as well.
In the case of posted notifications, summary updates are always triggered by GroupHelper. In the case of removed notifications,
if no summary updates are triggered (flags not updated or ungrouping not necessary), then NMS will trigger an icon update.
Both the icon drawable and the icon background color are updated according to this rule:
- if all child icons are identical => use the common icon
- if child icons are different: use the monochromatic app icon, if exists. Otherwise fall back to a generic icon representing a stack.
Flag: com.android.server.notification.autogroup_summary_icon_update
Test: atest NotificationManagerServiceTest
Test: atest GroupHelperTest
Test: atest IconTest
Bug: 227693160
Change-Id: Ia31d1f71bf43b8c2f5757200d79f0790bf843851
diff --git a/core/res/res/drawable/ic_notification_summary_auto.xml b/core/res/res/drawable/ic_notification_summary_auto.xml
new file mode 100644
index 0000000..908bba8
--- /dev/null
+++ b/core/res/res/drawable/ic_notification_summary_auto.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M260,760Q236,760 218,742Q200,724 200,700L200,140Q200,116 218,98Q236,80 260,80L820,80Q844,80 862,98Q880,116 880,140L880,700Q880,724 862,742Q844,760 820,760L260,760ZM260,700L820,700Q820,700 820,700Q820,700 820,700L820,140Q820,140 820,140Q820,140 820,140L260,140Q260,140 260,140Q260,140 260,140L260,700Q260,700 260,700Q260,700 260,700ZM140,880Q116,880 98,862Q80,844 80,820L80,200L140,200L140,820Q140,820 140,820Q140,820 140,820L760,820L760,880L140,880ZM260,140L260,140Q260,140 260,140Q260,140 260,140L260,700Q260,700 260,700Q260,700 260,700L260,700Q260,700 260,700Q260,700 260,700L260,140Q260,140 260,140Q260,140 260,140Z"/>
+</vector>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 603b902..3ade05b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3146,6 +3146,7 @@
<java-symbol type="drawable" name="ic_collapse_notification" />
<java-symbol type="drawable" name="ic_expand_bundle" />
<java-symbol type="drawable" name="ic_collapse_bundle" />
+ <java-symbol type="drawable" name="ic_notification_summary_auto" />
<java-symbol type="dimen" name="notification_header_shrink_min_width" />
<java-symbol type="dimen" name="notification_header_shrink_hide_width" />
<java-symbol type="dimen" name="notification_content_margin_start" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1b25d7f..ee1a4ac 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -66,6 +66,7 @@
"android.view.accessibility.flags-aconfig-java",
"androidx.core_core",
"androidx.core_core-ktx",
+ "androidx.test.core",
"androidx.test.espresso.core",
"androidx.test.ext.junit",
"androidx.test.runner",
@@ -90,6 +91,7 @@
"flickerlib-parsers",
"flickerlib-trace_processor_shell",
"mockito-target-extended-minus-junit4",
+ "TestParameterInjector",
],
libs: [
diff --git a/core/tests/coretests/res/drawable/adaptiveicon_drawable.xml b/core/tests/coretests/res/drawable/adaptiveicon_drawable.xml
new file mode 100644
index 0000000..dcffe75
--- /dev/null
+++ b/core/tests/coretests/res/drawable/adaptiveicon_drawable.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2023 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.
+ -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground android:drawable="@android:color/black"/>
+ <monochrome android:drawable="@android:color/system_accent2_800"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 49ed3a8..950925f 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -18,8 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.app.IUriGrantsManager;
import android.content.ContentProvider;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -32,13 +37,15 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
-import android.test.AndroidTestCase;
import android.util.Log;
-import androidx.test.filters.SmallTest;
+import androidx.test.core.app.ApplicationProvider;
import com.android.frameworks.coretests.R;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -46,13 +53,29 @@
import java.util.ArrayList;
import java.util.Arrays;
-public class IconTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(TestParameterInjector.class)
+public class IconTest {
public static final String TAG = IconTest.class.getSimpleName();
+ private Context mContext;
+
public static void L(String s, Object... parts) {
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
}
- @SmallTest
+ private Context getContext() {
+ return mContext;
+ }
+
+ @Before
+ public void setup() {
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
+ @Test
public void testWithBitmap() throws Exception {
final Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
final Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
@@ -119,7 +142,7 @@
}
}
- @SmallTest
+ @Test
public void testScaleDownIfNecessary() throws Exception {
final Bitmap bm = Bitmap.createBitmap(4321, 78, Bitmap.Config.ARGB_8888);
final Icon ic = Icon.createWithBitmap(bm);
@@ -132,7 +155,7 @@
assertThat(ic.getBitmap().getHeight()).isLessThan(21);
}
- @SmallTest
+ @Test
public void testWithAdaptiveBitmap() throws Exception {
final Bitmap bm1 = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
@@ -166,7 +189,7 @@
}
}
- @SmallTest
+ @Test
public void testWithBitmapResource() throws Exception {
final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
.getBitmap();
@@ -193,7 +216,7 @@
* Icon resource test that ensures we can load and draw non-bitmaps. (In this case,
* stat_sys_adb is assumed, and asserted, to be a vector drawable.)
*/
- @SmallTest
+ @Test
public void testWithStatSysAdbResource() throws Exception {
// establish reference bitmap
final float dp = getContext().getResources().getDisplayMetrics().density;
@@ -244,7 +267,7 @@
}
}
- @SmallTest
+ @Test
public void testWithFile() throws Exception {
final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
.getBitmap();
@@ -268,7 +291,55 @@
}
}
- @SmallTest
+ @Test
+ public void testWithAdaptiveIconResource_useMonochrome() throws Exception {
+ final int colorMono = ((ColorDrawable) getContext().getDrawable(
+ android.R.color.system_accent2_800)).getColor();
+ final Icon im1 = Icon.createWithResourceAdaptiveDrawable(getContext().getPackageName(),
+ R.drawable.adaptiveicon_drawable, true, 0.0f);
+ final Drawable draw1 = im1.loadDrawable(mContext);
+ assertThat(draw1 instanceof InsetDrawable).isTrue();
+ ColorDrawable colorDrawable = (ColorDrawable) ((DrawableWrapper) draw1).getDrawable();
+ assertThat(colorDrawable.getColor()).isEqualTo(colorMono);
+ }
+
+ @Test
+ public void testWithAdaptiveIconResource_dontUseMonochrome() throws Exception {
+ final int colorMono = ((ColorDrawable) getContext().getDrawable(
+ android.R.color.system_accent2_800)).getColor();
+ final int colorFg = ((ColorDrawable) getContext().getDrawable(
+ android.R.color.black)).getColor();
+ final int colorBg = ((ColorDrawable) getContext().getDrawable(
+ android.R.color.white)).getColor();
+
+ final Icon im1 = Icon.createWithResourceAdaptiveDrawable(getContext().getPackageName(),
+ R.drawable.adaptiveicon_drawable, false , 0.0f);
+ final Drawable draw1 = im1.loadDrawable(mContext);
+ assertThat(draw1 instanceof AdaptiveIconDrawable).isTrue();
+ ColorDrawable colorDrawableMono = (ColorDrawable) ((AdaptiveIconDrawable) draw1)
+ .getMonochrome();
+ assertThat(colorDrawableMono.getColor()).isEqualTo(colorMono);
+ ColorDrawable colorDrawableFg = (ColorDrawable) ((AdaptiveIconDrawable) draw1)
+ .getForeground();
+ assertThat(colorDrawableFg.getColor()).isEqualTo(colorFg);
+ ColorDrawable colorDrawableBg = (ColorDrawable) ((AdaptiveIconDrawable) draw1)
+ .getBackground();
+ assertThat(colorDrawableBg.getColor()).isEqualTo(colorBg);
+ }
+
+ @Test
+ public void testAdaptiveIconResource_sameAs(@TestParameter boolean useMonochrome)
+ throws Exception {
+ final Icon im1 = Icon.createWithResourceAdaptiveDrawable(getContext().getPackageName(),
+ R.drawable.adaptiveicon_drawable, useMonochrome, 1.0f);
+ final Parcel parcel = Parcel.obtain();
+ im1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ final Icon im2 = Icon.CREATOR.createFromParcel(parcel);
+ assertThat(im1.sameAs(im2)).isTrue();
+ }
+
+ @Test
public void testAsync() throws Exception {
final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
.getBitmap();
@@ -311,7 +382,7 @@
L(TAG, "asyncTest: done");
}
- @SmallTest
+ @Test
public void testParcel() throws Exception {
final Bitmap originalbits = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
.getBitmap();
@@ -391,7 +462,7 @@
return (int) Math.sqrt(maxNumPixels / aspRatio);
}
- @SmallTest
+ @Test
public void testScaleDownMaxSizeWithBitmap() throws Exception {
final int bmpWidth = 13_000;
final int bmpHeight = 10_000;
@@ -408,7 +479,7 @@
assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
}
- @SmallTest
+ @Test
public void testScaleDownMaxSizeWithAdaptiveBitmap() throws Exception {
final int bmpWidth = 20_000;
final int bmpHeight = 10_000;
@@ -427,7 +498,7 @@
assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
}
- @SmallTest
+ @Test
public void testScaleDownMaxSizeWithResource() throws Exception {
final Icon ic = Icon.createWithResource(getContext(), R.drawable.test_too_big);
final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
@@ -435,7 +506,7 @@
assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
}
- @SmallTest
+ @Test
public void testScaleDownMaxSizeWithFile() throws Exception {
final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.test_too_big))
.getBitmap();
@@ -450,7 +521,7 @@
assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
}
- @SmallTest
+ @Test
public void testScaleDownMaxSizeWithData() throws Exception {
final int bmpBpp = 4;
final Bitmap originalBits = ((BitmapDrawable) getContext().getDrawable(
@@ -465,7 +536,7 @@
assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
}
- @SmallTest
+ @Test
public void testLoadSafeDrawable_loadSuccessful() throws FileNotFoundException {
int uid = 12345;
String packageName = "test_pkg";
@@ -509,7 +580,7 @@
}
}
- @SmallTest
+ @Test
public void testLoadSafeDrawable_grantRejected_nullDrawable() throws FileNotFoundException {
int uid = 12345;
String packageName = "test_pkg";
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 45e29a8..f359025 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -154,6 +154,12 @@
// TYPE_DATA: data offset
private int mInt2;
+ // TYPE_RESOURCE: use the monochrome drawable from an AdaptiveIconDrawable
+ private boolean mUseMonochrome = false;
+
+ // TYPE_RESOURCE: wrap the monochrome drawable in an InsetDrawable with the specified inset
+ private float mInsetScale = 0.0f;
+
/**
* Gets the type of the icon provided.
* <p>
@@ -368,10 +374,34 @@
result.setTintList(mTintList);
result.setTintBlendMode(mBlendMode);
}
+
+ if (mUseMonochrome) {
+ return crateMonochromeDrawable(result, mInsetScale);
+ }
+
return result;
}
/**
+ * Gets the monochrome drawable from an {@link AdaptiveIconDrawable}.
+ *
+ * @param drawable An {@link AdaptiveIconDrawable}
+ * @return Adjusted (wrapped in {@link InsetDrawable}) monochrome drawable
+ * from an {@link AdaptiveIconDrawable}.
+ * Or the original drawable if no monochrome layer exists.
+ */
+ private static Drawable crateMonochromeDrawable(Drawable drawable, float inset) {
+ if (drawable instanceof AdaptiveIconDrawable) {
+ Drawable monochromeDrawable = ((AdaptiveIconDrawable) drawable).getMonochrome();
+ // wrap with negative inset => scale icon (inspired from BaseIconFactory)
+ if (monochromeDrawable != null) {
+ return new InsetDrawable(monochromeDrawable, inset);
+ }
+ }
+ return drawable;
+ }
+
+ /**
* Resizes image if size too large for Canvas to draw
* @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
* @return resized bitmap
@@ -693,7 +723,9 @@
&& Arrays.equals(getDataBytes(), otherIcon.getDataBytes());
case TYPE_RESOURCE:
return getResId() == otherIcon.getResId()
- && Objects.equals(getResPackage(), otherIcon.getResPackage());
+ && Objects.equals(getResPackage(), otherIcon.getResPackage())
+ && mUseMonochrome == otherIcon.mUseMonochrome
+ && mInsetScale == otherIcon.mInsetScale;
case TYPE_URI:
case TYPE_URI_ADAPTIVE_BITMAP:
return Objects.equals(getUriString(), otherIcon.getUriString());
@@ -748,6 +780,26 @@
}
/**
+ * Create an Icon pointing to a drawable resource.
+ * @param resPackage Name of the package containing the resource in question
+ * @param resId ID of the drawable resource
+ * @param useMonochrome if this icon should use the monochrome res from the adaptive drawable
+ * @hide
+ */
+ public static @NonNull Icon createWithResourceAdaptiveDrawable(@NonNull String resPackage,
+ @DrawableRes int resId, boolean useMonochrome, float inset) {
+ if (resPackage == null) {
+ throw new IllegalArgumentException("Resource package name must not be null.");
+ }
+ final Icon rep = new Icon(TYPE_RESOURCE);
+ rep.mInt1 = resId;
+ rep.mUseMonochrome = useMonochrome;
+ rep.mInsetScale = inset;
+ rep.mString1 = resPackage;
+ return rep;
+ }
+
+ /**
* Create an Icon pointing to a bitmap in memory.
* @param bits A valid {@link android.graphics.Bitmap} object
*/
@@ -986,6 +1038,8 @@
final int resId = in.readInt();
mString1 = pkg;
mInt1 = resId;
+ mUseMonochrome = in.readBoolean();
+ mInsetScale = in.readFloat();
break;
case TYPE_DATA:
final int len = in.readInt();
@@ -1027,6 +1081,8 @@
case TYPE_RESOURCE:
dest.writeString(getResPackage());
dest.writeInt(getResId());
+ dest.writeBoolean(mUseMonochrome);
+ dest.writeFloat(mInsetScale);
break;
case TYPE_DATA:
dest.writeInt(getDataLength());
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index dff02bf..e349fa3 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.app.Notification.COLOR_DEFAULT;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_GROUP_SUMMARY;
@@ -23,15 +24,24 @@
import static android.app.Notification.FLAG_ONGOING_EVENT;
import android.annotation.NonNull;
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* NotificationManagerService helper for auto-grouping notifications.
@@ -41,6 +51,8 @@
protected static final String AUTOGROUP_KEY = "ranker_group";
+ protected static final int FLAG_INVALID = -1;
+
// Flags that all autogroup summaries have
protected static final int BASE_FLAGS =
FLAG_AUTOGROUP_SUMMARY | FLAG_GROUP_SUMMARY | FLAG_LOCAL_ONLY;
@@ -51,17 +63,22 @@
private final Callback mCallback;
private final int mAutoGroupAtCount;
+ private final Context mContext;
+ private final PackageManager mPackageManager;
// Only contains notifications that are not explicitly grouped by the app (aka no group or
// sort key).
// userId|packageName -> (keys of notifications that aren't in an explicit app group -> flags)
@GuardedBy("mUngroupedNotifications")
- private final ArrayMap<String, ArrayMap<String, Integer>> mUngroupedNotifications
+ private final ArrayMap<String, ArrayMap<String, NotificationAttributes>> mUngroupedNotifications
= new ArrayMap<>();
- public GroupHelper(int autoGroupAtCount, Callback callback) {
+ public GroupHelper(Context context, PackageManager packageManager, int autoGroupAtCount,
+ Callback callback) {
mAutoGroupAtCount = autoGroupAtCount;
mCallback = callback;
+ mContext = context;
+ mPackageManager = packageManager;
}
private String generatePackageKey(int userId, String pkg) {
@@ -70,15 +87,16 @@
@VisibleForTesting
@GuardedBy("mUngroupedNotifications")
- protected int getAutogroupSummaryFlags(@NonNull final ArrayMap<String, Integer> children) {
+ protected int getAutogroupSummaryFlags(
+ @NonNull final ArrayMap<String, NotificationAttributes> children) {
boolean allChildrenHasFlag = children.size() > 0;
int anyChildFlagSet = 0;
for (int i = 0; i < children.size(); i++) {
- if (!hasAnyFlag(children.valueAt(i), ALL_CHILDREN_FLAG)) {
+ if (!hasAnyFlag(children.valueAt(i).flags, ALL_CHILDREN_FLAG)) {
allChildrenHasFlag = false;
}
- if (hasAnyFlag(children.valueAt(i), ANY_CHILDREN_FLAGS)) {
- anyChildFlagSet |= (children.valueAt(i) & ANY_CHILDREN_FLAGS);
+ if (hasAnyFlag(children.valueAt(i).flags, ANY_CHILDREN_FLAGS)) {
+ anyChildFlagSet |= (children.valueAt(i).flags & ANY_CHILDREN_FLAGS);
}
}
return BASE_FLAGS | (allChildrenHasFlag ? ALL_CHILDREN_FLAG : 0) | anyChildFlagSet;
@@ -95,7 +113,6 @@
} else {
maybeUngroup(sbn, false, sbn.getUserId());
}
-
} catch (Exception e) {
Slog.e(TAG, "Failure processing new notification", e);
}
@@ -121,25 +138,47 @@
private void maybeGroup(StatusBarNotification sbn, boolean autogroupSummaryExists) {
int flags = 0;
List<String> notificationsToGroup = new ArrayList<>();
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
synchronized (mUngroupedNotifications) {
String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
- final ArrayMap<String, Integer> children =
+ final ArrayMap<String, NotificationAttributes> children =
mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
- children.put(sbn.getKey(), sbn.getNotification().flags);
+ NotificationAttributes attr = new NotificationAttributes(sbn.getNotification().flags,
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
+ children.put(sbn.getKey(), attr);
mUngroupedNotifications.put(key, children);
if (children.size() >= mAutoGroupAtCount || autogroupSummaryExists) {
flags = getAutogroupSummaryFlags(children);
notificationsToGroup.addAll(children.keySet());
+ childrenAttr.addAll(children.values());
}
}
if (notificationsToGroup.size() > 0) {
if (autogroupSummaryExists) {
- mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), flags);
+ NotificationAttributes attr = new NotificationAttributes(flags,
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
+ if (Flags.autogroupSummaryIconUpdate()) {
+ attr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttr, attr);
+ }
+
+ mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), attr);
} else {
- mCallback.addAutoGroupSummary(
- sbn.getUserId(), sbn.getPackageName(), sbn.getKey(), flags);
+ Icon summaryIcon = sbn.getNotification().getSmallIcon();
+ int summaryIconColor = sbn.getNotification().color;
+ if (Flags.autogroupSummaryIconUpdate()) {
+ // Calculate the initial summary icon and icon color
+ NotificationAttributes iconAttr = getAutobundledSummaryIconAndColor(
+ sbn.getPackageName(), childrenAttr);
+ summaryIcon = iconAttr.icon;
+ summaryIconColor = iconAttr.iconColor;
+ }
+
+ NotificationAttributes attr = new NotificationAttributes(flags, summaryIcon,
+ summaryIconColor);
+ mCallback.addAutoGroupSummary(sbn.getUserId(), sbn.getPackageName(), sbn.getKey(),
+ attr);
}
for (String key : notificationsToGroup) {
mCallback.addAutoGroup(key);
@@ -154,16 +193,17 @@
* (b) if we need to remove our autogroup overlay for this notification
* (c) we need to remove the autogroup summary
*
- * And updates the internal state of un-app-grouped notifications and their flags
+ * And updates the internal state of un-app-grouped notifications and their flags.
*/
private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) {
boolean removeSummary = false;
- int summaryFlags = 0;
+ int summaryFlags = FLAG_INVALID;
boolean updateSummaryFlags = false;
boolean removeAutogroupOverlay = false;
+ List<NotificationAttributes> childrenAttrs = new ArrayList<>();
synchronized (mUngroupedNotifications) {
String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
- final ArrayMap<String, Integer> children =
+ final ArrayMap<String, NotificationAttributes> children =
mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
if (children.size() == 0) {
return;
@@ -173,7 +213,7 @@
if (children.containsKey(sbn.getKey())) {
// if this notification was contributing flags that aren't covered by other
// children to the summary, reevaluate flags for the summary
- int flags = children.remove(sbn.getKey());
+ int flags = children.remove(sbn.getKey()).flags;
// this
if (hasAnyFlag(flags, ANY_CHILDREN_FLAGS)) {
updateSummaryFlags = true;
@@ -188,14 +228,29 @@
// If there are no more children left to autogroup, remove the summary
if (children.size() == 0) {
removeSummary = true;
+ } else {
+ childrenAttrs.addAll(children.values());
}
}
}
+
if (removeSummary) {
mCallback.removeAutoGroupSummary(userId, sbn.getPackageName());
} else {
- if (updateSummaryFlags) {
- mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), summaryFlags);
+ NotificationAttributes attr = new NotificationAttributes(summaryFlags,
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
+ boolean iconUpdated = false;
+ if (Flags.autogroupSummaryIconUpdate()) {
+ NotificationAttributes newAttr = updateAutobundledSummaryIcon(sbn.getPackageName(),
+ childrenAttrs, attr);
+ if (!newAttr.equals(attr)) {
+ iconUpdated = true;
+ attr = newAttr;
+ }
+ }
+
+ if (updateSummaryFlags || iconUpdated) {
+ mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), attr);
}
}
if (removeAutogroupOverlay) {
@@ -207,17 +262,139 @@
int getNotGroupedByAppCount(int userId, String pkg) {
synchronized (mUngroupedNotifications) {
String key = generatePackageKey(userId, pkg);
- final ArrayMap<String, Integer> children =
+ final ArrayMap<String, NotificationAttributes> children =
mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
return children.size();
}
}
+ NotificationAttributes getAutobundledSummaryIconAndColor(@NonNull String packageName,
+ @NonNull List<NotificationAttributes> childrenAttr) {
+ Icon newIcon = null;
+ boolean childrenHaveSameIcon = true;
+ int newColor = Notification.COLOR_INVALID;
+ boolean childrenHaveSameColor = true;
+
+ // Both the icon drawable and the icon background color are updated according to this rule:
+ // - if all child icons are identical => use the common icon
+ // - if child icons are different: use the monochromatic app icon, if exists.
+ // Otherwise fall back to a generic icon representing a stack.
+ for (NotificationAttributes state: childrenAttr) {
+ // Check for icon
+ if (newIcon == null) {
+ newIcon = state.icon;
+ } else {
+ if (!newIcon.sameAs(state.icon)) {
+ childrenHaveSameIcon = false;
+ }
+ }
+ // Check for color
+ if (newColor == Notification.COLOR_INVALID) {
+ newColor = state.iconColor;
+ } else {
+ if (newColor != state.iconColor) {
+ childrenHaveSameColor = false;
+ }
+ }
+ }
+ if (!childrenHaveSameIcon) {
+ newIcon = getMonochromeAppIcon(packageName);
+ }
+ if (!childrenHaveSameColor) {
+ newColor = COLOR_DEFAULT;
+ }
+
+ return new NotificationAttributes(0, newIcon, newColor);
+ }
+
+ NotificationAttributes updateAutobundledSummaryIcon(@NonNull String packageName,
+ @NonNull List<NotificationAttributes> childrenAttr,
+ @NonNull NotificationAttributes oldAttr) {
+ NotificationAttributes newAttr = getAutobundledSummaryIconAndColor(packageName,
+ childrenAttr);
+ Icon newIcon = newAttr.icon;
+ int newColor = newAttr.iconColor;
+ if (newAttr.icon == null) {
+ newIcon = oldAttr.icon;
+ }
+ if (newAttr.iconColor == Notification.COLOR_INVALID) {
+ newColor = oldAttr.iconColor;
+ }
+
+ return new NotificationAttributes(oldAttr.flags, newIcon, newColor);
+ }
+
+ /**
+ * Get the monochrome app icon for an app from the adaptive launcher icon
+ * or a fallback generic icon for autogroup summaries.
+ *
+ * @param pkg packageName of the app
+ * @return a monochrome app icon or a fallback generic icon
+ */
+ @NonNull
+ Icon getMonochromeAppIcon(@NonNull final String pkg) {
+ Icon monochromeIcon = null;
+ final int fallbackIconResId = R.drawable.ic_notification_summary_auto;
+ try {
+ final Drawable appIcon = mPackageManager.getApplicationIcon(pkg);
+ if (appIcon instanceof AdaptiveIconDrawable) {
+ if (((AdaptiveIconDrawable) appIcon).getMonochrome() != null) {
+ monochromeIcon = Icon.createWithResourceAdaptiveDrawable(pkg,
+ ((AdaptiveIconDrawable) appIcon).getSourceDrawableResId(), true,
+ -2.0f * AdaptiveIconDrawable.getExtraInsetFraction());
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Failed to getApplicationIcon() in getMonochromeAppIcon()", e);
+ }
+ if (monochromeIcon != null) {
+ return monochromeIcon;
+ } else {
+ return Icon.createWithResource(mContext, fallbackIconResId);
+ }
+ }
+
+ protected static class NotificationAttributes {
+ public final int flags;
+ public final int iconColor;
+ public final Icon icon;
+
+ public NotificationAttributes(int flags, Icon icon, int iconColor) {
+ this.flags = flags;
+ this.icon = icon;
+ this.iconColor = iconColor;
+ }
+
+ public NotificationAttributes(@NonNull NotificationAttributes attr) {
+ this.flags = attr.flags;
+ this.icon = attr.icon;
+ this.iconColor = attr.iconColor;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof NotificationAttributes that)) {
+ return false;
+ }
+ return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(flags, iconColor, icon);
+ }
+ }
+
protected interface Callback {
void addAutoGroup(String key);
void removeAutoGroup(String key);
- void addAutoGroupSummary(int userId, String pkg, String triggeringKey, int flags);
+
+ void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
+ NotificationAttributes summaryAttr);
void removeAutoGroupSummary(int user, String pkg);
- void updateAutogroupSummary(int userId, String pkg, int flags);
+ void updateAutogroupSummary(int userId, String pkg, NotificationAttributes summaryAttr);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 638382e..6fa737d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -227,6 +227,7 @@
import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -341,6 +342,7 @@
import com.android.server.job.JobSchedulerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
+import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.notification.toast.CustomToastRecord;
@@ -996,17 +998,19 @@
}
/**
- * This method will update the flags of the summary.
+ * This method will update the flags and/or the icon of the summary.
* It will set it to FLAG_ONGOING_EVENT if any of its group members
- * has the same flag. It will delete the flag otherwise
+ * has the same flag. It will delete the flag otherwise.
+ * It will update the summary notification icon if the group children's
+ * icons are different.
* @param userId user id of the autogroup summary
* @param pkg package of the autogroup summary
- * @param flags the new flags for this summary
+ * @param summaryAttr the new flags and/or icon & color for this summary
* @param isAppForeground true if the app is currently in the foreground.
*/
@GuardedBy("mNotificationLock")
- protected void updateAutobundledSummaryFlags(int userId, String pkg, int flags,
- boolean isAppForeground) {
+ protected void updateAutobundledSummaryLocked(int userId, String pkg,
+ NotificationAttributes summaryAttr, boolean isAppForeground) {
ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
if (summaries == null) {
return;
@@ -1020,8 +1024,16 @@
return;
}
int oldFlags = summary.getSbn().getNotification().flags;
- if (oldFlags != flags) {
- summary.getSbn().getNotification().flags = flags;
+
+ boolean iconUpdated =
+ !summaryAttr.icon.sameAs(summary.getSbn().getNotification().getSmallIcon())
+ || summaryAttr.iconColor != summary.getSbn().getNotification().color;
+
+ if (oldFlags != summaryAttr.flags || iconUpdated) {
+ summary.getSbn().getNotification().flags =
+ summaryAttr.flags != GroupHelper.FLAG_INVALID ? summaryAttr.flags : oldFlags;
+ summary.getSbn().getNotification().setSmallIcon(summaryAttr.icon);
+ summary.getSbn().getNotification().color = summaryAttr.iconColor;
mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground,
mPostNotificationTrackerFactory.newTracker(null)));
}
@@ -2873,7 +2885,8 @@
private GroupHelper getGroupHelper() {
mAutoGroupAtCount =
getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
- return new GroupHelper(mAutoGroupAtCount, new GroupHelper.Callback() {
+ return new GroupHelper(getContext(), getContext().getPackageManager(),
+ mAutoGroupAtCount, new GroupHelper.Callback() {
@Override
public void addAutoGroup(String key) {
synchronized (mNotificationLock) {
@@ -2890,8 +2903,9 @@
@Override
public void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
- int flags) {
- NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey, flags);
+ NotificationAttributes summaryAttr) {
+ NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey,
+ summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor);
if (r != null) {
final boolean isAppForeground =
mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
@@ -2908,11 +2922,12 @@
}
@Override
- public void updateAutogroupSummary(int userId, String pkg, int flags) {
+ public void updateAutogroupSummary(int userId, String pkg,
+ NotificationAttributes summaryAttr) {
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
- updateAutobundledSummaryFlags(userId, pkg, flags, isAppForeground);
+ updateAutobundledSummaryLocked(userId, pkg, summaryAttr, isAppForeground);
}
}
});
@@ -6529,7 +6544,7 @@
// Creates a 'fake' summary for a package that has exceeded the solo-notification limit.
NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
- int flagsToSet) {
+ int flagsToSet, Icon summaryIcon, int summaryIconColor) {
NotificationRecord summaryRecord = null;
boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
synchronized (mNotificationLock) {
@@ -6555,14 +6570,15 @@
final Bundle extras = new Bundle();
extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
final String channelId = notificationRecord.getChannel().getId();
+
final Notification summaryNotification =
- new Notification.Builder(getContext(), channelId)
- .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
+ new Notification.Builder(getContext(), channelId)
+ .setSmallIcon(summaryIcon)
.setGroupSummary(true)
.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
.setGroup(GroupHelper.AUTOGROUP_KEY)
.setFlag(flagsToSet, true)
- .setColor(adjustedSbn.getNotification().color)
+ .setColor(summaryIconColor)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 516fb4a..5eb76e3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -15,37 +15,56 @@
*/
package com.android.server.notification;
+import static android.app.Notification.COLOR_DEFAULT;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.server.notification.GroupHelper.BASE_FLAGS;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
import android.app.Notification;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.UiServiceTestCase;
+import com.android.server.notification.GroupHelper.NotificationAttributes;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -59,23 +78,37 @@
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class.
@RunWith(AndroidJUnit4.class)
public class GroupHelperTest extends UiServiceTestCase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private @Mock GroupHelper.Callback mCallback;
+ private @Mock PackageManager mPackageManager;
private final static int AUTOGROUP_AT_COUNT = 7;
private GroupHelper mGroupHelper;
+ private @Mock Icon mSmallIcon;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mGroupHelper = new GroupHelper(AUTOGROUP_AT_COUNT, mCallback);
+ mGroupHelper = new GroupHelper(getContext(), mPackageManager, AUTOGROUP_AT_COUNT,
+ mCallback);
+
+ NotificationRecord r = mock(NotificationRecord.class);
+ StatusBarNotification sbn = getSbn("package", 0, "0", UserHandle.SYSTEM);
+ when(r.getNotification()).thenReturn(sbn.getNotification());
+ when(r.getSbn()).thenReturn(sbn);
+ when(mSmallIcon.sameAs(mSmallIcon)).thenReturn(true);
}
private StatusBarNotification getSbn(String pkg, int id, String tag,
- UserHandle user, String groupKey) {
+ UserHandle user, String groupKey, Icon smallIcon, int iconColor) {
Notification.Builder nb = new Notification.Builder(getContext(), "test_channel_id")
.setContentTitle("A")
- .setWhen(1205);
+ .setWhen(1205)
+ .setSmallIcon(smallIcon)
+ .setColor(iconColor);
if (groupKey != null) {
nb.setGroup(groupKey);
}
@@ -84,23 +117,32 @@
}
private StatusBarNotification getSbn(String pkg, int id, String tag,
+ UserHandle user, String groupKey) {
+ return getSbn(pkg, id, tag, user, groupKey, mSmallIcon, Notification.COLOR_DEFAULT);
+ }
+
+ private StatusBarNotification getSbn(String pkg, int id, String tag,
UserHandle user) {
return getSbn(pkg, id, tag, user, null);
}
+ private NotificationAttributes getNotificationAttributes(int flags) {
+ return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT);
+ }
+
@Test
public void testGetAutogroupSummaryFlags_noChildren() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
assertEquals(BASE_FLAGS, mGroupHelper.getAutogroupSummaryFlags(children));
}
@Test
public void testGetAutogroupSummaryFlags_oneOngoing() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", 0);
- children.put("b", FLAG_ONGOING_EVENT);
- children.put("c", FLAG_BUBBLE);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(0));
+ children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT));
+ children.put("c", getNotificationAttributes(FLAG_BUBBLE));
assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -108,10 +150,10 @@
@Test
public void testGetAutogroupSummaryFlags_oneOngoingNoClear() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", 0);
- children.put("b", FLAG_ONGOING_EVENT|FLAG_NO_CLEAR);
- children.put("c", FLAG_BUBBLE);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(0));
+ children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT | FLAG_NO_CLEAR));
+ children.put("c", getNotificationAttributes(FLAG_BUBBLE));
assertEquals(FLAG_NO_CLEAR | FLAG_ONGOING_EVENT | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -119,10 +161,10 @@
@Test
public void testGetAutogroupSummaryFlags_oneOngoingBubble() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", 0);
- children.put("b", FLAG_ONGOING_EVENT | FLAG_BUBBLE);
- children.put("c", FLAG_BUBBLE);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(0));
+ children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT | FLAG_BUBBLE));
+ children.put("c", getNotificationAttributes(FLAG_BUBBLE));
assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -130,11 +172,11 @@
@Test
public void testGetAutogroupSummaryFlags_multipleOngoing() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", 0);
- children.put("b", FLAG_ONGOING_EVENT);
- children.put("c", FLAG_BUBBLE);
- children.put("d", FLAG_ONGOING_EVENT);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(0));
+ children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT));
+ children.put("c", getNotificationAttributes(FLAG_BUBBLE));
+ children.put("d", getNotificationAttributes(FLAG_ONGOING_EVENT));
assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -142,10 +184,10 @@
@Test
public void testGetAutogroupSummaryFlags_oneAutoCancel() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", 0);
- children.put("b", FLAG_AUTO_CANCEL);
- children.put("c", FLAG_BUBBLE);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(0));
+ children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL));
+ children.put("c", getNotificationAttributes(FLAG_BUBBLE));
assertEquals(BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -153,11 +195,11 @@
@Test
public void testGetAutogroupSummaryFlags_allAutoCancel() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", FLAG_AUTO_CANCEL);
- children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE);
- children.put("c", FLAG_AUTO_CANCEL);
- children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(FLAG_AUTO_CANCEL));
+ children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE));
+ children.put("c", getNotificationAttributes(FLAG_AUTO_CANCEL));
+ children.put("d", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE));
assertEquals(FLAG_AUTO_CANCEL | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -165,11 +207,12 @@
@Test
public void testGetAutogroupSummaryFlags_allAutoCancelOneOngoing() {
- ArrayMap<String, Integer> children = new ArrayMap<>();
- children.put("a", FLAG_AUTO_CANCEL);
- children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE);
- children.put("c", FLAG_AUTO_CANCEL);
- children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE | FLAG_ONGOING_EVENT);
+ ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
+ children.put("a", getNotificationAttributes(FLAG_AUTO_CANCEL));
+ children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE));
+ children.put("c", getNotificationAttributes(FLAG_AUTO_CANCEL));
+ children.put("d", getNotificationAttributes(
+ FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE | FLAG_ONGOING_EVENT));
assertEquals(FLAG_AUTO_CANCEL| FLAG_ONGOING_EVENT | BASE_FLAGS,
mGroupHelper.getAutogroupSummaryFlags(children));
@@ -230,11 +273,11 @@
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -248,11 +291,11 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -266,11 +309,11 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -282,11 +325,11 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(BASE_FLAGS | FLAG_AUTO_CANCEL));
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -301,11 +344,11 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR));
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -329,8 +372,8 @@
mGroupHelper.onNotificationPosted(notifications.get(0), true);
// Summary should keep FLAG_ONGOING_EVENT if any child has it
- verify(mCallback).updateAutogroupSummary(
- anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
}
@Test
@@ -355,7 +398,8 @@
mGroupHelper.onNotificationRemoved(notifications.get(0));
// Summary is no longer ongoing
- verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
}
@Test
@@ -378,8 +422,8 @@
mGroupHelper.onNotificationPosted(notifications.get(0), true);
// Summary is now ongoing
- verify(mCallback).updateAutogroupSummary(
- anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
}
@Test
@@ -403,8 +447,8 @@
mGroupHelper.onNotificationPosted(sbn, true);
// Summary is now ongoing
- verify(mCallback).updateAutogroupSummary(
- anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
}
@Test
@@ -430,7 +474,8 @@
mGroupHelper.onNotificationPosted(sbn, true);
// Summary is no longer ongoing
- verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
}
@Test
@@ -455,7 +500,7 @@
mGroupHelper.onNotificationRemoved(notifications.get(1));
// Summary is still ongoing
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -479,7 +524,8 @@
mGroupHelper.onNotificationPosted(notifications.get(0), true);
// Summary should no longer be autocancelable
- verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
}
@Test
@@ -505,8 +551,8 @@
mGroupHelper.onNotificationPosted(notifications.get(0), true);
// Summary should now autocancelable
- verify(mCallback).updateAutogroupSummary(
- anyInt(), anyString(), eq(BASE_FLAGS | FLAG_AUTO_CANCEL));
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
}
@Test
@@ -530,7 +576,7 @@
mGroupHelper.onNotificationPosted(sbn, true);
// Summary should be still be autocancelable
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -552,7 +598,7 @@
mGroupHelper.onNotificationRemoved(notifications.get(0));
// Summary should still be autocancelable
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), any());
}
@Test
@@ -565,7 +611,7 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -593,7 +639,7 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -622,7 +668,7 @@
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -646,8 +692,213 @@
verify(mCallback, times(1)).addAutoGroup(sbn.getKey());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
- verify(mCallback, never()).addAutoGroupSummary(
- anyInt(), anyString(), anyString(), anyInt());
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(), any());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAddSummary_sameIcon_sameColor() {
+ final String pkg = "package";
+ final Icon icon = mock(Icon.class);
+ when(icon.sameAs(icon)).thenReturn(true);
+ final int iconColor = Color.BLUE;
+ final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
+
+ // Add notifications with same icon and color
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
+ icon, iconColor);
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
+ // Check that the summary would have the same icon and color
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(attr));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+
+ // After auto-grouping, add new notification with the same color
+ StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT,
+ String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor);
+ mGroupHelper.onNotificationPosted(sbn, true);
+
+ // Check that the summary was updated
+ //NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(attr));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAddSummary_diffIcon_diffColor() {
+ final String pkg = "package";
+ final Icon initialIcon = mock(Icon.class);
+ when(initialIcon.sameAs(initialIcon)).thenReturn(true);
+ final int initialIconColor = Color.BLUE;
+
+ // Spy GroupHelper for getMonochromeAppIcon
+ final Icon monochromeIcon = mock(Icon.class);
+ when(monochromeIcon.sameAs(monochromeIcon)).thenReturn(true);
+ GroupHelper groupHelper = spy(mGroupHelper);
+ doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
+
+ final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
+ initialIcon, initialIconColor);
+
+ // Add notifications with same icon and color
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
+ initialIcon, initialIconColor);
+ groupHelper.onNotificationPosted(sbn, false);
+ }
+ // Check that the summary would have the same icon and color
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(initialAttr));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+
+ // After auto-grouping, add new notification with a different color
+ final Icon newIcon = mock(Icon.class);
+ final int newIconColor = Color.YELLOW;
+ StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT,
+ String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, newIcon,
+ newIconColor);
+ groupHelper.onNotificationPosted(sbn, true);
+
+ // Summary should be updated to the default color and the icon to the monochrome icon
+ NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
+ COLOR_DEFAULT);
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor() {
+ final String pkg = "package";
+ final Icon initialIcon = mock(Icon.class);
+ when(initialIcon.sameAs(initialIcon)).thenReturn(true);
+ final int initialIconColor = Color.BLUE;
+ final NotificationAttributes initialAttr = new NotificationAttributes(
+ GroupHelper.FLAG_INVALID, initialIcon, initialIconColor);
+
+ // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
+ initialIcon, initialIconColor);
+ notifications.add(sbn);
+ }
+ // And an additional notification with different icon and color
+ final int lastIdx = AUTOGROUP_AT_COUNT - 1;
+ StatusBarNotification newSbn = getSbn(pkg, lastIdx,
+ String.valueOf(lastIdx), UserHandle.SYSTEM, null, mock(Icon.class),
+ Color.YELLOW);
+ notifications.add(newSbn);
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
+
+ // Remove last notification (the only one with different icon and color)
+ mGroupHelper.onNotificationRemoved(notifications.get(lastIdx));
+
+ // Summary should be updated to the common icon and color
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(initialAttr));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryIcon_sameIcon() {
+ final String pkg = "package";
+ final Icon icon = mock(Icon.class);
+ when(icon.sameAs(icon)).thenReturn(true);
+
+ // Create notifications with the same icon
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT));
+ }
+
+ //Check that the generated summary icon is the same as the child notifications'
+ Icon summaryIcon = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon;
+ assertThat(summaryIcon).isEqualTo(icon);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryIcon_diffIcon() {
+ final String pkg = "package";
+ // Spy GroupHelper for getMonochromeAppIcon
+ final Icon monochromeIcon = mock(Icon.class);
+ GroupHelper groupHelper = spy(mGroupHelper);
+ doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
+
+ // Create notifications with different icons
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT));
+ }
+
+ // Check that the generated summary icon is the monochrome icon
+ Icon summaryIcon = groupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon;
+ assertThat(summaryIcon).isEqualTo(monochromeIcon);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryIconColor_sameColor() {
+ final String pkg = "package";
+ final int iconColor = Color.BLUE;
+ // Create notifications with the same icon color
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor));
+ }
+
+ // Check that the generated summary icon color is the same as the child notifications'
+ int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg,
+ childrenAttr).iconColor;
+ assertThat(summaryIconColor).isEqualTo(iconColor);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryIconColor_diffColor() {
+ final String pkg = "package";
+ // Create notifications with different icon colors
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i));
+ }
+
+ // Check that the generated summary icon color is the default color
+ int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg,
+ childrenAttr).iconColor;
+ assertThat(summaryIconColor).isEqualTo(Notification.COLOR_DEFAULT);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testMonochromeAppIcon_adaptiveIconExists() throws Exception {
+ final String pkg = "testPackage";
+ final int monochromeIconResId = 1234;
+ AdaptiveIconDrawable adaptiveIcon = mock(AdaptiveIconDrawable.class);
+ Drawable monochromeIcon = mock(Drawable.class);
+ when(mPackageManager.getApplicationIcon(pkg)).thenReturn(adaptiveIcon);
+ when(adaptiveIcon.getMonochrome()).thenReturn(monochromeIcon);
+ when(adaptiveIcon.getSourceDrawableResId()).thenReturn(monochromeIconResId);
+ assertThat(mGroupHelper.getMonochromeAppIcon(pkg).getResId())
+ .isEqualTo(monochromeIconResId);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testMonochromeAppIcon_adaptiveIconMissing_fallback() throws Exception {
+ final String pkg = "testPackage";
+ final int fallbackIconResId = R.drawable.ic_notification_summary_auto;
+ when(mPackageManager.getApplicationIcon(pkg)).thenReturn(mock(Drawable.class));
+ assertThat(mGroupHelper.getMonochromeAppIcon(pkg).getResId())
+ .isEqualTo(fallbackIconResId);
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8261dee..3426cbe 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -279,6 +279,7 @@
import com.android.server.job.JobSchedulerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
+import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.notification.NotificationManagerService.PostNotificationTracker;
@@ -658,7 +659,8 @@
// NOTE: Prefer using the @EnableFlag annotation where possible. Do not add any android.app
// flags here.
mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER,
- Flags.FLAG_POLITE_NOTIFICATIONS);
+ Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE);
+
initNMS();
}
@@ -2332,8 +2334,9 @@
mService.mAutobundledSummaries.put(0, new ArrayMap<>());
mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
- mService.updateAutobundledSummaryFlags(
- 0, "pkg", GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, false);
+ mService.updateAutobundledSummaryLocked(0, "pkg",
+ new NotificationAttributes(GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT,
+ mock(Icon.class), 0), false);
waitForIdle();
assertTrue(summary.getSbn().isOngoing());
@@ -2350,7 +2353,9 @@
mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
mService.mSummaryByGroupKey.put("pkg", summary);
- mService.updateAutobundledSummaryFlags(0, "pkg", GroupHelper.BASE_FLAGS, false);
+ mService.updateAutobundledSummaryLocked(0, "pkg",
+ new NotificationAttributes(GroupHelper.BASE_FLAGS,
+ mock(Icon.class), 0), false);
waitForIdle();
assertFalse(summary.getSbn().isOngoing());
@@ -3427,8 +3432,8 @@
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
- NotificationRecord r = mService.createAutoGroupSummary(
- temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), 0);
+ NotificationRecord r = mService.createAutoGroupSummary(temp.getUserId(),
+ temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0);
assertThat(r.isImportanceFixed()).isTrue();
}
@@ -11962,7 +11967,7 @@
// add summary
mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(),
nr1.getSbn().getPackageName(), nr1.getKey(),
- GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT));
+ GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0));
// cancel both children
mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(),
@@ -11989,8 +11994,9 @@
// add notifications + summary for USER_SYSTEM
mService.addNotification(nr0);
mService.addNotification(nr1);
- mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(),
- nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS));
+ mService.addNotification(
+ mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(),
+ nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0));
// add notifications + summary for USER_ALL
NotificationRecord nr0_all =
@@ -12000,8 +12006,10 @@
mService.addNotification(nr0_all);
mService.addNotification(nr1_all);
- mService.addNotification(mService.createAutoGroupSummary(nr0_all.getUserId(),
- nr0_all.getSbn().getPackageName(), nr0_all.getKey(), GroupHelper.BASE_FLAGS));
+ mService.addNotification(
+ mService.createAutoGroupSummary(nr0_all.getUserId(),
+ nr0_all.getSbn().getPackageName(),
+ nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0));
// cancel both children for USER_ALL
mBinderService.cancelNotificationWithTag(PKG, PKG, nr0_all.getSbn().getTag(),