blob: 9a760d7f3a8b7c65f7479342f1fac7e6feb9e104 [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.model.DeviceGridState
28import com.android.launcher3.pm.InstallSessionHelper
29import com.android.launcher3.provider.RestoreDbTask
Stefan Andonian1d7f7032023-01-23 21:55:04 +000030import com.android.launcher3.states.RotationHelper
31import com.android.launcher3.util.DisplayController
Stefan Andoniand1b33b32022-12-16 21:22:27 +000032import com.android.launcher3.util.MainThreadInitializedObject
33import com.android.launcher3.util.Themes
Stefan Andonian146701c2022-11-10 23:07:40 +000034
Stefan Andoniand1b33b32022-12-16 21:22:27 +000035/**
36 * Use same context for shared preferences, so that we use a single cached instance
Jordan Demeulenaerebe82bc62023-03-01 09:11:48 +000037 *
Stefan Andoniand1b33b32022-12-16 21:22:27 +000038 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
Stefan Andonian956b88e2023-03-20 20:38:46 +000039 * TODO(b/274501660): Fix ReorderWidgets#simpleReorder test before enabling
40 * isBootAwareStartupDataEnabled
Stefan Andoniand1b33b32022-12-16 21:22:27 +000041 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000042class LauncherPrefs(private val encryptedContext: Context) {
43 private val deviceProtectedStorageContext =
44 encryptedContext.createDeviceProtectedStorageContext()
45
46 private val bootAwarePrefs
47 get() =
48 deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
49
50 private val Item.encryptedPrefs
51 get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
52
53 // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
54 // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
55 val isStartupDataMigrated: Boolean
56 get() =
57 bootAwarePrefs.getBoolean(
58 IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
59 IS_STARTUP_DATA_MIGRATED.defaultValue
60 )
61
62 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Stefan Andonian939d5f82023-09-25 18:07:43 +000063 if (
Stefan Andonian54495f32023-09-29 23:41:26 +000064 (moveStartupDataToDeviceProtectedStorageIsEnabled &&
65 item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
66 isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andonian939d5f82023-09-25 18:07:43 +000067 )
Stefan Andonian5bd9a222023-02-23 00:58:33 +000068 bootAwarePrefs
69 else item.encryptedPrefs
Stefan Andonian146701c2022-11-10 23:07:40 +000070
Stefan Andonian1d7f7032023-01-23 21:55:04 +000071 /** Wrapper around `getInner` for a `ContextualItem` */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000072 fun <T> get(item: ContextualItem<T>): T =
73 getInner(item, item.defaultValueFromContext(encryptedContext))
Stefan Andonian1d7f7032023-01-23 21:55:04 +000074
75 /** Wrapper around `getInner` for an `Item` */
Stefan Andonian4c9612b2023-02-22 00:00:03 +000076 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Stefan Andonian1d7f7032023-01-23 21:55:04 +000077
Stefan Andoniand1b33b32022-12-16 21:22:27 +000078 /**
79 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
80 * default value type, and will throw an error if the type of the item provided is not a
81 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
82 */
83 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
Stefan Andonian4c9612b2023-02-22 00:00:03 +000084 private fun <T> getInner(item: Item, default: T): T {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000085 val sp = chooseSharedPreferences(item)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000086
Stefan Andonian4c9612b2023-02-22 00:00:03 +000087 return when (item.type) {
88 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000089 Boolean::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000090 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000091 Int::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000092 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000093 Float::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000094 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000095 Long::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000096 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
Stefan Andonian4c9612b2023-02-22 00:00:03 +000097 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000098 else ->
99 throw IllegalArgumentException(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000100 "item type: ${item.type}" + " is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000101 )
102 }
103 as T
Stefan Andonian146701c2022-11-10 23:07:40 +0000104 }
105
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000106 /**
107 * Stores each of the values provided in `SharedPreferences` according to the configuration
108 * contained within the associated items provided. Internally, it uses apply, so the caller
109 * cannot assume that the values that have been put are immediately available for use.
110 *
111 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
112 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
113 * provided item configurations.
114 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000115 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000116 prepareToPutValues(itemsToValues).forEach { it.apply() }
117
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000118 /** See referenced `put` method above. */
119 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000120
121 /**
122 * Synchronously stores all the values provided according to their associated Item
123 * configuration.
124 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000125 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000126 prepareToPutValues(itemsToValues).forEach { it.commit() }
127
128 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000129 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
130 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
131 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
132 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
133 * already points to encrypted storage.
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000134 *
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000135 * Returns a list of editors with all transactions added so that the caller can determine to use
136 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000137 */
138 private fun prepareToPutValues(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000139 updates: Array<out Pair<Item, Any>>
140 ): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000141 val updatesPerPrefFile =
142 updates
143 .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
144 .groupBy { it.first.encryptedPrefs }
145 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000146
Stefan Andonian54495f32023-09-29 23:41:26 +0000147 val bootAwareUpdates =
148 updates.filter {
149 (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
150 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
151 it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000152 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000153 if (bootAwareUpdates.isNotEmpty()) {
154 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000155 }
156
157 return updatesPerPrefFile.map { prefToItemValueList ->
158 prefToItemValueList.key.edit().apply {
159 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
160 putValue(itemToValue.first, itemToValue.second)
161 }
162 }
163 }
164 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000165
166 /**
167 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
168 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
169 * types of Item values.
170 */
171 @Suppress("UNCHECKED_CAST")
172 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000173 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000174 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000175 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000176 when (item.type) {
177 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000178 Boolean::class.java,
179 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
180 Int::class.java,
181 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
182 Float::class.java,
183 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
184 Long::class.java,
185 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000186 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000187 else ->
188 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000189 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000190 )
191 }
192
193 /**
194 * After calling this method, the listener will be notified of any future updates to the
195 * `SharedPreferences` files associated with the provided list of items. The listener will need
196 * to filter update notifications so they don't activate for non-relevant updates.
197 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000198 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000199 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000200 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000201 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000202 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000203 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000204
205 /**
206 * Stops the listener from getting notified of any more updates to any of the
207 * `SharedPreferences` files associated with any of the provided list of [Item].
208 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000209 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000210 // If a listener is not registered to a SharedPreference, unregistering it does nothing
211 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000212 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000213 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000214 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000215 }
216
217 /**
218 * Checks if all the provided [Item] have values stored in their corresponding
219 * `SharedPreferences` files.
220 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000221 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000222 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000223 .groupBy { chooseSharedPreferences(it) }
224 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000225 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
226 }
227 return true
228 }
229
230 /**
231 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
232 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000233 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000234
235 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000236 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000237
238 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000239 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
240 * item is boot aware, this method removes the data from both the boot aware and encrypted
241 * files.
242 *
243 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000244 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000245 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000246 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000247 val itemsPerFile =
248 items
249 .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
250 .groupBy { it.encryptedPrefs }
251 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000252
Stefan Andonian54495f32023-09-29 23:41:26 +0000253 val bootAwareUpdates =
254 items.filter {
255 (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
256 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
257 it.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000258 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000259 if (bootAwareUpdates.isNotEmpty()) {
260 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000261 }
262
263 return itemsPerFile.map { (prefs, items) ->
264 prefs.edit().also { editor ->
265 items.forEach { item -> editor.remove(item.sharedPrefKey) }
266 }
267 }
268 }
269
270 fun migrateStartupDataToDeviceProtectedStorage() {
Stefan Andonian54495f32023-09-29 23:41:26 +0000271 if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000272
273 Log.d(
274 TAG,
275 "Migrating data to unencrypted shared preferences to enable preloading " +
276 "while the user is locked the next time the device reboots."
277 )
278
279 with(bootAwarePrefs.edit()) {
Stefan Andonian54495f32023-09-29 23:41:26 +0000280 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000281 putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
282 apply()
283 }
284 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000285
286 companion object {
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000287 private const val TAG = "LauncherPrefs"
288 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
289
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000290 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
291
292 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
293
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700294 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700295 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian54495f32023-09-29 23:41:26 +0000296 @JvmField
297 val ICON_STATE =
298 nonRestorableItem(
299 LauncherAppState.KEY_ICON_STATE,
300 "",
301 EncryptionType.MOVE_TO_DEVICE_PROTECTED
302 )
Andy Wickhamaf5bb302023-05-17 14:13:44 -0700303 @JvmField
304 val ALL_APPS_OVERVIEW_THRESHOLD =
Stefan Andonian54495f32023-09-29 23:41:26 +0000305 nonRestorableItem(
306 LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD,
307 180,
308 EncryptionType.MOVE_TO_DEVICE_PROTECTED
309 )
310 @JvmField
311 val THEMED_ICONS =
312 backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000313 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
Sunny Goyal576f7a52023-10-06 11:28:25 -0700314 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
Stefan Andonian54495f32023-09-29 23:41:26 +0000315 @JvmField
316 val WORKSPACE_SIZE =
317 backedUpItem(
318 DeviceGridState.KEY_WORKSPACE_SIZE,
319 "",
320 EncryptionType.MOVE_TO_DEVICE_PROTECTED
321 )
322 @JvmField
323 val HOTSEAT_COUNT =
324 backedUpItem(
325 DeviceGridState.KEY_HOTSEAT_COUNT,
326 -1,
327 EncryptionType.MOVE_TO_DEVICE_PROTECTED
328 )
329 @JvmField
330 val TASKBAR_PINNING =
331 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700332
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000333 @JvmField
334 val DEVICE_TYPE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000335 backedUpItem(
336 DeviceGridState.KEY_DEVICE_TYPE,
337 InvariantDeviceProfile.TYPE_PHONE,
338 EncryptionType.MOVE_TO_DEVICE_PROTECTED
339 )
340 @JvmField
341 val DB_FILE =
342 backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
fbarone58aaf12023-09-25 11:34:56 -0700343 @JvmField
344 val SHOULD_SHOW_SMARTSPACE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000345 backedUpItem(
346 SHOULD_SHOW_SMARTSPACE_KEY,
347 WIDGET_ON_FIRST_SCREEN,
348 EncryptionType.DEVICE_PROTECTED
349 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000350 @JvmField
351 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000352 backedUpItem(
353 RestoreDbTask.RESTORED_DEVICE_TYPE,
354 InvariantDeviceProfile.TYPE_PHONE,
Stefan Andonian54495f32023-09-29 23:41:26 +0000355 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000356 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000357 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
358 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000359 @JvmField
360 val GRID_NAME =
361 ConstantItem(
362 "idp_grid_name",
363 isBackedUp = true,
364 defaultValue = null,
Stefan Andonian54495f32023-09-29 23:41:26 +0000365 encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000366 type = String::class.java
367 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000368 @JvmField
369 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000370 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000371 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
372 }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000373 @JvmField
374 val IS_STARTUP_DATA_MIGRATED =
375 ConstantItem(
376 "is_startup_data_boot_aware",
377 isBackedUp = false,
378 defaultValue = false,
Stefan Andonian54495f32023-09-29 23:41:26 +0000379 encryptionType = EncryptionType.DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000380 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000381
Sunny Goyal576f7a52023-10-06 11:28:25 -0700382 // Preferences for widget configurations
383 @JvmField
384 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
385 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
386 @JvmField
387 val WIDGETS_EDUCATION_DIALOG_SEEN =
388 backedUpItem("launcher.widgets_education_dialog_seen", false)
389 @JvmField
390 val WIDGETS_EDUCATION_TIP_SEEN = backedUpItem("launcher.widgets_education_tip_seen", false)
391
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000392 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000393 fun <T> backedUpItem(
394 sharedPrefKey: String,
395 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000396 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000397 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000398 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000399
400 @JvmStatic
401 fun <T> backedUpItem(
402 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000403 type: Class<out T>,
Stefan Andonian54495f32023-09-29 23:41:26 +0000404 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000405 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000406 ): ContextualItem<T> =
407 ContextualItem(
408 sharedPrefKey,
409 isBackedUp = true,
410 defaultValueFromContext,
Stefan Andonian54495f32023-09-29 23:41:26 +0000411 encryptionType,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000412 type
413 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000414
415 @VisibleForTesting
416 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000417 fun <T> nonRestorableItem(
418 sharedPrefKey: String,
419 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000420 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000421 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000422 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000423
424 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
425 @JvmStatic
426 fun getPrefs(context: Context): SharedPreferences {
427 // Use application context for shared preferences, so we use single cached instance
428 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000429 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000430 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000431 )
432 }
433
434 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
435 @JvmStatic
436 fun getDevicePrefs(context: Context): SharedPreferences {
437 // Use application context for shared preferences, so we use a single cached instance
438 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000439 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000440 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000441 )
442 }
443 }
444}
445
Stefan Andoniandbc8ec52023-09-21 19:05:07 +0000446// It is a var because the unit tests are setting this to true so they can run.
Stefan Andonian54495f32023-09-29 23:41:26 +0000447var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
448 com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000449
Stefan Andonian54495f32023-09-29 23:41:26 +0000450private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000451
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000452abstract class Item {
453 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000454 abstract val isBackedUp: Boolean
455 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000456 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000457 val sharedPrefFile: String
458 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000459
460 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
461}
462
463data class ConstantItem<T>(
464 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000465 override val isBackedUp: Boolean,
466 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000467 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000468 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
469 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000470) : Item() {
471 init {
Stefan Andonian54495f32023-09-29 23:41:26 +0000472 if (
473 encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
474 moveStartupDataToDeviceProtectedStorageIsEnabled
475 ) {
476 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000477 }
478 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700479
480 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000481}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000482
483data class ContextualItem<T>(
484 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000485 override val isBackedUp: Boolean,
486 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000487 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000488 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000489) : Item() {
490 private var default: T? = null
491
492 fun defaultValueFromContext(context: Context): T {
493 if (default == null) {
494 default = defaultSupplier(context)
495 }
496 return default!!
497 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700498
499 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000500}
Stefan Andonian54495f32023-09-29 23:41:26 +0000501
502enum class EncryptionType {
503 ENCRYPTED,
504 DEVICE_PROTECTED,
505 MOVE_TO_DEVICE_PROTECTED
506}