Merge "Add Missed call People Tile" into sc-dev
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index 9a53b99..132af4b 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -53,6 +53,7 @@
     private boolean mIsImportantConversation;
     private String mNotificationKey;
     private CharSequence mNotificationContent;
+    private String mNotificationCategory;
     private Uri mNotificationDataUri;
     private Intent mIntent;
     private long mNotificationTimestamp;
@@ -70,6 +71,7 @@
         mIsImportantConversation = b.mIsImportantConversation;
         mNotificationKey = b.mNotificationKey;
         mNotificationContent = b.mNotificationContent;
+        mNotificationCategory = b.mNotificationCategory;
         mNotificationDataUri = b.mNotificationDataUri;
         mIntent = b.mIntent;
         mNotificationTimestamp = b.mNotificationTimestamp;
@@ -129,6 +131,10 @@
         return mNotificationContent;
     }
 
+    public String getNotificationCategory() {
+        return mNotificationCategory;
+    }
+
     public Uri getNotificationDataUri() {
         return mNotificationDataUri;
     }
@@ -166,6 +172,7 @@
         builder.setIsImportantConversation(mIsImportantConversation);
         builder.setNotificationKey(mNotificationKey);
         builder.setNotificationContent(mNotificationContent);
+        builder.setNotificationCategory(mNotificationCategory);
         builder.setNotificationDataUri(mNotificationDataUri);
         builder.setIntent(mIntent);
         builder.setNotificationTimestamp(mNotificationTimestamp);
@@ -186,6 +193,7 @@
         private boolean mIsImportantConversation;
         private String mNotificationKey;
         private CharSequence mNotificationContent;
+        private String mNotificationCategory;
         private Uri mNotificationDataUri;
         private Intent mIntent;
         private long mNotificationTimestamp;
@@ -299,6 +307,12 @@
             return this;
         }
 
+        /** Sets the associated notification's category. */
+        public Builder setNotificationCategory(String notificationCategory) {
+            mNotificationCategory = notificationCategory;
+            return this;
+        }
+
         /** Sets the associated notification's data URI. */
         public Builder setNotificationDataUri(Uri notificationDataUri) {
             mNotificationDataUri = notificationDataUri;
@@ -342,6 +356,7 @@
         mIsImportantConversation = in.readBoolean();
         mNotificationKey = in.readString();
         mNotificationContent = in.readCharSequence();
+        mNotificationCategory = in.readString();
         mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader());
         mIntent = in.readParcelable(Intent.class.getClassLoader());
         mNotificationTimestamp = in.readLong();
@@ -367,6 +382,7 @@
         dest.writeBoolean(mIsImportantConversation);
         dest.writeString(mNotificationKey);
         dest.writeCharSequence(mNotificationContent);
+        dest.writeString(mNotificationCategory);
         dest.writeParcelable(mNotificationDataUri, flags);
         dest.writeParcelable(mIntent, flags);
         dest.writeLong(mNotificationTimestamp);
diff --git a/packages/SystemUI/res/drawable/ic_phone_missed.xml b/packages/SystemUI/res/drawable/ic_phone_missed.xml
new file mode 100644
index 0000000..72e67d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_phone_missed.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2021 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5L11,4.5L11,3L5,3v6h1.5L6.5,5.5zM23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml
index 32314d2..30519ae 100644
--- a/packages/SystemUI/res/drawable/people_space_content_background.xml
+++ b/packages/SystemUI/res/drawable/people_space_content_background.xml
@@ -15,6 +15,6 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
-    <solid android:color="?android:attr/colorControlHighlight" />
+    <solid android:color="?android:attr/colorBackground" />
     <corners android:radius="@dimen/people_space_image_radius" />
 </shape>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 70e8b89..4b95c16 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2840,6 +2840,8 @@
     <string name="empty_user_name" translatable="false">Your friend</string>
     <!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] -->
     <string name="empty_status" translatable="false">Their status</string>
