Loading packages/SystemUI/res/xml/people_space_widget_info.xml +2 −2 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="120dp" android:minHeight="54dp" android:minHeight="50dp" android:minResizeWidth="60dp" android:minResizeHeight="54dp" android:minResizeHeight="50dp" android:maxResizeHeight="207dp" android:updatePeriodMillis="60000" android:description="@string/people_tile_description" Loading packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +26 −12 Original line number Diff line number Diff line Loading @@ -175,7 +175,7 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int appWidgetId) { int appWidgetId, Uri contactUri) { // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); Loading @@ -186,27 +186,24 @@ public class PeopleSpaceUtils { widgetEditor.apply(); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.putString(String.valueOf(appWidgetId), key.getShortcutId()); String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString(); editor.putString(String.valueOf(appWidgetId), contactUriString); // 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); addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString()); addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); editor.apply(); } /** Removes stored data when tile is deleted. */ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int widgetId) { int widgetId, String contactUriString) { // Delete widgetId mapping to key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); Set<String> storedWidgetIds = new HashSet<>( sp.getStringSet(key.toString(), new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(key.toString(), storedWidgetIds); editor.remove(String.valueOf(widgetId)); removeAppWidgetIdForKey(sp, editor, widgetId, key.toString()); removeAppWidgetIdForKey(sp, editor, widgetId, contactUriString); editor.apply(); // Delete all data specifically mapped to widgetId. Loading @@ -219,6 +216,23 @@ public class PeopleSpaceUtils { widgetEditor.apply(); } private static void addAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor, int widgetId, String storageKey) { Set<String> storedWidgetIdsByKey = new HashSet<>( sp.getStringSet(storageKey, new HashSet<>())); storedWidgetIdsByKey.add(String.valueOf(widgetId)); editor.putStringSet(storageKey, storedWidgetIdsByKey); } private static void removeAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor, int widgetId, String storageKey) { Set<String> storedWidgetIds = new HashSet<>( sp.getStringSet(storageKey, new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(storageKey, storedWidgetIds); } /** Augments a single {@link PeopleSpaceTile} with notification content, if one is present. */ public static PeopleSpaceTile augmentSingleTileFromVisibleNotifications(Context context, PeopleSpaceTile tile, NotificationEntryManager notificationEntryManager) { Loading Loading @@ -256,7 +270,7 @@ public class PeopleSpaceUtils { PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) { PeopleTileKey key = new PeopleTileKey( tile.getId(), getUserId(tile), tile.getPackageName()); // TODO: Match missed calls with matching Uris in addition to keys. if (!visibleNotifications.containsKey(key)) { if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString()); return tile; Loading packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +204 −20 Original line number Diff line number Diff line Loading @@ -16,17 +16,23 @@ package com.android.systemui.people.widget; import static android.Manifest.permission.READ_CONTACTS; import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.Notification.EXTRA_PEOPLE_LIST; 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 static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification; import static com.android.systemui.people.PeopleSpaceUtils.getMessagingStyleMessages; import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds; import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView; import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews; import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; import android.app.Person; Loading @@ -39,6 +45,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.net.Uri; import android.os.Bundle; Loading @@ -54,16 +61,20 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.inject.Inject; Loading @@ -83,11 +94,19 @@ public class PeopleSpaceWidgetManager { private SharedPreferences mSharedPrefs; private PeopleManager mPeopleManager; private NotificationEntryManager mNotificationEntryManager; private PackageManager mPackageManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @GuardedBy("mLock") public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener> mListeners = new HashMap<>(); @GuardedBy("mLock") // Map of notification key mapped to widget IDs previously updated by the contact Uri field. // This is required because on notification removal, the contact Uri field is stripped and we // only have the notification key to determine which widget IDs should be updated. private Map<String, Set<String>> mNotificationKeyToWidgetIdsMatchedByUri = new HashMap<>(); private boolean mIsForTesting; @Inject public PeopleSpaceWidgetManager(Context context) { if (DEBUG) Log.d(TAG, "constructor"); Loading @@ -99,6 +118,7 @@ public class PeopleSpaceWidgetManager { mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); mPeopleManager = mContext.getSystemService(PeopleManager.class); mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mPackageManager = mContext.getPackageManager(); } /** Loading @@ -108,12 +128,15 @@ public class PeopleSpaceWidgetManager { protected void setAppWidgetManager( AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, PeopleManager peopleManager, LauncherApps launcherApps, NotificationEntryManager notificationEntryManager) { NotificationEntryManager notificationEntryManager, PackageManager packageManager, boolean isForTesting) { mAppWidgetManager = appWidgetManager; mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; mLauncherApps = launcherApps; mNotificationEntryManager = notificationEntryManager; mPackageManager = packageManager; mIsForTesting = isForTesting; } /** Loading Loading @@ -222,6 +245,16 @@ public class PeopleSpaceWidgetManager { public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called"); if (mIsForTesting) { updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction); return; } ThreadUtils.postOnBackgroundThread( () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction)); } private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { try { String sbnShortcutId = sbn.getShortcutId(); if (sbnShortcutId == null) { Loading @@ -235,23 +268,175 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "No app widget ids returned"); return; } synchronized (mLock) { PeopleTileKey key = new PeopleTileKey( sbnShortcutId, UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(), sbn.getUser().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()); updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId); if (!key.isValid()) { Log.d(TAG, "Invalid key"); return; } synchronized (mLock) { // First, update People Tiles associated with the Notification's package/shortcut. Set<String> tilesUpdatedByKey = getStoredWidgetIds(mSharedPrefs, key); updateWidgetIdsForNotificationAction(tilesUpdatedByKey, sbn, action); // Then, update People Tiles across other packages that use the same Uri. updateTilesByUri(key, sbn, action); } } catch (Exception e) { Log.e(TAG, "Exception: " + e); } } /** Updates {@code widgetIdsToUpdate} with {@code action}. */ private void updateWidgetIdsForNotificationAction(Set<String> widgetIdsToUpdate, StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { for (String widgetIdString : widgetIdsToUpdate) { int widgetId = Integer.parseInt(widgetIdString); PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile for notification"); continue; } if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); updateStorageAndViewWithNotificationData(sbn, action, widgetId, storedTile); } } /** * Updates tiles with matched Uris, dependent on the {@code action}. * * <p>If the notification was added, adds the notification based on the contact Uri within * {@code sbn}. * <p>If the notification was removed, removes the notification based on the in-memory map of * widgets previously updated by Uri (since the contact Uri is stripped from the {@code sbn}). */ private void updateTilesByUri(PeopleTileKey key, StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { if (action.equals(PeopleSpaceUtils.NotificationAction.POSTED)) { Set<String> widgetIdsUpdatedByUri = supplementTilesByUri(sbn, action, key); if (widgetIdsUpdatedByUri != null && !widgetIdsUpdatedByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "Added due to uri: " + widgetIdsUpdatedByUri); mNotificationKeyToWidgetIdsMatchedByUri.put(sbn.getKey(), widgetIdsUpdatedByUri); } } else { // Remove the notification on any widgets where the notification was added // purely based on the Uri. Set<String> widgetsPreviouslyUpdatedByUri = mNotificationKeyToWidgetIdsMatchedByUri.remove(sbn.getKey()); if (widgetsPreviouslyUpdatedByUri != null && !widgetsPreviouslyUpdatedByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "Remove due to uri: " + widgetsPreviouslyUpdatedByUri); updateWidgetIdsForNotificationAction(widgetsPreviouslyUpdatedByUri, sbn, action); } } } /** * Retrieves from storage any tiles with the same contact Uri as linked via the {@code sbn}. * Supplements the tiles with the notification content only if they still have {@link * android.Manifest.permission.READ_CONTACTS} permission. */ @Nullable private Set<String> supplementTilesByUri(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, PeopleTileKey key) { if (!shouldMatchNotificationByUri(sbn)) { if (DEBUG) Log.d(TAG, "Should not supplement conversation"); return null; } // Try to get the Contact Uri from the Missed Call notification directly. String contactUri = getContactUri(sbn); if (contactUri == null) { if (DEBUG) Log.d(TAG, "No contact uri"); return null; } // Supplement any tiles with the same Uri. Set<String> storedWidgetIdsByUri = new HashSet<>(mSharedPrefs.getStringSet(contactUri, new HashSet<>())); if (storedWidgetIdsByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "No tiles for contact"); return null; } if (mPackageManager.checkPermission(READ_CONTACTS, sbn.getPackageName()) != PackageManager.PERMISSION_GRANTED) { if (DEBUG) Log.d(TAG, "Notifying app missing permissions"); return null; } Set<String> widgetIdsUpdatedByUri = new HashSet<>(); for (String widgetIdString : storedWidgetIdsByUri) { int widgetId = Integer.parseInt(widgetIdString); PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId); // Don't update a widget already updated. if (key.equals(new PeopleTileKey(storedTile))) { continue; } if (storedTile == null || mPackageManager.checkPermission(READ_CONTACTS, storedTile.getPackageName()) != PackageManager.PERMISSION_GRANTED) { if (DEBUG) Log.d(TAG, "Cannot supplement tile: " + storedTile.getUserName()); continue; } if (DEBUG) Log.d(TAG, "Adding notification by uri: " + sbn.getKey()); updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId, storedTile); widgetIdsUpdatedByUri.add(String.valueOf(widgetId)); } return widgetIdsUpdatedByUri; } /** * Try to retrieve a valid Uri via {@code sbn}, falling back to the {@code * contactUriFromShortcut} if valid. */ @Nullable private String getContactUri(StatusBarNotification sbn) { // First, try to get a Uri from the Person directly set on the Notification. ArrayList<Person> people = sbn.getNotification().extras.getParcelableArrayList( EXTRA_PEOPLE_LIST); if (people != null && people.get(0) != null) { String contactUri = people.get(0).getUri(); if (contactUri != null && !contactUri.isEmpty()) { return contactUri; } } // Then, try to get a Uri from the Person set on the Notification message. List<Notification.MessagingStyle.Message> messages = getMessagingStyleMessages(sbn.getNotification()); if (messages != null && !messages.isEmpty()) { Notification.MessagingStyle.Message message = messages.get(0); Person sender = message.getSenderPerson(); if (sender != null && sender.getUri() != null && !sender.getUri().isEmpty()) { return sender.getUri(); } } return null; } /** * Returns whether a notification should be matched to other Tiles by Uri. * * <p>Currently only matches missed calls. */ private boolean shouldMatchNotificationByUri(StatusBarNotification sbn) { Notification notification = sbn.getNotification(); if (notification == null) { if (DEBUG) Log.d(TAG, "Notification is null"); return false; } if (!Objects.equals(notification.category, CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Not missed call"); return false; } return true; } /** * Update the tiles associated with the incoming conversation update. */ Loading Loading @@ -309,16 +494,11 @@ public class PeopleSpaceWidgetManager { private void updateStorageAndViewWithNotificationData( StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, int appWidgetId) { PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); return; } int appWidgetId, PeopleSpaceTile storedTile) { if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); storedTile = augmentTileFromNotification(mContext, storedTile, sbn); } else { } else if (storedTile.getNotificationKey().equals(sbn.getKey())) { if (DEBUG) { Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); } Loading Loading @@ -440,7 +620,8 @@ public class PeopleSpaceWidgetManager { synchronized (mLock) { if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId()); PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId, tile.getContactUri()); } try { if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); Loading Loading @@ -496,6 +677,7 @@ public class PeopleSpaceWidgetManager { // Retrieve storage needed for widget deletion. PeopleTileKey key; Set<String> storedWidgetIdsForKey; String contactUriString; synchronized (mLock) { SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), Context.MODE_PRIVATE); Loading @@ -509,9 +691,11 @@ public class PeopleSpaceWidgetManager { } storedWidgetIdsForKey = new HashSet<>( mSharedPrefs.getStringSet(key.toString(), new HashSet<>())); contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null); } synchronized (mLock) { PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId); PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId, contactUriString); } // 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()); Loading packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +340 −25 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/res/xml/people_space_widget_info.xml +2 −2 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="120dp" android:minHeight="54dp" android:minHeight="50dp" android:minResizeWidth="60dp" android:minResizeHeight="54dp" android:minResizeHeight="50dp" android:maxResizeHeight="207dp" android:updatePeriodMillis="60000" android:description="@string/people_tile_description" Loading
packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +26 −12 Original line number Diff line number Diff line Loading @@ -175,7 +175,7 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int appWidgetId) { int appWidgetId, Uri contactUri) { // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); Loading @@ -186,27 +186,24 @@ public class PeopleSpaceUtils { widgetEditor.apply(); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.putString(String.valueOf(appWidgetId), key.getShortcutId()); String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString(); editor.putString(String.valueOf(appWidgetId), contactUriString); // 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); addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString()); addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); editor.apply(); } /** Removes stored data when tile is deleted. */ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int widgetId) { int widgetId, String contactUriString) { // Delete widgetId mapping to key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); Set<String> storedWidgetIds = new HashSet<>( sp.getStringSet(key.toString(), new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(key.toString(), storedWidgetIds); editor.remove(String.valueOf(widgetId)); removeAppWidgetIdForKey(sp, editor, widgetId, key.toString()); removeAppWidgetIdForKey(sp, editor, widgetId, contactUriString); editor.apply(); // Delete all data specifically mapped to widgetId. Loading @@ -219,6 +216,23 @@ public class PeopleSpaceUtils { widgetEditor.apply(); } private static void addAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor, int widgetId, String storageKey) { Set<String> storedWidgetIdsByKey = new HashSet<>( sp.getStringSet(storageKey, new HashSet<>())); storedWidgetIdsByKey.add(String.valueOf(widgetId)); editor.putStringSet(storageKey, storedWidgetIdsByKey); } private static void removeAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor, int widgetId, String storageKey) { Set<String> storedWidgetIds = new HashSet<>( sp.getStringSet(storageKey, new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(storageKey, storedWidgetIds); } /** Augments a single {@link PeopleSpaceTile} with notification content, if one is present. */ public static PeopleSpaceTile augmentSingleTileFromVisibleNotifications(Context context, PeopleSpaceTile tile, NotificationEntryManager notificationEntryManager) { Loading Loading @@ -256,7 +270,7 @@ public class PeopleSpaceUtils { PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) { PeopleTileKey key = new PeopleTileKey( tile.getId(), getUserId(tile), tile.getPackageName()); // TODO: Match missed calls with matching Uris in addition to keys. if (!visibleNotifications.containsKey(key)) { if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString()); return tile; Loading
packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +204 −20 Original line number Diff line number Diff line Loading @@ -16,17 +16,23 @@ package com.android.systemui.people.widget; import static android.Manifest.permission.READ_CONTACTS; import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.Notification.EXTRA_PEOPLE_LIST; 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 static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification; import static com.android.systemui.people.PeopleSpaceUtils.getMessagingStyleMessages; import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds; import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView; import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews; import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; import android.app.Person; Loading @@ -39,6 +45,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.net.Uri; import android.os.Bundle; Loading @@ -54,16 +61,20 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.inject.Inject; Loading @@ -83,11 +94,19 @@ public class PeopleSpaceWidgetManager { private SharedPreferences mSharedPrefs; private PeopleManager mPeopleManager; private NotificationEntryManager mNotificationEntryManager; private PackageManager mPackageManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @GuardedBy("mLock") public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener> mListeners = new HashMap<>(); @GuardedBy("mLock") // Map of notification key mapped to widget IDs previously updated by the contact Uri field. // This is required because on notification removal, the contact Uri field is stripped and we // only have the notification key to determine which widget IDs should be updated. private Map<String, Set<String>> mNotificationKeyToWidgetIdsMatchedByUri = new HashMap<>(); private boolean mIsForTesting; @Inject public PeopleSpaceWidgetManager(Context context) { if (DEBUG) Log.d(TAG, "constructor"); Loading @@ -99,6 +118,7 @@ public class PeopleSpaceWidgetManager { mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); mPeopleManager = mContext.getSystemService(PeopleManager.class); mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mPackageManager = mContext.getPackageManager(); } /** Loading @@ -108,12 +128,15 @@ public class PeopleSpaceWidgetManager { protected void setAppWidgetManager( AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, PeopleManager peopleManager, LauncherApps launcherApps, NotificationEntryManager notificationEntryManager) { NotificationEntryManager notificationEntryManager, PackageManager packageManager, boolean isForTesting) { mAppWidgetManager = appWidgetManager; mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; mLauncherApps = launcherApps; mNotificationEntryManager = notificationEntryManager; mPackageManager = packageManager; mIsForTesting = isForTesting; } /** Loading Loading @@ -222,6 +245,16 @@ public class PeopleSpaceWidgetManager { public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called"); if (mIsForTesting) { updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction); return; } ThreadUtils.postOnBackgroundThread( () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction)); } private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { try { String sbnShortcutId = sbn.getShortcutId(); if (sbnShortcutId == null) { Loading @@ -235,23 +268,175 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "No app widget ids returned"); return; } synchronized (mLock) { PeopleTileKey key = new PeopleTileKey( sbnShortcutId, UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(), sbn.getUser().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()); updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId); if (!key.isValid()) { Log.d(TAG, "Invalid key"); return; } synchronized (mLock) { // First, update People Tiles associated with the Notification's package/shortcut. Set<String> tilesUpdatedByKey = getStoredWidgetIds(mSharedPrefs, key); updateWidgetIdsForNotificationAction(tilesUpdatedByKey, sbn, action); // Then, update People Tiles across other packages that use the same Uri. updateTilesByUri(key, sbn, action); } } catch (Exception e) { Log.e(TAG, "Exception: " + e); } } /** Updates {@code widgetIdsToUpdate} with {@code action}. */ private void updateWidgetIdsForNotificationAction(Set<String> widgetIdsToUpdate, StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { for (String widgetIdString : widgetIdsToUpdate) { int widgetId = Integer.parseInt(widgetIdString); PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile for notification"); continue; } if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); updateStorageAndViewWithNotificationData(sbn, action, widgetId, storedTile); } } /** * Updates tiles with matched Uris, dependent on the {@code action}. * * <p>If the notification was added, adds the notification based on the contact Uri within * {@code sbn}. * <p>If the notification was removed, removes the notification based on the in-memory map of * widgets previously updated by Uri (since the contact Uri is stripped from the {@code sbn}). */ private void updateTilesByUri(PeopleTileKey key, StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) { if (action.equals(PeopleSpaceUtils.NotificationAction.POSTED)) { Set<String> widgetIdsUpdatedByUri = supplementTilesByUri(sbn, action, key); if (widgetIdsUpdatedByUri != null && !widgetIdsUpdatedByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "Added due to uri: " + widgetIdsUpdatedByUri); mNotificationKeyToWidgetIdsMatchedByUri.put(sbn.getKey(), widgetIdsUpdatedByUri); } } else { // Remove the notification on any widgets where the notification was added // purely based on the Uri. Set<String> widgetsPreviouslyUpdatedByUri = mNotificationKeyToWidgetIdsMatchedByUri.remove(sbn.getKey()); if (widgetsPreviouslyUpdatedByUri != null && !widgetsPreviouslyUpdatedByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "Remove due to uri: " + widgetsPreviouslyUpdatedByUri); updateWidgetIdsForNotificationAction(widgetsPreviouslyUpdatedByUri, sbn, action); } } } /** * Retrieves from storage any tiles with the same contact Uri as linked via the {@code sbn}. * Supplements the tiles with the notification content only if they still have {@link * android.Manifest.permission.READ_CONTACTS} permission. */ @Nullable private Set<String> supplementTilesByUri(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, PeopleTileKey key) { if (!shouldMatchNotificationByUri(sbn)) { if (DEBUG) Log.d(TAG, "Should not supplement conversation"); return null; } // Try to get the Contact Uri from the Missed Call notification directly. String contactUri = getContactUri(sbn); if (contactUri == null) { if (DEBUG) Log.d(TAG, "No contact uri"); return null; } // Supplement any tiles with the same Uri. Set<String> storedWidgetIdsByUri = new HashSet<>(mSharedPrefs.getStringSet(contactUri, new HashSet<>())); if (storedWidgetIdsByUri.isEmpty()) { if (DEBUG) Log.d(TAG, "No tiles for contact"); return null; } if (mPackageManager.checkPermission(READ_CONTACTS, sbn.getPackageName()) != PackageManager.PERMISSION_GRANTED) { if (DEBUG) Log.d(TAG, "Notifying app missing permissions"); return null; } Set<String> widgetIdsUpdatedByUri = new HashSet<>(); for (String widgetIdString : storedWidgetIdsByUri) { int widgetId = Integer.parseInt(widgetIdString); PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId); // Don't update a widget already updated. if (key.equals(new PeopleTileKey(storedTile))) { continue; } if (storedTile == null || mPackageManager.checkPermission(READ_CONTACTS, storedTile.getPackageName()) != PackageManager.PERMISSION_GRANTED) { if (DEBUG) Log.d(TAG, "Cannot supplement tile: " + storedTile.getUserName()); continue; } if (DEBUG) Log.d(TAG, "Adding notification by uri: " + sbn.getKey()); updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId, storedTile); widgetIdsUpdatedByUri.add(String.valueOf(widgetId)); } return widgetIdsUpdatedByUri; } /** * Try to retrieve a valid Uri via {@code sbn}, falling back to the {@code * contactUriFromShortcut} if valid. */ @Nullable private String getContactUri(StatusBarNotification sbn) { // First, try to get a Uri from the Person directly set on the Notification. ArrayList<Person> people = sbn.getNotification().extras.getParcelableArrayList( EXTRA_PEOPLE_LIST); if (people != null && people.get(0) != null) { String contactUri = people.get(0).getUri(); if (contactUri != null && !contactUri.isEmpty()) { return contactUri; } } // Then, try to get a Uri from the Person set on the Notification message. List<Notification.MessagingStyle.Message> messages = getMessagingStyleMessages(sbn.getNotification()); if (messages != null && !messages.isEmpty()) { Notification.MessagingStyle.Message message = messages.get(0); Person sender = message.getSenderPerson(); if (sender != null && sender.getUri() != null && !sender.getUri().isEmpty()) { return sender.getUri(); } } return null; } /** * Returns whether a notification should be matched to other Tiles by Uri. * * <p>Currently only matches missed calls. */ private boolean shouldMatchNotificationByUri(StatusBarNotification sbn) { Notification notification = sbn.getNotification(); if (notification == null) { if (DEBUG) Log.d(TAG, "Notification is null"); return false; } if (!Objects.equals(notification.category, CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Not missed call"); return false; } return true; } /** * Update the tiles associated with the incoming conversation update. */ Loading Loading @@ -309,16 +494,11 @@ public class PeopleSpaceWidgetManager { private void updateStorageAndViewWithNotificationData( StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, int appWidgetId) { PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); return; } int appWidgetId, PeopleSpaceTile storedTile) { if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); storedTile = augmentTileFromNotification(mContext, storedTile, sbn); } else { } else if (storedTile.getNotificationKey().equals(sbn.getKey())) { if (DEBUG) { Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); } Loading Loading @@ -440,7 +620,8 @@ public class PeopleSpaceWidgetManager { synchronized (mLock) { if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId()); PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId, tile.getContactUri()); } try { if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); Loading Loading @@ -496,6 +677,7 @@ public class PeopleSpaceWidgetManager { // Retrieve storage needed for widget deletion. PeopleTileKey key; Set<String> storedWidgetIdsForKey; String contactUriString; synchronized (mLock) { SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), Context.MODE_PRIVATE); Loading @@ -509,9 +691,11 @@ public class PeopleSpaceWidgetManager { } storedWidgetIdsForKey = new HashSet<>( mSharedPrefs.getStringSet(key.toString(), new HashSet<>())); contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null); } synchronized (mLock) { PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId); PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId, contactUriString); } // 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()); Loading
packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +340 −25 File changed.Preview size limit exceeded, changes collapsed. Show changes