Register conversation listeners

Newly registered after reboots and after being added to Launcher. Stored
in-memory to avoid duplicate registration. Refactors code out of
PeopleSpaceUtils to better handle race conditions

Test: PeopleSpaceWidgetManagerTest
Bug: 178792356
Change-Id: Ia1bc2671cb29696e537495ff8c1adabb873f0713
This commit is contained in:
Anna Zappone
2021-03-01 19:24:13 +00:00
committed by Flavio Fiszman
parent b8d3b7d210
commit f0fd893b6e
5 changed files with 523 additions and 258 deletions

View File

@@ -23,7 +23,6 @@ import android.app.Activity;
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
@@ -34,12 +33,10 @@ import android.provider.Settings;
import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
@@ -54,15 +51,14 @@ public class PeopleSpaceActivity extends Activity {
private ViewGroup mPeopleSpaceLayout;
private IPeopleManager mPeopleManager;
private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private INotificationManager mNotificationManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private Context mContext;
private AppWidgetManager mAppWidgetManager;
private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@Inject
public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
@@ -81,8 +77,8 @@ public class PeopleSpaceActivity extends Activity {
mPackageManager = getPackageManager();
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(mContext);
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mAppWidgetManager = AppWidgetManager.getInstance(mContext);
setTileViewsWithPriorityConversations();
mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
INVALID_APPWIDGET_ID);
@@ -142,29 +138,13 @@ public class PeopleSpaceActivity extends Activity {
+ mAppWidgetId);
}
}
PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
int[] widgetIds = new int[mAppWidgetId];
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
mPeopleManager);
if (mLauncherApps != null) {
try {
if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
mLauncherApps.cacheShortcuts(tile.getPackageName(),
Collections.singletonList(tile.getId()),
tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.w(TAG, "Exception caching shortcut:" + e);
}
}
mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
finishActivity();
}
/** Finish activity with a successful widget configuration result. */
private void finishActivity() {
if (DEBUG) Log.d(TAG, "Widget added!");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
setActivityResult(RESULT_OK);
finish();
}

View File

