diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index e870597bf6d1e..f60e42f6bfab0 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -28,7 +28,6 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; -import android.service.notification.StatusBarNotification; /** * The People Space tile contains all relevant information to render a tile in People Space: namely @@ -48,7 +47,10 @@ public class PeopleSpaceTile implements Parcelable { private long mLastInteractionTimestamp; private boolean mIsImportantConversation; private boolean mIsHiddenConversation; - private StatusBarNotification mNotification; + private String mNotificationKey; + // TODO: add mNotificationTimestamp + private CharSequence mNotificationContent; + private Uri mNotificationDataUri; private Intent mIntent; // TODO: add a List of the Status objects once created @@ -62,7 +64,9 @@ public class PeopleSpaceTile implements Parcelable { mLastInteractionTimestamp = b.mLastInteractionTimestamp; mIsImportantConversation = b.mIsImportantConversation; mIsHiddenConversation = b.mIsHiddenConversation; - mNotification = b.mNotification; + mNotificationKey = b.mNotificationKey; + mNotificationContent = b.mNotificationContent; + mNotificationDataUri = b.mNotificationDataUri; mIntent = b.mIntent; } @@ -112,10 +116,18 @@ public class PeopleSpaceTile implements Parcelable { /** * If a notification is currently active that maps to the relevant shortcut ID, provides the - * {@link StatusBarNotification} associated. + * associated notification's key. */ - public StatusBarNotification getNotification() { - return mNotification; + public String getNotificationKey() { + return mNotificationKey; + } + + public CharSequence getNotificationContent() { + return mNotificationContent; + } + + public Uri getNotificationDataUri() { + return mNotificationDataUri; } /** @@ -129,6 +141,22 @@ public class PeopleSpaceTile implements Parcelable { return mIntent; } + /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */ + public PeopleSpaceTile.Builder toBuilder() { + PeopleSpaceTile.Builder builder = + new PeopleSpaceTile.Builder(mId, mUserName.toString(), mUserIcon, mIntent); + builder.setContactUri(mContactUri); + builder.setUid(mUid); + builder.setPackageName(mPackageName); + builder.setLastInteractionTimestamp(mLastInteractionTimestamp); + builder.setIsImportantConversation(mIsImportantConversation); + builder.setIsHiddenConversation(mIsHiddenConversation); + builder.setNotificationKey(mNotificationKey); + builder.setNotificationContent(mNotificationContent); + builder.setNotificationDataUri(mNotificationDataUri); + return builder; + } + /** Builder to create a {@link PeopleSpaceTile}. */ public static class Builder { private String mId; @@ -140,7 +168,9 @@ public class PeopleSpaceTile implements Parcelable { private long mLastInteractionTimestamp; private boolean mIsImportantConversation; private boolean mIsHiddenConversation; - private StatusBarNotification mNotification; + private String mNotificationKey; + private CharSequence mNotificationContent; + private Uri mNotificationDataUri; private Intent mIntent; /** Builder for use only if a shortcut is not available for the tile. */ @@ -214,9 +244,21 @@ public class PeopleSpaceTile implements Parcelable { return this; } - /** Sets the associated notification. */ - public Builder setNotification(StatusBarNotification notification) { - mNotification = notification; + /** Sets the associated notification's key. */ + public Builder setNotificationKey(String notificationKey) { + mNotificationKey = notificationKey; + return this; + } + + /** Sets the associated notification's content. */ + public Builder setNotificationContent(CharSequence notificationContent) { + mNotificationContent = notificationContent; + return this; + } + + /** Sets the associated notification's data URI. */ + public Builder setNotificationDataUri(Uri notificationDataUri) { + mNotificationDataUri = notificationDataUri; return this; } @@ -242,7 +284,9 @@ public class PeopleSpaceTile implements Parcelable { mLastInteractionTimestamp = in.readLong(); mIsImportantConversation = in.readBoolean(); mIsHiddenConversation = in.readBoolean(); - mNotification = in.readParcelable(StatusBarNotification.class.getClassLoader()); + mNotificationKey = in.readString(); + mNotificationContent = in.readCharSequence(); + mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); mIntent = in.readParcelable(Intent.class.getClassLoader()); } @@ -259,9 +303,11 @@ public class PeopleSpaceTile implements Parcelable { dest.writeInt(mUid); dest.writeString(mPackageName); dest.writeLong(mLastInteractionTimestamp); - dest.writeParcelable(mNotification, flags); dest.writeBoolean(mIsImportantConversation); dest.writeBoolean(mIsHiddenConversation); + dest.writeString(mNotificationKey); + dest.writeCharSequence(mNotificationContent); + dest.writeParcelable(mNotificationDataUri, flags); dest.writeParcelable(mIntent, flags); } diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 7552ec404c137..1d830030fc5b5 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -import android.app.Notification; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -35,8 +34,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; -import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -186,17 +183,34 @@ public class PeopleSpaceTileTest { } @Test - public void testNotification() { - Notification notification = new Notification.Builder(mContext, "test").build(); - StatusBarNotification sbn = new StatusBarNotification("pkg" /* pkg */, "pkg" /* opPkg */, - 1 /* id */, "" /* tag */, 0 /* uid */, 0 /* initialPid */, 0 /* score */, - notification, UserHandle.CURRENT, 0 /* postTime */); + public void testNotificationKey() { PeopleSpaceTile tile = new PeopleSpaceTile .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) - .setNotification(sbn) + .setNotificationKey("test") .build(); - assertThat(tile.getNotification()).isEqualTo(sbn); + assertThat(tile.getNotificationKey()).isEqualTo("test"); + } + + @Test + public void testNotificationContent() { + PeopleSpaceTile tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setNotificationContent("test") + .build(); + + assertThat(tile.getNotificationContent()).isEqualTo("test"); + } + + @Test + public void testNotificationDataUri() { + PeopleSpaceTile tile = + new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build(), + mLauncherApps) + .setNotificationDataUri(Uri.parse("test")) + .build(); + + assertThat(tile.getNotificationDataUri()).isEqualTo(Uri.parse("test")); } @Test diff --git a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml index f47483054ea34..085e95540f6af 100644 --- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml +++ b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml @@ -82,16 +82,25 @@ android:ellipsize="end" /> - + android:layout_height="match_parent"> + + + \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 4a633c320999e..e43eb9f66fe12 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -136,8 +136,12 @@ public class PeopleSpaceActivity extends Activity { editor.putString(String.valueOf(mAppWidgetId), tile.getId()); editor.commit(); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile); + appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options); int[] widgetIds = appWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); + // TODO: Populate new widget with existing conversation notification, if there is any. PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager, mNotificationManager); finishActivity(); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index bc9500a68a525..d682379d379a1 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -16,7 +16,10 @@ package com.android.systemui.people; +import static android.app.Notification.EXTRA_MESSAGES; + import android.app.INotificationManager; +import android.app.Notification; import android.app.PendingIntent; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; @@ -26,7 +29,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.LauncherApps; -import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; @@ -34,20 +36,28 @@ import android.graphics.drawable.Drawable; import android.icu.text.MeasureFormat; import android.icu.util.Measure; import android.icu.util.MeasureUnit; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; import android.os.ServiceManager; import android.os.UserHandle; +import android.preference.PreferenceManager; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; +import android.service.notification.StatusBarNotification; import android.util.Log; +import android.view.View; import android.widget.RemoteViews; -import androidx.preference.PreferenceManager; - +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.systemui.R; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; import java.time.Duration; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -63,6 +73,13 @@ public class PeopleSpaceUtils { private static final int DAYS_IN_A_WEEK = 7; private static final int MIN_HOUR = 1; private static final int ONE_DAY = 1; + public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile"; + + /** Represents whether {@link StatusBarNotification} was posted or removed. */ + public enum NotificationAction { + POSTED, + REMOVED + } /** Returns a list of map entries corresponding to user's conversations. */ public static List> getTiles( @@ -93,75 +110,42 @@ public class PeopleSpaceUtils { /** Updates {@code appWidgetIds} with their associated conversation stored. */ public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds, AppWidgetManager appWidgetManager, INotificationManager notificationManager) { - PackageManager mPackageManager = context.getPackageManager(); - IPeopleManager mPeopleManager = IPeopleManager.Stub.asInterface( + IPeopleManager peopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); - LauncherApps mLauncherApps = context.getSystemService(LauncherApps.class); + LauncherApps launcherApps = context.getSystemService(LauncherApps.class); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - Intent activityIntent = new Intent(context, LaunchConversationActivity.class); - activityIntent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_NO_HISTORY - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); try { - List> shortcutInfos = - PeopleSpaceUtils.getTiles( - context, notificationManager, - mPeopleManager, mLauncherApps); + List> tiles = + PeopleSpaceUtils.getTiles(context, notificationManager, + peopleManager, launcherApps); + for (int appWidgetId : appWidgetIds) { String shortcutId = sp.getString(String.valueOf(appWidgetId), null); if (DEBUG) { Log.d(TAG, "Set widget: " + appWidgetId + " with shortcut ID: " + shortcutId); } - - Optional> entry = shortcutInfos.stream().filter( + Optional> entry = tiles.stream().filter( e -> e.getValue().getId().equals(shortcutId)).findFirst(); if (!entry.isPresent() || shortcutId == null) { if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); //TODO: Delete app widget id when crash is fixed (b/175486868) continue; } - PeopleSpaceTile tile = entry.get().getValue(); - RemoteViews views = new RemoteViews(context.getPackageName(), - getLayout(tile)); + PeopleSpaceTile tile = + augmentTileFromStorage(entry.get().getValue(), appWidgetManager, + appWidgetId); - String status = PeopleSpaceUtils.getLastInteractionString(context, - entry.get().getKey()); - views.setTextViewText(R.id.status, status); - views.setTextViewText(R.id.name, tile.getUserName().toString()); + RemoteViews views = createRemoteViews(context, tile, entry.get().getKey(), + appWidgetId); - activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); - activityIntent.putExtra( - PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); - activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); - views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity( - context, - appWidgetId, - activityIntent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE)); - - views.setImageViewBitmap( - R.id.package_icon, - PeopleSpaceUtils.convertDrawableToBitmap( - mPackageManager.getApplicationIcon(tile.getPackageName()) - ) - ); - views.setImageViewIcon(R.id.person_icon, tile.getUserIcon()); // Tell the AppWidgetManager to perform an update on the current app widget. appWidgetManager.updateAppWidget(appWidgetId, views); } } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversations to set tiles"); + Log.e(TAG, "Exception updating single conversation widgets: " + e); } } - /** Returns the layout ID for the {@code tile}. */ - private static int getLayout(PeopleSpaceTile tile) { - return tile.getNotification() == null ? R.layout.people_space_large_avatar_tile : - R.layout.people_space_small_avatar_tile; - } - /** Returns a list sorted by ascending last interaction time from {@code stream}. */ private static List> getSortedTiles( IPeopleManager peopleManager, Stream stream) { @@ -172,6 +156,152 @@ public class PeopleSpaceUtils { .collect(Collectors.toList()); } + /** Augment {@link PeopleSpaceTile} with fields from stored tile. */ + @VisibleForTesting + static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile, + AppWidgetManager appWidgetManager, int appWidgetId) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + if (storedTile == null) { + return tile; + } + return tile.toBuilder() + .setNotificationKey(storedTile.getNotificationKey()) + .setNotificationContent(storedTile.getNotificationContent()) + .setNotificationDataUri(storedTile.getNotificationDataUri()) + .build(); + } + + /** If incoming notification changed tile, store the changes in the tile options. */ + public static void storeNotificationChange(StatusBarNotification sbn, + NotificationAction notificationAction, AppWidgetManager appWidgetManager, + int appWidgetId) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { + if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); + Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn); + if (message == null) { + if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping."); + return; + } + storedTile = storedTile + .toBuilder() + .setNotificationKey(sbn.getKey()) + .setNotificationContent(message.getText()) + .setNotificationDataUri(message.getDataUri()) + .build(); + } else { + if (DEBUG) { + Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); + } + storedTile = storedTile + .toBuilder() + .setNotificationKey(null) + .setNotificationContent(null) + .setNotificationDataUri(null) + .build(); + } + Bundle newOptions = new Bundle(); + newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, storedTile); + appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions); + } + + private static RemoteViews createRemoteViews(Context context, PeopleSpaceTile tile, + long lastInteraction, int appWidgetId) throws Exception { + // TODO: If key is null or if text and data uri are null. + if (tile.getNotificationKey() == null) { + return createLastInteractionRemoteViews(context, tile, lastInteraction, appWidgetId); + } + return createNotificationRemoteViews(context, tile, lastInteraction, appWidgetId); + } + + private static RemoteViews createLastInteractionRemoteViews(Context context, + PeopleSpaceTile tile, long lastInteraction, int appWidgetId) + throws Exception { + RemoteViews views = new RemoteViews( + context.getPackageName(), R.layout.people_space_large_avatar_tile); + String status = PeopleSpaceUtils.getLastInteractionString( + context, lastInteraction); + views.setTextViewText(R.id.status, status); + + views = setCommonRemoteViewsFields(context, views, tile, appWidgetId); + return views; + } + + private static RemoteViews createNotificationRemoteViews(Context context, + PeopleSpaceTile tile, long lastInteraction, int appWidgetId) + throws Exception { + + RemoteViews views = new RemoteViews( + context.getPackageName(), R.layout.people_space_small_avatar_tile); + Uri image = tile.getNotificationDataUri(); + if (image != null) { + //TODO: Use NotificationInlineImageCache + views.setImageViewUri(R.id.image, image); + views.setViewVisibility(R.id.image, View.VISIBLE); + views.setViewVisibility(R.id.content, View.GONE); + } else { + views.setTextViewText(R.id.content, tile.getNotificationContent()); + views.setViewVisibility(R.id.content, View.VISIBLE); + views.setViewVisibility(R.id.image, View.GONE); + } + + views = setCommonRemoteViewsFields(context, views, tile, appWidgetId); + return views; + } + + private static RemoteViews setCommonRemoteViewsFields( + Context context, RemoteViews views, PeopleSpaceTile tile, int appWidgetId) + throws Exception { + views.setTextViewText(R.id.name, tile.getUserName().toString()); + views.setImageViewBitmap( + R.id.package_icon, + PeopleSpaceUtils.convertDrawableToBitmap( + context.getPackageManager().getApplicationIcon(tile.getPackageName()) + ) + ); + views.setImageViewIcon(R.id.person_icon, tile.getUserIcon()); + + Intent activityIntent = new Intent(context, LaunchConversationActivity.class); + activityIntent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_ACTIVITY_NO_HISTORY + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); + activityIntent.putExtra( + PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); + activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); + views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity( + context, + appWidgetId, + activityIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE)); + return views; + } + + /** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */ + public static Notification.MessagingStyle.Message getLastMessagingStyleMessage( + StatusBarNotification sbn) { + Notification notification = sbn.getNotification(); + if (notification == null) { + return null; + } + if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle()) + && notification.extras != null) { + final Parcelable[] messages = notification.extras.getParcelableArray(EXTRA_MESSAGES); + if (!ArrayUtils.isEmpty(messages)) { + List sortedMessages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); + sortedMessages.sort(Collections.reverseOrder( + Comparator.comparing(Notification.MessagingStyle.Message::getTimestamp))); + return sortedMessages.get(0); + } + } + return null; + } + /** Returns the last interaction time with the user specified by {@code PeopleSpaceTile}. */ private static Long getLastInteraction(IPeopleManager peopleManager, PeopleSpaceTile tile) { 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 ac32e193d9f64..ad6046ba489d0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -21,8 +21,10 @@ import android.app.NotificationChannel; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; +import android.content.SharedPreferences; import android.os.ServiceManager; import android.os.UserHandle; +import android.preference.PreferenceManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -35,6 +37,8 @@ import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Singleton; @@ -84,7 +88,6 @@ public class PeopleSpaceWidgetManager { int[] widgetIds = mAppWidgetService.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); - if (widgetIds.length == 0) { if (DEBUG) Log.d(TAG, "no widgets to update"); return; @@ -93,6 +96,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; + if (showSingleConversation) { PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager, mNotificationManager); @@ -106,6 +110,41 @@ public class PeopleSpaceWidgetManager { } } + /** + * Check if any existing People tiles match the incoming notification change, and store the + * change in the tile if so. + */ + public void storeNotificationChange(StatusBarNotification sbn, + PeopleSpaceUtils.NotificationAction notificationAction) { + if (DEBUG) Log.d(TAG, "storeNotificationChange called"); + boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; + if (!showSingleConversation) { + return; + } + try { + int[] widgetIds = mAppWidgetService.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class) + ); + if (widgetIds.length == 0) { + return; + } + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + for (int widgetId : widgetIds) { + String shortcutId = sp.getString(String.valueOf(widgetId), null); + if (!Objects.equals(sbn.getShortcutId(), shortcutId)) { + continue; + } + if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); + PeopleSpaceUtils.storeNotificationChange( + sbn, notificationAction, mAppWidgetManager, widgetId); + } + } catch (Exception e) { + Log.e(TAG, "Exception: " + e); + } + } + /** * Attaches the manager to the pipeline, making it ready to receive events. Should only be * called once. @@ -120,6 +159,7 @@ public class PeopleSpaceWidgetManager { public void onNotificationPosted( StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted"); + storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED); updateWidgets(); } @@ -129,6 +169,7 @@ public class PeopleSpaceWidgetManager { NotificationListenerService.RankingMap rankingMap ) { if (DEBUG) Log.d(TAG, "onNotificationRemoved"); + storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); updateWidgets(); } @@ -138,6 +179,7 @@ public class PeopleSpaceWidgetManager { NotificationListenerService.RankingMap rankingMap, int reason) { if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason); + storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); updateWidgets(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java new file mode 100644 index 0000000000000..e934f840169f5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 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. + */ + +package com.android.systemui.people; + +import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.Person; +import android.app.people.PeopleSpaceTile; +import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.provider.Settings; +import android.service.notification.StatusBarNotification; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.internal.appwidget.IAppWidgetService; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.SbnBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class PeopleSpaceUtilsTest extends SysuiTestCase { + + private static final int WIDGET_ID_WITH_SHORTCUT = 1; + private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2; + private static final String SHORTCUT_ID = "101"; + private static final String NOTIFICATION_KEY = "notification_key"; + private static final String NOTIFICATION_CONTENT = "notification_content"; + + @Mock + private NotificationListener mListenerService; + @Mock + private IAppWidgetService mIAppWidgetService; + @Mock + private AppWidgetManager mAppWidgetManager; + + private static Icon sIcon = Icon.createWithResource("package", R.drawable.ic_android); + private static Uri sUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("something") + .path("test") + .build(); + private static Person sPerson = new Person.Builder() + .setName("name") + .setKey("abc") + .setUri("uri") + .setBot(false) + .build(); + private static PeopleSpaceTile sPeopleSpaceTile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID, "username", sIcon, new Intent()) + .setNotificationKey(NOTIFICATION_KEY) + .setNotificationContent(NOTIFICATION_CONTENT) + .setNotificationDataUri(sUri) + .build(); + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + Bundle options = new Bundle(); + options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, sPeopleSpaceTile); + + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) + .thenReturn(new Bundle()); + } + + @Test + public void testGetLastMessagingStyleMessageNoMessage() { + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID) + .build(); + StatusBarNotification sbn = new SbnBuilder() + .setNotification(notification) + .build(); + + Notification.MessagingStyle.Message lastMessage = + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + + assertThat(lastMessage).isNull(); + } + + @Test + public void testGetLastMessagingStyleMessage() { + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID) + .setStyle(new Notification.MessagingStyle(sPerson) + .addMessage(new Notification.MessagingStyle.Message("text1", 0, sPerson)) + .addMessage(new Notification.MessagingStyle.Message("text2", 20, sPerson)) + .addMessage(new Notification.MessagingStyle.Message("text3", 10, sPerson)) + ) + .build(); + StatusBarNotification sbn = new SbnBuilder() + .setNotification(notification) + .build(); + + Notification.MessagingStyle.Message lastMessage = + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + + assertThat(lastMessage.getText()).isEqualTo("text2"); + } + + @Test + public void testAugmentTileFromStorageWithNotification() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder("id", "userName", sIcon, new Intent()) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT); + + assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); + assertThat(actual.getNotificationDataUri()).isEqualTo(sUri); + } + + @Test + public void testAugmentTileFromStorageWithoutNotification() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder("id", "userName", sIcon, new Intent()) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT); + + assertThat(actual.getNotificationKey()).isEqualTo(null); + assertThat(actual.getNotificationKey()).isEqualTo(null); + assertThat(actual.getNotificationDataUri()).isEqualTo(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 019424cd48f03..cd4f51ee9c924 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 @@ -19,6 +19,8 @@ package com.android.systemui.people.widget; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -30,15 +32,24 @@ import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; import android.app.INotificationManager; +import android.app.Notification; import android.app.NotificationChannel; +import android.app.Person; +import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; +import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.widget.RemoteViews; @@ -46,9 +57,11 @@ import androidx.preference.PreferenceManager; import androidx.test.filters.SmallTest; import com.android.internal.appwidget.IAppWidgetService; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.collection.NoManSimulator; import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -79,6 +92,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final int WIDGET_ID_WITH_SHORTCUT = 1; private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2; private static final String SHORTCUT_ID = "101"; + private static final String OTHER_SHORTCUT_ID = "102"; + private static final String NOTIFICATION_KEY = "notification_key"; + private static final String NOTIFICATION_CONTENT = "notification_content"; private PeopleSpaceWidgetManager mManager; @@ -94,6 +110,26 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Captor private ArgumentCaptor mListenerCaptor; + private static Icon sIcon = Icon.createWithResource("package", R.drawable.ic_android); + private static Uri sUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("something") + .path("test") + .build(); + private static Person sPerson = new Person.Builder() + .setName("name") + .setKey("abc") + .setUri("uri") + .setBot(false) + .build(); + private static PeopleSpaceTile sPeopleSpaceTile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID, "username", sIcon, new Intent()) + .setNotificationKey(NOTIFICATION_KEY) + .setNotificationContent(NOTIFICATION_CONTENT) + .setNotificationDataUri(sUri) + .build(); + private final NoManSimulator mNoMan = new NoManSimulator(); private final FakeSystemClock mClock = new FakeSystemClock(); @@ -110,6 +146,18 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mNoMan.addListener(serviceListener); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID); + editor.commit(); + Bundle options = new Bundle(); + options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, sPeopleSpaceTile); + + when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) + .thenReturn(new Bundle()); } @Test @@ -123,7 +171,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPkg(TEST_PACKAGE_A)); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(1)).getAppWidgetIds(any()); verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt()); } @@ -140,7 +187,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPkg(TEST_PACKAGE_A)); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(1)).getAppWidgetIds(any()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class)); verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt()); } @@ -156,7 +202,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPkg(TEST_PACKAGE_A)); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(1)).getAppWidgetIds(any()); verify(mIAppWidgetService, times(1)) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(), @@ -171,17 +216,12 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mINotificationManager.getConversations(true)).thenReturn( new ParceledListSlice(getConversationWithShortcutId())); int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - SharedPreferences.Editor editor = sp.edit(); - editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID); - editor.commit(); when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setPkg(TEST_PACKAGE_A) .setId(1)); - verify(mIAppWidgetService, times(1)).getAppWidgetIds(any()); verify(mIAppWidgetService, never()) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), @@ -198,10 +238,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mINotificationManager.getConversations(true)).thenReturn( new ParceledListSlice(getConversationWithShortcutId())); int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - SharedPreferences.Editor editor = sp.edit(); - editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID); - editor.commit(); when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -212,7 +248,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPkg(TEST_PACKAGE_B) .setId(2)); - verify(mIAppWidgetService, times(2)).getAppWidgetIds(any()); verify(mIAppWidgetService, never()) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), @@ -234,7 +269,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPkg(TEST_PACKAGE_B) .setId(2)); - verify(mIAppWidgetService, times(2)).getAppWidgetIds(any()); verify(mIAppWidgetService, times(2)) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), @@ -252,7 +286,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mClock.advanceTime(4); NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); - verify(mIAppWidgetService, times(2)).getAppWidgetIds(any()); verify(mIAppWidgetService, times(2)) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), @@ -290,13 +323,124 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(1)).getAppWidgetIds(any()); verify(mIAppWidgetService, times(1)) .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class)); } + @Test + public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + when(mINotificationManager.getConversations(true)).thenReturn( + new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbn) + .setPkg(TEST_PACKAGE_A) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + } + + @Test + public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + when(mINotificationManager.getConversations(true)).thenReturn( + new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbn) + .setPkg(TEST_PACKAGE_A) + .setId(1)); + mClock.advanceTime(4); + NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(anyInt(), any()); + } + + @Test + public void testUpdateNotificationPostedIfExistingTile() throws RemoteException { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + when(mINotificationManager.getConversations(true)).thenReturn( + new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbn) + .setPkg(TEST_PACKAGE_A) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + } + + @Test + public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile() + throws RemoteException { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + when(mINotificationManager.getConversations(true)).thenReturn( + new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + Notification notification = new Notification.Builder(mContext) + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID) + .build(); + StatusBarNotification sbn = new SbnBuilder() + .setNotification(notification) + .build(); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbn) + .setPkg(TEST_PACKAGE_A) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + } + + @Test + public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + when(mINotificationManager.getConversations(true)).thenReturn( + new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbn) + .setPkg(TEST_PACKAGE_A) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(2)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + } + /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */ private List getConversationWithShortcutId() { List convos = new ArrayList<>(); @@ -306,4 +450,18 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { convos.add(convo1); return convos; } + + private StatusBarNotification createConversationNotification(String shortcutId) { + Notification notification = new Notification.Builder(mContext) + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(shortcutId) + .setStyle(new Notification.MessagingStyle(sPerson) + .addMessage(new Notification.MessagingStyle.Message("text3", 10, sPerson)) + ) + .build(); + return new SbnBuilder() + .setNotification(notification) + .build(); + } }