+    <!-- Default text for missed call notifications [CHAR LIMIT=30] -->
+    <string name="missed_call" translatable="false">Missed call</string>
 
     <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
     [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index cd1131b..5dda23e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.people;
 
+import static android.app.Notification.CATEGORY_MISSED_CALL;
 import static android.app.Notification.EXTRA_MESSAGES;
 import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
@@ -189,7 +190,7 @@
             tiles.addAll(recentTiles);
         }
 
-        tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
+        tiles = augmentTilesFromVisibleNotifications(context, tiles, notificationEntryManager);
         return tiles;
     }
 
@@ -357,8 +358,8 @@
                 && storedUserId == userId;
     }
 
-    static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
-            NotificationEntryManager notificationEntryManager) {
+    static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
+            List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
         if (notificationEntryManager == null) {
             Log.w(TAG, "NotificationEntryManager is null");
             return tiles;
@@ -374,12 +375,13 @@
         }
         return tiles
                 .stream()
-                .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+                .map(entry -> augmentTileFromVisibleNotifications(
+                        context, entry, visibleNotifications))
                 .collect(Collectors.toList());
     }
 
-    static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
-            Map<String, NotificationEntry> visibleNotifications) {
+    static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context,
+            PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
         String shortcutId = tile.getId();
         String packageName = tile.getPackageName();
         int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
@@ -389,7 +391,7 @@
             return tile;
         }
         if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
-        return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+        return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
     }
 
     /**
@@ -408,7 +410,7 @@
         }
         if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
             if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
-            storedTile = augmentTileFromNotification(storedTile, sbn);
+            storedTile = augmentTileFromNotification(context, storedTile, sbn);
         } else {
             if (DEBUG) {
                 Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -418,23 +420,40 @@
                     .setNotificationKey(null)
                     .setNotificationContent(null)
                     .setNotificationDataUri(null)
+                    .setNotificationCategory(null)
                     .build();
         }
         updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
     }
 
-    static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+    static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
             StatusBarNotification sbn) {
-        Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
-        if (message == null) {
-            if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+        Notification notification = sbn.getNotification();
+        if (notification == null) {
+            if (DEBUG) Log.d(TAG, "Notification is null");
             return tile;
         }
+        boolean isMissedCall = Objects.equals(notification.category, CATEGORY_MISSED_CALL);
+        Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(notification);
+
+        if (!isMissedCall && message == null) {
+            if (DEBUG) Log.d(TAG, "Notification has no content");
+            return tile;
+        }
+
+        // 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());
+        CharSequence content = (isMissedCall && !hasMessageText)
+                ? context.getString(R.string.missed_call) : message.getText();
+        Uri dataUri = message != null ? message.getDataUri() : null;
+
         return tile
                 .toBuilder()
                 .setNotificationKey(sbn.getKey())
-                .setNotificationContent(message.getText())
-                .setNotificationDataUri(message.getDataUri())
+                .setNotificationCategory(notification.category)
+                .setNotificationContent(content)
+                .setNotificationDataUri(dataUri)
                 .build();
     }
 
@@ -462,6 +481,11 @@
      * content, then birthdays, then the most recent status, and finally last interaction.
      */
     private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) {
+        if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
+            if (DEBUG) Log.d(TAG, "Create missed call view");
+            return createMissedCallRemoteViews(context, tile);
+        }
+
         if (tile.getNotificationKey() != null) {
             if (DEBUG) Log.d(TAG, "Create notification view");
             return createNotificationRemoteViews(context, tile);
@@ -630,6 +654,16 @@
         return views;
     }
 
