blob: bdd92eb4ccfb859cd00134e8b2f5697df5729a90 [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 android.content.SharedPreferences.OnSharedPreferenceChangeListener
Stefan Andonian5bd9a222023-02-23 00:58:33 +000022import android.util.Log
Stefan Andoniand1b33b32022-12-16 21:22:27 +000023import androidx.annotation.VisibleForTesting
fbarone58aaf12023-09-25 11:34:56 -070024import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
Stefan Andonian4c9612b2023-02-22 00:00:03 +000025import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
26import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
Stefan Andoniand1b33b32022-12-16 21:22:27 +000027import com.android.launcher3.allapps.WorkProfileManager
28import com.android.launcher3.model.DeviceGridState
29import com.android.launcher3.pm.InstallSessionHelper
30import com.android.launcher3.provider.RestoreDbTask
Stefan Andonian1d7f7032023-01-23 21:55:04 +000031import com.android.launcher3.states.RotationHelper
32import com.android.launcher3.util.DisplayController
Stefan Andoniand1b33b32022-12-16 21:22:27 +000033import com.android.launcher3.util.MainThreadInitializedObject
34import com.android.launcher3.util.Themes
Stefan Andonian146701c2022-11-10 23:07:40 +000035
Stefan Andoniand1b33b32022-12-16 21:22:27 +000036/**
37 * Use same context for shared preferences, so that we use a single cached instance
Jordan Demeulenaerebe82bc62023-03-01 09:11:48 +000038 *
Stefan Andoniand1b33b32022-12-16 21:22:27 +000039 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
Stefan Andonian956b88e2023-03-20 20:38:46 +000040 * TODO(b/274501660): Fix ReorderWidgets#simpleReorder test before enabling
41 * isBootAwareStartupDataEnabled
Stefan Andoniand1b33b32022-12-16 21:22:27 +000042 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000043class LauncherPrefs(private val encryptedContext: Context) {
44 private val deviceProtectedStorageContext =
45 encryptedContext.createDeviceProtectedStorageContext()
46
47 private val bootAwarePrefs
48 get() =
49 deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
50
51 private val Item.encryptedPrefs
52 get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
53
54 // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
55 // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
56 val isStartupDataMigrated: Boolean
57 get() =
58 bootAwarePrefs.getBoolean(
59 IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
60 IS_STARTUP_DATA_MIGRATED.defaultValue
61 )
62
Stefan Andonian939d5f82023-09-25 18:07:43 +000063 // TODO: Remove `item == TASKBAR_PINNING` once isBootAwareStartupDataEnabled is always true
Stefan Andonian5bd9a222023-02-23 00:58:33 +000064 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Stefan Andonian939d5f82023-09-25 18:07:43 +000065 if (
66 (isBootAwareStartupDataEnabled && item.isBootAware && isStartupDataMigrated) ||
67 item == TASKBAR_PINNING
68 )
Stefan Andonian5bd9a222023-02-23 00:58:33 +000069 bootAwarePrefs
70 else item.encryptedPrefs
Stefan Andonian146701c2022-11-10 23:07:40 +000071
Stefan Andonian1d7f7032023-01-23 21:55:04 +000072 /** Wrapper around `getInner` for a `ContextualItem` */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000073 fun <T> get(item: ContextualItem<T>): T =
74 getInner(item, item.defaultValueFromContext(encryptedContext))
Stefan Andonian1d7f7032023-01-23 21:55:04 +000075
76 /** Wrapper around `getInner` for an `Item` */
Stefan Andonian4c9612b2023-02-22 00:00:03 +000077 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Stefan Andonian1d7f7032023-01-23 21:55:04 +000078
Stefan Andoniand1b33b32022-12-16 21:22:27 +000079 /**
80 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
81 * default value type, and will throw an error if the type of the item provided is not a
82 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
83 */
84 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
Stefan Andonian4c9612b2023-02-22 00:00:03 +000085 private fun <T> getInner(item: Item, default: T): T {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000086 val sp = chooseSharedPreferences(item)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000087
Stefan Andonian4c9612b2023-02-22 00:00:03 +000088 return when (item.type) {
89 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000090 Boolean::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000091 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000092 Int::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000093 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000094 Float::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000095 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000096 Long::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000097 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
Stefan Andonian4c9612b2023-02-22 00:00:03 +000098 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000099 else ->
100 throw IllegalArgumentException(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000101 "item type: ${item.type}" + " is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000102 )
103 }
104 as T
Stefan Andonian146701c2022-11-10 23:07:40 +0000105 }
106
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000107 /**
108 * Stores each of the values provided in `SharedPreferences` according to the configuration
109 * contained within the associated items provided. Internally, it uses apply, so the caller
110 * cannot assume that the values that have been put are immediately available for use.
111 *
112 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
113 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
114 * provided item configurations.
115 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000116 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000117 prepareToPutValues(itemsToValues).forEach { it.apply() }
118
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000119 /** See referenced `put` method above. */
120 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000121
122 /**
123 * Synchronously stores all the values provided according to their associated Item
124 * configuration.
125 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000126 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000127 prepareToPutValues(itemsToValues).forEach { it.commit() }
128
129 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000130 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
131 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
132 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
133 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
134 * already points to encrypted storage.
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000135 *
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000136 * Returns a list of editors with all transactions added so that the caller can determine to use
137 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000138 */
139 private fun prepareToPutValues(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000140 updates: Array<out Pair<Item, Any>>
141 ): List<SharedPreferences.Editor> {
142 val updatesPerPrefFile = updates.groupBy { it.first.encryptedPrefs }.toMutableMap()
143
144 if (isBootAwareStartupDataEnabled) {
145 val bootAwareUpdates = updates.filter { it.first.isBootAware }
146 if (bootAwareUpdates.isNotEmpty()) {
147 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000148 }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000149 }
150
151 return updatesPerPrefFile.map { prefToItemValueList ->
152 prefToItemValueList.key.edit().apply {
153 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
154 putValue(itemToValue.first, itemToValue.second)
155 }
156 }
157 }
158 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000159
160 /**
161 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
162 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
163 * types of Item values.
164 */
165 @Suppress("UNCHECKED_CAST")
166 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000167 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000168 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000169 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000170 when (item.type) {
171 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000172 Boolean::class.java,
173 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
174 Int::class.java,
175 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
176 Float::class.java,
177 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
178 Long::class.java,
179 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000180 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000181 else ->
182 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000183 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000184 )
185 }
186
187 /**
188 * After calling this method, the listener will be notified of any future updates to the
189 * `SharedPreferences` files associated with the provided list of items. The listener will need
190 * to filter update notifications so they don't activate for non-relevant updates.
191 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000192 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000193 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000194 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000195 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000196 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000197 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000198
199 /**
200 * Stops the listener from getting notified of any more updates to any of the
201 * `SharedPreferences` files associated with any of the provided list of [Item].
202 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000203 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000204 // If a listener is not registered to a SharedPreference, unregistering it does nothing
205 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000206 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000207 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000208 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000209 }
210
211 /**
212 * Checks if all the provided [Item] have values stored in their corresponding
213 * `SharedPreferences` files.
214 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000215 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000216 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000217 .groupBy { chooseSharedPreferences(it) }
218 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000219 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
220 }
221 return true
222 }
223
224 /**
225 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
226 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000227 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000228
229 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000230 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000231
232 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000233 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
234 * item is boot aware, this method removes the data from both the boot aware and encrypted
235 * files.
236 *
237 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000238 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000239 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000240 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
241 val itemsPerFile = items.groupBy { it.encryptedPrefs }.toMutableMap()
242
243 if (isBootAwareStartupDataEnabled) {
244 val bootAwareUpdates = items.filter { it.isBootAware }
245 if (bootAwareUpdates.isNotEmpty()) {
246 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000247 }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000248 }
249
250 return itemsPerFile.map { (prefs, items) ->
251 prefs.edit().also { editor ->
252 items.forEach { item -> editor.remove(item.sharedPrefKey) }
253 }
254 }
255 }
256
257 fun migrateStartupDataToDeviceProtectedStorage() {
258 if (!isBootAwareStartupDataEnabled) return
259
260 Log.d(
261 TAG,
262 "Migrating data to unencrypted shared preferences to enable preloading " +
263 "while the user is locked the next time the device reboots."
264 )
265
266 with(bootAwarePrefs.edit()) {
267 BOOT_AWARE_ITEMS.forEach { putValue(it, get(it)) }
268 putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
269 apply()
270 }
271 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000272
273 companion object {
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000274 private const val TAG = "LauncherPrefs"
275 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
276
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000277 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
278
279 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
280
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700281 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700282 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000283 @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true)
Andy Wickhamaf5bb302023-05-17 14:13:44 -0700284 @JvmField
285 val ALL_APPS_OVERVIEW_THRESHOLD =
Andy Wickham3d066122023-06-27 16:37:42 -0700286 nonRestorableItem(LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD, 180, true)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000287 @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000288 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
289 @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000290 @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", true)
291 @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, true)
Jagrut Desai3a7d1392023-09-06 15:56:19 -0700292 @JvmField val TASKBAR_PINNING = backedUpItem(TASKBAR_PINNING_KEY, false, true)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700293
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000294 @JvmField
295 val DEVICE_TYPE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000296 backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE, true)
fbarone58aaf12023-09-25 11:34:56 -0700297 @JvmField
298 val SHOULD_SHOW_SMARTSPACE =
299 backedUpItem("SHOULD_SHOW_SMARTSPACE_KEY", WIDGET_ON_FIRST_SCREEN, true)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000300 @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", true)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000301 @JvmField
302 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000303 backedUpItem(
304 RestoreDbTask.RESTORED_DEVICE_TYPE,
305 InvariantDeviceProfile.TYPE_PHONE,
306 true
307 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000308 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
309 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000310 @JvmField
311 val GRID_NAME =
312 ConstantItem(
313 "idp_grid_name",
314 isBackedUp = true,
315 defaultValue = null,
316 isBootAware = true,
317 type = String::class.java
318 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000319 @JvmField
320 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000321 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000322 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
323 }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000324 @JvmField
325 val IS_STARTUP_DATA_MIGRATED =
326 ConstantItem(
327 "is_startup_data_boot_aware",
328 isBackedUp = false,
329 defaultValue = false,
330 isBootAware = true
331 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000332
333 @VisibleForTesting
334 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000335 fun <T> backedUpItem(
336 sharedPrefKey: String,
337 defaultValue: T,
338 isBootAware: Boolean = false
339 ): ConstantItem<T> =
340 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, isBootAware)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000341
342 @JvmStatic
343 fun <T> backedUpItem(
344 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000345 type: Class<out T>,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000346 isBootAware: Boolean = false,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000347 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000348 ): ContextualItem<T> =
349 ContextualItem(
350 sharedPrefKey,
351 isBackedUp = true,
352 defaultValueFromContext,
353 isBootAware,
354 type
355 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000356
357 @VisibleForTesting
358 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000359 fun <T> nonRestorableItem(
360 sharedPrefKey: String,
361 defaultValue: T,
362 isBootAware: Boolean = false
363 ): ConstantItem<T> =
364 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, isBootAware)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000365
366 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
367 @JvmStatic
368 fun getPrefs(context: Context): SharedPreferences {
369 // Use application context for shared preferences, so we use single cached instance
370 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000371 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000372 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000373 )
374 }
375
376 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
377 @JvmStatic
378 fun getDevicePrefs(context: Context): SharedPreferences {
379 // Use application context for shared preferences, so we use a single cached instance
380 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000381 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000382 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000383 )
384 }
385 }
386}
387
Stefan Andoniandbc8ec52023-09-21 19:05:07 +0000388// It is a var because the unit tests are setting this to true so they can run.
389@VisibleForTesting
390var isBootAwareStartupDataEnabled: Boolean =
391 com.android.launcher3.config.FeatureFlags.ENABLE_BOOT_AWARE_STARTUP_DATA.get()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000392
393private val BOOT_AWARE_ITEMS: MutableSet<ConstantItem<*>> = mutableSetOf()
394
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000395abstract class Item {
396 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000397 abstract val isBackedUp: Boolean
398 abstract val type: Class<*>
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000399 abstract val isBootAware: Boolean
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000400 val sharedPrefFile: String
401 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000402
403 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
404}
405
406data class ConstantItem<T>(
407 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000408 override val isBackedUp: Boolean,
409 val defaultValue: T,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000410 override val isBootAware: Boolean,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000411 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
412 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000413) : Item() {
414 init {
415 if (isBootAware && isBootAwareStartupDataEnabled) {
416 BOOT_AWARE_ITEMS.add(this)
417 }
418 }
419}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000420
421data class ContextualItem<T>(
422 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000423 override val isBackedUp: Boolean,
424 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000425 override val isBootAware: Boolean,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000426 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000427) : Item() {
428 private var default: T? = null
429
430 fun defaultValueFromContext(context: Context): T {
431 if (default == null) {
432 default = defaultSupplier(context)
433 }
434 return default!!
435 }
Thales Lima03ac3772023-01-06 15:16:41 +0000436}