Converting LauncherPrefs to dagger
Fixing ENABLE_TWOLINE_ALLAPPS_TOGGLE not properly tied to IDP:
http://recall/-/ep7WJ8pKwCEklUN5J1mAkM
Bug: 361850561
Flag: EXEMPT dagger-migration
Test: atest LauncherPrefsTest FakeLauncherPrefsTest
Change-Id: Iba63d060f4a8c2e31033fca2a4638c559c161338
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5b9c2fa..ad592d8 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -23,59 +23,212 @@
import com.android.launcher3.InvariantDeviceProfile.GRID_NAME_PREFS_KEY
import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.model.DeviceGridState
import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
import com.android.launcher3.settings.SettingsActivity
import com.android.launcher3.states.RotationHelper
+import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.MainThreadInitializedObject
-import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.Themes
+import javax.inject.Inject
/**
* Manages Launcher [SharedPreferences] through [Item] instances.
*
* TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
*/
-abstract class LauncherPrefs : SafeCloseable {
+@LauncherAppSingleton
+open class LauncherPrefs
+@Inject
+constructor(@ApplicationContext private val encryptedContext: Context) {
+
+ private val deviceProtectedSharedPrefs: SharedPreferences by lazy {
+ encryptedContext
+ .createDeviceProtectedStorageContext()
+ .getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
+ }
+
+ open val Item.sharedPrefs: SharedPreferences
+ get() =
+ if (encryptionType == EncryptionType.DEVICE_PROTECTED) deviceProtectedSharedPrefs
+ else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
/** Returns the value with type [T] for [item]. */
- abstract fun <T> get(item: ContextualItem<T>): T
+ fun <T> get(item: ContextualItem<T>): T =
+ getInner(item, item.defaultValueFromContext(encryptedContext))
/** Returns the value with type [T] for [item]. */
- abstract fun <T> get(item: ConstantItem<T>): T
+ fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
- /** Stores the values for each item in preferences. */
- abstract fun put(vararg itemsToValues: Pair<Item, Any>)
+ /**
+ * 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")
+ private fun <T> getInner(item: Item, default: T): T {
+ val sp = item.sharedPrefs
- /** Stores the [value] with type [T] for [item] in preferences. */
- abstract fun <T : Any> put(item: Item, value: T)
+ return when (item.type) {
+ String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
+ Boolean::class.java,
+ java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
+ Int::class.java,
+ java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
+ Float::class.java,
+ java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
+ Long::class.java,
+ java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
+ Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
+ else ->
+ throw IllegalArgumentException(
+ "item type: ${item.type}" + " is not compatible with sharedPref methods"
+ )
+ }
+ as T
+ }
- /** Synchronous version of [put]. */
- abstract fun putSync(vararg itemsToValues: Pair<Item, Any>)
+ /**
+ * 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() }
- /** Registers [listener] for [items]. */
- abstract fun addListener(listener: LauncherPrefChangeListener, vararg items: Item)
+ /** See referenced `put` method above. */
+ fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
- /** Unregisters [listener] for [items]. */
- abstract fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item)
+ /**
+ * 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() }
- /** Returns `true` iff all [items] have a value. */
- abstract fun has(vararg items: Item): Boolean
+ /**
+ * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
+ * the item is boot aware, this method updates both the boot aware and the encrypted files. This
+ * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
+ * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
+ * already points to encrypted storage.
+ *
+ * Returns a list of editors with all transactions added so that the caller can determine to use
+ * .apply() or .commit()
+ */
+ private fun prepareToPutValues(
+ updates: Array<out Pair<Item, Any>>
+ ): List<SharedPreferences.Editor> {
+ val updatesPerPrefFile = updates.groupBy { it.first.sharedPrefs }.toMap()
- /** Removes the value for each item in [items]. */
- abstract fun remove(vararg items: Item)
+ return updatesPerPrefFile.map { (sharedPref, itemList) ->
+ sharedPref.edit().apply { itemList.forEach { (item, value) -> putValue(item, value) } }
+ }
+ }
- /** Synchronous version of [remove]. */
- abstract fun removeSync(vararg items: Item)
+ /**
+ * 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 (item.type) {
+ 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.type} 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: LauncherPrefChangeListener, vararg items: Item) {
+ items
+ .map { it.sharedPrefs }
+ .distinct()
+ .forEach { it.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: LauncherPrefChangeListener, vararg items: Item) {
+ // If a listener is not registered to a SharedPreference, unregistering it does nothing
+ items
+ .map { it.sharedPrefs }
+ .distinct()
+ .forEach { it.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.sharedPrefs }
+ .forEach { (prefs, itemsSublist) ->
+ 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() }
+
+ /**
+ * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
+ * item is boot aware, this method removes the data from both the boot aware and encrypted
+ * files.
+ *
+ * @return a list of editors with all transactions added so that the caller can determine to use
+ * .apply() or .commit()
+ */
+ private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
+ val itemsPerFile = items.groupBy { it.sharedPrefs }.toMap()
+
+ return itemsPerFile.map { (prefs, items) ->
+ prefs.edit().also { editor ->
+ items.forEach { item -> editor.remove(item.sharedPrefKey) }
+ }
+ }
+ }
companion object {
@VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
- @JvmField
- var INSTANCE = MainThreadInitializedObject<LauncherPrefs> { LauncherPrefsImpl(it) }
+ @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLauncherPrefs)
@JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
@@ -212,214 +365,6 @@
}
}
-private class LauncherPrefsImpl(private val encryptedContext: Context) : LauncherPrefs() {
- private val deviceProtectedStorageContext =
- encryptedContext.createDeviceProtectedStorageContext()
-
- private val bootAwarePrefs
- get() =
- deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
-
- private val Item.encryptedPrefs
- get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
-
- private fun chooseSharedPreferences(item: Item): SharedPreferences =
- if (item.encryptionType == EncryptionType.DEVICE_PROTECTED) bootAwarePrefs
- else item.encryptedPrefs
-
- /** Wrapper around `getInner` for a `ContextualItem` */
- override fun <T> get(item: ContextualItem<T>): T =
- getInner(item, item.defaultValueFromContext(encryptedContext))
-
- /** Wrapper around `getInner` for an `Item` */
- override fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
-
- /**
- * 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")
- private fun <T> getInner(item: Item, default: T): T {
- val sp = chooseSharedPreferences(item)
-
- return when (item.type) {
- String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
- Boolean::class.java,
- java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
- Int::class.java,
- java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
- Float::class.java,
- java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
- Long::class.java,
- java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
- Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
- else ->
- throw IllegalArgumentException(
- "item type: ${item.type}" + " is not compatible with sharedPref methods"
- )
- }
- as T
- }
-
- /**
- * 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.
- */
- override fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
- prepareToPutValues(itemsToValues).forEach { it.apply() }
-
- /** See referenced `put` method above. */
- override fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
-
- /**
- * Synchronously stores all the values provided according to their associated Item
- * configuration.
- */
- override fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
- prepareToPutValues(itemsToValues).forEach { it.commit() }
-
- /**
- * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
- * the item is boot aware, this method updates both the boot aware and the encrypted files. This
- * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
- * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
- * already points to encrypted storage.
- *
- * Returns a list of editors with all transactions added so that the caller can determine to use
- * .apply() or .commit()
- */
- private fun prepareToPutValues(
- updates: Array<out Pair<Item, Any>>
- ): List<SharedPreferences.Editor> {
- val updatesPerPrefFile =
- updates
- .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
- .groupBy { it.first.encryptedPrefs }
- .toMutableMap()
-
- val bootAwareUpdates =
- updates.filter { it.first.encryptionType == EncryptionType.DEVICE_PROTECTED }
- if (bootAwareUpdates.isNotEmpty()) {
- updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
- }
-
- return updatesPerPrefFile.map { prefToItemValueList ->
- prefToItemValueList.key.edit().apply {
- prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
- 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 (item.type) {
- 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.type} 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.
- */
- override fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- items
- .map { chooseSharedPreferences(it) }
- .distinct()
- .forEach { it.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].
- */
- override fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- // If a listener is not registered to a SharedPreference, unregistering it does nothing
- items
- .map { chooseSharedPreferences(it) }
- .distinct()
- .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
- }
-
- /**
- * Checks if all the provided [Item] have values stored in their corresponding
- * `SharedPreferences` files.
- */
- override fun has(vararg items: Item): Boolean {
- items
- .groupBy { chooseSharedPreferences(it) }
- .forEach { (prefs, itemsSublist) ->
- if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
- }
- return true
- }
-
- /**
- * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
- */
- override fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
-
- /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
- override fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
-
- /**
- * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
- * item is boot aware, this method removes the data from both the boot aware and encrypted
- * files.
- *
- * @return a list of editors with all transactions added so that the caller can determine to use
- * .apply() or .commit()
- */
- private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
- val itemsPerFile =
- items
- .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
- .groupBy { it.encryptedPrefs }
- .toMutableMap()
-
- val bootAwareUpdates = items.filter { it.encryptionType == EncryptionType.DEVICE_PROTECTED }
- if (bootAwareUpdates.isNotEmpty()) {
- itemsPerFile[bootAwarePrefs] = bootAwareUpdates
- }
-
- return itemsPerFile.map { (prefs, items) ->
- prefs.edit().also { editor ->
- items.forEach { item -> editor.remove(item.sharedPrefKey) }
- }
- }
- }
-
- override fun close() {}
-}
-
abstract class Item {
abstract val sharedPrefKey: String
abstract val isBackedUp: Boolean