blob: b0a644b2697f5a6010b6094a5a296b1c784edbf5 [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
Anushree Ganjameb5cb752024-01-18 11:14:54 -080023import android.view.ViewConfiguration
Stefan Andoniand1b33b32022-12-16 21:22:27 +000024import androidx.annotation.VisibleForTesting
fbarone58aaf12023-09-25 11:34:56 -070025import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
Stefan Andonian4c9612b2023-02-22 00:00:03 +000026import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
27import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
Stefan Andoniand1b33b32022-12-16 21:22:27 +000028import com.android.launcher3.model.DeviceGridState
29import com.android.launcher3.pm.InstallSessionHelper
30import com.android.launcher3.provider.RestoreDbTask
Charlie Anderson511421c2023-10-26 11:22:26 -040031import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +000032import com.android.launcher3.states.RotationHelper
33import com.android.launcher3.util.DisplayController
Stefan Andoniand1b33b32022-12-16 21:22:27 +000034import com.android.launcher3.util.MainThreadInitializedObject
35import com.android.launcher3.util.Themes
Stefan Andonian146701c2022-11-10 23:07:40 +000036
Stefan Andoniand1b33b32022-12-16 21:22:27 +000037/**
38 * Use same context for shared preferences, so that we use a single cached instance
Jordan Demeulenaerebe82bc62023-03-01 09:11:48 +000039 *
Stefan Andoniand1b33b32022-12-16 21:22:27 +000040 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
Priyanka Advania80997d2024-02-23 18:05:39 +000041 * TODO(b/274501660): Fix ReorderWidgets#simpleReorder test before enabling
42 * isBootAwareStartupDataEnabled
Stefan Andoniand1b33b32022-12-16 21:22:27 +000043 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000044class LauncherPrefs(private val encryptedContext: Context) {
45 private val deviceProtectedStorageContext =
46 encryptedContext.createDeviceProtectedStorageContext()
47
48 private val bootAwarePrefs
49 get() =
50 deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
51
52 private val Item.encryptedPrefs
53 get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
54
Priyanka Advania80997d2024-02-23 18:05:39 +000055 // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
56 // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
57 val isStartupDataMigrated: Boolean
58 get() =
59 bootAwarePrefs.getBoolean(
60 IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
61 IS_STARTUP_DATA_MIGRATED.defaultValue
62 )
63
Stefan Andonian5bd9a222023-02-23 00:58:33 +000064 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Priyanka Advania80997d2024-02-23 18:05:39 +000065 if (
66 (moveStartupDataToDeviceProtectedStorageIsEnabled &&
67 item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
68 isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
69 )
70 bootAwarePrefs
Stefan Andonian5bd9a222023-02-23 00:58:33 +000071 else item.encryptedPrefs
Stefan Andonian146701c2022-11-10 23:07:40 +000072
Stefan Andonian1d7f7032023-01-23 21:55:04 +000073 /** Wrapper around `getInner` for a `ContextualItem` */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000074 fun <T> get(item: ContextualItem<T>): T =
75 getInner(item, item.defaultValueFromContext(encryptedContext))
Stefan Andonian1d7f7032023-01-23 21:55:04 +000076
77 /** Wrapper around `getInner` for an `Item` */
Stefan Andonian4c9612b2023-02-22 00:00:03 +000078 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Stefan Andonian1d7f7032023-01-23 21:55:04 +000079
Stefan Andoniand1b33b32022-12-16 21:22:27 +000080 /**
81 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
82 * default value type, and will throw an error if the type of the item provided is not a
83 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
84 */
85 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
Stefan Andonian4c9612b2023-02-22 00:00:03 +000086 private fun <T> getInner(item: Item, default: T): T {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000087 val sp = chooseSharedPreferences(item)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000088
Stefan Andonian4c9612b2023-02-22 00:00:03 +000089 return when (item.type) {
90 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000091 Boolean::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000092 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000093 Int::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000094 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000095 Float::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000096 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000097 Long::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000098 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
Stefan Andonian4c9612b2023-02-22 00:00:03 +000099 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000100 else ->
101 throw IllegalArgumentException(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000102 "item type: ${item.type}" + " is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000103 )
104 }
105 as T
Stefan Andonian146701c2022-11-10 23:07:40 +0000106 }
107
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000108 /**
109 * Stores each of the values provided in `SharedPreferences` according to the configuration
110 * contained within the associated items provided. Internally, it uses apply, so the caller
111 * cannot assume that the values that have been put are immediately available for use.
112 *
113 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
114 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
115 * provided item configurations.
116 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000117 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000118 prepareToPutValues(itemsToValues).forEach { it.apply() }
119
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000120 /** See referenced `put` method above. */
121 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000122
123 /**
124 * Synchronously stores all the values provided according to their associated Item
125 * configuration.
126 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000127 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000128 prepareToPutValues(itemsToValues).forEach { it.commit() }
129
130 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000131 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
132 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
133 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
134 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
135 * already points to encrypted storage.
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000136 *
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000137 * Returns a list of editors with all transactions added so that the caller can determine to use
138 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000139 */
140 private fun prepareToPutValues(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000141 updates: Array<out Pair<Item, Any>>
142 ): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000143 val updatesPerPrefFile =
144 updates
145 .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
146 .groupBy { it.first.encryptedPrefs }
147 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000148
Stefan Andonian54495f32023-09-29 23:41:26 +0000149 val bootAwareUpdates =
Priyanka Advania80997d2024-02-23 18:05:39 +0000150 updates.filter {
151 (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
152 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
153 it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
154 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000155 if (bootAwareUpdates.isNotEmpty()) {
156 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000157 }
158
159 return updatesPerPrefFile.map { prefToItemValueList ->
160 prefToItemValueList.key.edit().apply {
161 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
162 putValue(itemToValue.first, itemToValue.second)
163 }
164 }
165 }
166 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000167
168 /**
169 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
170 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
171 * types of Item values.
172 */
173 @Suppress("UNCHECKED_CAST")
174 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000175 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000176 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000177 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000178 when (item.type) {
179 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000180 Boolean::class.java,
181 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
182 Int::class.java,
183 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
184 Float::class.java,
185 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
186 Long::class.java,
187 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000188 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000189 else ->
190 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000191 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000192 )
193 }
194
195 /**
196 * After calling this method, the listener will be notified of any future updates to the
197 * `SharedPreferences` files associated with the provided list of items. The listener will need
198 * to filter update notifications so they don't activate for non-relevant updates.
199 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000200 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000201 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000202 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000203 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000204 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000205 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000206
207 /**
208 * Stops the listener from getting notified of any more updates to any of the
209 * `SharedPreferences` files associated with any of the provided list of [Item].
210 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000211 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000212 // If a listener is not registered to a SharedPreference, unregistering it does nothing
213 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000214 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000215 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000216 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000217 }
218
219 /**
220 * Checks if all the provided [Item] have values stored in their corresponding
221 * `SharedPreferences` files.
222 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000223 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000224 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000225 .groupBy { chooseSharedPreferences(it) }
226 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000227 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
228 }
229 return true
230 }
231
232 /**
233 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
234 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000235 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000236
237 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000238 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000239
240 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000241 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
242 * item is boot aware, this method removes the data from both the boot aware and encrypted
243 * files.
244 *
245 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000246 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000247 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000248 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000249 val itemsPerFile =
250 items
251 .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
252 .groupBy { it.encryptedPrefs }
253 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000254
Priyanka Advania80997d2024-02-23 18:05:39 +0000255 val bootAwareUpdates =
256 items.filter {
257 (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
258 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
259 it.encryptionType == EncryptionType.DEVICE_PROTECTED
260 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000261 if (bootAwareUpdates.isNotEmpty()) {
262 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000263 }
264
265 return itemsPerFile.map { (prefs, items) ->
266 prefs.edit().also { editor ->
267 items.forEach { item -> editor.remove(item.sharedPrefKey) }
268 }
269 }
270 }
271
Priyanka Advania80997d2024-02-23 18:05:39 +0000272 fun migrateStartupDataToDeviceProtectedStorage() {
273 if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return
274
275 Log.d(
276 TAG,
277 "Migrating data to unencrypted shared preferences to enable preloading " +
278 "while the user is locked the next time the device reboots."
279 )
280
281 with(bootAwarePrefs.edit()) {
282 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
283 putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
284 apply()
285 }
286 }
287
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000288 companion object {
Priyanka Advania80997d2024-02-23 18:05:39 +0000289 private const val TAG = "LauncherPrefs"
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000290 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
291
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000292 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
293
294 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
295
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700296 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700297 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian54495f32023-09-29 23:41:26 +0000298 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000299 val ICON_STATE =
300 nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickhamaf5bb302023-05-17 14:13:44 -0700301 @JvmField
302 val ALL_APPS_OVERVIEW_THRESHOLD =
Priyanka Advania80997d2024-02-23 18:05:39 +0000303 nonRestorableItem(
304 "pref_all_apps_overview_threshold",
305 180,
306 EncryptionType.MOVE_TO_DEVICE_PROTECTED
307 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000308 @JvmField
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700309 val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000310 nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700311 @JvmField
312 val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
Charlie Andersoned98c482024-01-09 16:04:10 -0500313 nonRestorableItem(
Anushree Ganjameb5cb752024-01-18 11:14:54 -0800314 "LPNH_TIMEOUT_MS",
315 ViewConfiguration.getLongPressTimeout(),
Priyanka Advania80997d2024-02-23 18:05:39 +0000316 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Charlie Andersoned98c482024-01-09 16:04:10 -0500317 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200318 @JvmField
319 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000320 nonRestorableItem(
321 "LPNH_HAPTIC_HINT_START_SCALE_PERCENT",
322 0,
323 EncryptionType.MOVE_TO_DEVICE_PROTECTED
324 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200325 @JvmField
326 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000327 nonRestorableItem(
328 "LPNH_HAPTIC_HINT_END_SCALE_PERCENT",
329 100,
330 EncryptionType.MOVE_TO_DEVICE_PROTECTED
331 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200332 @JvmField
333 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000334 nonRestorableItem(
335 "LPNH_HAPTIC_HINT_SCALE_EXPONENT",
336 1,
337 EncryptionType.MOVE_TO_DEVICE_PROTECTED
338 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200339 @JvmField
340 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
Priyanka Advania80997d2024-02-23 18:05:39 +0000341 nonRestorableItem(
342 "LPNH_HAPTIC_HINT_ITERATIONS",
343 50,
344 EncryptionType.MOVE_TO_DEVICE_PROTECTED
345 )
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700346 @JvmField
Andreas Agvardfc74c092023-11-16 15:06:46 +0100347 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
Priyanka Advania80997d2024-02-23 18:05:39 +0000348 nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andreas Agvardfc74c092023-11-16 15:06:46 +0100349 @JvmField
Brandon Dayauon96901c42023-11-17 15:14:49 -0800350 val PRIVATE_SPACE_APPS =
Priyanka Advania80997d2024-02-23 18:05:39 +0000351 nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
352 @JvmField val ENABLE_TWOLINE_ALLAPPS_TOGGLE =
353 backedUpItem("pref_enable_two_line_toggle", false)
Brandon Dayauon96901c42023-11-17 15:14:49 -0800354 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000355 val THEMED_ICONS =
356 backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000357 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
Sunny Goyal576f7a52023-10-06 11:28:25 -0700358 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
Stefan Andonian54495f32023-09-29 23:41:26 +0000359 @JvmField
360 val WORKSPACE_SIZE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000361 backedUpItem(
362 DeviceGridState.KEY_WORKSPACE_SIZE,
363 "",
364 EncryptionType.MOVE_TO_DEVICE_PROTECTED
365 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000366 @JvmField
367 val HOTSEAT_COUNT =
Priyanka Advania80997d2024-02-23 18:05:39 +0000368 backedUpItem(
369 DeviceGridState.KEY_HOTSEAT_COUNT,
370 -1,
371 EncryptionType.MOVE_TO_DEVICE_PROTECTED
372 )
Stefan Andonian54495f32023-09-29 23:41:26 +0000373 @JvmField
374 val TASKBAR_PINNING =
375 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700376
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000377 @JvmField
378 val DEVICE_TYPE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000379 backedUpItem(
380 DeviceGridState.KEY_DEVICE_TYPE,
381 InvariantDeviceProfile.TYPE_PHONE,
Priyanka Advania80997d2024-02-23 18:05:39 +0000382 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian54495f32023-09-29 23:41:26 +0000383 )
384 @JvmField
Priyanka Advania80997d2024-02-23 18:05:39 +0000385 val DB_FILE =
386 backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
fbarone58aaf12023-09-25 11:34:56 -0700387 @JvmField
388 val SHOULD_SHOW_SMARTSPACE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000389 backedUpItem(
390 SHOULD_SHOW_SMARTSPACE_KEY,
391 WIDGET_ON_FIRST_SCREEN,
392 EncryptionType.DEVICE_PROTECTED
393 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000394 @JvmField
395 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000396 backedUpItem(
397 RestoreDbTask.RESTORED_DEVICE_TYPE,
398 InvariantDeviceProfile.TYPE_PHONE,
Priyanka Advania80997d2024-02-23 18:05:39 +0000399 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000400 )
Charlie Anderson511421c2023-10-26 11:22:26 -0400401 @JvmField
402 val IS_FIRST_LOAD_AFTER_RESTORE =
Priyanka Advania80997d2024-02-23 18:05:39 +0000403 nonRestorableItem(
404 FIRST_LOAD_AFTER_RESTORE_KEY,
405 false,
406 EncryptionType.MOVE_TO_DEVICE_PROTECTED
407 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000408 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
409 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000410 @JvmField
411 val GRID_NAME =
412 ConstantItem(
413 "idp_grid_name",
414 isBackedUp = true,
415 defaultValue = null,
Priyanka Advania80997d2024-02-23 18:05:39 +0000416 encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000417 type = String::class.java
418 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000419 @JvmField
420 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000421 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000422 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
423 }
Priyanka Advania80997d2024-02-23 18:05:39 +0000424 @JvmField
425 val IS_STARTUP_DATA_MIGRATED =
426 ConstantItem(
427 "is_startup_data_boot_aware",
428 isBackedUp = false,
429 defaultValue = false,
430 encryptionType = EncryptionType.DEVICE_PROTECTED
431 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000432
Sunny Goyal576f7a52023-10-06 11:28:25 -0700433 // Preferences for widget configurations
434 @JvmField
435 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
436 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
437 @JvmField
438 val WIDGETS_EDUCATION_DIALOG_SEEN =
439 backedUpItem("launcher.widgets_education_dialog_seen", false)
440 @JvmField
441 val WIDGETS_EDUCATION_TIP_SEEN = backedUpItem("launcher.widgets_education_tip_seen", false)
442
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000443 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000444 fun <T> backedUpItem(
445 sharedPrefKey: String,
446 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000447 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000448 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000449 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000450
451 @JvmStatic
452 fun <T> backedUpItem(
453 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000454 type: Class<out T>,
Stefan Andonian54495f32023-09-29 23:41:26 +0000455 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000456 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000457 ): ContextualItem<T> =
458 ContextualItem(
459 sharedPrefKey,
460 isBackedUp = true,
461 defaultValueFromContext,
Stefan Andonian54495f32023-09-29 23:41:26 +0000462 encryptionType,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000463 type
464 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000465
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000466 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000467 fun <T> nonRestorableItem(
468 sharedPrefKey: String,
469 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000470 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000471 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000472 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000473
474 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
475 @JvmStatic
476 fun getPrefs(context: Context): SharedPreferences {
477 // Use application context for shared preferences, so we use single cached instance
478 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000479 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000480 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000481 )
482 }
483
484 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
485 @JvmStatic
486 fun getDevicePrefs(context: Context): SharedPreferences {
487 // Use application context for shared preferences, so we use a single cached instance
488 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000489 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000490 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000491 )
492 }
493 }
494}
495
Priyanka Advania80997d2024-02-23 18:05:39 +0000496// It is a var because the unit tests are setting this to true so they can run.
497var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
498 com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()
499
500private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()
501
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000502abstract class Item {
503 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000504 abstract val isBackedUp: Boolean
505 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000506 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000507 val sharedPrefFile: String
508 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000509
510 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
511}
512
513data class ConstantItem<T>(
514 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000515 override val isBackedUp: Boolean,
516 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000517 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000518 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
519 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000520) : Item() {
Priyanka Advania80997d2024-02-23 18:05:39 +0000521 init {
522 if (
523 encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
524 moveStartupDataToDeviceProtectedStorageIsEnabled
525 ) {
526 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
527 }
528 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700529
530 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000531}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000532
533data class ContextualItem<T>(
534 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000535 override val isBackedUp: Boolean,
536 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000537 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000538 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000539) : Item() {
540 private var default: T? = null
541
542 fun defaultValueFromContext(context: Context): T {
543 if (default == null) {
544 default = defaultSupplier(context)
545 }
546 return default!!
547 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700548
549 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000550}
Stefan Andonian54495f32023-09-29 23:41:26 +0000551
552enum class EncryptionType {
553 ENCRYPTED,
554 DEVICE_PROTECTED,
Priyanka Advania80997d2024-02-23 18:05:39 +0000555 MOVE_TO_DEVICE_PROTECTED
Stefan Andonian54495f32023-09-29 23:41:26 +0000556}