summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/res/values/colors.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java183
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java13
5 files changed, 162 insertions, 47 deletions
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 5e8779fa289a..88c6185a53e7 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -39,4 +39,7 @@
<color name="dark_mode_icon_color_single_tone">#99000000</color>
<color name="light_mode_icon_color_single_tone">#ffffff</color>
+
+ <!-- Yellow 600, used for highlighting "important" conversations in settings & notifications -->
+ <color name="important_conversation">#f9ab00</color>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index 885b7d396a6c..9dc454f53834 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -15,32 +15,48 @@
*/
package com.android.settingslib.notification;
+import android.annotation.ColorInt;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
+import android.util.Log;
import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
+import com.android.settingslib.R;
/**
* Factory for creating normalized conversation icons.
* We are not using Launcher's IconFactory because conversation rendering only runs on the UI
- * thread, so there is no need to manage a pool across multiple threads.
+ * thread, so there is no need to manage a pool across multiple threads. Launcher's rendering
+ * also includes shadows, which are only appropriate on top of wallpaper, not embedded in UI.
*/
public class ConversationIconFactory extends BaseIconFactory {
+ // Geometry of the various parts of the design. All values are 1dp on a 48x48dp icon grid.
+ // Space is left around the "head" (main avatar) for
+ // ........
+ // .HHHHHH.
+ // .HHHrrrr
+ // .HHHrBBr
+ // ....rrrr
+
+ private static final float BASE_ICON_SIZE = 48f;
+ private static final float RING_STROKE_WIDTH = 2f;
+ private static final float HEAD_SIZE = BASE_ICON_SIZE - RING_STROKE_WIDTH * 2 - 2; // 40
+ private static final float BADGE_SIZE = HEAD_SIZE * 0.4f; // 16
final LauncherApps mLauncherApps;
final PackageManager mPackageManager;
final IconDrawableFactory mIconDrawableFactory;
+ private int mImportantConversationColor;
public ConversationIconFactory(Context context, LauncherApps la, PackageManager pm,
IconDrawableFactory iconDrawableFactory, int iconSizePx) {
@@ -49,65 +65,156 @@ public class ConversationIconFactory extends BaseIconFactory {
mLauncherApps = la;
mPackageManager = pm;
mIconDrawableFactory = iconDrawableFactory;
+ mImportantConversationColor = context.getResources().getColor(
+ R.color.important_conversation, null);
}
- private int getBadgeSize() {
- return mContext.getResources().getDimensionPixelSize(
- com.android.launcher3.icons.R.dimen.profile_badge_size);
- }
/**
* Returns the conversation info drawable
*/
- private Drawable getConversationDrawable(ShortcutInfo shortcutInfo) {
+ private Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
}
/**
- * Get the {@link Drawable} that represents the app icon
+ * Get the {@link Drawable} that represents the app icon, badged with the work profile icon
+ * if appropriate.
*/
- private Drawable getBadgedIcon(String packageName, int userId) {
+ private Drawable getAppBadge(String packageName, int userId) {
+ Drawable badge = null;
try {
final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
packageName, PackageManager.GET_META_DATA, userId);
- return mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+ badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId);
} catch (PackageManager.NameNotFoundException e) {
- return mPackageManager.getDefaultActivityIcon();
+ badge = mPackageManager.getDefaultActivityIcon();
}
+ return badge;
}
/**
- * Turns a Drawable into a Bitmap
+ * Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged
+ * with the launcher icon of the app specified by packageName.
*/
- BitmapInfo toBitmap(Drawable userBadgedAppIcon) {
- Bitmap bitmap = createIconBitmap(
- userBadgedAppIcon, 1f, getBadgeSize());
-
- Canvas c = new Canvas();
- ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
- c.setBitmap(bitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(bitmap), c);
- return createIconBitmap(bitmap);
+ public Drawable getConversationDrawable(ShortcutInfo info, String packageName, int uid,
+ boolean important) {
+ return getConversationDrawable(getBaseIconDrawable(info), packageName, uid, important);
}
/**
- * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ * Returns a {@link Drawable} for the entire conversation. The drawable will be badged
+ * with the launcher icon of the app specified by packageName.
*/
- public Bitmap getConversationBitmap(ShortcutInfo info, String packageName, int uid) {
- return getConversationBitmap(getConversationDrawable(info), packageName, uid);
+ public Drawable getConversationDrawable(Drawable baseIcon, String packageName, int uid,
+ boolean important) {
+ return new ConversationIconDrawable(baseIcon,
+ getAppBadge(packageName, UserHandle.getUserId(uid)),
+ mIconBitmapSize,
+ mImportantConversationColor,
+ important);
}
/**
- * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ * Custom Drawable that overlays a badge drawable (e.g. notification small icon or app icon) on
+ * a base icon (conversation/person avatar), plus decorations indicating conversation
+ * importance.
*/
- public Bitmap getConversationBitmap(Drawable baseIcon, String packageName, int uid) {
- int userId = UserHandle.getUserId(uid);
- Drawable badge = getBadgedIcon(packageName, userId);
- BitmapInfo iconInfo = createBadgedIconBitmap(baseIcon,
- UserHandle.of(userId),
- true /* shrinkNonAdaptiveIcons */);
-
- badgeWithDrawable(iconInfo.icon,
- new BitmapDrawable(mContext.getResources(), toBitmap(badge).icon));
- return iconInfo.icon;
+ public static class ConversationIconDrawable extends Drawable {
+ private Drawable mBaseIcon;
+ private Drawable mBadgeIcon;
+ private int mIconSize;
+ private Paint mRingPaint;
+ private boolean mShowRing;
+
+ public ConversationIconDrawable(Drawable baseIcon,
+ Drawable badgeIcon,
+ int iconSize,
+ @ColorInt int ringColor,
+ boolean showImportanceRing) {
+ mBaseIcon = baseIcon;
+ mBadgeIcon = badgeIcon;
+ mIconSize = iconSize;
+ mShowRing = showImportanceRing;
+ mRingPaint = new Paint();
+ mRingPaint.setStyle(Paint.Style.STROKE);
+ mRingPaint.setColor(ringColor);
+ }
+
+ /**
+ * Show or hide the importance ring.
+ */
+ public void setImportant(boolean important) {
+ if (important != mShowRing) {
+ mShowRing = important;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mIconSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mIconSize;
+ }
+
+ // Similar to badgeWithDrawable, but relying on the bounds of each underlying drawable
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect bounds = getBounds();
+
+ // scale to our internal 48x48 grid
+ final float scale = bounds.width() / BASE_ICON_SIZE;
+ final int centerX = bounds.centerX();
+ final int centerY = bounds.centerX();
+ final int ringStrokeWidth = (int) (RING_STROKE_WIDTH * scale);
+ final int headSize = (int) (HEAD_SIZE * scale);
+ final int badgeSize = (int) (BADGE_SIZE * scale);
+
+ if (mBaseIcon != null) {
+ mBaseIcon.setBounds(
+ centerX - headSize / 2,
+ centerY - headSize / 2,
+ centerX + headSize / 2,
+ centerY + headSize / 2);
+ mBaseIcon.draw(canvas);
+ } else {
+ Log.w("ConversationIconFactory", "ConversationIconDrawable has null base icon");
+ }
+ if (mBadgeIcon != null) {
+ mBadgeIcon.setBounds(
+ bounds.right - badgeSize - ringStrokeWidth,
+ bounds.bottom - badgeSize - ringStrokeWidth,
+ bounds.right - ringStrokeWidth,
+ bounds.bottom - ringStrokeWidth);
+ mBadgeIcon.draw(canvas);
+ } else {
+ Log.w("ConversationIconFactory", "ConversationIconDrawable has null badge icon");
+ }
+ if (mShowRing) {
+ mRingPaint.setStrokeWidth(ringStrokeWidth);
+ final float radius = badgeSize * 0.5f + ringStrokeWidth * 0.5f; // stroke outside
+ final float cx = bounds.right - badgeSize * 0.5f - ringStrokeWidth;
+ final float cy = bounds.bottom - badgeSize * 0.5f - ringStrokeWidth;
+ canvas.drawCircle(cx, cy, radius, mRingPaint);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // unimplemented
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // unimplemented
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index bab7840e57d7..a5258fdef975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -325,8 +325,9 @@ public class NotificationConversationInfo extends LinearLayout implements
private void bindIcon() {
ImageView image = findViewById(R.id.conversation_icon);
if (mShortcutInfo != null) {
- image.setImageBitmap(mIconFactory.getConversationBitmap(
- mShortcutInfo, mPackageName, mAppUid));
+ image.setImageDrawable(mIconFactory.getConversationDrawable(
+ mShortcutInfo, mPackageName, mAppUid,
+ mNotificationChannel.isImportantConversation()));
} else {
if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
// TODO: maybe use a generic group icon, or a composite of recent senders
@@ -480,6 +481,9 @@ public class NotificationConversationInfo extends LinearLayout implements
mContext.getString(R.string.notification_conversation_mute));
mute.setImageResource(R.drawable.ic_notifications_silence);
}
+
+ // update icon in case importance has changed
+ bindIcon();
}
private void updateChannel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index c01f6c4ef0d2..d746822ddcff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -390,7 +390,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
};
}
ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
- launcherApps, pmUser, IconDrawableFactory.newInstance(mContext),
+ launcherApps, pmUser, IconDrawableFactory.newInstance(mContext, false),
mContext.getResources().getDimensionPixelSize(
R.dimen.notification_guts_conversation_icon_size));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 138ea392f8ef..6c12c7697c8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -30,6 +30,7 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -53,8 +54,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.provider.Settings;
@@ -117,7 +117,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Mock
private ShortcutInfo mShortcutInfo;
@Mock
- private Bitmap mImage;
+ private Drawable mIconDrawable;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -183,8 +183,9 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
- when(mIconFactory.getConversationBitmap(any(ShortcutInfo.class), anyString(), anyInt()))
- .thenReturn(mImage);
+ when(mIconFactory.getConversationDrawable(
+ any(ShortcutInfo.class), anyString(), anyInt(), anyBoolean()))
+ .thenReturn(mIconDrawable);
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
@@ -233,7 +234,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mIconFactory,
true);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
- assertEquals(mImage, ((BitmapDrawable) view.getDrawable()).getBitmap());
+ assertEquals(mIconDrawable, view.getDrawable());
}
@Test