Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6eac98a5 authored by Valentin Iftime's avatar Valentin Iftime
Browse files

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
parent 5dbd01f0
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
<?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>
+1 −0
Original line number Diff line number Diff line
@@ -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" />
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ android_test {
        "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 @@ android_test {
        "flickerlib-parsers",
        "flickerlib-trace_processor_shell",
        "mockito-target-extended-minus-junit4",
        "TestParameterInjector",
    ],

    libs: [
+22 −0
Original line number Diff line number Diff line
<?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
+89 −18
Original line number Diff line number Diff line
@@ -18,8 +18,13 @@ package android.graphics.drawable;

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.HandlerThread;
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.io.FileOutputStream;
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 @@ public class IconTest extends AndroidTestCase {
        }
    }

    @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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        }
    }

    @SmallTest
    @Test
    public void testWithBitmapResource() throws Exception {
        final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
                .getBitmap();
@@ -193,7 +216,7 @@ public class IconTest extends AndroidTestCase {
     * 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 @@ public class IconTest extends AndroidTestCase {
        }
    }

    @SmallTest
    @Test
    public void testWithFile() throws Exception {
        final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
                .getBitmap();
@@ -268,7 +291,55 @@ public class IconTest extends AndroidTestCase {
        }
    }

    @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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        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 @@ public class IconTest extends AndroidTestCase {
        }
    }

    @SmallTest
    @Test
    public void testLoadSafeDrawable_grantRejected_nullDrawable() throws FileNotFoundException {
        int uid = 12345;
        String packageName = "test_pkg";
Loading