diff options
| author | 2021-03-01 19:24:13 +0000 | |
|---|---|---|
| committer | 2021-03-05 13:31:50 +0000 | |
| commit | f0fd893b6ef42e66f947169a5e76206722cd6ab7 (patch) | |
| tree | 93af7d045d1a14dddebd05a1bd99543ac5195a38 | |
| parent | b8d3b7d210dee38195a18631194b7f1bcbde24db (diff) | |
Register conversation listeners
Newly registered after reboots and after being added to Launcher. Stored
in-memory to avoid duplicate registration. Refactors code out of
PeopleSpaceUtils to better handle race conditions
Test: PeopleSpaceWidgetManagerTest
Bug: 178792356
Change-Id: Ia1bc2671cb29696e537495ff8c1adabb873f0713
5 files changed, 523 insertions, 258 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 2ea8657f88e5..a69ec278be91 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -23,7 +23,6 @@ import android.app.Activity; import android.app.INotificationManager; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; -import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -34,12 +33,10 @@ import android.provider.Settings; import android.util.Log; import android.view.ViewGroup; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -54,15 +51,14 @@ public class PeopleSpaceActivity extends Activity { private ViewGroup mPeopleSpaceLayout; private IPeopleManager mPeopleManager; + private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private INotificationManager mNotificationManager; private PackageManager mPackageManager; private LauncherApps mLauncherApps; private Context mContext; - private AppWidgetManager mAppWidgetManager; private NotificationEntryManager mNotificationEntryManager; private int mAppWidgetId; private boolean mShowSingleConversation; - private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Inject public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) { @@ -81,8 +77,8 @@ public class PeopleSpaceActivity extends Activity { mPackageManager = getPackageManager(); mPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); + mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(mContext); mLauncherApps = mContext.getSystemService(LauncherApps.class); - mAppWidgetManager = AppWidgetManager.getInstance(mContext); setTileViewsWithPriorityConversations(); mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); @@ -142,29 +138,13 @@ public class PeopleSpaceActivity extends Activity { + mAppWidgetId); } } - - PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId); - int[] widgetIds = new int[mAppWidgetId]; - // TODO: Populate new widget with existing conversation notification, if there is any. - PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager, - mPeopleManager); - if (mLauncherApps != null) { - try { - if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); - mLauncherApps.cacheShortcuts(tile.getPackageName(), - Collections.singletonList(tile.getId()), - tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); - } catch (Exception e) { - Log.w(TAG, "Exception caching shortcut:" + e); - } - } + mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId); finishActivity(); } /** Finish activity with a successful widget configuration result. */ private void finishActivity() { if (DEBUG) Log.d(TAG, "Widget added!"); - mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED); setActivityResult(RESULT_OK); finish(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 41080afb3604..f9a16c1c1970 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -220,7 +220,7 @@ public class PeopleSpaceUtils { } @Nullable - private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, + public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, AppWidgetManager appWidgetManager, Context context, int appWidgetId) { try { @@ -230,7 +230,7 @@ public class PeopleSpaceUtils { String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING); int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING); - if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) { + if (!validKey(shortcutId, pkg, userId)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); shortcutId = sp.getString(String.valueOf(appWidgetId), null); if (shortcutId == null) { @@ -268,6 +268,17 @@ public class PeopleSpaceUtils { } } + /** Returns stored widgets for the conversation specified. */ + public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId, + String packageName, int userId) { + if (shortcutId == null || packageName == null) { + return new HashSet<>(); + } + String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + return new HashSet<>(sp.getStringSet(key, new HashSet<>())); + } + + /** Best-effort attempts to migrate existing users to the new storage format. */ // TODO: Remove after sufficient time. Temporary migration storage for existing users. private static void migrateExistingUsersToNewStorage(Context context, String shortcutId, @@ -286,7 +297,11 @@ public class PeopleSpaceUtils { if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName()); setStorageForTile(context, entry.get(), appWidgetId); } else { - Log.e(TAG, "Could not migrate user"); + Log.e(TAG, "Could not migrate user. Delete old storage"); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sp.edit(); + editor.remove(String.valueOf(appWidgetId)); + editor.apply(); } } catch (Exception e) { Log.e(TAG, "Could not query conversations"); @@ -320,17 +335,10 @@ public class PeopleSpaceUtils { } /** Removes stored data when tile is deleted. */ - public static void removeStorageForTile(Context context, int widgetId) { - SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), - Context.MODE_PRIVATE); - String packageName = widgetSp.getString(PACKAGE_NAME, null); - String shortcutId = widgetSp.getString(SHORTCUT_ID, null); - int userId = widgetSp.getInt(USER_ID, -1); - + public static void removeStorageForTile(Context context, String key, int widgetId) { // Delete widgetId mapping to key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(key, storedWidgetIds); @@ -338,6 +346,8 @@ public class PeopleSpaceUtils { editor.apply(); // Delete all data specifically mapped to widgetId. + SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); SharedPreferences.Editor widgetEditor = widgetSp.edit(); widgetEditor.remove(PACKAGE_NAME); widgetEditor.remove(USER_ID); @@ -345,21 +355,6 @@ public class PeopleSpaceUtils { widgetEditor.apply(); } - /** - * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the - * requested update data. - */ - public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId, - String packageName, int userId) { - SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId), - Context.MODE_PRIVATE); - String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING); - int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID); - String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING); - return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId) - && storedUserId == userId; - } - static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context, List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) { if (notificationEntryManager == null) { @@ -396,39 +391,8 @@ public class PeopleSpaceUtils { return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn()); } - /** - * If incoming notification changed tile, store the changes in the tile options. - */ - public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager, - Context context, - StatusBarNotification sbn, - NotificationAction notificationAction, AppWidgetManager appWidgetManager, - int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context, - appWidgetId); - if (storedTile == null) { - if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); - return; - } - if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { - if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); - storedTile = augmentTileFromNotification(context, storedTile, sbn); - } else { - if (DEBUG) { - Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); - } - storedTile = storedTile - .toBuilder() - .setNotificationKey(null) - .setNotificationContent(null) - .setNotificationDataUri(null) - .setNotificationCategory(null) - .build(); - } - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); - } - - static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, + /** Augments {@code tile} with the notification content from {@code sbn}. */ + public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, StatusBarNotification sbn) { Notification notification = sbn.getNotification(); if (notification == null) { @@ -992,7 +956,7 @@ public class PeopleSpaceUtils { } /** Update app widget options and the current view. */ - private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, + public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, Context context, int appWidgetId, PeopleSpaceTile tile) { updateAppWidgetOptions(appWidgetManager, appWidgetId, tile); RemoteViews views = createRemoteViews(context, tile, appWidgetId); @@ -1065,10 +1029,19 @@ public class PeopleSpaceUtils { * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li> * </ul> */ + @Nullable public static String getKey(String shortcutId, String packageName, int userId) { + if (!validKey(shortcutId, packageName, userId)) { + return null; + } return shortcutId + "/" + userId + "/" + packageName; } + /** Returns whether the key is valid. */ + public static boolean validKey(String shortcutId, String packageName, int userId) { + return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0; + } + /** Returns the userId associated with a {@link PeopleSpaceTile} */ public static int getUserId(PeopleSpaceTile tile) { return tile.getUserHandle().getIdentifier(); 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 9e5c786b9a63..22ee9e89d0a0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -16,12 +16,28 @@ package com.android.systemui.people.widget; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification; +import static com.android.systemui.people.PeopleSpaceUtils.getPeopleSpaceTile; +import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds; +import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView; + import android.app.NotificationChannel; +import android.app.Person; +import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; +import android.app.people.PeopleManager; +import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.net.Uri; import android.os.ServiceManager; import android.os.UserHandle; import android.preference.PreferenceManager; @@ -30,14 +46,18 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.appwidget.IAppWidgetService; -import com.android.systemui.R; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -49,52 +69,49 @@ public class PeopleSpaceWidgetManager { private static final String TAG = "PeopleSpaceWidgetMgr"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; + private final Object mLock = new Object(); private final Context mContext; - private IAppWidgetService mAppWidgetService; + private LauncherApps mLauncherApps; private AppWidgetManager mAppWidgetManager; - private IPeopleManager mPeopleManager; + private IPeopleManager mIPeopleManager; + private SharedPreferences mSharedPrefs; + private PeopleManager mPeopleManager; + public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + @GuardedBy("mLock") + public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners = + new HashMap<>(); @Inject - public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) { + public PeopleSpaceWidgetManager(Context context) { if (DEBUG) Log.d(TAG, "constructor"); mContext = context; - mAppWidgetService = appWidgetService; mAppWidgetManager = AppWidgetManager.getInstance(context); - mPeopleManager = IPeopleManager.Stub.asInterface( + mIPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); - } - - /** - * Constructor used for testing. - */ - @VisibleForTesting - protected PeopleSpaceWidgetManager(Context context) { - if (DEBUG) Log.d(TAG, "constructor"); - mContext = context; - mAppWidgetService = IAppWidgetService.Stub.asInterface( - ServiceManager.getService(Context.APPWIDGET_SERVICE)); + mLauncherApps = context.getSystemService(LauncherApps.class); + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); + mPeopleManager = mContext.getSystemService(PeopleManager.class); } /** * AppWidgetManager setter used for testing. */ @VisibleForTesting - protected void setAppWidgetManager(IAppWidgetService appWidgetService, - AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { - mAppWidgetService = appWidgetService; + protected void setAppWidgetManager( + AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, + PeopleManager peopleManager, LauncherApps launcherApps) { mAppWidgetManager = appWidgetManager; + mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; + mLauncherApps = launcherApps; } /** * Updates People Space widgets. */ - public void updateWidgets() { + public void updateWidgets(int[] widgetIds) { try { if (DEBUG) Log.d(TAG, "updateWidgets called"); - int[] widgetIds = mAppWidgetService.getAppWidgetIds( - new ComponentName(mContext, PeopleSpaceWidgetProvider.class) - ); if (widgetIds.length == 0) { if (DEBUG) Log.d(TAG, "no widgets to update"); return; @@ -103,14 +120,11 @@ 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, mPeopleManager); - } else { - mAppWidgetService - .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds, - R.id.widget_list_view); + synchronized (mLock) { + PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, + mAppWidgetManager, mIPeopleManager); + } } } catch (Exception e) { Log.e(TAG, "Exception: " + e); @@ -121,9 +135,9 @@ 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 updateWidgetWithNotificationChanged(StatusBarNotification sbn, + public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called"); + if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (!showSingleConversation) { @@ -135,26 +149,22 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Sbn shortcut id is null"); return; } - int[] widgetIds = mAppWidgetService.getAppWidgetIds( + int[] widgetIds = mAppWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); if (widgetIds.length == 0) { Log.d(TAG, "No app widget ids returned"); return; } - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(); - String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId); - Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); - if (storedWidgetIds.isEmpty()) { - Log.d(TAG, "No stored widget ids"); - return; - } - for (String widgetIdString : storedWidgetIds) { - int widgetId = Integer.parseInt(widgetIdString); - if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); - PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext, - sbn, notificationAction, mAppWidgetManager, widgetId); + synchronized (mLock) { + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId, + sbn.getPackageName(), + UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier()); + for (String widgetIdString : storedWidgetIds) { + int widgetId = Integer.parseInt(widgetIdString); + if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); + updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId); + } } } catch (Exception e) { Log.e(TAG, "Exception: " + e); @@ -162,6 +172,91 @@ public class PeopleSpaceWidgetManager { } /** + * Update the tiles associated with the incoming conversation update. + */ + public void updateWidgetsWithConversationChanged(ConversationChannel conversation) { + ShortcutInfo info = conversation.getShortcutInfo(); + synchronized (mLock) { + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(), + info.getPackage(), + info.getUserId()); + for (String widgetIdString : storedWidgetIds) { + if (DEBUG) { + Log.d(TAG, + "Conversation update for widget " + widgetIdString + " , " + + info.getLabel()); + } + updateStorageAndViewWithConversationData(conversation, + Integer.valueOf(widgetIdString)); + } + } + } + + /** + * Update {@code appWidgetId} with the new data provided by {@code conversation}. + */ + private void updateStorageAndViewWithConversationData(ConversationChannel conversation, + int appWidgetId) { + PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, + mContext, + appWidgetId); + if (storedTile == null) { + if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to"); + return; + } + ShortcutInfo info = conversation.getShortcutInfo(); + Uri uri = null; + if (info.getPersons() != null && info.getPersons().length > 0) { + Person person = info.getPersons()[0]; + uri = person.getUri() == null ? null : Uri.parse(person.getUri()); + } + storedTile = storedTile.toBuilder() + .setUserName(info.getLabel()) + .setUserIcon( + PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable( + info, 0)) + ) + .setContactUri(uri) + .setStatuses(conversation.getStatuses()) + .setLastInteractionTimestamp(conversation.getLastEventTimestamp()) + .setIsImportantConversation(conversation.getParentNotificationChannel() != null + && conversation.getParentNotificationChannel().isImportantConversation()) + .build(); + updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile); + } + + /** + * Update {@code appWidgetId} with the new data provided by {@code sbn}. + */ + private void updateStorageAndViewWithNotificationData( + StatusBarNotification sbn, + PeopleSpaceUtils.NotificationAction notificationAction, + int appWidgetId) { + PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, + mContext, + appWidgetId); + if (storedTile == null) { + if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); + return; + } + if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { + if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); + storedTile = augmentTileFromNotification(mContext, storedTile, sbn); + } else { + if (DEBUG) { + Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); + } + storedTile = storedTile + .toBuilder() + .setNotificationKey(null) + .setNotificationContent(null) + .setNotificationDataUri(null) + .build(); + } + updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile); + } + + /** * Attaches the manager to the pipeline, making it ready to receive events. Should only be * called once. */ @@ -174,8 +269,7 @@ public class PeopleSpaceWidgetManager { @Override public void onNotificationPosted( StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) { - if (DEBUG) Log.d(TAG, "onNotificationPosted"); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED); } @Override @@ -183,8 +277,7 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap ) { - if (DEBUG) Log.d(TAG, "onNotificationRemoved"); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -192,8 +285,7 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap, int reason) { - if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -204,7 +296,6 @@ public class PeopleSpaceWidgetManager { @Override public void onNotificationsInitialized() { if (DEBUG) Log.d(TAG, "onNotificationsInitialized"); - updateWidgets(); } @Override @@ -213,11 +304,131 @@ public class PeopleSpaceWidgetManager { UserHandle user, NotificationChannel channel, int modificationType) { - if (DEBUG) Log.d(TAG, "onNotificationChannelModified"); if (channel.isConversation()) { - updateWidgets(); + updateWidgets(mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class) + )); } } }; -}
\ No newline at end of file + /** Adds {@code tile} mapped to {@code appWidgetId}. */ + public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) { + mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED); + synchronized (mLock) { + if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName()); + PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId); + } + try { + if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); + mLauncherApps.cacheShortcuts(tile.getPackageName(), + Collections.singletonList(tile.getId()), + tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + } catch (Exception e) { + Log.w(TAG, "Exception caching shortcut:" + e); + } + PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider(); + provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId}); + } + + /** Registers a conversation listener for {@code appWidgetId} if not already registered. */ + public void registerConversationListenerIfNeeded(int widgetId, + PeopleSpaceWidgetProvider.TileConversationListener newListener) { + // Retrieve storage needed for registration. + String packageName; + String shortcutId; + int userId; + String key; + synchronized (mLock) { + SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); + packageName = widgetSp.getString(PACKAGE_NAME, null); + shortcutId = widgetSp.getString(SHORTCUT_ID, null); + userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + if (key == null) { + if (DEBUG) Log.e(TAG, "Could not register " + widgetId); + return; + } + } + synchronized (mListeners) { + if (mListeners.containsKey(key)) { + if (DEBUG) Log.d(TAG, "Already registered listener"); + return; + } + if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key); + mListeners.put(key, newListener); + } + mPeopleManager.registerConversationListener(packageName, + userId, + shortcutId, newListener, + mContext.getMainExecutor()); + } + + /** Deletes all storage, listeners, and caching for {@code appWidgetIds}. */ + public void deleteWidgets(int[] appWidgetIds) { + for (int widgetId : appWidgetIds) { + if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId); + mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED); + // Retrieve storage needed for widget deletion. + String packageName; + String shortcutId; + int userId; + String key; + Set<String> storedWidgetIdsForKey; + synchronized (mLock) { + SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); + packageName = widgetSp.getString(PACKAGE_NAME, null); + shortcutId = widgetSp.getString(SHORTCUT_ID, null); + userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + if (key == null) { + if (DEBUG) Log.e(TAG, "Could not delete " + widgetId); + return; + } + storedWidgetIdsForKey = new HashSet<>( + mSharedPrefs.getStringSet(key, new HashSet<>())); + } + synchronized (mLock) { + PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId); + } + // If another tile with the conversation is still stored, we need to keep the listener. + if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString()); + if (storedWidgetIdsForKey.contains(String.valueOf(widgetId)) + && storedWidgetIdsForKey.size() == 1) { + if (DEBUG) Log.d(TAG, "Remove caching and listener"); + unregisterConversationListener(key, widgetId); + uncacheConversationShortcut(shortcutId, packageName, userId); + } + } + } + + /** Unregisters the conversation listener for {@code appWidgetId}. */ + private void unregisterConversationListener(String key, int appWidgetId) { + PeopleSpaceWidgetProvider.TileConversationListener registeredListener; + synchronized (mListeners) { + registeredListener = mListeners.get(key); + if (registeredListener == null) { + if (DEBUG) Log.d(TAG, "Cannot find listener to unregister"); + return; + } + if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key); + mListeners.remove(key); + } + mPeopleManager.unregisterConversationListener(registeredListener); + } + + /** Uncaches the conversation shortcut. */ + private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) { + try { + if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId); + mLauncherApps.uncacheShortcuts(packageName, + Collections.singletonList(shortcutId), + UserHandle.of(userId), + LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + } catch (Exception e) { + Log.d(TAG, "Exception uncaching shortcut:" + e); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 90baf56e0137..c0c18471ba32 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -16,31 +16,17 @@ package com.android.systemui.people.widget; -import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; -import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; -import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; - -import android.app.PendingIntent; -import android.app.people.IPeopleManager; +import android.annotation.NonNull; +import android.app.people.ConversationChannel; +import android.app.people.PeopleManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.LauncherApps; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.provider.Settings; import android.util.Log; -import android.widget.RemoteViews; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; -import com.android.systemui.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.people.PeopleSpaceUtils; -import java.util.Collections; - /** People Space Widget Provider class. */ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { private static final String TAG = "PeopleSpaceWidgetPvd"; @@ -50,7 +36,26 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; public static final String EXTRA_USER_HANDLE = "extra_user_handle"; - public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + public PeopleSpaceWidgetManager peopleSpaceWidgetManager; + + /** Listener for the shortcut data changes. */ + public class TileConversationListener implements PeopleManager.ConversationListener { + + @Override + public void onConversationUpdate(@NonNull ConversationChannel conversation) { + if (DEBUG) { + Log.d(TAG, + "Received updated conversation: " + + conversation.getShortcutInfo().getLabel()); + } + if (peopleSpaceWidgetManager == null) { + // This shouldn't happen since onUpdate is called at reboot. + Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized"); + return; + } + peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation); + } + } /** Called when widget updates. */ @Override @@ -58,70 +63,32 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { super.onUpdate(context, appWidgetManager, appWidgetIds); if (DEBUG) Log.d(TAG, "onUpdate called"); - boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; - if (showSingleConversation) { - PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds, - appWidgetManager, IPeopleManager.Stub.asInterface( - ServiceManager.getService(Context.PEOPLE_SERVICE))); - return; - } - // Perform this loop procedure for each App Widget that belongs to this provider + ensurePeopleSpaceWidgetManagerInitialized(context); + peopleSpaceWidgetManager.updateWidgets(appWidgetIds); for (int appWidgetId : appWidgetIds) { - RemoteViews views = - new RemoteViews(context.getPackageName(), R.layout.people_space_widget); - - Intent intent = new Intent(context, PeopleSpaceWidgetService.class); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - views.setRemoteAdapter(R.id.widget_list_view, intent); - - 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); - PendingIntent pendingIntent = PendingIntent.getActivity( - context, - appWidgetId, - activityIntent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); - views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent); + PeopleSpaceWidgetProvider.TileConversationListener + newListener = new PeopleSpaceWidgetProvider.TileConversationListener(); + peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId, + newListener); + } + return; + } - // Tell the AppWidgetManager to perform an update on the current app widget - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view); - appWidgetManager.updateAppWidget(appWidgetId, views); + private void ensurePeopleSpaceWidgetManagerInitialized(Context context) { + if (peopleSpaceWidgetManager == null) { + peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context); } } @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); - LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - - for (int widgetId : appWidgetIds) { - if (DEBUG) Log.d(TAG, "Widget removed"); - mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED); - if (launcherApps != null) { - SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), - Context.MODE_PRIVATE); - String packageName = widgetSp.getString(PACKAGE_NAME, null); - String shortcutId = widgetSp.getString(SHORTCUT_ID, null); - int userId = widgetSp.getInt(USER_ID, -1); + ensurePeopleSpaceWidgetManagerInitialized(context); + peopleSpaceWidgetManager.deleteWidgets(appWidgetIds); + } - if (packageName != null && shortcutId != null && userId != -1) { - try { - if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId); - launcherApps.uncacheShortcuts(packageName, - Collections.singletonList(shortcutId), - UserHandle.of(userId), - LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); - } catch (Exception e) { - Log.d(TAG, "Exception uncaching shortcut:" + e); - } - } - } - PeopleSpaceUtils.removeStorageForTile(context, widgetId); - } + @VisibleForTesting + public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) { + peopleSpaceWidgetManager = manager; } } 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 800d8593035d..f60fa099feaa 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,14 +19,21 @@ 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; +import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; +import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; +import static android.app.people.ConversationStatus.ACTIVITY_GAME; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -38,12 +45,15 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; import android.app.people.ConversationChannel; +import android.app.people.ConversationStatus; import android.app.people.IPeopleManager; +import android.app.people.PeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; @@ -56,7 +66,6 @@ import android.testing.AndroidTestingRunner; 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.people.PeopleSpaceUtils; @@ -76,7 +85,9 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; @SmallTest @@ -99,6 +110,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final String NOTIFICATION_CONTENT = "message text"; 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 KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0); private static final Person PERSON = new Person.Builder() .setName("name") .setKey("abc") @@ -121,12 +133,15 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Mock private NotificationListener mListenerService; - @Mock - private IAppWidgetService mIAppWidgetService; + @Mock private AppWidgetManager mAppWidgetManager; @Mock private IPeopleManager mIPeopleManager; + @Mock + private PeopleManager mPeopleManager; + @Mock + private LauncherApps mLauncherApps; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; @@ -136,13 +151,19 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private final NoManSimulator mNoMan = new NoManSimulator(); private final FakeSystemClock mClock = new FakeSystemClock(); + private PeopleSpaceWidgetProvider mProvider; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mLauncherApps = mock(LauncherApps.class); mManager = new PeopleSpaceWidgetManager(mContext); - mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager); + mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager, + mLauncherApps); mManager.attach(mListenerService); + mProvider = new PeopleSpaceWidgetProvider(); + mProvider.setPeopleSpaceWidgetManager(mManager); verify(mListenerService).addNotificationHandler(mListenerCaptor.capture()); NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue()); @@ -166,7 +187,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false); @@ -182,7 +203,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); Notification notificationWithoutShortcut = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") @@ -206,7 +227,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithoutPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -224,7 +245,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); @@ -240,7 +261,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateAppWidgetIfConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); @@ -257,7 +278,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -277,7 +298,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -298,7 +319,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -318,7 +339,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -339,9 +360,74 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test + public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID, + ACTIVITY_GAME).setDescription("Playing a game!").build(); + ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID, + ACTIVITY_BIRTHDAY).build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(OTHER_SHORTCUT_ID, + Arrays.asList(status1, status2)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); + } + + @Test + public void testUpdateStatusPostedIfExistingTile() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID, + ACTIVITY_GAME).setDescription("Playing a game!").build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID, + Arrays.asList(status)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getStatuses()).containsExactly(status); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception { + addSecondWidgetForPersonTile(); + + ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID, + ACTIVITY_ANNIVERSARY).build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID, + Arrays.asList(status)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test public void testUpdateNotificationPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( @@ -362,17 +448,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception { - Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); - when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) - .thenReturn(options); - // Set the same Person associated on another People Tile widget ID. - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); - - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, - SECOND_WIDGET_ID_WITH_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + addSecondWidgetForPersonTile(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( @@ -395,19 +471,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson() throws Exception { - Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); - when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) - .thenReturn(options); - // Set the same Person associated on another People Tile widget ID. - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + addSecondWidgetForPersonTile(); - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, - SECOND_WIDGET_ID_WITH_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - - PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT); + PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) @@ -430,7 +496,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -456,7 +522,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -480,7 +546,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRemovedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -502,14 +568,82 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { any()); } + @Test + public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() { + addSecondWidgetForPersonTile(); + mProvider.onUpdate(mContext, mAppWidgetManager, + new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT}); + + // Delete only one widget for the conversation. + mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT}); + + // Check deleted storage. + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull(); + assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull(); + assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT)); + // Check listener & shortcut caching remain for other widget. + verify(mPeopleManager, never()).unregisterConversationListener(any()); + verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A), + eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), + eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + + // Delete all widgets for the conversation. + mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT}); + + // Check deleted storage. + SharedPreferences secondWidgetSp = mContext.getSharedPreferences( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull(); + assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull(); + assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty(); + // Check listener is removed and shortcut is uncached. + verify(mPeopleManager, times(1)).unregisterConversationListener(any()); + verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A), + eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), + eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + } + + /** + * Adds another widget for {@code PERSON_TILE} with widget ID: {@code + * SECOND_WIDGET_ID_WITH_SHORTCUT}. + */ + private void addSecondWidgetForPersonTile() { + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + // Set the same Person associated on another People Tile widget ID. + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, + SECOND_WIDGET_ID_WITH_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + } + /** * Returns a single conversation associated with {@code shortcutId}. */ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception { + return getConversationWithShortcutId(shortcutId, Arrays.asList()); + } + + /** + * Returns a single conversation associated with {@code shortcutId} and {@code statuses}. + */ + private ConversationChannel getConversationWithShortcutId(String shortcutId, + List<ConversationStatus> statuses) throws Exception { ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel( "name").build(); ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null, - 0L, false); + 0L, false, false, statuses); return convo; } |