From b331ca659b421a7ab765ca4e8da4c482966e86be Mon Sep 17 00:00:00 2001 From: Flavio Fiszman Date: Thu, 29 Apr 2021 10:38:47 +0100 Subject: Decorating People Tile background Change-Id: Ie88ad8482e0dcd6b5f1640827fe71b9d9137cf0c Test: manual and PeopleTileViewHelperTest Bug: 184741504 --- .../layout/people_tile_emoji_background_large.xml | 58 ++++++ .../layout/people_tile_emoji_background_medium.xml | 58 ++++++ .../res/layout/people_tile_large_with_content.xml | 115 ++++++------ .../res/layout/people_tile_medium_with_content.xml | 198 +++++++++++---------- .../people_tile_punctuation_background_large.xml | 109 ++++++++++++ .../people_tile_punctuation_background_medium.xml | 95 ++++++++++ .../systemui/people/PeopleTileViewHelper.java | 141 ++++++++++++--- .../systemui/people/PeopleTileViewHelperTest.java | 113 +++++++++--- 8 files changed, 693 insertions(+), 194 deletions(-) create mode 100644 packages/SystemUI/res/layout/people_tile_emoji_background_large.xml create mode 100644 packages/SystemUI/res/layout/people_tile_emoji_background_medium.xml create mode 100644 packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml create mode 100644 packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml diff --git a/packages/SystemUI/res/layout/people_tile_emoji_background_large.xml b/packages/SystemUI/res/layout/people_tile_emoji_background_large.xml new file mode 100644 index 000000000000..61d3ea5df6df --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_emoji_background_large.xml @@ -0,0 +1,58 @@ + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_tile_emoji_background_medium.xml b/packages/SystemUI/res/layout/people_tile_emoji_background_medium.xml new file mode 100644 index 000000000000..a5f300fa54b1 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_emoji_background_medium.xml @@ -0,0 +1,58 @@ + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml index af2c5de3b52f..6f8de3b68589 100644 --- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml @@ -67,68 +67,79 @@ android:visibility="gone" /> - - + android:layout_height="match_parent"> + + - + + + + + + + + + + android:id="@+id/image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/people_space_content_background" + android:gravity="center" + android:scaleType="centerCrop" /> - - - - - + + 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 70706600e6da..a8c15abc8459 100644 --- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml @@ -21,121 +21,127 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - + + + android:layout_height="match_parent"> + + - + - + + + + + + + + + + + - + android:layout_height="wrap_content" + android:clipToOutline="true"> - - - + android:ellipsize="end" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + android:visibility="gone" + /> + - - - - - - - + diff --git a/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml b/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml new file mode 100644 index 000000000000..2ffe59a2d032 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml @@ -0,0 +1,109 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml b/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml new file mode 100644 index 000000000000..75cdde0e97e4 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml @@ -0,0 +1,95 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index d4ddc6546a19..a23db63a70fc 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -51,6 +51,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; +import android.util.Pair; import android.view.View; import android.widget.RemoteViews; import android.widget.TextView; @@ -63,6 +64,7 @@ import com.android.systemui.people.widget.PeopleTileKey; import java.text.NumberFormat; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -102,6 +104,39 @@ public class PeopleTileViewHelper { private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+"); private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!"); + // This regex can be used to match Unicode emoji characters and character sequences. It's from + // the official Unicode site (https://unicode.org/reports/tr51/#EBNF_and_Regex) with minor + // changes to fit our needs. It should be updated once new emoji categories are added. + // + // Emoji categories that can be matched by this regex: + // - Country flags. "\p{RI}\p{RI}" matches country flags since they always consist of 2 Unicode + // scalars. + // - Single-Character Emoji. "\p{Emoji}" matches Single-Character Emojis. + // - Emoji with modifiers. E.g. Emojis with different skin tones. "\p{Emoji}\p{EMod}" matches + // them. + // - Emoji Presentation. Those are characters which can normally be drawn as either text or as + // Emoji. "\p{Emoji}\x{FE0F}" matches them. + // - Emoji Keycap. E.g. Emojis for number 0 to 9. "\p{Emoji}\x{FE0F}\x{20E3}" matches them. + // - Emoji tag sequence. "\p{Emoji}[\x{E0020}-\x{E007E}]+\x{E007F}" matches them. + // - Emoji Zero-Width Joiner (ZWJ) Sequence. A ZWJ emoji is actually multiple emojis joined by + // the jointer "0x200D". + // + // Note: since "\p{Emoji}" also matches some ASCII characters like digits 0-9, we use + // "\p{Emoji}&&\p{So}" to exclude them. This is the change we made from the official emoji + // regex. + private static final String UNICODE_EMOJI_REGEX = + "\\p{RI}\\p{RI}|" + + "(" + + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})" + + "|[\\p{Emoji}&&\\p{So}]" + + ")" + + "(" + + "\\x{200D}" + + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})" + + "?)*"; + + private static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX); + public static final String EMPTY_STRING = ""; private int mMediumVerticalPadding; @@ -375,7 +410,7 @@ public class PeopleTileViewHelper { } else { setMaxLines(views, !TextUtils.isEmpty(sender)); CharSequence content = mTile.getNotificationContent(); - views = setPunctuationRemoteViewsFields(views, content); + views = decorateBackground(views, content); views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorPrimary); views.setTextViewText(R.id.text_content, mTile.getNotificationContent()); views.setViewVisibility(R.id.image, View.GONE); @@ -506,33 +541,53 @@ public class PeopleTileViewHelper { } } - private RemoteViews setPunctuationRemoteViewsFields( - RemoteViews views, CharSequence content) { - String punctuation = getBackgroundTextFromMessage(content.toString()); + private RemoteViews decorateBackground(RemoteViews views, CharSequence content) { int visibility = View.GONE; - if (punctuation != null) { - visibility = View.VISIBLE; - } - views.setTextViewText(R.id.punctuation1, punctuation); - views.setTextViewText(R.id.punctuation2, punctuation); - views.setTextViewText(R.id.punctuation3, punctuation); - views.setTextViewText(R.id.punctuation4, punctuation); - views.setTextViewText(R.id.punctuation5, punctuation); - views.setTextViewText(R.id.punctuation6, punctuation); - - views.setViewVisibility(R.id.punctuation1, visibility); - views.setViewVisibility(R.id.punctuation2, visibility); - views.setViewVisibility(R.id.punctuation3, visibility); - views.setViewVisibility(R.id.punctuation4, visibility); - views.setViewVisibility(R.id.punctuation5, visibility); - views.setViewVisibility(R.id.punctuation6, visibility); + CharSequence emoji = getDoubleEmoji(content); + if (!TextUtils.isEmpty(emoji)) { + setEmojiBackground(views, emoji); + setPunctuationBackground(views, null); + return views; + } + + CharSequence punctuation = getDoublePunctuation(content); + setEmojiBackground(views, null); + setPunctuationBackground(views, punctuation); + return views; + } + + private RemoteViews setEmojiBackground(RemoteViews views, CharSequence content) { + if (TextUtils.isEmpty(content)) { + views.setViewVisibility(R.id.emojis, View.GONE); + return views; + } + views.setTextViewText(R.id.emoji1, content); + views.setTextViewText(R.id.emoji2, content); + views.setTextViewText(R.id.emoji3, content); + + views.setViewVisibility(R.id.emojis, View.VISIBLE); + return views; + } + + private RemoteViews setPunctuationBackground(RemoteViews views, CharSequence content) { + if (TextUtils.isEmpty(content)) { + views.setViewVisibility(R.id.punctuations, View.GONE); + return views; + } + views.setTextViewText(R.id.punctuation1, content); + views.setTextViewText(R.id.punctuation2, content); + views.setTextViewText(R.id.punctuation3, content); + views.setTextViewText(R.id.punctuation4, content); + views.setTextViewText(R.id.punctuation5, content); + views.setTextViewText(R.id.punctuation6, content); + views.setViewVisibility(R.id.punctuations, View.VISIBLE); return views; } - /** Gets character for mTile background decoration based on notification content. */ + /** Returns punctuation character(s) if {@code message} has double punctuation ("!" or "?"). */ @VisibleForTesting - String getBackgroundTextFromMessage(String message) { + CharSequence getDoublePunctuation(CharSequence message) { if (!ANY_DOUBLE_MARK_PATTERN.matcher(message).find()) { return null; } @@ -554,6 +609,48 @@ public class PeopleTileViewHelper { return "!"; } + /** Returns emoji if {@code message} has two of the same emoji in sequence. */ + @VisibleForTesting + CharSequence getDoubleEmoji(CharSequence message) { + Matcher unicodeEmojiMatcher = EMOJI_PATTERN.matcher(message); + // Stores the start and end indices of each matched emoji. + List> emojiIndices = new ArrayList<>(); + // Stores each emoji text. + List emojiTexts = new ArrayList<>(); + + // Scan message for emojis + while (unicodeEmojiMatcher.find()) { + int start = unicodeEmojiMatcher.start(); + int end = unicodeEmojiMatcher.end(); + emojiIndices.add(new Pair(start, end)); + emojiTexts.add(message.subSequence(start, end)); + } + + if (DEBUG) Log.d(TAG, "Number of emojis in the message: " + emojiIndices.size()); + if (emojiIndices.size() < 2) { + return null; + } + + for (int i = 1; i < emojiIndices.size(); ++i) { + Pair second = emojiIndices.get(i); + Pair first = emojiIndices.get(i - 1); + + // Check if second emoji starts right after first starts + if (second.first == first.second) { + // Check if emojis in sequence are the same + if (Objects.equals(emojiTexts.get(i), emojiTexts.get(i - 1))) { + if (DEBUG) { + Log.d(TAG, "Two of the same emojis in sequence: " + emojiTexts.get(i)); + } + return emojiTexts.get(i); + } + } + } + + // No equal emojis in sequence. + return null; + } + private RemoteViews getViewForContentLayout() { RemoteViews views = new RemoteViews(mContext.getPackageName(), getLayoutForContent()); 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 d353d529865b..764cdee7e36d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -76,6 +76,14 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { private static final String NAME = "username"; private static final UserHandle USER = new UserHandle(0); private static final String SENDER = "sender"; + + private static final CharSequence EMOJI_BR_FLAG = "\ud83c\udde7\ud83c\uddf7"; + private static final CharSequence EMOJI_BEAR = "\ud83d\udc3b"; + private static final CharSequence EMOJI_THUMBS_UP_BROWN_SKIN = "\uD83D\uDC4D\uD83C\uDFFD"; + private static final CharSequence EMOJI_JOY = "\uD83D\uDE02"; + private static final CharSequence EMOJI_FAMILY = + "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"; + private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION = new PeopleSpaceTile .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent()) @@ -701,94 +709,151 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { @Test - public void testGetBackgroundTextFromMessageNoPunctuation() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test"); + public void testGetDoublePunctuationNoPunctuation() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("test"); assertThat(backgroundText).isNull(); } @Test - public void testGetBackgroundTextFromMessageSingleExclamation() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test!"); + public void testGetDoublePunctuationSingleExclamation() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("test!"); assertThat(backgroundText).isNull(); } @Test - public void testGetBackgroundTextFromMessageSingleQuestion() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("?test"); + public void testGetDoublePunctuationSingleQuestion() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("?test"); assertThat(backgroundText).isNull(); } @Test - public void testGetBackgroundTextFromMessageSeparatedMarks() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test! right!"); + public void testGetDoublePunctuationSeparatedMarks() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("test! right!"); assertThat(backgroundText).isNull(); } @Test - public void testGetBackgroundTextFromMessageDoubleExclamation() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("!!test"); + public void testGetDoublePunctuationDoubleExclamation() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("!!test"); assertThat(backgroundText).isEqualTo("!"); } @Test - public void testGetBackgroundTextFromMessageDoubleQuestion() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test??"); + public void testGetDoublePunctuationDoubleQuestion() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("test??"); assertThat(backgroundText).isEqualTo("?"); } @Test - public void testGetBackgroundTextFromMessageMixed() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test?!"); + public void testGetDoublePunctuationMixed() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation("test?!"); assertThat(backgroundText).isEqualTo("!?"); } @Test - public void testGetBackgroundTextFromMessageMixedInTheMiddle() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage( + public void testGetDoublePunctuationMixedInTheMiddle() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation( "test!? in the middle"); assertThat(backgroundText).isEqualTo("!?"); } @Test - public void testGetBackgroundTextFromMessageMixedDifferentOrder() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage( + public void testGetDoublePunctuationMixedDifferentOrder() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation( "test!? in the middle"); assertThat(backgroundText).isEqualTo("!?"); } @Test - public void testGetBackgroundTextFromMessageMultiple() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage( + public void testGetDoublePunctuationMultiple() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation( "test!?!!? in the middle"); assertThat(backgroundText).isEqualTo("!?"); } @Test - public void testGetBackgroundTextFromMessageQuestionFirst() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage( + public void testGetDoublePunctuationQuestionFirst() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation( "test?? in the middle!!"); assertThat(backgroundText).isEqualTo("?"); } @Test - public void testGetBackgroundTextFromMessageExclamationFirst() { - String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage( + public void testGetDoublePunctuationExclamationFirst() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoublePunctuation( "test!! in the middle??"); assertThat(backgroundText).isEqualTo("!"); } + @Test + public void testGetDoubleEmojisNoEmojis() { + CharSequence backgroundText = mPeopleTileViewHelper + .getDoubleEmoji("This string has no emojis."); + assertThat(backgroundText).isNull(); + } + + @Test + public void testGetDoubleEmojisSingleEmoji() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "This string has one emoji " + EMOJI_JOY + " in the middle."); + assertThat(backgroundText).isNull(); + } + + @Test + public void testGetDoubleEmojisSingleEmojiThenTwoEmojis() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "This string has one emoji " + EMOJI_JOY + " in the middle, then two " + + EMOJI_BEAR + EMOJI_BEAR); + assertEquals(backgroundText, EMOJI_BEAR); + } + + @Test + public void testGetDoubleEmojisTwoEmojisWithModifier() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "Yes! " + EMOJI_THUMBS_UP_BROWN_SKIN + EMOJI_THUMBS_UP_BROWN_SKIN + " Sure."); + assertEquals(backgroundText, EMOJI_THUMBS_UP_BROWN_SKIN); + } + + @Test + public void testGetDoubleEmojisTwoFlagEmojis() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "Let's travel to " + EMOJI_BR_FLAG + EMOJI_BR_FLAG + " next year."); + assertEquals(backgroundText, EMOJI_BR_FLAG); + } + + @Test + public void testGetDoubleEmojiTwoBears() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + EMOJI_BEAR.toString() + EMOJI_BEAR.toString() + "bears!"); + assertEquals(backgroundText, EMOJI_BEAR); + } + + @Test + public void testGetDoubleEmojiTwoEmojisTwice() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "Two sets of two emojis: " + EMOJI_FAMILY + EMOJI_FAMILY + EMOJI_BEAR + EMOJI_BEAR); + assertEquals(backgroundText, EMOJI_FAMILY); + } + + @Test + public void testGetDoubleEmojiTwoEmojisSeparated() { + CharSequence backgroundText = mPeopleTileViewHelper.getDoubleEmoji( + "Two emojis " + EMOJI_BEAR + " separated " + EMOJI_BEAR + "."); + assertThat(backgroundText).isNull(); + } + private int getSizeInDp(int dimenResourceId) { return (int) (mContext.getResources().getDimension(dimenResourceId) / mContext.getResources().getDisplayMetrics().density); -- cgit v1.2.3-59-g8ed1b