@@ -220,7 +220,7 @@ public class PeopleSpaceUtils {
}
@Nullable
private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
AppWidgetManager appWidgetManager,
Context context, int appWidgetId) {
try {
@@ -230,7 +230,7 @@ public class PeopleSpaceUtils {
String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
if (!validKey(shortcutId, pkg, userId)) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
shortcutId = sp.getString(String.valueOf(appWidgetId), null);
if (shortcutId == null) {
@@ -268,6 +268,17 @@ public class PeopleSpaceUtils {
}
}
/** Returns stored widgets for the conversation specified. */
public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId,
String packageName, int userId) {
if (shortcutId == null || packageName == null) {
return new HashSet<>();
}
String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
return new HashSet<>(sp.getStringSet(key, new HashSet<>()));
}
/** Best-effort attempts to migrate existing users to the new storage format. */
// TODO: Remove after sufficient time. Temporary migration storage for existing users.
private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
@@ -286,7 +297,11 @@ public class PeopleSpaceUtils {
if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
setStorageForTile(context, entry.get(), appWidgetId);
} else {
Log.e(TAG, "Could not migrate user");
Log.e(TAG, "Could not migrate user. Delete old storage");
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
editor.remove(String.valueOf(appWidgetId));
editor.apply();
}
} catch (Exception e) {
Log.e(TAG, "Could not query conversations");
@@ -320,17 +335,10 @@ public class PeopleSpaceUtils {
}
/** Removes stored data when tile is deleted. */
public static void removeStorageForTile(Context context, int widgetId) {
SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
String packageName = widgetSp.getString(PACKAGE_NAME, null);
String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
int userId = widgetSp.getInt(USER_ID, -1);
public static void removeStorageForTile(Context context, String key, int widgetId) {
// Delete widgetId mapping to key.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
storedWidgetIds.remove(String.valueOf(widgetId));
editor.putStringSet(key, storedWidgetIds);
@@ -338,6 +346,8 @@ public class PeopleSpaceUtils {
editor.apply();
// Delete all data specifically mapped to widgetId.
SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
SharedPreferences.Editor widgetEditor = widgetSp.edit();
widgetEditor.remove(PACKAGE_NAME);
widgetEditor.remove(USER_ID);
@@ -345,21 +355,6 @@ public class PeopleSpaceUtils {
widgetEditor.apply();
}
/**
* Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
* requested update data.
*/
public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
String packageName, int userId) {
SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
Context.MODE_PRIVATE);
String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
&& storedUserId == userId;
}
static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
if (notificationEntryManager == null) {
@@ -396,39 +391,8 @@ public class PeopleSpaceUtils {
return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
}
/**
* If incoming notification changed tile, store the changes in the tile options.
*/
public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
Context context,
StatusBarNotification sbn,
NotificationAction notificationAction, AppWidgetManager appWidgetManager,
int appWidgetId) {
PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
storedTile = augmentTileFromNotification(context, storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
}
storedTile = storedTile
.toBuilder()
.setNotificationKey(null)
.setNotificationContent(null)
.setNotificationDataUri(null)
.setNotificationCategory(null)
.build();
}
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
/** Augments {@code tile} with the notification content from {@code sbn}. */
public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification == null) {
@@ -992,7 +956,7 @@ public class PeopleSpaceUtils {
}
/** Update app widget options and the current view. */
private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile) {
updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
RemoteViews views = createRemoteViews(context, tile, appWidgetId);
@@ -1065,10 +1029,19 @@ public class PeopleSpaceUtils {
* <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
* </ul>
*/
@Nullable
public static String getKey(String shortcutId, String packageName, int userId) {
if (!validKey(shortcutId, packageName, userId)) {
return null;
}
return shortcutId + "/" + userId + "/" + packageName;
}
/** Returns whether the key is valid. */
public static boolean validKey(String shortcutId, String packageName, int userId) {
return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0;
}
/** Returns the userId associated with a {@link PeopleSpaceTile} */
public static int getUserId(PeopleSpaceTile tile) {
return tile.getUserHandle().getIdentifier();

View File

@@ -16,12 +16,28 @@
package com.android.systemui.people.widget;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification;
import static com.android.systemui.people.PeopleSpaceUtils.getPeopleSpaceTile;
import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds;
import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
import android.app.NotificationChannel;
import android.app.Person;
import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
import android.app.people.PeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
@@ -30,14 +46,18 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
@@ -49,52 +69,49 @@ public class PeopleSpaceWidgetManager {
private static final String TAG = "PeopleSpaceWidgetMgr";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private final Object mLock = new Object();
private final Context mContext;
private IAppWidgetService mAppWidgetService;
private LauncherApps mLauncherApps;
private AppWidgetManager mAppWidgetManager;
private IPeopleManager mPeopleManager;
private IPeopleManager mIPeopleManager;
private SharedPreferences mSharedPrefs;
private PeopleManager mPeopleManager;
public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@GuardedBy("mLock")
public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners =
new HashMap<>();
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
public PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
mContext = context;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
mPeopleManager = IPeopleManager.Stub.asInterface(
mIPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
}
/**
* Constructor used for testing.
*/
@VisibleForTesting
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
mContext = context;
mAppWidgetService = IAppWidgetService.Stub.asInterface(
ServiceManager.getService(Context.APPWIDGET_SERVICE));
mLauncherApps = context.getSystemService(LauncherApps.class);
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mPeopleManager = mContext.getSystemService(PeopleManager.class);
}
/**
* AppWidgetManager setter used for testing.
*/
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
mAppWidgetService = appWidgetService;
protected void setAppWidgetManager(
AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
PeopleManager peopleManager, LauncherApps launcherApps) {
mAppWidgetManager = appWidgetManager;
mIPeopleManager = iPeopleManager;
mPeopleManager = peopleManager;
mLauncherApps = launcherApps;
}
/**
* Updates People Space widgets.
*/
public void updateWidgets() {
public void updateWidgets(int[] widgetIds) {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
if (DEBUG) Log.d(TAG, "no widgets to update");
return;
@@ -103,14 +120,11 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
mAppWidgetManager, mPeopleManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
R.id.widget_list_view);
synchronized (mLock) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
mAppWidgetManager, mIPeopleManager);
}
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
@@ -121,9 +135,9 @@ public class PeopleSpaceWidgetManager {
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
@@ -135,32 +149,113 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
return;
}
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
Log.d(TAG, "No app widget ids returned");
return;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
if (storedWidgetIds.isEmpty()) {
Log.d(TAG, "No stored widget ids");
return;
}
for (String widgetIdString : storedWidgetIds) {
int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
sbn, notificationAction, mAppWidgetManager, widgetId);
synchronized (mLock) {
Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId,
sbn.getPackageName(),
UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier());
for (String widgetIdString : storedWidgetIds) {
int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId);
}
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
}
}
/**
* Update the tiles associated with the incoming conversation update.
*/
public void updateWidgetsWithConversationChanged(ConversationChannel conversation) {
ShortcutInfo info = conversation.getShortcutInfo();
synchronized (mLock) {
Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(),
info.getPackage(),
info.getUserId());
for (String widgetIdString : storedWidgetIds) {
if (DEBUG) {
Log.d(TAG,
"Conversation update for widget " + widgetIdString + " , "
+ info.getLabel());
}
updateStorageAndViewWithConversationData(conversation,
Integer.valueOf(widgetIdString));
}
}
}
/**
* Update {@code appWidgetId} with the new data provided by {@code conversation}.
*/
private void updateStorageAndViewWithConversationData(ConversationChannel conversation,
int appWidgetId) {
PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
mContext,
appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to");
return;
}
ShortcutInfo info = conversation.getShortcutInfo();
Uri uri = null;
if (info.getPersons() != null && info.getPersons().length > 0) {
Person person = info.getPersons()[0];
uri = person.getUri() == null ? null : Uri.parse(person.getUri());
}
storedTile = storedTile.toBuilder()
.setUserName(info.getLabel())
.setUserIcon(
PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable(
info, 0))
)
.setContactUri(uri)
.setStatuses(conversation.getStatuses())
.setLastInteractionTimestamp(conversation.getLastEventTimestamp())
.setIsImportantConversation(conversation.getParentNotificationChannel() != null
&& conversation.getParentNotificationChannel().isImportantConversation())
.build();
updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
}
/**
* Update {@code appWidgetId} with the new data provided by {@code sbn}.
*/
private void updateStorageAndViewWithNotificationData(
StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction,
int appWidgetId) {
PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
mContext,
appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
}
storedTile = storedTile
.toBuilder()
.setNotificationKey(null)
.setNotificationContent(null)
.setNotificationDataUri(null)
.build();
}
updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
}
/**
* Attaches the manager to the pipeline, making it ready to receive events. Should only be
* called once.
@@ -174,8 +269,7 @@ public class PeopleSpaceWidgetManager {
@Override
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted");
updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -183,8 +277,7 @@ public class PeopleSpaceWidgetManager {
StatusBarNotification sbn,
NotificationListenerService.RankingMap rankingMap
) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved");
updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -192,8 +285,7 @@ public class PeopleSpaceWidgetManager {
StatusBarNotification sbn,
NotificationListenerService.RankingMap rankingMap,
int reason) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -204,7 +296,6 @@ public class PeopleSpaceWidgetManager {
@Override
public void onNotificationsInitialized() {
if (DEBUG) Log.d(TAG, "onNotificationsInitialized");
updateWidgets();
}
@Override
@@ -213,11 +304,131 @@ public class PeopleSpaceWidgetManager {
UserHandle user,
NotificationChannel channel,
int modificationType) {
if (DEBUG) Log.d(TAG, "onNotificationChannelModified");
if (channel.isConversation()) {
updateWidgets();
updateWidgets(mAppWidgetManager.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
));
}
}
};
}
/** Adds {@code tile} mapped to {@code appWidgetId}. */
public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) {
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
synchronized (mLock) {
if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName());
PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId);
}
try {
if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
mLauncherApps.cacheShortcuts(tile.getPackageName(),
Collections.singletonList(tile.getId()),
tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.w(TAG, "Exception caching shortcut:" + e);
}
PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider();
provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId});
}
/** Registers a conversation listener for {@code appWidgetId} if not already registered. */
public void registerConversationListenerIfNeeded(int widgetId,
PeopleSpaceWidgetProvider.TileConversationListener newListener) {
// Retrieve storage needed for registration.
String packageName;
String shortcutId;
int userId;
String key;
synchronized (mLock) {
SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
packageName = widgetSp.getString(PACKAGE_NAME, null);
shortcutId = widgetSp.getString(SHORTCUT_ID, null);
userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
if (key == null) {
if (DEBUG) Log.e(TAG, "Could not register " + widgetId);
return;
}
}
synchronized (mListeners) {
if (mListeners.containsKey(key)) {
if (DEBUG) Log.d(TAG, "Already registered listener");
return;
}
if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key);
mListeners.put(key, newListener);
}
mPeopleManager.registerConversationListener(packageName,
userId,
shortcutId, newListener,
mContext.getMainExecutor());
}
/** Deletes all storage, listeners, and caching for {@code appWidgetIds}. */
public void deleteWidgets(int[] appWidgetIds) {
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId);
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
// Retrieve storage needed for widget deletion.
String packageName;
String shortcutId;
int userId;
String key;
Set<String> storedWidgetIdsForKey;
synchronized (mLock) {
SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
packageName = widgetSp.getString(PACKAGE_NAME, null);
shortcutId = widgetSp.getString(SHORTCUT_ID, null);
userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
if (key == null) {
if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
return;
}
storedWidgetIdsForKey = new HashSet<>(
mSharedPrefs.getStringSet(key, new HashSet<>()));
}
synchronized (mLock) {
PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId);
}
// If another tile with the conversation is still stored, we need to keep the listener.
if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
if (storedWidgetIdsForKey.contains(String.valueOf(widgetId))
&& storedWidgetIdsForKey.size() == 1) {
if (DEBUG) Log.d(TAG, "Remove caching and listener");
unregisterConversationListener(key, widgetId);
uncacheConversationShortcut(shortcutId, packageName, userId);
}
}
}
/** Unregisters the conversation listener for {@code appWidgetId}. */
private void unregisterConversationListener(String key, int appWidgetId) {
PeopleSpaceWidgetProvider.TileConversationListener registeredListener;
synchronized (mListeners) {
registeredListener = mListeners.get(key);
if (registeredListener == null) {
if (DEBUG) Log.d(TAG, "Cannot find listener to unregister");
return;
}
if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key);
mListeners.remove(key);
}
mPeopleManager.unregisterConversationListener(registeredListener);
}
/** Uncaches the conversation shortcut. */
private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) {
try {
if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
mLauncherApps.uncacheShortcuts(packageName,
Collections.singletonList(shortcutId),
UserHandle.of(userId),
LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.d(TAG, "Exception uncaching shortcut:" + e);
}
}
}

