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

Commit 018dbd83 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

Use monochrome app icon for notifs

In the shelf and status bar, we want to experiment with showing the
monochrome app icon if it exists.

I'm using the same method as in GroupHelper to fetch this icon.

This should be done on the background thread since it requires a binder
call, but I'll make that change in a separate CL.

There's a known issue similar to b/329091967 where the calendar icon
doesn't get cropped at the bottom.

Bug: 335211019
Test: IconManagerTest + tested manually with a couple of apps (gmail,
play store, calendar)
Flag: android.app.notifications_use_monochrome_app_icon DEVELOPMENT
Change-Id: Ibc12ebb9a6a5a3d3619ade7c009c7aae24913fd9

Change-Id: Id8ebf0668769e013c496b22eddb047f01b880a9d
parent 91c55ede
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -3127,6 +3127,28 @@ public class Notification implements Parcelable
        return 0;
    }
    /**
     * @hide
     */
    public Drawable loadHeaderAppIcon(Context context) {
        Trace.beginSection("Notification#loadHeaderAppIcon");
        try {
            final PackageManager pm = context.getPackageManager();
            if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
                final ApplicationInfo info = extras.getParcelable(
                        EXTRA_BUILDER_APPLICATION_INFO,
                        ApplicationInfo.class);
                if (info != null) {
                    return pm.getApplicationIcon(info);
                }
            }
            return pm.getApplicationIcon(context.getApplicationInfo());
        } finally {
            Trace.endSection();
        }
    }
    /**
     * Removes heavyweight parts of the Notification object for archival or for sending to
     * listeners when the full contents are not necessary.
+7 −0
Original line number Diff line number Diff line
@@ -59,6 +59,13 @@ flag {
  bug: "335211019"
}

flag {
  name: "notifications_use_monochrome_app_icon"
  namespace: "systemui"
  description: "Experiment to replace the notification icon in the status bar and shelf with the monochrome app icon, if available."
  bug: "335211019"
}

flag {
  name: "notification_expansion_optional"
  namespace: "systemui"
+11 −9
Original line number Diff line number Diff line
@@ -18,25 +18,27 @@ package com.android.systemui.statusbar.notification.icon

import android.app.Notification
import android.content.Context
import android.graphics.drawable.Drawable
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.contentDescForNotification
import javax.inject.Inject

/**
 * Testable wrapper around Context.
 */
