Merge "Expanded LauncherPrefs APIs to Replace Direct Shared Preference Usage." into tm-qpr-dev

This commit is contained in:
Stefan Andonian
2023-01-17 18:23:10 +00:00
committed by Android (Google) Code Review
14 changed files with 462 additions and 90 deletions
@@ -16,6 +16,7 @@
package com.android.quickstep.logging;
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
import static com.android.launcher3.LauncherPrefs.getPrefs;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
@@ -39,6 +40,7 @@ import android.util.Log;
import android.util.Xml;
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
@@ -178,7 +180,7 @@ public class SettingsChangeLogger implements
logger::log);
SharedPreferences prefs = getPrefs(mContext);
logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
logger.log(LauncherPrefs.get(mContext).get(THEMED_ICONS)
? LAUNCHER_THEMED_ICON_ENABLED
: LAUNCHER_THEMED_ICON_DISABLED);
@@ -18,7 +18,8 @@ package com.android.launcher3;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
import static com.android.launcher3.LauncherPrefs.ICON_STATE;
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
@@ -55,7 +56,7 @@ import com.android.launcher3.widget.custom.CustomWidgetManager;
public class LauncherAppState implements SafeCloseable {
public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
private static final String KEY_ICON_STATE = "pref_icon_shape_path";
public static final String KEY_ICON_STATE = "pref_icon_shape_path";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -117,10 +118,9 @@ public class LauncherAppState implements SafeCloseable {
observer, MODEL_EXECUTOR.getHandler());
mOnTerminateCallback.add(iconChangeTracker::close);
MODEL_EXECUTOR.execute(observer::verifyIconChanged);
SharedPreferences prefs = LauncherPrefs.getPrefs(mContext);
prefs.registerOnSharedPreferenceChangeListener(observer);
LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
mOnTerminateCallback.add(
() -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
() -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
@@ -207,12 +207,12 @@ public class LauncherAppState implements SafeCloseable {
public void onSystemIconStateChanged(String iconState) {
IconShape.init(mContext);
refreshAndReloadLauncher();
getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
LauncherPrefs.get(mContext).put(ICON_STATE, iconState);
}
void verifyIconChanged() {
String iconState = mIconProvider.getSystemIconState();
if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) {
onSystemIconStateChanged(iconState);
}
}
+246 -15
View File
@@ -2,24 +2,255 @@ package com.android.launcher3
import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.annotation.VisibleForTesting
import com.android.launcher3.allapps.WorkProfileManager
import com.android.launcher3.model.DeviceGridState
import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.MainThreadInitializedObject
import com.android.launcher3.util.Themes
object LauncherPrefs {
/**
* Use same context for shared preferences, so that we use a single cached instance
* TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
*/
class LauncherPrefs(private val context: Context) {
@JvmStatic
fun getPrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so that we use a single cached instance
return context.applicationContext.getSharedPreferences(
LauncherFiles.SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
/**
* Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
* default value type, and will throw an error if the type of the item provided is not a
* `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
*/
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
fun <T : Any> get(item: Item<T>): T {
val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
return when (item.defaultValue::class.java) {
String::class.java -> sp.getString(item.sharedPrefKey, item.defaultValue as String)
Boolean::class.java,
java.lang.Boolean::class.java ->
sp.getBoolean(item.sharedPrefKey, item.defaultValue as Boolean)
Int::class.java,
java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, item.defaultValue as Int)
Float::class.java,
java.lang.Float::class.java ->
sp.getFloat(item.sharedPrefKey, item.defaultValue as Float)
Long::class.java,
java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, item.defaultValue as Long)
Set::class.java -> sp.getStringSet(item.sharedPrefKey, item.defaultValue as Set<String>)
else ->
throw IllegalArgumentException(
"item type: ${item.defaultValue::class.java}" +
" is not compatible with sharedPref methods"
)
}
as T
}
@JvmStatic
fun getDevicePrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so that we use a single cached instance
return context.applicationContext.getSharedPreferences(
LauncherFiles.DEVICE_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
/**
* Stores each of the values provided in `SharedPreferences` according to the configuration
* contained within the associated items provided. Internally, it uses apply, so the caller
* cannot assume that the values that have been put are immediately available for use.
*
* The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
* prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
* provided item configurations.
*/
fun put(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
prepareToPutValues(itemsToValues).forEach { it.apply() }
/**
* Stores the value provided in `SharedPreferences` according to the item configuration provided
* It is asynchronous, so the caller can't assume that the value put is immediately available.
*/
fun <T : Any> put(item: Item<T>, value: T): Unit =
context
.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
.edit()
.putValue(item, value)
.apply()
/**
* Synchronously stores all the values provided according to their associated Item
* configuration.
*/
fun putSync(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
prepareToPutValues(itemsToValues).forEach { it.commit() }
/**
* Update each shared preference file with the item - value pairs provided. This method is
* optimized to avoid retrieving the same shared preference file multiple times.
*
* @return `List<SharedPreferences.Editor>` 1 for each distinct shared preference file among the
* items given as part of the itemsToValues parameter
*/
private fun prepareToPutValues(
itemsToValues: Array<out Pair<Item<*>, Any>>
): List<SharedPreferences.Editor> =
itemsToValues
.groupBy { it.first.sharedPrefFile }
.map { fileToItemValueList ->
context
.getSharedPreferences(fileToItemValueList.key, Context.MODE_PRIVATE)
.edit()
.apply {
fileToItemValueList.value.forEach { itemToValue ->
putValue(itemToValue.first, itemToValue.second)
}
}
}
/**
* Handles adding values to `SharedPreferences` regardless of type. This method is especially
* helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
* types of Item values.
*/
@Suppress("UNCHECKED_CAST")
private fun SharedPreferences.Editor.putValue(
item: Item<*>,
value: Any
): SharedPreferences.Editor =
when (value::class.java) {
String::class.java -> putString(item.sharedPrefKey, value as String)
Boolean::class.java,
java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
Int::class.java,
java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
Float::class.java,
java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
Long::class.java,
java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Set::class.java -> putStringSet(item.sharedPrefKey, value as Set<String>)
else ->
throw IllegalArgumentException(
"item type: " +
"${item.defaultValue!!::class} is not compatible with sharedPref methods"
)
}
/**
* After calling this method, the listener will be notified of any future updates to the
* `SharedPreferences` files associated with the provided list of items. The listener will need
* to filter update notifications so they don't activate for non-relevant updates.
*/
fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
items
.map { it.sharedPrefFile }
.distinct()
.forEach {
context
.getSharedPreferences(it, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(listener)
}
}
/**
* Stops the listener from getting notified of any more updates to any of the
* `SharedPreferences` files associated with any of the provided list of [Item].
*/
fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
// If a listener is not registered to a SharedPreference, unregistering it does nothing
items
.map { it.sharedPrefFile }
.distinct()
.forEach {
context
.getSharedPreferences(it, Context.MODE_PRIVATE)
.unregisterOnSharedPreferenceChangeListener(listener)
}
}
/**
* Checks if all the provided [Item] have values stored in their corresponding
* `SharedPreferences` files.
*/
fun has(vararg items: Item<*>): Boolean {
items
.groupBy { it.sharedPrefFile }
.forEach { (file, itemsSublist) ->
val prefs: SharedPreferences =
context.getSharedPreferences(file, Context.MODE_PRIVATE)
if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
}
return true
}
/**
* Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
*/
fun remove(vararg items: Item<*>) = prepareToRemove(items).forEach { it.apply() }
/** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
fun removeSync(vararg items: Item<*>) = prepareToRemove(items).forEach { it.commit() }
/**
* Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values
* from their respective `SharedPreferences` files. These returned `Editors` can then be
* committed or applied for synchronous or async behavior.
*/
private fun prepareToRemove(items: Array<out Item<*>>): List<SharedPreferences.Editor> =
items
.groupBy { it.sharedPrefFile }
.map { (file, items) ->
context.getSharedPreferences(file, Context.MODE_PRIVATE).edit().also { editor ->
items.forEach { item -> editor.remove(item.sharedPrefKey) }
}
}
companion object {
@JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
@JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
@JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "")
@JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false)
@JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
@JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
@JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "")
@JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1)
@JvmField
val DEVICE_TYPE =
backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
@JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "")
@JvmField
val RESTORE_DEVICE =
backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
@JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
@JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
@VisibleForTesting
@JvmStatic
fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): Item<T> =
Item(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue)
@VisibleForTesting
@JvmStatic
fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): Item<T> =
Item(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue)
@Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
@JvmStatic
fun getPrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use single cached instance
return context.applicationContext.getSharedPreferences(
LauncherFiles.SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
@Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
@JvmStatic
fun getDevicePrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use a single cached instance
return context.applicationContext.getSharedPreferences(
LauncherFiles.DEVICE_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
}
}
data class Item<T>(val sharedPrefKey: String, val sharedPrefFile: String, val defaultValue: T) {
fun to(value: T): Pair<Item<T>, T> = Pair(this, value)
}
@@ -57,7 +57,6 @@ import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
@@ -161,10 +160,8 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
R.dimen.dynamic_grid_cell_border_spacing);
mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
mWorkManager = new WorkProfileManager(
mActivityContext.getSystemService(UserManager.class),
this, LauncherPrefs.getPrefs(mActivityContext),
mActivityContext.getStatsLogManager());
mWorkManager = new WorkProfileManager(mActivityContext.getSystemService(UserManager.class),
this, mActivityContext.getStatsLogManager());
mAH = Arrays.asList(null, null, null);
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
import android.content.Context;
@@ -85,8 +86,7 @@ public class WorkEduCard extends FrameLayout implements
@Override
public void onClick(View view) {
startAnimation(mDismissAnim);
LauncherPrefs.getPrefs(getContext()).edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP,
1).apply();
LauncherPrefs.get(getContext()).put(WORK_EDU_STEP, 1);
}
@Override
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.MAIN;
@@ -26,7 +27,6 @@ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
@@ -86,14 +87,12 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP
@WorkProfileState
private int mCurrentState;
private SharedPreferences mPreferences;
public WorkProfileManager(
UserManager userManager, BaseAllAppsContainerView<?> allApps, SharedPreferences prefs,
UserManager userManager, BaseAllAppsContainerView<?> allApps,
StatsLogManager statsLogManager) {
mUserManager = userManager;
mAllApps = allApps;
mPreferences = prefs;
mMatcher = mAllApps.mPersonalMatcher.negate();
mStatsLogManager = statsLogManager;
}
@@ -225,7 +224,7 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP
}
private boolean isEduSeen() {
return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
return LauncherPrefs.get(mAllApps.getContext()).get(WORK_EDU_STEP) != 0;
}
private void onWorkFabClicked(View view) {
@@ -1,8 +1,7 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.LauncherPrefs.getPrefs;
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
import static com.android.launcher3.util.Themes.isThemedIconEnabled;
import android.annotation.TargetApi;
@@ -25,6 +24,7 @@ import android.util.Log;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Executors;
@@ -142,9 +142,8 @@ public class GridCustomizationsProvider extends ContentProvider {
}
case ICON_THEMED:
case SET_ICON_THEMED: {
getPrefs(getContext()).edit()
.putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE))
.apply();
LauncherPrefs.get(getContext())
.put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
return 1;
}
default:
@@ -17,7 +17,10 @@
package com.android.launcher3.model;
import static com.android.launcher3.InvariantDeviceProfile.DeviceType;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
import static com.android.launcher3.LauncherPrefs.DB_FILE;
import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE;
import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT;
import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
@@ -25,7 +28,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import com.android.launcher3.InvariantDeviceProfile;
@@ -58,11 +60,11 @@ public class DeviceGridState implements Comparable<DeviceGridState> {
}
public DeviceGridState(Context context) {
SharedPreferences prefs = LauncherPrefs.getPrefs(context);
mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
mDbFile = prefs.getString(KEY_DB_FILE, "");
LauncherPrefs lp = LauncherPrefs.get(context);
mGridSizeString = lp.get(WORKSPACE_SIZE);
mNumHotseat = lp.get(HOTSEAT_COUNT);
mDeviceType = lp.get(DEVICE_TYPE);
mDbFile = lp.get(DB_FILE);
}
/**
@@ -90,12 +92,11 @@ public class DeviceGridState implements Comparable<DeviceGridState> {
* Stores the device state to shared preferences
*/
public void writeToPrefs(Context context) {
LauncherPrefs.getPrefs(context).edit()
.putString(KEY_WORKSPACE_SIZE, mGridSizeString)
.putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
.putInt(KEY_DEVICE_TYPE, mDeviceType)
.putString(KEY_DB_FILE, mDbFile)
.apply();
LauncherPrefs.get(context).put(
WORKSPACE_SIZE.to(mGridSizeString),
HOTSEAT_COUNT.to(mNumHotseat),
DEVICE_TYPE.to(mDeviceType),
DB_FILE.to(mDbFile));
}
/**
@@ -16,8 +16,6 @@
package com.android.launcher3.pm;
import static com.android.launcher3.LauncherPrefs.getPrefs;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
@@ -34,6 +32,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
@@ -65,7 +64,7 @@ public class InstallSessionHelper {
// Set<String> of session ids of promise icons that have been added to the home screen
// as FLAG_PROMISE_NEW_INSTALLS.
@NonNull
protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
public static final String PROMISE_ICON_IDS = "promise_icon_ids";
private static final boolean DEBUG = false;
@@ -102,7 +101,7 @@ public class InstallSessionHelper {
return mPromiseIconIds;
}
mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
LauncherPrefs.get(mAppContext).get(LauncherPrefs.PROMISE_ICON_IDS)));
IntArray existingIds = new IntArray();
for (SessionInfo info : getActiveSessions().values()) {
@@ -146,9 +145,8 @@ public class InstallSessionHelper {
}
private void updatePromiseIconPrefs() {
getPrefs(mAppContext).edit()
.putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
.apply();
LauncherPrefs.get(mAppContext).put(LauncherPrefs.PROMISE_ICON_IDS,
getPromiseIconIds().getArray().toConcatString());
}
@Nullable
@@ -17,13 +17,14 @@
package com.android.launcher3.provider;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
@@ -62,13 +63,13 @@ import java.util.Arrays;
public class RestoreDbTask {
private static final String TAG = "RestoreDbTask";
private static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
public static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
private static final String INFO_COLUMN_NAME = "name";
private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
private static final String APPWIDGET_IDS = "appwidget_ids";
public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
public static final String APPWIDGET_IDS = "appwidget_ids";
/**
* Tries to restore the backup DB if needed
@@ -87,7 +88,7 @@ public class RestoreDbTask {
// Set is pending to false irrespective of the result, so that it doesn't get
// executed again.
LauncherPrefs.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit();
LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
idp.reinitializeAfterRestore(context);
}
@@ -240,8 +241,7 @@ public class RestoreDbTask {
}
// If restored from a single display backup, remove gaps between screenIds
if (LauncherPrefs.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)
!= TYPE_MULTI_DISPLAY) {
if (LauncherPrefs.get(context).get(RESTORE_DEVICE) != TYPE_MULTI_DISPLAY) {
removeScreenIdGaps(db);
}
@@ -339,7 +339,7 @@ public class RestoreDbTask {
}
public static boolean isPending(Context context) {
return LauncherPrefs.getPrefs(context).contains(RESTORED_DEVICE_TYPE);
return LauncherPrefs.get(context).has(RESTORE_DEVICE);
}
/**
@@ -347,34 +347,31 @@ public class RestoreDbTask {
*/
public static void setPending(Context context) {
FileLog.d(TAG, "Restore data received through full backup ");
LauncherPrefs.getPrefs(context).edit()
.putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType())
.commit();
LauncherPrefs.get(context)
.putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
}
private void restoreAppWidgetIdsIfExists(Context context) {
SharedPreferences prefs = LauncherPrefs.getPrefs(context);
if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
LauncherWidgetHolder holder = LauncherWidgetHolder.newInstance(context);
AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray(),
IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
holder);
holder.destroy();
} else {
FileLog.d(TAG, "No app widget ids to restore.");
}
prefs.edit().remove(APPWIDGET_OLD_IDS)
.remove(APPWIDGET_IDS).apply();
lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
}
public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
@NonNull int[] newIds) {
LauncherPrefs.getPrefs(context).edit()
.putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString())
.putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString())
.commit();
LauncherPrefs.get(context).putSync(
OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()),
APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
}
}
+3 -1
View File
@@ -19,6 +19,8 @@ package com.android.launcher3.util;
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -74,7 +76,7 @@ public class Themes {
* Returns true if workspace icon theming is enabled
*/
public static boolean isThemedIconEnabled(Context context) {
return LauncherPrefs.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false);
return LauncherPrefs.get(context).get(THEMED_ICONS);
}
public static String getDefaultBodyFont(Context context) {
@@ -0,0 +1,148 @@
package com.android.launcher3
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.junit.Test
import org.junit.runner.RunWith
private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
@SmallTest
@RunWith(AndroidJUnit4::class)
class LauncherPrefsTest {
private val launcherPrefs by lazy {
LauncherPrefs.get(InstrumentationRegistry.getInstrumentation().targetContext).apply {
remove(TEST_BOOLEAN_ITEM, TEST_STRING_ITEM, TEST_INT_ITEM)
}
}
@Test
fun has_keyMissingFromLauncherPrefs_returnsFalse() {
assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
}
@Test
fun has_keyPresentInLauncherPrefs_returnsTrue() {
with(launcherPrefs) {
putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue))
assertThat(has(TEST_BOOLEAN_ITEM)).isTrue()
remove(TEST_BOOLEAN_ITEM)
}
}
@Test
fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() {
val latch = CountDownLatch(1)
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
with(launcherPrefs) {
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue))
addListener(listener, TEST_STRING_ITEM)
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
remove(TEST_STRING_ITEM)
}
}
@Test
fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() {
val latch = CountDownLatch(1)
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
with(launcherPrefs) {
addListener(listener, TEST_STRING_ITEM)
removeListener(listener, TEST_STRING_ITEM)
putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
// latch will be still be 1 (and await will return false) if the listener was not called
assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
remove(TEST_STRING_ITEM)
}
}
@Test
fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() {
var latch = CountDownLatch(3)
val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
with(launcherPrefs) {
addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
putSync(
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123),
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
)
assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
latch = CountDownLatch(1)
putSync(
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
)
remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
}
}
@Test
fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() {
assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
}
@Test
fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() {
assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue)
}
@Test
fun get_intItemNotInLauncherprefs_returnsDefaultValue() {
assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue)
}
@Test
fun put_storesItemInLauncherPrefs_successfully() {
val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
with(launcherPrefs) {
putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue)
remove(TEST_BOOLEAN_ITEM)
}
}
@Test
fun put_storesListOfItemsInLauncherPrefs_successfully() {
with(launcherPrefs) {
putSync(
TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
)
assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
}
}
@Test
fun remove_deletesItemFromLauncherPrefs_successfully() {
val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
with(launcherPrefs) {
putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
remove(TEST_BOOLEAN_ITEM)
assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
}
}
}
@@ -24,7 +24,8 @@ import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherFiles
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
import com.android.launcher3.LauncherSettings.Favorites.*
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
@@ -754,11 +755,7 @@ class GridSizeMigrationUtilTest {
.edit()
.putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
.commit()
context
.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize)
.commit()
LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
FeatureFlags.initialize(context)
}
@@ -55,6 +55,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
@@ -506,7 +507,7 @@ public class LauncherModelHelper {
SanboxModelContext() {
super(ApplicationProvider.getApplicationContext(),
UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,