View File

@@ -16,31 +16,17 @@
package com.android.systemui.people.widget;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
import android.app.PendingIntent;
import android.app.people.IPeopleManager;
import android.annotation.NonNull;
import android.app.people.ConversationChannel;
import android.app.people.PeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.widget.RemoteViews;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.people.PeopleSpaceUtils;
import java.util.Collections;
/** People Space Widget Provider class. */
public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
private static final String TAG = "PeopleSpaceWidgetPvd";
@@ -50,7 +36,26 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
public static final String EXTRA_USER_HANDLE = "extra_user_handle";
public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
public PeopleSpaceWidgetManager peopleSpaceWidgetManager;
/** Listener for the shortcut data changes. */
public class TileConversationListener implements PeopleManager.ConversationListener {
@Override
public void onConversationUpdate(@NonNull ConversationChannel conversation) {
if (DEBUG) {
Log.d(TAG,
"Received updated conversation: "
+ conversation.getShortcutInfo().getLabel());
}
if (peopleSpaceWidgetManager == null) {
// This shouldn't happen since onUpdate is called at reboot.
Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized");
return;
}
peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation);
}
}
/** Called when widget updates. */
@Override
@@ -58,70 +63,32 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
super.onUpdate(context, appWidgetManager, appWidgetIds);
if (DEBUG) Log.d(TAG, "onUpdate called");
boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
appWidgetManager, IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
ensurePeopleSpaceWidgetManagerInitialized(context);
peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
for (int appWidgetId : appWidgetIds) {
RemoteViews views =
new RemoteViews(context.getPackageName(), R.layout.people_space_widget);
PeopleSpaceWidgetProvider.TileConversationListener
newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
newListener);
}
return;
}
Intent intent = new Intent(context, PeopleSpaceWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
views.setRemoteAdapter(R.id.widget_list_view, intent);
Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
activityIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
appWidgetId,
activityIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
appWidgetManager.updateAppWidget(appWidgetId, views);
private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
if (peopleSpaceWidgetManager == null) {
peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
ensurePeopleSpaceWidgetManagerInitialized(context);
peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
}
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
if (launcherApps != null) {
SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
String packageName = widgetSp.getString(PACKAGE_NAME, null);
String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
int userId = widgetSp.getInt(USER_ID, -1);
if (packageName != null && shortcutId != null && userId != -1) {
try {
if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
launcherApps.uncacheShortcuts(packageName,
Collections.singletonList(shortcutId),
UserHandle.of(userId),
LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.d(TAG, "Exception uncaching shortcut:" + e);
}
}
}
PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
@VisibleForTesting
public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
peopleSpaceWidgetManager = manager;
}
}

