diff options
8 files changed, 531 insertions, 237 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index ff32b6993d5b..378e49deb699 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -35,6 +35,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; +import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.List; @@ -138,7 +139,9 @@ public class PeopleSpaceActivity extends Activity { + mAppWidgetId); } } - mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId); + PeopleTileKey key = new PeopleTileKey( + tile.getId(), tile.getUserHandle().getIdentifier(), tile.getPackageName()); + mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key); finishActivity(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index c0595eb5924f..aa45178b6439 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -52,9 +52,7 @@ 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.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; @@ -71,10 +69,11 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ArrayUtils; import com.android.settingslib.utils.ThreadUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.people.widget.AppWidgetOptionsHelper; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; +import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -106,7 +105,6 @@ 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"; public static final String PACKAGE_NAME = "package_name"; public static final String USER_ID = "user_id"; public static final String SHORTCUT_ID = "shortcut_id"; @@ -115,6 +113,9 @@ public class PeopleSpaceUtils { public static final int INVALID_WIDGET_ID = -1; public static final int INVALID_USER_ID = -1; + public static final PeopleTileKey EMPTY_KEY = + new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING); + private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+"); private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+"); private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+"); @@ -200,67 +201,76 @@ public class PeopleSpaceUtils { AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>(); for (int appWidgetId : appWidgetIds) { - PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context, - appWidgetId); + PeopleSpaceTile tile = getPeopleSpaceTile( + context, appWidgetId, appWidgetManager, peopleManager); if (tile == null) { if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); //TODO: Delete app widget id when crash is fixed (b/172932636) continue; } - - if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName()); - RemoteViews views = createRemoteViews(context, tile, appWidgetId); - - // Tell the AppWidgetManager to perform an update on the current app widget. - appWidgetManager.updateAppWidget(appWidgetId, views); - + updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, tile); widgetIdToTile.put(appWidgetId, tile); } getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds); } + /** + * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. If the PeopleSpaceTile + * isn't cached, store it in AppWidgetOptions. + */ @Nullable - public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, - AppWidgetManager appWidgetManager, - Context context, int appWidgetId) { - try { - // Migrate storage for existing users. - SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), - Context.MODE_PRIVATE); - 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 (!validKey(shortcutId, pkg, userId)) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - shortcutId = sp.getString(String.valueOf(appWidgetId), null); - if (shortcutId == null) { - Log.e(TAG, "Cannot restore widget"); - return null; - } - migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId); - pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING); - userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); - } + public static PeopleSpaceTile getPeopleSpaceTile(Context context, int appWidgetId, + AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { + // First, check if tile is cached in AppWidgetOptions. + PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(appWidgetManager, appWidgetId); + if (tile != null) { + if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId); + return tile; + } - // Check if tile is cached. - Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); - PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); - if (tile != null) { - return tile; - } + // If not, we get the PeopleTileKey from SharedPreferences, retrieve the Conversation from + // persisted storage, and cache it in AppWidgetOptions. + SharedPreferences widgetSp = context.getSharedPreferences( + String.valueOf(appWidgetId), + Context.MODE_PRIVATE); + PeopleTileKey sharedPreferencesKey = new PeopleTileKey( + widgetSp.getString(SHORTCUT_ID, EMPTY_STRING), + widgetSp.getInt(USER_ID, INVALID_USER_ID), + widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); - // If tile is null, we need to retrieve from persisted storage. - if (DEBUG) { - Log.d(TAG, - "Retrieving from storage after reboots: " + shortcutId + " user: " + userId - + " pkg: " + pkg); - } + if (!sharedPreferencesKey.isValid()) { + Log.e(TAG, "Cannot find shortcut info for widgetId: " + appWidgetId); + return null; + } + + if (DEBUG) Log.d(TAG, "PeopleTile key is present in sharedPreferences: " + appWidgetId); + // If tile is null, we need to retrieve from persisted storage. + return getPeopleTileFromPersistentStorage(context, sharedPreferencesKey, peopleManager); + } + + /** + * Returns a {@link PeopleSpaceTile} based on {@link ConversationChannel} returned by + * {@link IPeopleManager}. + */ + public static PeopleSpaceTile getPeopleTileFromPersistentStorage(Context context, + PeopleTileKey peopleTileKey, IPeopleManager peopleManager) { + try { + if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + peopleTileKey.toString()); LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId); + if (launcherApps == null) { + Log.d(TAG, "LauncherApps is null"); + return null; + } + + ConversationChannel channel = peopleManager.getConversation( + peopleTileKey.getPackageName(), + peopleTileKey.getUserId(), + peopleTileKey.getShortcutId()); if (channel == null) { Log.d(TAG, "Could not retrieve conversation from storage"); return null; } + return new PeopleSpaceTile.Builder(channel, launcherApps).build(); } catch (Exception e) { Log.e(TAG, "Failed to retrieve conversation for tile: " + e); @@ -269,79 +279,46 @@ 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) { + public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { + if (!key.isValid()) { 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, - int appWidgetId) { - try { - List<PeopleSpaceTile> tiles = - PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)), - IPeopleManager.Stub.asInterface( - ServiceManager.getService(Context.PEOPLE_SERVICE)), - context.getSystemService(LauncherApps.class), - Dependency.get(NotificationEntryManager.class)); - Optional<PeopleSpaceTile> entry = tiles.stream().filter( - e -> e.getId().equals(shortcutId)).findFirst(); - if (entry.isPresent()) { - if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName()); - setStorageForTile(context, entry.get(), appWidgetId); - } else { - 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"); - } + return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>())); } /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ - public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) { + public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, + int appWidgetId) { // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); SharedPreferences.Editor widgetEditor = widgetSp.edit(); - widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName()); - widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId()); - int userId = getUserId(tile); - widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId); + widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName()); + widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId()); + widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId()); widgetEditor.apply(); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - editor.putString(String.valueOf(appWidgetId), tile.getId()); - String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId); + editor.putString(String.valueOf(appWidgetId), key.getShortcutId()); + // Don't overwrite existing widgets with the same key. - Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + Set<String> storedWidgetIds = new HashSet<>( + sp.getStringSet(key.toString(), new HashSet<>())); storedWidgetIds.add(String.valueOf(appWidgetId)); - editor.putStringSet(key, storedWidgetIds); + editor.putStringSet(key.toString(), storedWidgetIds); editor.apply(); - - // Write cached storage. - updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId, - tile); } /** Removes stored data when tile is deleted. */ - public static void removeStorageForTile(Context context, String key, int widgetId) { + public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, + int widgetId) { // Delete widgetId mapping to key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + Set<String> storedWidgetIds = new HashSet<>( + sp.getStringSet(key.toString(), new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); - editor.putStringSet(key, storedWidgetIds); + editor.putStringSet(key.toString(), storedWidgetIds); editor.remove(String.valueOf(widgetId)); editor.apply(); @@ -361,12 +338,12 @@ public class PeopleSpaceUtils { Log.w(TAG, "NotificationEntryManager is null"); return tiles; } - Map<String, NotificationEntry> visibleNotifications = notificationEntryManager + Map<PeopleTileKey, NotificationEntry> visibleNotifications = notificationEntryManager .getVisibleNotifications() .stream() .filter(entry -> entry.getRanking() != null && entry.getRanking().getConversationShortcutInfo() != null) - .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e)); + .collect(Collectors.toMap(PeopleTileKey::new, e -> e)); if (DEBUG) { Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size()); } @@ -378,16 +355,15 @@ public class PeopleSpaceUtils { } static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context, - PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) { - String shortcutId = tile.getId(); - String packageName = tile.getPackageName(); - int userId = getUserId(tile); - String key = getKey(shortcutId, packageName, userId); + PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) { + PeopleTileKey key = new PeopleTileKey( + tile.getId(), getUserId(tile), tile.getPackageName()); + if (!visibleNotifications.containsKey(key)) { - if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key); + if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString()); return tile; } - if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key); + if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key.toString()); return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn()); } @@ -423,17 +399,6 @@ public class PeopleSpaceUtils { .build(); } - private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId, - PeopleSpaceTile tile) { - if (tile == null) { - if (DEBUG) Log.d(TAG, "Requested to store null tile"); - return; - } - Bundle newOptions = new Bundle(); - newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, tile); - appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions); - } - /** Creates a {@link RemoteViews} for {@code tile}. */ public static RemoteViews createRemoteViews(Context context, PeopleSpaceTile tile, int appWidgetId) { @@ -935,11 +900,15 @@ public class PeopleSpaceUtils { removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); } - /** Update app widget options and the current view. */ + /** Updates tile in app widget options and the current view. */ public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, Context context, int appWidgetId, PeopleSpaceTile tile) { - updateAppWidgetOptions(appWidgetManager, appWidgetId, tile); + AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile); + + if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName()); RemoteViews views = createRemoteViews(context, tile, appWidgetId); + + // Tell the AppWidgetManager to perform an update on the current app widget. appWidgetManager.updateAppWidget(appWidgetId, views); } @@ -984,44 +953,6 @@ public class PeopleSpaceUtils { return lookupKeysWithBirthdaysToday; } - static String getKey(NotificationEntry entry) { - if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null - || entry.getSbn() == null || entry.getSbn().getUser() == null) { - return null; - } - return getKey(entry.getRanking().getConversationShortcutInfo().getId(), - entry.getSbn().getPackageName(), - entry.getSbn().getUser().getIdentifier()); - } - - /** - * Returns the uniquely identifying key for the conversation. - * - * <p>{@code userId} will always be a number, so we put user ID as the - * delimiter between the app-provided strings of shortcut ID and package name. - * - * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring - * a {@code packageName} to always start with a letter. This restriction means we are - * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first - * case is impossible given the package name restrictions: - * <ul> - * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li> - * <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/AppWidgetOptionsHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java new file mode 100644 index 000000000000..df08ee4a42bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people.widget; + +import static com.android.systemui.people.PeopleSpaceUtils.DEBUG; +import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_KEY; +import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; +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 android.app.people.PeopleSpaceTile; +import android.appwidget.AppWidgetManager; +import android.os.Bundle; +import android.util.Log; + +/** Helper class encapsulating AppWidgetOptions for People Tile. */ +public class AppWidgetOptionsHelper { + private static final String TAG = "AppWidgetOptionsHelper"; + + /** Key to store {@link PeopleSpaceTile} in AppWidgetOptions Bundle. */ + public static final String OPTIONS_PEOPLE_TILE = "options_people_tile"; + + /** Sets {@link PeopleSpaceTile} in AppWidgetOptions. */ + public static void setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId, + PeopleSpaceTile tile) { + if (tile == null) { + if (DEBUG) Log.d(TAG, "Requested to store null tile"); + return; + } + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + options.putParcelable(OPTIONS_PEOPLE_TILE, tile); + appWidgetManager.updateAppWidgetOptions(appWidgetId, options); + } + + /** Gets {@link PeopleSpaceTile} from AppWidgetOptions. */ + public static PeopleSpaceTile getPeopleTile(AppWidgetManager appWidgetManager, + int appWidgetId) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + return options != null ? options.getParcelable(OPTIONS_PEOPLE_TILE) : null; + } + + /** Sets {@link PeopleTileKey} in AppWidgetOptions. */ + public static void setPeopleTileKey(AppWidgetManager appWidgetManager, int appWidgetId, + PeopleTileKey key) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + options.putString(SHORTCUT_ID, key.getShortcutId()); + options.putInt(USER_ID, key.getUserId()); + options.putString(PACKAGE_NAME, key.getPackageName()); + appWidgetManager.updateAppWidgetOptions(appWidgetId, options); + } + + /** Gets {@link PeopleTileKey} from AppWidgetOptions. */ + public static PeopleTileKey getPeopleTileKey(AppWidgetManager appWidgetManager, + int appWidgetId) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + if (options == null) { + return EMPTY_KEY; + } + return getPeopleTileKeyFromBundle(options); + } + + /** Gets {@link PeopleTileKey} from Bundle {@code options}. */ + public static PeopleTileKey getPeopleTileKeyFromBundle(Bundle options) { + String pkg = options.getString(PACKAGE_NAME, EMPTY_STRING); + int userId = options.getInt(USER_ID, INVALID_USER_ID); + String shortcutId = options.getString(SHORTCUT_ID, EMPTY_STRING); + return new PeopleTileKey(shortcutId, userId, pkg); + } + + /** Removes {@link PeopleTileKey} from AppWidgetOptions. */ + public static void removePeopleTileKey(AppWidgetManager appWidgetManager, + int appWidgetId) { + setPeopleTileKey(appWidgetManager, appWidgetId, EMPTY_KEY); + } +} 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 22ee9e89d0a0..7da9a80ca287 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; 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; @@ -38,6 +39,7 @@ import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.net.Uri; +import android.os.Bundle; import android.os.ServiceManager; import android.os.UserHandle; import android.preference.PreferenceManager; @@ -78,8 +80,8 @@ public class PeopleSpaceWidgetManager { private PeopleManager mPeopleManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @GuardedBy("mLock") - public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners = - new HashMap<>(); + public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener> + mListeners = new HashMap<>(); @Inject public PeopleSpaceWidgetManager(Context context) { @@ -122,8 +124,8 @@ public class PeopleSpaceWidgetManager { Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (showSingleConversation) { synchronized (mLock) { - PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, - mAppWidgetManager, mIPeopleManager); + PeopleSpaceUtils.updateSingleConversationWidgets( + mContext, widgetIds, mAppWidgetManager, mIPeopleManager); } } } catch (Exception e) { @@ -157,9 +159,11 @@ public class PeopleSpaceWidgetManager { return; } synchronized (mLock) { - Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId, - sbn.getPackageName(), - UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier()); + PeopleTileKey key = new PeopleTileKey( + sbnShortcutId, + UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(), + sbn.getPackageName()); + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key); for (String widgetIdString : storedWidgetIds) { int widgetId = Integer.parseInt(widgetIdString); if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); @@ -177,9 +181,9 @@ public class PeopleSpaceWidgetManager { public void updateWidgetsWithConversationChanged(ConversationChannel conversation) { ShortcutInfo info = conversation.getShortcutInfo(); synchronized (mLock) { - Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(), - info.getPackage(), - info.getUserId()); + PeopleTileKey key = new PeopleTileKey( + info.getId(), info.getUserId(), info.getPackage()); + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key); for (String widgetIdString : storedWidgetIds) { if (DEBUG) { Log.d(TAG, @@ -197,9 +201,8 @@ public class PeopleSpaceWidgetManager { */ private void updateStorageAndViewWithConversationData(ConversationChannel conversation, int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, - mContext, - appWidgetId); + PeopleSpaceTile storedTile = getPeopleSpaceTile( + mContext, appWidgetId, mAppWidgetManager, mIPeopleManager); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to"); return; @@ -232,9 +235,8 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, - mContext, - appWidgetId); + PeopleSpaceTile storedTile = getPeopleSpaceTile( + mContext, appWidgetId, mAppWidgetManager, mIPeopleManager); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); return; @@ -312,18 +314,39 @@ public class PeopleSpaceWidgetManager { } }; - /** Adds {@code tile} mapped to {@code appWidgetId}. */ - public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) { + /** + * Checks if this widget has been added externally, and this the first time we are learning + * about the widget. If so, the widget adder should have populated options with PeopleTileKey + * arguments. + */ + public void onAppWidgetOptionsChanged(int appWidgetId, Bundle newOptions) { + // Check if this widget has been added externally, and this the first time we are + // learning about the widget. If so, the widget adder should have populated options with + // PeopleTileKey arguments. + if (DEBUG) Log.d(TAG, "onAppWidgetOptionsChanged called for widget: " + appWidgetId); + PeopleTileKey optionsKey = AppWidgetOptionsHelper.getPeopleTileKeyFromBundle(newOptions); + if (optionsKey.isValid()) { + if (DEBUG) { + Log.d(TAG, "PeopleTileKey was present in Options, shortcutId: " + + optionsKey.getShortcutId()); + } + addNewWidget(appWidgetId, optionsKey); + AppWidgetOptionsHelper.removePeopleTileKey(mAppWidgetManager, appWidgetId); + } + } + + /** Adds{@code tile} mapped to {@code appWidgetId}. */ + public void addNewWidget(int appWidgetId, PeopleTileKey key) { 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); + if (DEBUG) Log.d(TAG, "Add storage for : " + key.getShortcutId()); + PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, 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); + if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.getShortcutId()); + mLauncherApps.cacheShortcuts(key.getPackageName(), + Collections.singletonList(key.getShortcutId()), + UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { Log.w(TAG, "Exception caching shortcut:" + e); } @@ -335,19 +358,16 @@ public class PeopleSpaceWidgetManager { public void registerConversationListenerIfNeeded(int widgetId, PeopleSpaceWidgetProvider.TileConversationListener newListener) { // Retrieve storage needed for registration. - String packageName; - String shortcutId; - int userId; - String key; + PeopleTileKey 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); + key = new PeopleTileKey( + widgetSp.getString(SHORTCUT_ID, EMPTY_STRING), + widgetSp.getInt(USER_ID, INVALID_USER_ID), + widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); + if (!key.isValid()) { + if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId); return; } } @@ -359,9 +379,9 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key); mListeners.put(key, newListener); } - mPeopleManager.registerConversationListener(packageName, - userId, - shortcutId, newListener, + mPeopleManager.registerConversationListener(key.getPackageName(), + key.getUserId(), + key.getShortcutId(), newListener, mContext.getMainExecutor()); } @@ -371,27 +391,24 @@ public class PeopleSpaceWidgetManager { 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; + PeopleTileKey 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) { + key = new PeopleTileKey( + widgetSp.getString(SHORTCUT_ID, null), + widgetSp.getInt(USER_ID, INVALID_USER_ID), + widgetSp.getString(PACKAGE_NAME, null)); + if (!key.isValid()) { if (DEBUG) Log.e(TAG, "Could not delete " + widgetId); return; } storedWidgetIdsForKey = new HashSet<>( - mSharedPrefs.getStringSet(key, new HashSet<>())); + mSharedPrefs.getStringSet(key.toString(), new HashSet<>())); } synchronized (mLock) { - PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId); + PeopleSpaceUtils.removeSharedPreferencesStorageForTile(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()); @@ -399,13 +416,13 @@ public class PeopleSpaceWidgetManager { && storedWidgetIdsForKey.size() == 1) { if (DEBUG) Log.d(TAG, "Remove caching and listener"); unregisterConversationListener(key, widgetId); - uncacheConversationShortcut(shortcutId, packageName, userId); + uncacheConversationShortcut(key); } } } /** Unregisters the conversation listener for {@code appWidgetId}. */ - private void unregisterConversationListener(String key, int appWidgetId) { + private void unregisterConversationListener(PeopleTileKey key, int appWidgetId) { PeopleSpaceWidgetProvider.TileConversationListener registeredListener; synchronized (mListeners) { registeredListener = mListeners.get(key); @@ -420,12 +437,12 @@ public class PeopleSpaceWidgetManager { } /** Uncaches the conversation shortcut. */ - private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) { + private void uncacheConversationShortcut(PeopleTileKey key) { try { - if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId); - mLauncherApps.uncacheShortcuts(packageName, - Collections.singletonList(shortcutId), - UserHandle.of(userId), + if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + key.getShortcutId()); + mLauncherApps.uncacheShortcuts(key.getPackageName(), + Collections.singletonList(key.getShortcutId()), + UserHandle.of(key.getUserId()), 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 cccf7aa13028..3bc5b29bd05d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -22,6 +22,7 @@ import android.app.people.PeopleManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; +import android.os.Bundle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -67,18 +68,21 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { ensurePeopleSpaceWidgetManagerInitialized(context); peopleSpaceWidgetManager.updateWidgets(appWidgetIds); for (int appWidgetId : appWidgetIds) { + if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId); PeopleSpaceWidgetProvider.TileConversationListener newListener = new PeopleSpaceWidgetProvider.TileConversationListener(); peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId, newListener); } - return; } - private void ensurePeopleSpaceWidgetManagerInitialized(Context context) { - if (peopleSpaceWidgetManager == null) { - peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context); - } + /** Called when widget updates. */ + @Override + public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, + int appWidgetId, Bundle newOptions) { + super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); + ensurePeopleSpaceWidgetManagerInitialized(context); + peopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions); } @Override @@ -88,6 +92,12 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { peopleSpaceWidgetManager.deleteWidgets(appWidgetIds); } + private void ensurePeopleSpaceWidgetManagerInitialized(Context context) { + if (peopleSpaceWidgetManager == null) { + peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context); + } + } + @VisibleForTesting public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) { peopleSpaceWidgetManager = manager; diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java new file mode 100644 index 000000000000..ac42cb08090a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people.widget; + +import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; + +import android.text.TextUtils; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.Objects; + +/** Class that encapsulates fields identifying a Conversation. */ +public class PeopleTileKey { + private String mShortcutId; + private int mUserId; + private String mPackageName; + + public PeopleTileKey(String shortcutId, int userId, String packageName) { + mShortcutId = shortcutId; + mUserId = userId; + mPackageName = packageName; + } + + public PeopleTileKey(NotificationEntry entry) { + mShortcutId = entry.getRanking() != null + && entry.getRanking().getConversationShortcutInfo() != null + ? entry.getRanking().getConversationShortcutInfo().getId() + : EMPTY_STRING; + mUserId = entry.getSbn().getUser() != null + ? entry.getSbn().getUser().getIdentifier() : INVALID_USER_ID; + mPackageName = entry.getSbn().getPackageName(); + } + + public String getShortcutId() { + return mShortcutId; + } + + public int getUserId() { + return mUserId; + } + + public String getPackageName() { + return mPackageName; + } + + /** Returns whether PeopleTileKey is valid/well-formed. */ + public boolean isValid() { + return !TextUtils.isEmpty(mShortcutId) && !TextUtils.isEmpty(mPackageName) && mUserId >= 0; + } + + /** + * Returns the uniquely identifying key for the conversation. + * + * <p>{@code userId} will always be a number, so we put user ID as the + * delimiter between the app-provided strings of shortcut ID and package name. + * + * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring + * a {@code packageName} to always start with a letter. This restriction means we are + * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first + * case is impossible given the package name restrictions: + * <ul> + * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li> + * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li> + * </ul> + */ + @Override + public String toString() { + if (!isValid()) return null; + return mShortcutId + "/" + mUserId + "/" + mPackageName; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof PeopleTileKey)) { + return false; + } + final PeopleTileKey o = (PeopleTileKey) other; + return Objects.equals(o.toString(), this.toString()); + } + + @Override + public int hashCode() { + return mPackageName.hashCode() + Integer.valueOf(mUserId).hashCode() + + mShortcutId.hashCode(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 2a4b41cbfe32..3fed07472c35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -22,8 +22,9 @@ import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE; -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.getPeopleTileFromPersistentStorage; +import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE; import static com.google.common.truth.Truth.assertThat; @@ -76,6 +77,7 @@ import android.widget.TextView; import com.android.internal.appwidget.IAppWidgetService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -232,7 +234,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; Bundle options = new Bundle(); - options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE); when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) @@ -500,7 +502,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromVisibleNotifications(mContext, tile, - Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); + Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @@ -515,7 +517,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromVisibleNotifications(mContext, tile, - Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); + Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent()).isEqualTo(null); } @@ -818,6 +820,23 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { assertEquals(statusContent.getText(), NOTIFICATION_CONTENT); } + @Test + public void testGetPeopleTileFromPersistentStorageExistingConversation() + throws Exception { + when(mPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID_1)).thenReturn( + getConversationChannelWithoutTimestamp(SHORTCUT_ID_1)); + PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_1, 0, PACKAGE_NAME); + PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager); + assertThat(tile.getId()).isEqualTo(key.getShortcutId()); + } + + @Test + public void testGetPeopleTileFromPersistentStorageNoConversation() { + PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_2, 0, PACKAGE_NAME); + PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager); + assertThat(tile).isNull(); + } + private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId, boolean importantConversation, long lastInteractionTimestamp) throws Exception { ConversationChannelWrapper convo = new ConversationChannelWrapper(); @@ -843,4 +862,13 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { eq(shortcutId))).thenReturn(lastInteractionTimestamp); return convo; } + + private ConversationChannel getConversationChannelWithoutTimestamp(String shortcutId) + throws Exception { + ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel( + "name").build(); + ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null, + 0L, false); + return convo; + } }
\ No newline at end of file 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 f60fa099feaa..aef75beb3d56 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 @@ -23,10 +23,11 @@ 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.EMPTY_STRING; 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.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE; import static com.google.common.truth.Truth.assertThat; @@ -104,13 +105,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final int WIDGET_ID_WITH_SHORTCUT = 1; private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3; private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2; + private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4; private static final String SHORTCUT_ID = "101"; private static final String OTHER_SHORTCUT_ID = "102"; private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0"; 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 PeopleTileKey KEY = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); private static final Person PERSON = new Person.Builder() .setName("name") .setKey("abc") @@ -172,10 +174,11 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + clearStorage(); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) .thenReturn(options); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) @@ -395,7 +398,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); Bundle bundle = mBundleArgumentCaptor.getValue(); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); assertThat(tile.getStatuses()).containsExactly(status); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); @@ -439,7 +442,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); Bundle bundle = mBundleArgumentCaptor.getValue(); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), @@ -473,7 +476,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { throws Exception { addSecondWidgetForPersonTile(); - PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT); + PeopleSpaceUtils.removeSharedPreferencesStorageForTile( + mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) @@ -510,7 +514,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mBundleArgumentCaptor.capture()); Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); assertThat(tile.getNotificationContent()) .isEqualTo(mContext.getString(R.string.missed_call)); @@ -536,7 +540,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mBundleArgumentCaptor.capture()); Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), @@ -547,6 +551,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testUpdateNotificationRemovedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); StatusBarNotification sbn = createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -560,11 +565,11 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); Bundle bundle = mBundleArgumentCaptor.getValue(); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); assertThat(tile.getNotificationKey()).isEqualTo(null); assertThat(tile.getNotificationContent()).isEqualTo(null); assertThat(tile.getNotificationDataUri()).isEqualTo(null); - verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(), + verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -585,7 +590,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { 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( + assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).containsExactly( String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT)); // Check listener & shortcut caching remain for other widget. verify(mPeopleManager, never()).unregisterConversationListener(any()); @@ -603,7 +608,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { 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(); + assertThat(sp.getStringSet(KEY.toString(), 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), @@ -611,13 +616,96 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); } + @Test + public void testUpdateWidgetsWithEmptyOptionsAddsPeopleTileToOptions() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(new Bundle()); + + mManager.updateWidgets(widgetIdsArray); + mClock.advanceTime(MIN_LINGER_DURATION); + + // If we had to fetch Tile from persistent storage, we want to make sure we write it to + // options. + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getId()).isEqualTo(SHORTCUT_ID); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testOnAppWidgetOptionsChangedNoWidgetAdded() { + Bundle newOptions = new Bundle(); + newOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE); + mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions); + + + // Check that options is not modified + verify(mAppWidgetManager, never()).updateAppWidgetOptions( + eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any()); + // Check listener is not added and shortcut is not cached. + verify(mPeopleManager, never()).registerConversationListener(any(), anyInt(), any(), any(), + any()); + verify(mLauncherApps, never()).cacheShortcuts(any(), any(), any(), anyInt()); + // Check no added storage. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())) + .doesNotContain(SECOND_WIDGET_ID_WITH_SHORTCUT); + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING); + assertThat(widgetSp.getString(SHORTCUT_ID, EMPTY_STRING)).isEqualTo(EMPTY_STRING); + assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + + } + + @Test + public void testOnAppWidgetOptionsChangedWidgetAdded() { + Bundle newOptions = new Bundle(); + newOptions.putString(PeopleSpaceUtils.SHORTCUT_ID, SHORTCUT_ID); + newOptions.putInt(USER_ID, 0); + newOptions.putString(PACKAGE_NAME, TEST_PACKAGE_A); + when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(newOptions); + + mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions); + + verify(mAppWidgetManager, times(1)).updateAppWidgetOptions( + eq(SECOND_WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + assertThat(bundle.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING)) + .isEqualTo(EMPTY_STRING); + assertThat(bundle.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + assertThat(bundle.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING); + verify(mLauncherApps, times(1)).cacheShortcuts(eq(TEST_PACKAGE_A), + eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), + eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).contains( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT)); + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(TEST_PACKAGE_A); + assertThat(widgetSp.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING)) + .isEqualTo(SHORTCUT_ID); + assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0); + } + /** * 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); + options.putParcelable(OPTIONS_PEOPLE_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. @@ -676,6 +764,27 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .build(); } + private void clearStorage() { + SharedPreferences widgetSp1 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + widgetSp1.edit().clear().commit(); + SharedPreferences widgetSp2 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT), + Context.MODE_PRIVATE); + widgetSp2.edit().clear().commit(); + SharedPreferences widgetSp3 = mContext.getSharedPreferences( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + widgetSp3.edit().clear().commit(); + SharedPreferences widgetSp4 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), + Context.MODE_PRIVATE); + widgetSp4.edit().clear().commit(); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + sp.edit().clear().commit(); + } + private void setStorageForTile(String shortcutId, String packageName, int widgetId) { SharedPreferences widgetSp = mContext.getSharedPreferences( String.valueOf(widgetId), @@ -689,7 +798,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences.Editor editor = sp.edit(); editor.putString(String.valueOf(widgetId), shortcutId); - String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0); + String key = new PeopleTileKey(shortcutId, 0, packageName).toString(); Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); storedWidgetIds.add(String.valueOf(widgetId)); editor.putStringSet(key, storedWidgetIds); |