+    private static RemoteViews createMissedCallRemoteViews(Context context,
+            PeopleSpaceTile tile) {
+        RemoteViews views = new RemoteViews(
+                context.getPackageName(), R.layout.people_space_small_avatar_tile);
+        views.setTextViewText(R.id.status, tile.getNotificationContent());
+        views.setImageViewResource(R.id.status_defined_icon, R.drawable.ic_phone_missed);
+        views.setBoolean(R.id.content_background, "setClipToOutline", true);
+        return views;
+    }
+
     private static RemoteViews createNotificationRemoteViews(Context context,
             PeopleSpaceTile tile) {
         RemoteViews views = new RemoteViews(
@@ -715,8 +749,7 @@
     /** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */
     @VisibleForTesting
     public static Notification.MessagingStyle.Message getLastMessagingStyleMessage(
-            StatusBarNotification sbn) {
-        Notification notification = sbn.getNotification();
+            Notification notification) {
         if (notification == null) {
             return null;
         }
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 bee9889..9e5c786 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -29,7 +29,6 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
-import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -124,8 +123,6 @@
      */
     public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
-        RemoteViews views = new RemoteViews(
-                mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
         if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
         boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
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 d79155c..c8e9396 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.people;
 
+import static android.app.Notification.CATEGORY_MISSED_CALL;
 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
 import static android.app.people.ConversationStatus.ACTIVITY_GAME;
 import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
@@ -113,6 +114,7 @@
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
     private static final String GAME_DESCRIPTION = "Playing a game!";
+    private static final CharSequence MISSED_CALL = "Custom missed call message";
     private static final String NAME = "username";
     private static final Person PERSON = new Person.Builder()
             .setName("name")
@@ -346,7 +348,7 @@
                 .build();
 
         Notification.MessagingStyle.Message lastMessage =
-                PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
+                PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification());
 
         assertThat(lastMessage).isNull();
     }
@@ -447,7 +449,7 @@
                 .build();
 
         Notification.MessagingStyle.Message lastMessage =
-                PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
+                PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification());
 
         assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
     }
@@ -465,7 +467,7 @@
                         .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromNotification(tile, sbn);
+                .augmentTileFromNotification(mContext, tile, sbn);
 
         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
     }
@@ -483,9 +485,8 @@
                         .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromNotification(tile, sbn);
+                .augmentTileFromNotification(mContext, tile, sbn);
 
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
         assertThat(actual.getNotificationContent()).isEqualTo(null);
     }
 