View File

@@ -19,14 +19,21 @@ package com.android.systemui.people.widget;
import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -38,12 +45,15 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
import android.app.people.ConversationChannel;
import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.people.PeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -56,7 +66,6 @@ import android.testing.AndroidTestingRunner;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.PeopleSpaceUtils;
@@ -76,7 +85,9 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@SmallTest
@@ -99,6 +110,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final String KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0);
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
@@ -121,12 +133,15 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Mock
private NotificationListener mListenerService;
@Mock
private IAppWidgetService mIAppWidgetService;
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
private IPeopleManager mIPeopleManager;
@Mock
private PeopleManager mPeopleManager;
@Mock
private LauncherApps mLauncherApps;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -136,13 +151,19 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
private PeopleSpaceWidgetProvider mProvider;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLauncherApps = mock(LauncherApps.class);
mManager =
new PeopleSpaceWidgetManager(mContext);
mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
mLauncherApps);
mManager.attach(mListenerService);
mProvider = new PeopleSpaceWidgetProvider();
mProvider.setPeopleSpaceWidgetManager(mManager);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
@@ -166,7 +187,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
@@ -182,7 +203,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
Notification notificationWithoutShortcut = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
@@ -206,7 +227,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -224,7 +245,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotificationChannel channel =
new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -240,7 +261,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotificationChannel channel =
new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -257,7 +278,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -277,7 +298,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -298,7 +319,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -318,7 +339,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -338,10 +359,75 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
any());
}
@Test
public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
ACTIVITY_GAME).setDescription("Playing a game!").build();
ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
ACTIVITY_BIRTHDAY).build();
ConversationChannel conversationChannel = getConversationWithShortcutId(OTHER_SHORTCUT_ID,
Arrays.asList(status1, status2));
mManager.updateWidgetsWithConversationChanged(conversationChannel);
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(anyInt(), any());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any());
}
@Test
public void testUpdateStatusPostedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
ACTIVITY_GAME).setDescription("Playing a game!").build();
ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
Arrays.asList(status));
mManager.updateWidgetsWithConversationChanged(conversationChannel);
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
mBundleArgumentCaptor.capture());
Bundle bundle = mBundleArgumentCaptor.getValue();
PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
assertThat(tile.getStatuses()).containsExactly(status);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@Test
public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception {
addSecondWidgetForPersonTile();
ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
ACTIVITY_ANNIVERSARY).build();
ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
Arrays.asList(status));
mManager.updateWidgetsWithConversationChanged(conversationChannel);
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
any());
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
verify(mAppWidgetManager, times(1))
.updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
any());
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
any());
}
@Test
public void testUpdateNotificationPostedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -362,17 +448,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
Bundle options = new Bundle();
options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
// Set the same Person associated on another People Tile widget ID.
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
SECOND_WIDGET_ID_WITH_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
addSecondWidgetForPersonTile();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -395,19 +471,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
throws Exception {
Bundle options = new Bundle();
options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
// Set the same Person associated on another People Tile widget ID.
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
addSecondWidgetForPersonTile();
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
SECOND_WIDGET_ID_WITH_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -430,7 +496,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -456,7 +522,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -480,7 +546,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -502,14 +568,82 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
any());
}
@Test
public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() {
addSecondWidgetForPersonTile();
mProvider.onUpdate(mContext, mAppWidgetManager,
new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
// Delete only one widget for the conversation.
mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT});
// Check deleted storage.
SharedPreferences widgetSp = mContext.getSharedPreferences(
String.valueOf(WIDGET_ID_WITH_SHORTCUT),
Context.MODE_PRIVATE);
assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull();
assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull();
assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly(
String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
// Check listener & shortcut caching remain for other widget.
verify(mPeopleManager, never()).unregisterConversationListener(any());
verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A),
eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
// Delete all widgets for the conversation.
mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT});
// Check deleted storage.
SharedPreferences secondWidgetSp = mContext.getSharedPreferences(
String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
Context.MODE_PRIVATE);
assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull();
assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull();
assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty();
// Check listener is removed and shortcut is uncached.
verify(mPeopleManager, times(1)).unregisterConversationListener(any());
verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A),
eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
}
/**
* Adds another widget for {@code PERSON_TILE} with widget ID: {@code
* SECOND_WIDGET_ID_WITH_SHORTCUT}.
*/
private void addSecondWidgetForPersonTile() {
Bundle options = new Bundle();
options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
// Set the same Person associated on another People Tile widget ID.
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
SECOND_WIDGET_ID_WITH_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
}
/**
* Returns a single conversation associated with {@code shortcutId}.
*/
private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
return getConversationWithShortcutId(shortcutId, Arrays.asList());
}
/**
* Returns a single conversation associated with {@code shortcutId} and {@code statuses}.
*/
private ConversationChannel getConversationWithShortcutId(String shortcutId,
List<ConversationStatus> statuses) throws Exception {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
"name").build();
ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
0L, false);
0L, false, false, statuses);
return convo;
}