blob: 2a5cd637528546ca4c06cae019ad92f41747e2d1 [file] [log] [blame]
Stefan Andonian5bd9a222023-02-23 00:58:33 +00001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stefan Andonian146701c2022-11-10 23:07:40 +000016package com.android.launcher3
17
18import android.content.Context
Stefan Andonian5bd9a222023-02-23 00:58:33 +000019import android.content.Context.MODE_PRIVATE
Stefan Andonian146701c2022-11-10 23:07:40 +000020import android.content.SharedPreferences
Stefan Andoniand1b33b32022-12-16 21:22:27 +000021import androidx.annotation.VisibleForTesting
fbarone58aaf12023-09-25 11:34:56 -070022import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
Charlie Andersonbad2be42024-12-06 16:32:03 -050023import com.android.launcher3.InvariantDeviceProfile.GRID_NAME_PREFS_KEY
Federico Baron54b09dc2025-02-06 18:58:16 +000024import com.android.launcher3.InvariantDeviceProfile.NON_FIXED_LANDSCAPE_GRID_NAME_PREFS_KEY
Stefan Andonian4c9612b2023-02-22 00:00:03 +000025import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
26import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
Sunny Goyale79d4532024-12-31 00:10:20 -080027import com.android.launcher3.dagger.ApplicationContext
28import com.android.launcher3.dagger.LauncherAppComponent
29import com.android.launcher3.dagger.LauncherAppSingleton
Stefan Andoniand1b33b32022-12-16 21:22:27 +000030import com.android.launcher3.model.DeviceGridState
31import com.android.launcher3.pm.InstallSessionHelper
32import com.android.launcher3.provider.RestoreDbTask
Charlie Anderson511421c2023-10-26 11:22:26 -040033import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
Sebastian Franco9e4c99b2024-10-02 15:16:37 -070034import com.android.launcher3.settings.SettingsActivity
Stefan Andonian1d7f7032023-01-23 21:55:04 +000035import com.android.launcher3.states.RotationHelper
Sunny Goyale79d4532024-12-31 00:10:20 -080036import com.android.launcher3.util.DaggerSingletonObject
Stefan Andonian1d7f7032023-01-23 21:55:04 +000037import com.android.launcher3.util.DisplayController
Sunny Goyalc9d9e332025-02-05 13:49:04 -080038import java.util.concurrent.ConcurrentHashMap
Sunny Goyale79d4532024-12-31 00:10:20 -080039import javax.inject.Inject
Stefan Andonian146701c2022-11-10 23:07:40 +000040
Stefan Andoniand1b33b32022-12-16 21:22:27 +000041/**
Brian Isganitis9cbc4782024-10-08 14:47:17 -040042 * Manages Launcher [SharedPreferences] through [Item] instances.
Jordan Demeulenaerebe82bc62023-03-01 09:11:48 +000043 *
Stefan Andoniand1b33b32022-12-16 21:22:27 +000044 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
45 */
Sunny Goyale79d4532024-12-31 00:10:20 -080046@LauncherAppSingleton
47open class LauncherPrefs
48@Inject
49constructor(@ApplicationContext private val encryptedContext: Context) {
50
51 private val deviceProtectedSharedPrefs: SharedPreferences by lazy {
52 encryptedContext
53 .createDeviceProtectedStorageContext()
54 .getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
55 }
56
Sunny Goyalc9d9e332025-02-05 13:49:04 -080057 open protected fun getSharedPrefs(item: Item): SharedPreferences =
58 item.run {
Sunny Goyale79d4532024-12-31 00:10:20 -080059 if (encryptionType == EncryptionType.DEVICE_PROTECTED) deviceProtectedSharedPrefs
60 else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
Sunny Goyalc9d9e332025-02-05 13:49:04 -080061 }
Brian Isganitis9cbc4782024-10-08 14:47:17 -040062
63 /** Returns the value with type [T] for [item]. */
Sunny Goyalc9d9e332025-02-05 13:49:04 -080064 fun <T> get(item: ContextualItem<T>): T =
Sunny Goyale79d4532024-12-31 00:10:20 -080065 getInner(item, item.defaultValueFromContext(encryptedContext))
Brian Isganitis9cbc4782024-10-08 14:47:17 -040066
67 /** Returns the value with type [T] for [item]. */
Sunny Goyalc9d9e332025-02-05 13:49:04 -080068 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Brian Isganitis9cbc4782024-10-08 14:47:17 -040069
Sunny Goyale79d4532024-12-31 00:10:20 -080070 /**
71 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
72 * default value type, and will throw an error if the type of the item provided is not a
73 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
74 */
75 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
76 private fun <T> getInner(item: Item, default: T): T {
Sunny Goyalc9d9e332025-02-05 13:49:04 -080077 val sp = getSharedPrefs(item)
Brian Isganitis9cbc4782024-10-08 14:47:17 -040078
Sunny Goyale79d4532024-12-31 00:10:20 -080079 return when (item.type) {
80 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
81 Boolean::class.java,
82 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
83 Int::class.java,
84 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
85 Float::class.java,
86 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
87 Long::class.java,
88 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
89 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
90 else ->
91 throw IllegalArgumentException(
92 "item type: ${item.type}" + " is not compatible with sharedPref methods"
93 )
94 }
95 as T
96 }
Brian Isganitis9cbc4782024-10-08 14:47:17 -040097
Sunny Goyale79d4532024-12-31 00:10:20 -080098 /**
99 * Stores each of the values provided in `SharedPreferences` according to the configuration
100 * contained within the associated items provided. Internally, it uses apply, so the caller
101 * cannot assume that the values that have been put are immediately available for use.
102 *
103 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
104 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
105 * provided item configurations.
106 */
107 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
108 prepareToPutValues(itemsToValues).forEach { it.apply() }
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400109
Sunny Goyale79d4532024-12-31 00:10:20 -0800110 /** See referenced `put` method above. */
111 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400112
Sunny Goyale79d4532024-12-31 00:10:20 -0800113 /**
114 * Synchronously stores all the values provided according to their associated Item
115 * configuration.
116 */
117 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
118 prepareToPutValues(itemsToValues).forEach { it.commit() }
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400119
Sunny Goyale79d4532024-12-31 00:10:20 -0800120 /**
121 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
122 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
123 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
124 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
125 * already points to encrypted storage.
126 *
127 * Returns a list of editors with all transactions added so that the caller can determine to use
128 * .apply() or .commit()
129 */
130 private fun prepareToPutValues(
131 updates: Array<out Pair<Item, Any>>
132 ): List<SharedPreferences.Editor> {
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800133 val updatesPerPrefFile = updates.groupBy { getSharedPrefs(it.first) }.toMap()
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400134
Sunny Goyale79d4532024-12-31 00:10:20 -0800135 return updatesPerPrefFile.map { (sharedPref, itemList) ->
136 sharedPref.edit().apply { itemList.forEach { (item, value) -> putValue(item, value) } }
137 }
138 }
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400139
Sunny Goyale79d4532024-12-31 00:10:20 -0800140 /**
141 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
142 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
143 * types of Item values.
144 */
145 @Suppress("UNCHECKED_CAST")
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800146 internal fun SharedPreferences.Editor.putValue(
Sunny Goyale79d4532024-12-31 00:10:20 -0800147 item: Item,
148 value: Any?,
149 ): SharedPreferences.Editor =
150 when (item.type) {
151 String::class.java -> putString(item.sharedPrefKey, value as? String)
152 Boolean::class.java,
153 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
154 Int::class.java,
155 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
156 Float::class.java,
157 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
158 Long::class.java,
159 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
160 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
161 else ->
162 throw IllegalArgumentException(
163 "item type: ${item.type} is not compatible with sharedPref methods"
164 )
165 }
166
167 /**
168 * After calling this method, the listener will be notified of any future updates to the
169 * `SharedPreferences` files associated with the provided list of items. The listener will need
170 * to filter update notifications so they don't activate for non-relevant updates.
171 */
172 fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
173 items
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800174 .map { getSharedPrefs(it) }
Sunny Goyale79d4532024-12-31 00:10:20 -0800175 .distinct()
176 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
177 }
178
179 /**
180 * Stops the listener from getting notified of any more updates to any of the
181 * `SharedPreferences` files associated with any of the provided list of [Item].
182 */
183 fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
184 // If a listener is not registered to a SharedPreference, unregistering it does nothing
185 items
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800186 .map { getSharedPrefs(it) }
Sunny Goyale79d4532024-12-31 00:10:20 -0800187 .distinct()
188 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
189 }
190
191 /**
192 * Checks if all the provided [Item] have values stored in their corresponding
193 * `SharedPreferences` files.
194 */
195 fun has(vararg items: Item): Boolean {
196 items
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800197 .groupBy { getSharedPrefs(it) }
Sunny Goyale79d4532024-12-31 00:10:20 -0800198 .forEach { (prefs, itemsSublist) ->
199 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
200 }
201 return true
202 }
203
204 /**
205 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
206 */
207 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
208
209 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
210 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
211
212 /**
213 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
214 * item is boot aware, this method removes the data from both the boot aware and encrypted
215 * files.
216 *
217 * @return a list of editors with all transactions added so that the caller can determine to use
218 * .apply() or .commit()
219 */
220 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800221 val itemsPerFile = items.groupBy { getSharedPrefs(it) }.toMap()
Sunny Goyale79d4532024-12-31 00:10:20 -0800222
223 return itemsPerFile.map { (prefs, items) ->
224 prefs.edit().also { editor ->
225 items.forEach { item -> editor.remove(item.sharedPrefKey) }
226 }
227 }
228 }
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400229
230 companion object {
231 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
232
Sunny Goyale79d4532024-12-31 00:10:20 -0800233 @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLauncherPrefs)
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400234
235 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
236
237 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
238 const val TASKBAR_PINNING_DESKTOP_MODE_KEY = "TASKBAR_PINNING_DESKTOP_MODE_KEY"
239 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400240
241 @JvmField
242 val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400243 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
244 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
245 @JvmField
246 val WORKSPACE_SIZE =
247 backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", EncryptionType.ENCRYPTED)
248 @JvmField
249 val HOTSEAT_COUNT =
250 backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, EncryptionType.ENCRYPTED)
251 @JvmField
252 val TASKBAR_PINNING =
253 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
254 @JvmField
255 val TASKBAR_PINNING_IN_DESKTOP_MODE =
256 backedUpItem(TASKBAR_PINNING_DESKTOP_MODE_KEY, true, EncryptionType.DEVICE_PROTECTED)
257
258 @JvmField
259 val DEVICE_TYPE =
260 backedUpItem(
261 DeviceGridState.KEY_DEVICE_TYPE,
262 InvariantDeviceProfile.TYPE_PHONE,
263 EncryptionType.ENCRYPTED,
264 )
265 @JvmField
266 val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.ENCRYPTED)
267 @JvmField
268 val SHOULD_SHOW_SMARTSPACE =
269 backedUpItem(
270 SHOULD_SHOW_SMARTSPACE_KEY,
271 WIDGET_ON_FIRST_SCREEN,
272 EncryptionType.DEVICE_PROTECTED,
273 )
274 @JvmField
275 val RESTORE_DEVICE =
276 backedUpItem(
277 RestoreDbTask.RESTORED_DEVICE_TYPE,
278 InvariantDeviceProfile.TYPE_PHONE,
279 EncryptionType.ENCRYPTED,
280 )
281 @JvmField
Stefan Andonian8c0a7872024-10-21 14:26:27 -0700282 val NO_DB_FILES_RESTORED =
283 nonRestorableItem("no_db_files_restored", false, EncryptionType.DEVICE_PROTECTED)
284 @JvmField
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400285 val IS_FIRST_LOAD_AFTER_RESTORE =
286 nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
287 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
288 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Sebastian Franco9e4c99b2024-10-02 15:16:37 -0700289
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400290 @JvmField
291 val GRID_NAME =
292 ConstantItem(
Charlie Andersonbad2be42024-12-06 16:32:03 -0500293 GRID_NAME_PREFS_KEY,
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400294 isBackedUp = true,
295 defaultValue = null,
296 encryptionType = EncryptionType.ENCRYPTED,
297 type = String::class.java,
298 )
299 @JvmField
300 val ALLOW_ROTATION =
301 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
302 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
303 }
304
Sebastian Franco9e4c99b2024-10-02 15:16:37 -0700305 @JvmField
306 val FIXED_LANDSCAPE_MODE = backedUpItem(SettingsActivity.FIXED_LANDSCAPE_MODE, false)
307
Federico Baron54b09dc2025-02-06 18:58:16 +0000308 @JvmField
309 val NON_FIXED_LANDSCAPE_GRID_NAME =
310 ConstantItem(
311 NON_FIXED_LANDSCAPE_GRID_NAME_PREFS_KEY,
312 isBackedUp = true,
313 defaultValue = null,
314 encryptionType = EncryptionType.ENCRYPTED,
315 type = String::class.java,
316 )
317
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400318 // Preferences for widget configurations
319 @JvmField
320 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
321 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
322
323 @JvmStatic
324 fun <T> backedUpItem(
325 sharedPrefKey: String,
326 defaultValue: T,
327 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
328 ): ConstantItem<T> =
329 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
330
331 @JvmStatic
332 fun <T> backedUpItem(
333 sharedPrefKey: String,
334 type: Class<out T>,
335 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
336 defaultValueFromContext: (c: Context) -> T,
337 ): ContextualItem<T> =
338 ContextualItem(
339 sharedPrefKey,
340 isBackedUp = true,
341 defaultValueFromContext,
342 encryptionType,
343 type,
344 )
345
346 @JvmStatic
347 fun <T> nonRestorableItem(
348 sharedPrefKey: String,
349 defaultValue: T,
350 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
351 ): ConstantItem<T> =
352 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
353
354 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
355 @JvmStatic
356 fun getPrefs(context: Context): SharedPreferences {
357 // Use application context for shared preferences, so we use single cached instance
358 return context.applicationContext.getSharedPreferences(
359 SHARED_PREFERENCES_KEY,
360 MODE_PRIVATE,
361 )
362 }
363
364 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
365 @JvmStatic
366 fun getDevicePrefs(context: Context): SharedPreferences {
367 // Use application context for shared preferences, so we use a single cached instance
368 return context.applicationContext.getSharedPreferences(
369 DEVICE_PREFERENCES_KEY,
370 MODE_PRIVATE,
371 )
372 }
373 }
374}
375
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000376abstract class Item {
377 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000378 abstract val isBackedUp: Boolean
379 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000380 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000381 val sharedPrefFile: String
382 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000383
384 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
385}
386
387data class ConstantItem<T>(
388 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000389 override val isBackedUp: Boolean,
390 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000391 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000392 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400393 override val type: Class<out T> = defaultValue!!::class.java,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000394) : Item() {
Sunny Goyalcd447402023-10-06 13:47:50 -0700395
396 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000397}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000398
399data class ContextualItem<T>(
400 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000401 override val isBackedUp: Boolean,
402 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000403 override val encryptionType: EncryptionType,
Brian Isganitis9cbc4782024-10-08 14:47:17 -0400404 override val type: Class<out T>,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000405) : Item() {
406 private var default: T? = null
407
408 fun defaultValueFromContext(context: Context): T {
409 if (default == null) {
410 default = defaultSupplier(context)
411 }
412 return default!!
413 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700414
415 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000416}
Stefan Andonian54495f32023-09-29 23:41:26 +0000417
418enum class EncryptionType {
419 ENCRYPTED,
420 DEVICE_PROTECTED,
Stefan Andonian54495f32023-09-29 23:41:26 +0000421}
Sunny Goyal4c261f72025-01-27 23:30:06 -0800422
423/**
424 * LauncherPrefs which delegates all lookup to [prefs] but uses the real prefs for initial values
425 */
426class ProxyPrefs(context: Context, private val prefs: SharedPreferences) : LauncherPrefs(context) {
427
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800428 private val copiedPrefs = ConcurrentHashMap<SharedPreferences, Boolean>()
Sunny Goyal4c261f72025-01-27 23:30:06 -0800429
Sunny Goyalc9d9e332025-02-05 13:49:04 -0800430 override fun getSharedPrefs(item: Item): SharedPreferences {
431 val originalPrefs = super.getSharedPrefs(item)
432 // Copy all existing values, when the pref is accessed for the first time
433 copiedPrefs.computeIfAbsent(originalPrefs) { op ->
434 val editor = prefs.edit()
435 op.all.forEach { (key, value) ->
436 if (value != null) {
437 editor.putValue(backedUpItem(key, value), value)
438 }
439 }
440 editor.commit()
441 }
442 return prefs
443 }
Sunny Goyal4c261f72025-01-27 23:30:06 -0800444}