Loading packages/SystemUI/AndroidManifest.xml +7 −1 Original line number Diff line number Diff line Loading @@ -281,6 +281,8 @@ <!-- Permission for Smartspace. --> <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> <uses-permission android:name="android.permission.READ_PEOPLE_DATA" /> <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> Loading Loading @@ -604,7 +606,8 @@ </activity> <activity android:name=".people.widget.LaunchConversationActivity" android:windowDisablePreview="true" /> android:windowDisablePreview="true" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" /> <!-- People Space Widget --> <receiver Loading @@ -630,6 +633,9 @@ android:permission="android.permission.GET_PEOPLE_TILE_PREVIEW"> </provider> <service android:name=".people.PeopleBackupFollowUpJob" android:permission="android.permission.BIND_JOB_SERVICE"/> <!-- a gallery of delicious treats --> <service android:name=".DessertCaseDream" Loading packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt +13 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.os.UserHandle import android.util.Log import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper import com.android.systemui.people.widget.PeopleBackupHelper /** * Helper for backing up elements in SystemUI Loading @@ -45,18 +46,29 @@ class BackupHelper : BackupAgentHelper() { private const val TAG = "BackupHelper" internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite" private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" val controlsDataLock = Any() const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED" private const val PERMISSION_SELF = "com.android.systemui.permission.SELF" } override fun onCreate() { override fun onCreate(userHandle: UserHandle, operationType: Int) { super.onCreate() // The map in mapOf is guaranteed to be order preserving val controlsMap = mapOf(CONTROLS to getPPControlsFile(this)) NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also { addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it) } // Conversations widgets backup only works for system user, because widgets' information is // stored in system user's SharedPreferences files and we can't open those from other users. if (!userHandle.isSystem) { return } val keys = PeopleBackupHelper.getFilesToBackup() addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper( this, userHandle, keys.toTypedArray())) } override fun onRestoreFinished() { Loading packages/SystemUI/src/com/android/systemui/people/PeopleBackupFollowUpJob.java 0 → 100644 +229 −0 Original line number Diff line number Diff line /* * 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; import static com.android.systemui.people.PeopleSpaceUtils.DEBUG; import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; import static com.android.systemui.people.PeopleSpaceUtils.removeSharedPreferencesStorageForTile; import static com.android.systemui.people.widget.PeopleBackupHelper.isReadyForRestore; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.app.people.IPeopleManager; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.PersistableBundle; import android.os.ServiceManager; import android.preference.PreferenceManager; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.systemui.people.widget.PeopleBackupHelper; import com.android.systemui.people.widget.PeopleTileKey; import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Follow-up job that runs after a Conversations widgets restore operation. Check if shortcuts that * were not available before are now available. If any shortcut doesn't become available after * 1 day, we clean up its storage. */ public class PeopleBackupFollowUpJob extends JobService { private static final String TAG = "PeopleBackupFollowUpJob"; private static final String START_DATE = "start_date"; /** Follow-up job id. */ public static final int JOB_ID = 74823873; private static final long JOB_PERIODIC_DURATION = Duration.ofHours(6).toMillis(); private static final long CLEAN_UP_STORAGE_AFTER_DURATION = Duration.ofHours(24).toMillis(); /** SharedPreferences file name for follow-up specific storage.*/ public static final String SHARED_FOLLOW_UP = "shared_follow_up"; private final Object mLock = new Object(); private Context mContext; private PackageManager mPackageManager; private IPeopleManager mIPeopleManager; private JobScheduler mJobScheduler; /** Schedules a PeopleBackupFollowUpJob every 2 hours. */ public static void scheduleJob(Context context) { JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); PersistableBundle bundle = new PersistableBundle(); bundle.putLong(START_DATE, System.currentTimeMillis()); JobInfo jobInfo = new JobInfo .Builder(JOB_ID, new ComponentName(context, PeopleBackupFollowUpJob.class)) .setPeriodic(JOB_PERIODIC_DURATION) .setExtras(bundle) .build(); jobScheduler.schedule(jobInfo); } @Override public void onCreate() { super.onCreate(); mContext = getApplicationContext(); mPackageManager = getApplicationContext().getPackageManager(); mIPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); mJobScheduler = mContext.getSystemService(JobScheduler.class); } /** Sets necessary managers for testing. */ @VisibleForTesting public void setManagers(Context context, PackageManager packageManager, IPeopleManager iPeopleManager, JobScheduler jobScheduler) { mContext = context; mPackageManager = packageManager; mIPeopleManager = iPeopleManager; mJobScheduler = jobScheduler; } @Override public boolean onStartJob(JobParameters params) { if (DEBUG) Log.d(TAG, "Starting job."); synchronized (mLock) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sp.edit(); SharedPreferences followUp = this.getSharedPreferences( SHARED_FOLLOW_UP, Context.MODE_PRIVATE); SharedPreferences.Editor followUpEditor = followUp.edit(); // Remove from SHARED_FOLLOW_UP storage all widgets that are now ready to be updated. Map<String, Set<String>> remainingWidgets = processFollowUpFile(followUp, followUpEditor); // Check if all widgets were restored or if enough time elapsed to cancel the job. long start = params.getExtras().getLong(START_DATE); long now = System.currentTimeMillis(); if (shouldCancelJob(remainingWidgets, start, now)) { cancelJobAndClearRemainingWidgets(remainingWidgets, followUpEditor, sp); } editor.apply(); followUpEditor.apply(); } // Ensure all widgets modified from SHARED_FOLLOW_UP storage are now updated. PeopleBackupHelper.updateWidgets(mContext); return false; } /** * Iterates through follow-up file entries and checks which shortcuts are now available. * Returns a map of shortcuts that should be checked at a later time. */ public Map<String, Set<String>> processFollowUpFile(SharedPreferences followUp, SharedPreferences.Editor followUpEditor) { Map<String, Set<String>> remainingWidgets = new HashMap<>(); Map<String, ?> all = followUp.getAll(); for (Map.Entry<String, ?> entry : all.entrySet()) { String key = entry.getKey(); PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); boolean restored = isReadyForRestore(mIPeopleManager, mPackageManager, peopleTileKey); if (restored) { if (DEBUG) Log.d(TAG, "Removing key from follow-up: " + key); followUpEditor.remove(key); continue; } if (DEBUG) Log.d(TAG, "Key should not be restored yet, try later: " + key); try { remainingWidgets.put(entry.getKey(), (Set<String>) entry.getValue()); } catch (Exception e) { Log.e(TAG, "Malformed entry value: " + entry.getValue()); } } return remainingWidgets; } /** Returns whether all shortcuts were restored or if enough time elapsed to cancel the job. */ public boolean shouldCancelJob(Map<String, Set<String>> remainingWidgets, long start, long now) { if (remainingWidgets.isEmpty()) { if (DEBUG) Log.d(TAG, "All widget storage was successfully restored."); return true; } boolean oneDayHasPassed = (now - start) > CLEAN_UP_STORAGE_AFTER_DURATION; if (oneDayHasPassed) { if (DEBUG) { Log.w(TAG, "One or more widgets were not properly restored, " + "but cancelling job because it has been a day."); } return true; } if (DEBUG) Log.d(TAG, "There are still non-restored widgets, run job again."); return false; } /** Cancels job and removes storage of any shortcut that was not restored. */ public void cancelJobAndClearRemainingWidgets(Map<String, Set<String>> remainingWidgets, SharedPreferences.Editor followUpEditor, SharedPreferences sp) { if (DEBUG) Log.d(TAG, "Cancelling follow up job."); removeUnavailableShortcutsFromSharedStorage(remainingWidgets, sp); followUpEditor.clear(); mJobScheduler.cancel(JOB_ID); } private void removeUnavailableShortcutsFromSharedStorage(Map<String, Set<String>> remainingWidgets, SharedPreferences sp) { for (Map.Entry<String, Set<String>> entry : remainingWidgets.entrySet()) { PeopleTileKey peopleTileKey = PeopleTileKey.fromString(entry.getKey()); if (!PeopleTileKey.isValid(peopleTileKey)) { Log.e(TAG, "Malformed peopleTileKey in follow-up file: " + entry.getKey()); continue; } Set<String> widgetIds; try { widgetIds = (Set<String>) entry.getValue(); } catch (Exception e) { Log.e(TAG, "Malformed widget ids in follow-up file: " + e); continue; } for (String id : widgetIds) { int widgetId; try { widgetId = Integer.parseInt(id); } catch (NumberFormatException ex) { Log.e(TAG, "Malformed widget id in follow-up file: " + ex); continue; } String contactUriString = sp.getString(String.valueOf(widgetId), EMPTY_STRING); removeSharedPreferencesStorageForTile( mContext, peopleTileKey, widgetId, contactUriString); } } } @Override public boolean onStopJob(JobParameters params) { return false; } } packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +13 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.android.systemui.people.NotificationHelper.shouldMatchNotifica import android.annotation.Nullable; import android.app.Notification; import android.app.backup.BackupManager; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; Loading Loading @@ -89,7 +90,7 @@ public class PeopleSpaceUtils { /** Returns stored widgets for the conversation specified. */ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { if (!key.isValid()) { if (!PeopleTileKey.isValid(key)) { return new HashSet<>(); } return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>())); Loading @@ -97,19 +98,16 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int appWidgetId, Uri contactUri) { if (!key.isValid()) { int appWidgetId, Uri contactUri, BackupManager backupManager) { if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "Not storing for invalid key"); return; } // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); SharedPreferences.Editor widgetEditor = widgetSp.edit(); widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName()); widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId()); widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId()); widgetEditor.apply(); SharedPreferencesHelper.setPeopleTileKey(widgetSp, key); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString(); Loading @@ -117,14 +115,18 @@ public class PeopleSpaceUtils { // Don't overwrite existing widgets with the same key. addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString()); if (!TextUtils.isEmpty(contactUriString)) { addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); } editor.apply(); backupManager.dataChanged(); } /** Removes stored data when tile is deleted. */ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int widgetId, String contactUriString) { // Delete widgetId mapping to key. if (DEBUG) Log.d(TAG, "Removing widget info from sharedPrefs"); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.remove(String.valueOf(widgetId)); Loading Loading @@ -230,7 +232,7 @@ public class PeopleSpaceUtils { */ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount, Optional<Integer> appWidgetId) { Optional<Integer> appWidgetId, BackupManager backupManager) { if (notificationEntry == null || notificationEntry.getSbn().getNotification() == null) { if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification is null"); return removeNotificationFields(tile); Loading @@ -246,7 +248,7 @@ public class PeopleSpaceUtils { Uri contactUri = Uri.parse(uriFromNotification); // Update storage. setSharedPreferencesStorageForTile(context, new PeopleTileKey(tile), appWidgetId.get(), contactUri); contactUri, backupManager); // Update cached tile in-memory. updatedTile.setContactUri(contactUri); } Loading packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +16 −6 Original line number Diff line number Diff line Loading @@ -330,11 +330,8 @@ public class PeopleTileViewHelper { R.layout.people_tile_suppressed_layout); } Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon); Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon); FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); drawable.setIsDisabled(true); Bitmap convertedBitmap = convertDrawableToBitmap(drawable); views.setImageViewBitmap(R.id.icon, convertedBitmap); Bitmap disabledBitmap = convertDrawableToDisabledBitmap(appIcon); views.setImageViewBitmap(R.id.icon, disabledBitmap); return views; } Loading Loading @@ -504,6 +501,11 @@ public class PeopleTileViewHelper { } private RemoteViews setLaunchIntents(RemoteViews views) { if (!PeopleTileKey.isValid(mKey) || mTile == null) { if (DEBUG) Log.d(TAG, "Skipping launch intent, Null tile or invalid key: " + mKey); return views; } try { Intent activityIntent = new Intent(mContext, LaunchConversationActivity.class); activityIntent.addFlags( Loading Loading @@ -1067,7 +1069,8 @@ public class PeopleTileViewHelper { Icon icon = tile.getUserIcon(); if (icon == null) { return null; Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge); return convertDrawableToDisabledBitmap(placeholder); } PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context, context.getPackageManager(), Loading Loading @@ -1179,4 +1182,11 @@ public class PeopleTileViewHelper { mAvatarSize = avatarSize; } } private static Bitmap convertDrawableToDisabledBitmap(Drawable icon) { Bitmap appIconAsBitmap = convertDrawableToBitmap(icon); FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); drawable.setIsDisabled(true); return convertDrawableToBitmap(drawable); } } Loading
packages/SystemUI/AndroidManifest.xml +7 −1 Original line number Diff line number Diff line Loading @@ -281,6 +281,8 @@ <!-- Permission for Smartspace. --> <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> <uses-permission android:name="android.permission.READ_PEOPLE_DATA" /> <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> Loading Loading @@ -604,7 +606,8 @@ </activity> <activity android:name=".people.widget.LaunchConversationActivity" android:windowDisablePreview="true" /> android:windowDisablePreview="true" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" /> <!-- People Space Widget --> <receiver Loading @@ -630,6 +633,9 @@ android:permission="android.permission.GET_PEOPLE_TILE_PREVIEW"> </provider> <service android:name=".people.PeopleBackupFollowUpJob" android:permission="android.permission.BIND_JOB_SERVICE"/> <!-- a gallery of delicious treats --> <service android:name=".DessertCaseDream" Loading
packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt +13 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.os.UserHandle import android.util.Log import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper import com.android.systemui.people.widget.PeopleBackupHelper /** * Helper for backing up elements in SystemUI Loading @@ -45,18 +46,29 @@ class BackupHelper : BackupAgentHelper() { private const val TAG = "BackupHelper" internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite" private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" val controlsDataLock = Any() const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED" private const val PERMISSION_SELF = "com.android.systemui.permission.SELF" } override fun onCreate() { override fun onCreate(userHandle: UserHandle, operationType: Int) { super.onCreate() // The map in mapOf is guaranteed to be order preserving val controlsMap = mapOf(CONTROLS to getPPControlsFile(this)) NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also { addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it) } // Conversations widgets backup only works for system user, because widgets' information is // stored in system user's SharedPreferences files and we can't open those from other users. if (!userHandle.isSystem) { return } val keys = PeopleBackupHelper.getFilesToBackup() addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper( this, userHandle, keys.toTypedArray())) } override fun onRestoreFinished() { Loading
packages/SystemUI/src/com/android/systemui/people/PeopleBackupFollowUpJob.java 0 → 100644 +229 −0 Original line number Diff line number Diff line /* * 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; import static com.android.systemui.people.PeopleSpaceUtils.DEBUG; import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; import static com.android.systemui.people.PeopleSpaceUtils.removeSharedPreferencesStorageForTile; import static com.android.systemui.people.widget.PeopleBackupHelper.isReadyForRestore; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.app.people.IPeopleManager; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.PersistableBundle; import android.os.ServiceManager; import android.preference.PreferenceManager; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.systemui.people.widget.PeopleBackupHelper; import com.android.systemui.people.widget.PeopleTileKey; import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Follow-up job that runs after a Conversations widgets restore operation. Check if shortcuts that * were not available before are now available. If any shortcut doesn't become available after * 1 day, we clean up its storage. */ public class PeopleBackupFollowUpJob extends JobService { private static final String TAG = "PeopleBackupFollowUpJob"; private static final String START_DATE = "start_date"; /** Follow-up job id. */ public static final int JOB_ID = 74823873; private static final long JOB_PERIODIC_DURATION = Duration.ofHours(6).toMillis(); private static final long CLEAN_UP_STORAGE_AFTER_DURATION = Duration.ofHours(24).toMillis(); /** SharedPreferences file name for follow-up specific storage.*/ public static final String SHARED_FOLLOW_UP = "shared_follow_up"; private final Object mLock = new Object(); private Context mContext; private PackageManager mPackageManager; private IPeopleManager mIPeopleManager; private JobScheduler mJobScheduler; /** Schedules a PeopleBackupFollowUpJob every 2 hours. */ public static void scheduleJob(Context context) { JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); PersistableBundle bundle = new PersistableBundle(); bundle.putLong(START_DATE, System.currentTimeMillis()); JobInfo jobInfo = new JobInfo .Builder(JOB_ID, new ComponentName(context, PeopleBackupFollowUpJob.class)) .setPeriodic(JOB_PERIODIC_DURATION) .setExtras(bundle) .build(); jobScheduler.schedule(jobInfo); } @Override public void onCreate() { super.onCreate(); mContext = getApplicationContext(); mPackageManager = getApplicationContext().getPackageManager(); mIPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); mJobScheduler = mContext.getSystemService(JobScheduler.class); } /** Sets necessary managers for testing. */ @VisibleForTesting public void setManagers(Context context, PackageManager packageManager, IPeopleManager iPeopleManager, JobScheduler jobScheduler) { mContext = context; mPackageManager = packageManager; mIPeopleManager = iPeopleManager; mJobScheduler = jobScheduler; } @Override public boolean onStartJob(JobParameters params) { if (DEBUG) Log.d(TAG, "Starting job."); synchronized (mLock) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sp.edit(); SharedPreferences followUp = this.getSharedPreferences( SHARED_FOLLOW_UP, Context.MODE_PRIVATE); SharedPreferences.Editor followUpEditor = followUp.edit(); // Remove from SHARED_FOLLOW_UP storage all widgets that are now ready to be updated. Map<String, Set<String>> remainingWidgets = processFollowUpFile(followUp, followUpEditor); // Check if all widgets were restored or if enough time elapsed to cancel the job. long start = params.getExtras().getLong(START_DATE); long now = System.currentTimeMillis(); if (shouldCancelJob(remainingWidgets, start, now)) { cancelJobAndClearRemainingWidgets(remainingWidgets, followUpEditor, sp); } editor.apply(); followUpEditor.apply(); } // Ensure all widgets modified from SHARED_FOLLOW_UP storage are now updated. PeopleBackupHelper.updateWidgets(mContext); return false; } /** * Iterates through follow-up file entries and checks which shortcuts are now available. * Returns a map of shortcuts that should be checked at a later time. */ public Map<String, Set<String>> processFollowUpFile(SharedPreferences followUp, SharedPreferences.Editor followUpEditor) { Map<String, Set<String>> remainingWidgets = new HashMap<>(); Map<String, ?> all = followUp.getAll(); for (Map.Entry<String, ?> entry : all.entrySet()) { String key = entry.getKey(); PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); boolean restored = isReadyForRestore(mIPeopleManager, mPackageManager, peopleTileKey); if (restored) { if (DEBUG) Log.d(TAG, "Removing key from follow-up: " + key); followUpEditor.remove(key); continue; } if (DEBUG) Log.d(TAG, "Key should not be restored yet, try later: " + key); try { remainingWidgets.put(entry.getKey(), (Set<String>) entry.getValue()); } catch (Exception e) { Log.e(TAG, "Malformed entry value: " + entry.getValue()); } } return remainingWidgets; } /** Returns whether all shortcuts were restored or if enough time elapsed to cancel the job. */ public boolean shouldCancelJob(Map<String, Set<String>> remainingWidgets, long start, long now) { if (remainingWidgets.isEmpty()) { if (DEBUG) Log.d(TAG, "All widget storage was successfully restored."); return true; } boolean oneDayHasPassed = (now - start) > CLEAN_UP_STORAGE_AFTER_DURATION; if (oneDayHasPassed) { if (DEBUG) { Log.w(TAG, "One or more widgets were not properly restored, " + "but cancelling job because it has been a day."); } return true; } if (DEBUG) Log.d(TAG, "There are still non-restored widgets, run job again."); return false; } /** Cancels job and removes storage of any shortcut that was not restored. */ public void cancelJobAndClearRemainingWidgets(Map<String, Set<String>> remainingWidgets, SharedPreferences.Editor followUpEditor, SharedPreferences sp) { if (DEBUG) Log.d(TAG, "Cancelling follow up job."); removeUnavailableShortcutsFromSharedStorage(remainingWidgets, sp); followUpEditor.clear(); mJobScheduler.cancel(JOB_ID); } private void removeUnavailableShortcutsFromSharedStorage(Map<String, Set<String>> remainingWidgets, SharedPreferences sp) { for (Map.Entry<String, Set<String>> entry : remainingWidgets.entrySet()) { PeopleTileKey peopleTileKey = PeopleTileKey.fromString(entry.getKey()); if (!PeopleTileKey.isValid(peopleTileKey)) { Log.e(TAG, "Malformed peopleTileKey in follow-up file: " + entry.getKey()); continue; } Set<String> widgetIds; try { widgetIds = (Set<String>) entry.getValue(); } catch (Exception e) { Log.e(TAG, "Malformed widget ids in follow-up file: " + e); continue; } for (String id : widgetIds) { int widgetId; try { widgetId = Integer.parseInt(id); } catch (NumberFormatException ex) { Log.e(TAG, "Malformed widget id in follow-up file: " + ex); continue; } String contactUriString = sp.getString(String.valueOf(widgetId), EMPTY_STRING); removeSharedPreferencesStorageForTile( mContext, peopleTileKey, widgetId, contactUriString); } } } @Override public boolean onStopJob(JobParameters params) { return false; } }
packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +13 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.android.systemui.people.NotificationHelper.shouldMatchNotifica import android.annotation.Nullable; import android.app.Notification; import android.app.backup.BackupManager; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; Loading Loading @@ -89,7 +90,7 @@ public class PeopleSpaceUtils { /** Returns stored widgets for the conversation specified. */ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { if (!key.isValid()) { if (!PeopleTileKey.isValid(key)) { return new HashSet<>(); } return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>())); Loading @@ -97,19 +98,16 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int appWidgetId, Uri contactUri) { if (!key.isValid()) { int appWidgetId, Uri contactUri, BackupManager backupManager) { if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "Not storing for invalid key"); return; } // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); SharedPreferences.Editor widgetEditor = widgetSp.edit(); widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName()); widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId()); widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId()); widgetEditor.apply(); SharedPreferencesHelper.setPeopleTileKey(widgetSp, key); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString(); Loading @@ -117,14 +115,18 @@ public class PeopleSpaceUtils { // Don't overwrite existing widgets with the same key. addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString()); if (!TextUtils.isEmpty(contactUriString)) { addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); } editor.apply(); backupManager.dataChanged(); } /** Removes stored data when tile is deleted. */ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int widgetId, String contactUriString) { // Delete widgetId mapping to key. if (DEBUG) Log.d(TAG, "Removing widget info from sharedPrefs"); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.remove(String.valueOf(widgetId)); Loading Loading @@ -230,7 +232,7 @@ public class PeopleSpaceUtils { */ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount, Optional<Integer> appWidgetId) { Optional<Integer> appWidgetId, BackupManager backupManager) { if (notificationEntry == null || notificationEntry.getSbn().getNotification() == null) { if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification is null"); return removeNotificationFields(tile); Loading @@ -246,7 +248,7 @@ public class PeopleSpaceUtils { Uri contactUri = Uri.parse(uriFromNotification); // Update storage. setSharedPreferencesStorageForTile(context, new PeopleTileKey(tile), appWidgetId.get(), contactUri); contactUri, backupManager); // Update cached tile in-memory. updatedTile.setContactUri(contactUri); } Loading
packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +16 −6 Original line number Diff line number Diff line Loading @@ -330,11 +330,8 @@ public class PeopleTileViewHelper { R.layout.people_tile_suppressed_layout); } Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon); Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon); FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); drawable.setIsDisabled(true); Bitmap convertedBitmap = convertDrawableToBitmap(drawable); views.setImageViewBitmap(R.id.icon, convertedBitmap); Bitmap disabledBitmap = convertDrawableToDisabledBitmap(appIcon); views.setImageViewBitmap(R.id.icon, disabledBitmap); return views; } Loading Loading @@ -504,6 +501,11 @@ public class PeopleTileViewHelper { } private RemoteViews setLaunchIntents(RemoteViews views) { if (!PeopleTileKey.isValid(mKey) || mTile == null) { if (DEBUG) Log.d(TAG, "Skipping launch intent, Null tile or invalid key: " + mKey); return views; } try { Intent activityIntent = new Intent(mContext, LaunchConversationActivity.class); activityIntent.addFlags( Loading Loading @@ -1067,7 +1069,8 @@ public class PeopleTileViewHelper { Icon icon = tile.getUserIcon(); if (icon == null) { return null; Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge); return convertDrawableToDisabledBitmap(placeholder); } PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context, context.getPackageManager(), Loading Loading @@ -1179,4 +1182,11 @@ public class PeopleTileViewHelper { mAvatarSize = avatarSize; } } private static Bitmap convertDrawableToDisabledBitmap(Drawable icon) { Bitmap appIconAsBitmap = convertDrawableToBitmap(icon); FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); drawable.setIsDisabled(true); return convertDrawableToBitmap(drawable); } }