blob: cb19b143a418c1a163b16a1101ef28e03994aeb9 [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
Priyanka Advania80997d2024-02-23 18:05:39 +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
Charlie Anderson511421c2023-10-26 11:22:26 -040030import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
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.
Priyanka Advania80997d2024-02-23 18:05:39 +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
Priyanka Advania80997d2024-02-23 18:05:39 +000054 // 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 Andonian5bd9a222023-02-23 00:58:33 +000063 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Priyanka Advania80997d2024-02-23 18:05:39 +000064 if (
65 (moveStartupDataToDeviceProtectedStorageIsEnabled &&
66 item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
67 isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
68 )
69 bootAwarePrefs
Stefan Andonian5bd9a222023-02-23 00:58:33 +000070 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> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000142 val updatesPerPrefFile =
143 updates
144 .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
145 .groupBy { it.first.encryptedPrefs }
146 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000147
Stefan Andonian54495f32023-09-29 23:41:26 +0000148 val bootAwareUpdates =
Priyanka Advania80997d2024-02-23 18:05:39 +0000149 updates.filter {
150 (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
151 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
152 it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
153 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000154 if (bootAwareUpdates.isNotEmpty()) {
155 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000156 }
157
158 return updatesPerPrefFile.map { prefToItemValueList ->
159 prefToItemValueList.key.edit().apply {
160 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
161 putValue(itemToValue.first, itemToValue.second)
162 }
163 }
164 }
165 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000166
167 /**
168 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
169 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
170 * types of Item values.
171 */
172 @Suppress("UNCHECKED_CAST")
173 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000174 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000175 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000176 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000177 when (item.type) {
178 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000179 Boolean::class.java,
180 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
181 Int::class.java,
182 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
183 Float::class.java,
184 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
185 Long::class.java,
186 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000187 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000188 else ->
189 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000190 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000191 )
192 }
193
194 /**
195 * After calling this method, the listener will be notified of any future updates to the
196 * `SharedPreferences` files associated with the provided list of items. The listener will need
197 * to filter update notifications so they don't activate for non-relevant updates.
198 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000199 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000200 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000201 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000202 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000203 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000204 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000205
206 /**
207 * Stops the listener from getting notified of any more updates to any of the
208 * `SharedPreferences` files associated with any of the provided list of [Item].
209 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000210 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000211 // If a listener is not registered to a SharedPreference, unregistering it does nothing
212 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000213 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000214 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000215 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000216 }
217
218 /**
219 * Checks if all the provided [Item] have values stored in their corresponding
220 * `SharedPreferences` files.
221 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000222 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000223 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000224 .groupBy { chooseSharedPreferences(it) }
225 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000226 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
227 }
228 return true
229 }
230
231 /**
232 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
233 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000234 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000235
236 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000237 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000238
239 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000240 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
241 * item is boot aware, this method removes the data from both the boot aware and encrypted
242 * files.
243 *
244 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000245 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000246 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000247 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000248 val itemsPerFile =
249 items
250 .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
251 .groupBy { it.encryptedPrefs }
252 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000253
Priyanka Advania80997d2024-02-23 18:05:39 +0000254 val bootAwareUpdates =
255 items.filter {
256 (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
257 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
258 it.encryptionType == EncryptionType.DEVICE_PROTECTED
259 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000260 if (bootAwareUpdates.isNotEmpty()) {
261 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000262 }
263
264 return itemsPerFile.map { (prefs, items) ->
265 prefs.edit().also { editor ->
266 items.forEach { item -> editor.remove(item.sharedPrefKey) }
267 }
268 }
269 }
270
Priyanka Advania80997d2024-02-23 18:05:39 +0000271 fun migrateStartupDataToDeviceProtectedStorage() {
272 if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return
273
274 Log.d(
275 TAG,
276 "Migrating data to unencrypted shared preferences to enable preloading " +
277 "while the user is locked the next time the device reboots."
278 )
279
280 with(bootAwarePrefs.edit()) {
281 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
282 putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
283 apply()
284 }
285 }
286
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000287 companion object {
Priyanka Advania80997d2024-02-23 18:05:39 +0000288 private const val TAG = "LauncherPrefs"
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000289 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
290
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000291 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
292
293 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
294
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700295 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700296 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian54495f32023-09-29 23:41:26 +0000297 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000298 val ICON_STATE =
299 nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickhamaf5bb302023-05-17 14:13:44 -0700300 @JvmField
301 val ALL_APPS_OVERVIEW_THRESHOLD =
Priyanka Advania80997d2024-02-23 18:05:39 +0000302 nonRestorableItem(
303 "pref_all_apps_overview_threshold",
304 180,
305 EncryptionType.MOVE_TO_DEVICE_PROTECTED
306 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000307 @JvmField
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700308 val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000309 nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700310 @JvmField
Andy Wickhame92f25d2023-10-03 12:48:57 -0700311 val LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP =
312 nonRestorableItem(
313 "LPNH_EXTRA_TOUCH_WIDTH_DP",
314 0,
315 EncryptionType.MOVE_TO_DEVICE_PROTECTED
316 )
317 @JvmField
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700318 val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
Charlie Andersoned98c482024-01-09 16:04:10 -0500319 nonRestorableItem(
Anushree Ganjameb5cb752024-01-18 11:14:54 -0800320 "LPNH_TIMEOUT_MS",
Anushree Ganjamf1b8e4e2024-03-29 12:29:32 -0700321 450,
Priyanka Advania80997d2024-02-23 18:05:39 +0000322 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Charlie Andersoned98c482024-01-09 16:04:10 -0500323 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200324 @JvmField
325 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000326 nonRestorableItem(
327 "LPNH_HAPTIC_HINT_START_SCALE_PERCENT",
328 0,
329 EncryptionType.MOVE_TO_DEVICE_PROTECTED
330 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200331 @JvmField
332 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000333 nonRestorableItem(
334 "LPNH_HAPTIC_HINT_END_SCALE_PERCENT",
335 100,
336 EncryptionType.MOVE_TO_DEVICE_PROTECTED
337 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200338 @JvmField
339 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000340 nonRestorableItem(
341 "LPNH_HAPTIC_HINT_SCALE_EXPONENT",
342 1,
343 EncryptionType.MOVE_TO_DEVICE_PROTECTED
344 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200345 @JvmField
346 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
Priyanka Advania80997d2024-02-23 18:05:39 +0000347 nonRestorableItem(
348 "LPNH_HAPTIC_HINT_ITERATIONS",
349 50,
350 EncryptionType.MOVE_TO_DEVICE_PROTECTED
351 )
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700352 @JvmField
Andreas Agvardfc74c092023-11-16 15:06:46 +0100353 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
Priyanka Advania80997d2024-02-23 18:05:39 +0000354 nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andreas Agvardfc74c092023-11-16 15:06:46 +0100355 @JvmField
Brandon Dayauon96901c42023-11-17 15:14:49 -0800356 val PRIVATE_SPACE_APPS =
Priyanka Advania80997d2024-02-23 18:05:39 +0000357 nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickhame92f25d2023-10-03 12:48:57 -0700358 @JvmField
359 val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
Brandon Dayauon96901c42023-11-17 15:14:49 -0800360 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000361 val THEMED_ICONS =
362 backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000363 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
Sunny Goyal576f7a52023-10-06 11:28:25 -0700364 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
Stefan Andonian54495f32023-09-29 23:41:26 +0000365 @JvmField
366 val WORKSPACE_SIZE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000367 backedUpItem(
368 DeviceGridState.KEY_WORKSPACE_SIZE,
369 "",
370 EncryptionType.MOVE_TO_DEVICE_PROTECTED
371 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000372 @JvmField
373 val HOTSEAT_COUNT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000374 backedUpItem(
375 DeviceGridState.KEY_HOTSEAT_COUNT,
376 -1,
377 EncryptionType.MOVE_TO_DEVICE_PROTECTED
378 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000379 @JvmField
380 val TASKBAR_PINNING =
381 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700382
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000383 @JvmField
384 val DEVICE_TYPE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000385 backedUpItem(
386 DeviceGridState.KEY_DEVICE_TYPE,
387 InvariantDeviceProfile.TYPE_PHONE,
Priyanka Advania80997d2024-02-23 18:05:39 +0000388 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian54495f32023-09-29 23:41:26 +0000389 )
390 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000391 val DB_FILE =
392 backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
fbarone58aaf12023-09-25 11:34:56 -0700393 @JvmField
394 val SHOULD_SHOW_SMARTSPACE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000395 backedUpItem(
396 SHOULD_SHOW_SMARTSPACE_KEY,
397 WIDGET_ON_FIRST_SCREEN,
398 EncryptionType.DEVICE_PROTECTED
399 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000400 @JvmField
401 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000402 backedUpItem(
403 RestoreDbTask.RESTORED_DEVICE_TYPE,
404 InvariantDeviceProfile.TYPE_PHONE,
Priyanka Advania80997d2024-02-23 18:05:39 +0000405 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000406 )
Charlie Anderson511421c2023-10-26 11:22:26 -0400407 @JvmField
408 val IS_FIRST_LOAD_AFTER_RESTORE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000409 nonRestorableItem(
410 FIRST_LOAD_AFTER_RESTORE_KEY,
411 false,
412 EncryptionType.MOVE_TO_DEVICE_PROTECTED
413 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000414 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
415 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000416 @JvmField
417 val GRID_NAME =
418 ConstantItem(
419 "idp_grid_name",
420 isBackedUp = true,
421 defaultValue = null,
Priyanka Advania80997d2024-02-23 18:05:39 +0000422 encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000423 type = String::class.java
424 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000425 @JvmField
426 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000427 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000428 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
429 }
Priyanka Advania80997d2024-02-23 18:05:39 +0000430 @JvmField
431 val IS_STARTUP_DATA_MIGRATED =
432 ConstantItem(
433 "is_startup_data_boot_aware",
434 isBackedUp = false,
435 defaultValue = false,
436 encryptionType = EncryptionType.DEVICE_PROTECTED
437 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000438
Sunny Goyal576f7a52023-10-06 11:28:25 -0700439 // Preferences for widget configurations
440 @JvmField
441 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
442 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
443 @JvmField
444 val WIDGETS_EDUCATION_DIALOG_SEEN =
445 backedUpItem("launcher.widgets_education_dialog_seen", false)
446 @JvmField
447 val WIDGETS_EDUCATION_TIP_SEEN = backedUpItem("launcher.widgets_education_tip_seen", false)
448
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000449 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000450 fun <T> backedUpItem(
451 sharedPrefKey: String,
452 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000453 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000454 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000455 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000456
457 @JvmStatic
458 fun <T> backedUpItem(
459 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000460 type: Class<out T>,
Stefan Andonian54495f32023-09-29 23:41:26 +0000461 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000462 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000463 ): ContextualItem<T> =
464 ContextualItem(
465 sharedPrefKey,
466 isBackedUp = true,
467 defaultValueFromContext,
Stefan Andonian54495f32023-09-29 23:41:26 +0000468 encryptionType,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000469 type
470 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000471
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000472 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000473 fun <T> nonRestorableItem(
474 sharedPrefKey: String,
475 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000476 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000477 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000478 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000479
480 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
481 @JvmStatic
482 fun getPrefs(context: Context): SharedPreferences {
483 // Use application context for shared preferences, so we use single cached instance
484 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000485 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000486 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000487 )
488 }
489
490 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
491 @JvmStatic
492 fun getDevicePrefs(context: Context): SharedPreferences {
493 // Use application context for shared preferences, so we use a single cached instance
494 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000495 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000496 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000497 )
498 }
499 }
500}
501
Priyanka Advania80997d2024-02-23 18:05:39 +0000502// It is a var because the unit tests are setting this to true so they can run.
503var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
504 com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()
505
506private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()
507
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000508abstract class Item {
509 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000510 abstract val isBackedUp: Boolean
511 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000512 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000513 val sharedPrefFile: String
514 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000515
516 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
517}
518
519data class ConstantItem<T>(
520 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000521 override val isBackedUp: Boolean,
522 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000523 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000524 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
525 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000526) : Item() {
Priyanka Advania80997d2024-02-23 18:05:39 +0000527 init {
528 if (
529 encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
530 moveStartupDataToDeviceProtectedStorageIsEnabled
531 ) {
532 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
533 }
534 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700535
536 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000537}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000538
539data class ContextualItem<T>(
540 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000541 override val isBackedUp: Boolean,
542 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000543 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000544 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000545) : Item() {
546 private var default: T? = null
547
548 fun defaultValueFromContext(context: Context): T {
549 if (default == null) {
550 default = defaultSupplier(context)
551 }
552 return default!!
553 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700554
555 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000556}
Stefan Andonian54495f32023-09-29 23:41:26 +0000557
558enum class EncryptionType {
559 ENCRYPTED,
560 DEVICE_PROTECTED,
Priyanka Advania80997d2024-02-23 18:05:39 +0000561 MOVE_TO_DEVICE_PROTECTED
Stefan Andonian54495f32023-09-29 23:41:26 +0000562}