class IconBuilder @Inject constructor(
    private val context: Context
) {
/** Testable wrapper around Context. */
class IconBuilder @Inject constructor(private val context: Context) {
    fun createIconView(entry: NotificationEntry): StatusBarIconView {
        return StatusBarIconView(
            context,
            "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
                entry.sbn)
            entry.sbn
        )
    }

    fun getIconContentDescription(n: Notification): CharSequence {
        return contentDescForNotification(context, n)
    }

    fun getAppIcon(n: Notification): Drawable {
        return n.loadHeaderAppIcon(context)
    }
}
+86 −21
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.app.Notification
import android.app.Notification.MessagingStyle
import android.app.Person
import android.content.pm.LauncherApps
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
@@ -165,7 +167,7 @@ constructor(
                Log.wtf(
                    TAG,
                    "Updating using the cache is not supported when the " +
                        "notifications_background_conversation_icons flag is off"
                        "notifications_background_icons flag is off"
                )
            }
            if (!usingCache || !Flags.notificationsBackgroundIcons()) {
@@ -216,39 +218,79 @@ constructor(

    @Throws(InflationException::class)
    private fun getIconDescriptor(entry: NotificationEntry, redact: Boolean): StatusBarIcon {
        val n = entry.sbn.notification
        val showPeopleAvatar = !redact && isImportantConversation(entry)

        // If the descriptor is already cached, return it
        getCachedIconDescriptor(entry, showPeopleAvatar)?.also {
            return it
        }

        val smallIcon = entry.sbn.notification.smallIcon
        var usingMonochromeAppIcon = false
        val icon: Icon?
        if (showPeopleAvatar) {
            icon = createPeopleAvatar(entry)
        } else if (android.app.Flags.notificationsUseMonochromeAppIcon()) {
            icon = getMonochromeAppIcon(entry)?.also { usingMonochromeAppIcon = true } ?: smallIcon
        } else {
            icon = smallIcon
        }

        if (icon == null) {
            throw InflationException("No icon in notification from ${entry.sbn.packageName}")
        }

        val sbi = icon.toStatusBarIcon(entry)
        cacheIconDescriptor(entry, sbi, showPeopleAvatar, usingMonochromeAppIcon)
        return sbi
    }

    private fun getCachedIconDescriptor(
        entry: NotificationEntry,
        showPeopleAvatar: Boolean
    ): StatusBarIcon? {
        val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
        val appIconDescriptor = entry.icons.appIconDescriptor
        val smallIconDescriptor = entry.icons.smallIconDescriptor

        // If cached, return corresponding cached values
        if (showPeopleAvatar && peopleAvatarDescriptor != null) {
            return peopleAvatarDescriptor
        } else if (!showPeopleAvatar && smallIconDescriptor != null) {
            return smallIconDescriptor
        return when {
            showPeopleAvatar && peopleAvatarDescriptor != null -> peopleAvatarDescriptor
            android.app.Flags.notificationsUseMonochromeAppIcon() && appIconDescriptor != null ->
                appIconDescriptor
            smallIconDescriptor != null -> smallIconDescriptor
            else -> null
        }
    }

        val icon =
            (if (showPeopleAvatar) {
                createPeopleAvatar(entry)
    private fun cacheIconDescriptor(
        entry: NotificationEntry,
        descriptor: StatusBarIcon,
        showPeopleAvatar: Boolean,
        usingMonochromeAppIcon: Boolean
    ) {
        if (android.app.Flags.notificationsUseAppIcon() ||
            android.app.Flags.notificationsUseMonochromeAppIcon()
        ) {
            // If either of the new icon flags is enabled, we cache the icon all the time.
            if (showPeopleAvatar) {
                entry.icons.peopleAvatarDescriptor = descriptor
            } else if (usingMonochromeAppIcon) {
                // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor.
                entry.icons.appIconDescriptor = descriptor
            } else {
                n.smallIcon
            })
                ?: throw InflationException("No icon in notification from " + entry.sbn.packageName)

        val sbi = icon.toStatusBarIcon(entry)

        // Cache if important conversation or app icon.
        if (isImportantConversation(entry) || android.app.Flags.notificationsUseAppIcon()) {
                // When notificationsUseAppIcon is enabled, the app icon overrides the small icon.
                // But either way, it's a good idea to cache the descriptor.
                entry.icons.smallIconDescriptor = descriptor
            }
        } else if (isImportantConversation(entry)) {
            // Old approach: cache only if important conversation.
            if (showPeopleAvatar) {
                entry.icons.peopleAvatarDescriptor = sbi
                entry.icons.peopleAvatarDescriptor = descriptor
            } else {
                entry.icons.smallIconDescriptor = sbi
                entry.icons.smallIconDescriptor = descriptor
            }
        }

        return sbi
    }

    @Throws(InflationException::class)
@@ -276,6 +318,29 @@ constructor(
        )
    }

    // TODO(b/335211019): Should we merge this with the method in GroupHelper?
    private fun getMonochromeAppIcon(entry: NotificationEntry): Icon? {
        // TODO(b/335211019): This should be done in the background.
        var monochromeIcon: Icon? = null
        try {
            val appIcon: Drawable = iconBuilder.getAppIcon(entry.sbn.notification)
            if (appIcon is AdaptiveIconDrawable) {
                if (appIcon.monochrome != null) {
                    monochromeIcon =
                        Icon.createWithResourceAdaptiveDrawable(
                            /* resPackage = */ entry.sbn.packageName,
                            /* resId = */ appIcon.sourceDrawableResId,
                            /* useMonochrome = */ true,
                            /* inset = */ -3.0f * AdaptiveIconDrawable.getExtraInsetFraction()
                        )
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Failed to getAppIcon() in getMonochromeAppIcon()", e)
        }
        return monochromeIcon
    }

    private suspend fun getLauncherShortcutIconForPeopleAvatar(entry: NotificationEntry) =
        withContext(bgCoroutineContext) {
            var icon: Icon? = null
+10 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ public final class IconPack {
    @Nullable private final StatusBarIconView mAodIcon;

    @Nullable private StatusBarIcon mSmallIconDescriptor;
    @Nullable private StatusBarIcon mAppIconDescriptor;
    @Nullable private StatusBarIcon mPeopleAvatarDescriptor;

    private boolean mIsImportantConversation;
@@ -111,6 +112,15 @@ public final class IconPack {
        mPeopleAvatarDescriptor = peopleAvatarDescriptor;
    }

    @Nullable
    StatusBarIcon getAppIconDescriptor() {
        return mAppIconDescriptor;
    }

    void setAppIconDescriptor(@Nullable StatusBarIcon appIconDescriptor) {
        mAppIconDescriptor = appIconDescriptor;
    }

    boolean isImportantConversation() {
        return mIsImportantConversation;
    }