From 87eec50482141da49ac77ec1f9057af65acdd42b Mon Sep 17 00:00:00 2001 From: Flavio Fiszman Date: Mon, 22 Mar 2021 12:51:00 +0000 Subject: [PATCH] Show messages count in People Tile Change-Id: I85b262dda92227cbea2672d9725bda163a5bb42a Test: manual Bug: 183382000 --- .../android/app/people/PeopleSpaceTile.java | 16 +++++++++ .../app/people/PeopleSpaceTileTest.java | 13 ++++++++ ...people_space_messages_count_background.xml | 20 +++++++++++ .../people_tile_medium_with_content.xml | 20 +++++++++-- packages/SystemUI/res/values/dimens.xml | 1 + packages/SystemUI/res/values/strings.xml | 2 ++ .../systemui/people/PeopleSpaceUtils.java | 18 +++++++--- .../systemui/people/PeopleTileViewHelper.java | 30 +++++++++++++++++ .../widget/PeopleSpaceWidgetManager.java | 1 + .../systemui/people/PeopleSpaceUtilsTest.java | 19 ++++++----- .../people/PeopleTileViewHelperTest.java | 33 +++++++++++++++++++ 11 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 packages/SystemUI/res/drawable/people_space_messages_count_background.xml diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index dd2ba7db03ae7..e645831a31b19 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -56,6 +56,7 @@ public class PeopleSpaceTile implements Parcelable { private CharSequence mNotificationContent; private String mNotificationCategory; private Uri mNotificationDataUri; + private int mMessagesCount; private Intent mIntent; private long mNotificationTimestamp; private List mStatuses; @@ -74,6 +75,7 @@ public class PeopleSpaceTile implements Parcelable { mNotificationContent = b.mNotificationContent; mNotificationCategory = b.mNotificationCategory; mNotificationDataUri = b.mNotificationDataUri; + mMessagesCount = b.mMessagesCount; mIntent = b.mIntent; mNotificationTimestamp = b.mNotificationTimestamp; mStatuses = b.mStatuses; @@ -140,6 +142,10 @@ public class PeopleSpaceTile implements Parcelable { return mNotificationDataUri; } + public int getMessagesCount() { + return mMessagesCount; + } + /** * Provides an intent to launch. If present, we should manually launch the intent on tile * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID. @@ -175,6 +181,7 @@ public class PeopleSpaceTile implements Parcelable { builder.setNotificationContent(mNotificationContent); builder.setNotificationCategory(mNotificationCategory); builder.setNotificationDataUri(mNotificationDataUri); + builder.setMessagesCount(mMessagesCount); builder.setIntent(mIntent); builder.setNotificationTimestamp(mNotificationTimestamp); builder.setStatuses(mStatuses); @@ -196,6 +203,7 @@ public class PeopleSpaceTile implements Parcelable { private CharSequence mNotificationContent; private String mNotificationCategory; private Uri mNotificationDataUri; + private int mMessagesCount; private Intent mIntent; private long mNotificationTimestamp; private List mStatuses; @@ -320,6 +328,12 @@ public class PeopleSpaceTile implements Parcelable { return this; } + /** Sets the number of messages associated with the Tile. */ + public Builder setMessagesCount(int messagesCount) { + mMessagesCount = messagesCount; + return this; + } + /** Sets an intent to launch on click. */ public Builder setIntent(Intent intent) { mIntent = intent; @@ -359,6 +373,7 @@ public class PeopleSpaceTile implements Parcelable { mNotificationContent = in.readCharSequence(); mNotificationCategory = in.readString(); mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); + mMessagesCount = in.readInt(); mIntent = in.readParcelable(Intent.class.getClassLoader()); mNotificationTimestamp = in.readLong(); mStatuses = new ArrayList<>(); @@ -385,6 +400,7 @@ public class PeopleSpaceTile implements Parcelable { dest.writeCharSequence(mNotificationContent); dest.writeString(mNotificationCategory); dest.writeParcelable(mNotificationDataUri, flags); + dest.writeInt(mMessagesCount); dest.writeParcelable(mIntent, flags); dest.writeLong(mNotificationTimestamp); dest.writeParcelableList(mStatuses, flags); diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 7cb680499d98f..36da927116b7c 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -235,6 +235,7 @@ public class PeopleSpaceTileTest { .setStatuses(statusList).setNotificationKey("key") .setNotificationContent("content") .setNotificationDataUri(Uri.parse("data")) + .setMessagesCount(2) .setIntent(new Intent()) .build(); @@ -256,6 +257,7 @@ public class PeopleSpaceTileTest { assertThat(readTile.getNotificationKey()).isEqualTo(tile.getNotificationKey()); assertThat(readTile.getNotificationContent()).isEqualTo(tile.getNotificationContent()); assertThat(readTile.getNotificationDataUri()).isEqualTo(tile.getNotificationDataUri()); + assertThat(readTile.getMessagesCount()).isEqualTo(tile.getMessagesCount()); assertThat(readTile.getIntent().toString()).isEqualTo(tile.getIntent().toString()); } @@ -290,6 +292,17 @@ public class PeopleSpaceTileTest { assertThat(tile.getNotificationDataUri()).isEqualTo(Uri.parse("test")); } + @Test + public void testMessagesCount() { + PeopleSpaceTile tile = + new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build(), + mLauncherApps) + .setMessagesCount(2) + .build(); + + assertThat(tile.getMessagesCount()).isEqualTo(2); + } + @Test public void testIntent() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( diff --git a/packages/SystemUI/res/drawable/people_space_messages_count_background.xml b/packages/SystemUI/res/drawable/people_space_messages_count_background.xml new file mode 100644 index 0000000000000..0fc112e16ee4d --- /dev/null +++ b/packages/SystemUI/res/drawable/people_space_messages_count_background.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml index e4e4cd8956a72..db1d46d2117a3 100644 --- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml @@ -98,8 +98,8 @@ android:orientation="horizontal" android:paddingTop="4dp" android:layout_width="match_parent" - android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:clipToOutline="true"> - + 28dp 20dp + 12dp 6dp 146dp 138dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 691d111089b8f..94bf86ab07da6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2865,6 +2865,8 @@ Let’s chat tonight! Missed call + + %d+ See recent messages, missed calls, and status updates diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 93ce5a83f6840..5bc128062adc0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -274,13 +274,17 @@ public class PeopleSpaceUtils { return tile; } boolean isMissedCall = Objects.equals(notification.category, CATEGORY_MISSED_CALL); - Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(notification); + List messages = + getMessagingStyleMessages(notification); - if (!isMissedCall && message == null) { + if (!isMissedCall && ArrayUtils.isEmpty(messages)) { if (DEBUG) Log.d(TAG, "Notification has no content"); return tile; } + // messages are in chronological order from most recent to least. + Notification.MessagingStyle.Message message = messages != null ? messages.get(0) : null; + int messagesCount = messages != null ? messages.size() : 0; // If it's a missed call notification and it doesn't include content, use fallback value, // otherwise, use notification content. boolean hasMessageText = message != null && !TextUtils.isEmpty(message.getText()); @@ -294,12 +298,16 @@ public class PeopleSpaceUtils { .setNotificationCategory(notification.category) .setNotificationContent(content) .setNotificationDataUri(dataUri) + .setMessagesCount(messagesCount) .build(); } - /** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */ + /** + * Returns {@link Notification.MessagingStyle.Message}s from the Notification in chronological + * order from most recent to least. + */ @VisibleForTesting - public static Notification.MessagingStyle.Message getLastMessagingStyleMessage( + public static List getMessagingStyleMessages( Notification notification) { if (notification == null) { return null; @@ -312,7 +320,7 @@ public class PeopleSpaceUtils { Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); sortedMessages.sort(Collections.reverseOrder( Comparator.comparing(Notification.MessagingStyle.Message::getTimestamp))); - return sortedMessages.get(0); + return sortedMessages; } } return null; diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index ae81ab04ec101..bc196bf68b665 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -58,8 +58,10 @@ import com.android.systemui.R; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; +import java.text.NumberFormat; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.regex.Matcher; @@ -82,6 +84,8 @@ class PeopleTileViewHelper { private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8; private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4; + private static final int MESSAGES_COUNT_OVERFLOW = 7; + private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+"); private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+"); private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+"); @@ -97,6 +101,9 @@ class PeopleTileViewHelper { private int mHeight; private int mLayoutSize; + private Locale mLocale; + private NumberFormat mIntegerFormat; + PeopleTileViewHelper(Context context, PeopleSpaceTile tile, int appWidgetId, Bundle options) { mContext = context; @@ -354,12 +361,35 @@ class PeopleTileViewHelper { views.setViewVisibility(R.id.image, View.GONE); views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message); } + if (mTile.getMessagesCount() > 1 && mLayoutSize == LAYOUT_MEDIUM) { + views.setViewVisibility(R.id.messages_count, View.VISIBLE); + views.setTextViewText(R.id.messages_count, + getMessagesCountText(mTile.getMessagesCount())); + } // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile and // subtract 1 from maxLines when present. views.setViewVisibility(R.id.subtext, View.GONE); return views; } + // Some messaging apps only include up to 7 messages in their notifications. + private String getMessagesCountText(int count) { + if (count >= MESSAGES_COUNT_OVERFLOW) { + return mContext.getResources().getString( + R.string.messages_count_overflow_indicator, MESSAGES_COUNT_OVERFLOW); + } + + // Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed + // non-null, so the first time this is called we will always get the appropriate + // NumberFormat, then never regenerate it unless the locale changes on the fly. + final Locale curLocale = mContext.getResources().getConfiguration().getLocales().get(0); + if (!curLocale.equals(mLocale)) { + mLocale = curLocale; + mIntegerFormat = NumberFormat.getIntegerInstance(curLocale); + } + return mIntegerFormat.format(count); + } + private RemoteViews createStatusRemoteViews(ConversationStatus status) { RemoteViews views = getViewForContentLayout(); CharSequence statusText = status.getDescription(); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 4ad685eae1078..776e8a246bf67 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -328,6 +328,7 @@ public class PeopleSpaceWidgetManager { .setNotificationKey(null) .setNotificationContent(null) .setNotificationDataUri(null) + .setMessagesCount(0) // Reset missed calls category. .setNotificationCategory(null) .build(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 1c7a84a364046..1f4dffa09d861 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -65,6 +65,7 @@ import android.util.DisplayMetrics; import androidx.test.filters.SmallTest; import com.android.internal.appwidget.IAppWidgetService; +import com.android.internal.util.ArrayUtils; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.people.widget.PeopleTileKey; @@ -119,6 +120,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setNotificationKey(NOTIFICATION_KEY) .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) + .setMessagesCount(1) .build(); private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext, @@ -318,7 +320,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @Test - public void testGetLastMessagingStyleMessageNoMessage() { + public void testGetMessagingStyleMessagesNoMessage() { Notification notification = new Notification.Builder(mContext, "test") .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") @@ -328,22 +330,23 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setNotification(notification) .build(); - Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); + List messages = + PeopleSpaceUtils.getMessagingStyleMessages(sbn.getNotification()); - assertThat(lastMessage).isNull(); + assertThat(ArrayUtils.isEmpty(messages)).isTrue(); } @Test - public void testGetLastMessagingStyleMessage() { + public void testGetMessagingStyleMessages() { StatusBarNotification sbn = new SbnBuilder() .setNotification(mNotification1) .build(); - Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); + List messages = + PeopleSpaceUtils.getMessagingStyleMessages(sbn.getNotification()); - assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2); + assertThat(messages.size()).isEqualTo(3); + assertThat(messages.get(0).getText().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index 39bf060507419..c2e0d6b49a762 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -467,6 +467,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility()); + // Has a single message, no count shown. + assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility()); + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, @@ -491,6 +494,36 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { assertThat(statusContent.getMaxLines()).isEqualTo(3); } + @Test + public void testCreateRemoteViewsWithNotificationTemplateTwoMessages() { + PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder() + .setNotificationDataUri(null) + .setStatuses(Arrays.asList(GAME_STATUS, + NEW_STORY_WITH_AVAILABILITY)) + .setMessagesCount(2).build(); + RemoteViews views = new PeopleTileViewHelper(mContext, + tileWithStatusAndNotification, 0, mOptions).getViews(); + View result = views.apply(mContext, null); + + TextView name = (TextView) result.findViewById(R.id.name); + assertEquals(name.getText(), NAME); + TextView subtext = (TextView) result.findViewById(R.id.subtext); + assertEquals(View.GONE, subtext.getVisibility()); + // Has availability. + View availability = result.findViewById(R.id.availability); + assertEquals(View.VISIBLE, availability.getVisibility()); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); + // Has notification content. + TextView statusContent = (TextView) result.findViewById(R.id.text_content); + assertEquals(statusContent.getText(), NOTIFICATION_CONTENT); + + // Has 2 messages, show count. + assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility()); + } + + @Test public void testGetBackgroundTextFromMessageNoPunctuation() { String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test");