@@ -498,7 +499,7 @@
                         .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromVisibleNotifications(tile,
+                .augmentTileFromVisibleNotifications(mContext, tile,
                         Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
 
         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
@@ -513,7 +514,7 @@
                         .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromVisibleNotifications(tile,
+                .augmentTileFromVisibleNotifications(mContext, tile,
                         Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
 
         assertThat(actual.getNotificationContent()).isEqualTo(null);
@@ -528,7 +529,8 @@
                         .setUid(0)
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
-                .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+                .augmentTilesFromVisibleNotifications(
+                        mContext, List.of(tile), mNotificationEntryManager);
 
         assertThat(actualList.size()).isEqualTo(1);
         assertThat(actualList.get(0).getNotificationContent().toString())
@@ -552,7 +554,7 @@
                         .setUid(0)
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
-                .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+                .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2),
                         mNotificationEntryManager);
 
         assertThat(actualList.size()).isEqualTo(2);
@@ -763,6 +765,33 @@
     }
 
     @Test
+    public void testCreateRemoteViewsWithMissedCallNotification() {
+        PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
+                .setNotificationDataUri(null)
+                .setNotificationCategory(CATEGORY_MISSED_CALL)
+                .setNotificationContent(MISSED_CALL)
+                .build();
+        RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
+                tileWithMissedCallNotification, 0);
+        View result = views.apply(mContext, null);
+
+        TextView name = (TextView) result.findViewById(R.id.name);
+        assertEquals(name.getText(), NAME);
+        // Has availability.
+        View availability = result.findViewById(R.id.availability);
+        assertEquals(View.GONE, availability.getVisibility());
+        // Has new story.
+        View personIcon = result.findViewById(R.id.person_icon_only);
+        View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
+        assertEquals(View.VISIBLE, personIcon.getVisibility());
+        assertEquals(View.GONE, personIconWithStory.getVisibility());
+        // Has status.
+        TextView statusContent = (TextView) result.findViewById(R.id.status);
+        assertEquals(statusContent.getText(), MISSED_CALL);
+    }
+
+
+    @Test
     public void testCreateRemoteViewsWithNotificationTemplate() {
         PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
                 .setNotificationDataUri(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 9470141..1c8324c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.people.widget;
 
+import static android.app.Notification.CATEGORY_MISSED_CALL;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
@@ -167,7 +168,8 @@
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        StatusBarNotification sbn = createNotification(
+                OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
                 .setId(1));
@@ -207,7 +209,8 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
-                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setNotification(createMessagingStyleNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbnWithoutPackageName)
@@ -256,7 +259,8 @@
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        StatusBarNotification sbn = createNotification(
+                OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
                 .setId(1));
@@ -276,7 +280,8 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
-                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setNotification(createMessagingStyleNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
                 .setPkg(TEST_PACKAGE_B)
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -295,7 +300,8 @@
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        StatusBarNotification sbn = createNotification(
+                OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
                 .setId(1));
@@ -315,7 +321,8 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
-                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setNotification(createMessagingStyleNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
                 .setPkg(TEST_PACKAGE_B)
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -337,7 +344,8 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
@@ -367,7 +375,8 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
@@ -400,7 +409,8 @@
 
         PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
@@ -417,33 +427,52 @@
     }
 
     @Test
-    public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
+    public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
             throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
-        Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
-                .setContentTitle("TEST_TITLE")
-                .setContentText("TEST_TEXT")
-                .setShortcutId(SHORTCUT_ID)
-                .build();
-        StatusBarNotification sbn = new SbnBuilder()
-                .setNotification(notificationWithoutMessagingStyle)
-                .setPkg(TEST_PACKAGE_A)
-                .setUid(0)
-                .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(sbn)
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ true))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, times(1))
                 .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
                         mBundleArgumentCaptor.capture());
-        Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
-        assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
-                .isEqualTo(PERSON_TILE);
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tile.getNotificationContent())
+                .isEqualTo(mContext.getString(R.string.missed_call));
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
+            throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createNotification(
+                        SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
                 any());
     }
@@ -453,7 +482,8 @@
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+        StatusBarNotification sbn = createNotification(
+                SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
                 .setId(1));
@@ -483,21 +513,29 @@
         return convo;
     }
 
-    private Notification createMessagingStyleNotification(String shortcutId) {
-        return new Notification.Builder(mContext)
+    private Notification createMessagingStyleNotification(String shortcutId,
+            boolean isMessagingStyle, boolean isMissedCall) {
+        Notification.Builder builder = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
-                .setShortcutId(shortcutId)
-                .setStyle(new Notification.MessagingStyle(PERSON)
-                        .addMessage(
-                                new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
-                                        PERSON))
-                )
-                .build();
+                .setShortcutId(shortcutId);
+        if (isMessagingStyle) {
+            builder.setStyle(new Notification.MessagingStyle(PERSON)
+                    .addMessage(
+                            new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                    PERSON))
+            );
+        }
+        if (isMissedCall) {
+            builder.setCategory(CATEGORY_MISSED_CALL);
+        }
+        return builder.build();
     }
 
-    private StatusBarNotification createConversationNotification(String shortcutId) {
-        Notification notification = createMessagingStyleNotification(shortcutId);
+    private StatusBarNotification createNotification(String shortcutId,
+            boolean isMessagingStyle, boolean isMissedCall) {
+        Notification notification = createMessagingStyleNotification(
+                shortcutId, isMessagingStyle, isMissedCall);
         return new SbnBuilder()
                 .setNotification(notification)
                 .setPkg(TEST_PACKAGE_A)