Handle onAppWidgetOptionsChanged widget add

Change-Id: I2df633086c61d973effec4dbe54a691ac31125f3
Test: PeopleSpaceUtilsTest and PeopleSpaceWidgetManagerTest
Bug: 182265091
This commit is contained in:
Flavio Fiszman
2021-03-05 17:52:14 +00:00
parent 384db6dd05
commit af8cc8d0ee
8 changed files with 534 additions and 240 deletions

View File

@@ -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();
}

View File

@@ -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) {
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;
}
// 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 (!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 {
// 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);
}
// Check if tile is cached.
Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
if (tile != null) {
return tile;
}
// 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 (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);
// Don't overwrite existing widgets with the same key.
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
storedWidgetIds.add(String.valueOf(appWidgetId));
editor.putStringSet(key, storedWidgetIds);
editor.apply();
editor.putString(String.valueOf(appWidgetId), key.getShortcutId());
// Write cached storage.
updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
tile);
// Don't overwrite existing widgets with the same key.
Set<String> storedWidgetIds = new HashSet<>(
sp.getStringSet(key.toString(), new HashSet<>()));
storedWidgetIds.add(String.valueOf(appWidgetId));
editor.putStringSet(key.toString(), storedWidgetIds);
editor.apply();
}
/** 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();

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);