blob: 067d150fc60c9ee7996698d05342a5e272f73c68 [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
Andreas Agvardfc74c092023-11-16 15:06:46 +010027import com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY
Andreas Agvardb36bcff2023-11-10 15:16:55 +010028import com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT
29import com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS
30import com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_SCALE_EXPONENT
31import com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_START_SCALE_PERCENT
Anushree Ganjamcb571da2023-10-24 14:47:09 -070032import com.android.launcher3.config.FeatureFlags.LPNH_SLOP_PERCENTAGE
33import com.android.launcher3.config.FeatureFlags.LPNH_TIMEOUT_MS
Stefan Andoniand1b33b32022-12-16 21:22:27 +000034import com.android.launcher3.model.DeviceGridState
35import com.android.launcher3.pm.InstallSessionHelper
36import com.android.launcher3.provider.RestoreDbTask
Charlie Anderson511421c2023-10-26 11:22:26 -040037import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +000038import com.android.launcher3.states.RotationHelper
39import com.android.launcher3.util.DisplayController
Stefan Andoniand1b33b32022-12-16 21:22:27 +000040import com.android.launcher3.util.MainThreadInitializedObject
41import com.android.launcher3.util.Themes
Stefan Andonian146701c2022-11-10 23:07:40 +000042
Stefan Andoniand1b33b32022-12-16 21:22:27 +000043/**
44 * Use same context for shared preferences, so that we use a single cached instance
Jordan Demeulenaerebe82bc62023-03-01 09:11:48 +000045 *
Stefan Andoniand1b33b32022-12-16 21:22:27 +000046 * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
Stefan Andonian956b88e2023-03-20 20:38:46 +000047 * TODO(b/274501660): Fix ReorderWidgets#simpleReorder test before enabling
48 * isBootAwareStartupDataEnabled
Stefan Andoniand1b33b32022-12-16 21:22:27 +000049 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000050class LauncherPrefs(private val encryptedContext: Context) {
51 private val deviceProtectedStorageContext =
52 encryptedContext.createDeviceProtectedStorageContext()
53
54 private val bootAwarePrefs
55 get() =
56 deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
57
58 private val Item.encryptedPrefs
59 get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
60
61 // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
62 // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
63 val isStartupDataMigrated: Boolean
64 get() =
65 bootAwarePrefs.getBoolean(
66 IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
67 IS_STARTUP_DATA_MIGRATED.defaultValue
68 )
69
70 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Stefan Andonian939d5f82023-09-25 18:07:43 +000071 if (
Stefan Andonian54495f32023-09-29 23:41:26 +000072 (moveStartupDataToDeviceProtectedStorageIsEnabled &&
73 item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
74 isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andonian939d5f82023-09-25 18:07:43 +000075 )
Stefan Andonian5bd9a222023-02-23 00:58:33 +000076 bootAwarePrefs
77 else item.encryptedPrefs
Stefan Andonian146701c2022-11-10 23:07:40 +000078
Stefan Andonian1d7f7032023-01-23 21:55:04 +000079 /** Wrapper around `getInner` for a `ContextualItem` */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000080 fun <T> get(item: ContextualItem<T>): T =
81 getInner(item, item.defaultValueFromContext(encryptedContext))
Stefan Andonian1d7f7032023-01-23 21:55:04 +000082
83 /** Wrapper around `getInner` for an `Item` */
Stefan Andonian4c9612b2023-02-22 00:00:03 +000084 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Stefan Andonian1d7f7032023-01-23 21:55:04 +000085
Stefan Andoniand1b33b32022-12-16 21:22:27 +000086 /**
87 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
88 * default value type, and will throw an error if the type of the item provided is not a
89 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
90 */
91 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
Stefan Andonian4c9612b2023-02-22 00:00:03 +000092 private fun <T> getInner(item: Item, default: T): T {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000093 val sp = chooseSharedPreferences(item)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000094
Stefan Andonian4c9612b2023-02-22 00:00:03 +000095 return when (item.type) {
96 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000097 Boolean::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000098 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000099 Int::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000100 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000101 Float::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000102 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000103 Long::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000104 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000105 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000106 else ->
107 throw IllegalArgumentException(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000108 "item type: ${item.type}" + " is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000109 )
110 }
111 as T
Stefan Andonian146701c2022-11-10 23:07:40 +0000112 }
113
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000114 /**
115 * Stores each of the values provided in `SharedPreferences` according to the configuration
116 * contained within the associated items provided. Internally, it uses apply, so the caller
117 * cannot assume that the values that have been put are immediately available for use.
118 *
119 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
120 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
121 * provided item configurations.
122 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000123 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000124 prepareToPutValues(itemsToValues).forEach { it.apply() }
125
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000126 /** See referenced `put` method above. */
127 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000128
129 /**
130 * Synchronously stores all the values provided according to their associated Item
131 * configuration.
132 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000133 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000134 prepareToPutValues(itemsToValues).forEach { it.commit() }
135
136 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000137 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
138 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
139 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
140 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
141 * already points to encrypted storage.
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000142 *
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000143 * Returns a list of editors with all transactions added so that the caller can determine to use
144 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000145 */
146 private fun prepareToPutValues(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000147 updates: Array<out Pair<Item, Any>>
148 ): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000149 val updatesPerPrefFile =
150 updates
151 .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
152 .groupBy { it.first.encryptedPrefs }
153 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000154
Stefan Andonian54495f32023-09-29 23:41:26 +0000155 val bootAwareUpdates =
156 updates.filter {
157 (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
158 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
159 it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000160 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000161 if (bootAwareUpdates.isNotEmpty()) {
162 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000163 }
164
165 return updatesPerPrefFile.map { prefToItemValueList ->
166 prefToItemValueList.key.edit().apply {
167 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
168 putValue(itemToValue.first, itemToValue.second)
169 }
170 }
171 }
172 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000173
174 /**
175 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
176 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
177 * types of Item values.
178 */
179 @Suppress("UNCHECKED_CAST")
180 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000181 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000182 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000183 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000184 when (item.type) {
185 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000186 Boolean::class.java,
187 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
188 Int::class.java,
189 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
190 Float::class.java,
191 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
192 Long::class.java,
193 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000194 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000195 else ->
196 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000197 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000198 )
199 }
200
201 /**
202 * After calling this method, the listener will be notified of any future updates to the
203 * `SharedPreferences` files associated with the provided list of items. The listener will need
204 * to filter update notifications so they don't activate for non-relevant updates.
205 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000206 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000207 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000208 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000209 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000210 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000211 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000212
213 /**
214 * Stops the listener from getting notified of any more updates to any of the
215 * `SharedPreferences` files associated with any of the provided list of [Item].
216 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000217 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000218 // If a listener is not registered to a SharedPreference, unregistering it does nothing
219 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000220 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000221 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000222 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000223 }
224
225 /**
226 * Checks if all the provided [Item] have values stored in their corresponding
227 * `SharedPreferences` files.
228 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000229 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000230 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000231 .groupBy { chooseSharedPreferences(it) }
232 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000233 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
234 }
235 return true
236 }
237
238 /**
239 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
240 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000241 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000242
243 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000244 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000245
246 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000247 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
248 * item is boot aware, this method removes the data from both the boot aware and encrypted
249 * files.
250 *
251 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000252 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000253 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000254 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000255 val itemsPerFile =
256 items
257 .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
258 .groupBy { it.encryptedPrefs }
259 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000260
Stefan Andonian54495f32023-09-29 23:41:26 +0000261 val bootAwareUpdates =
262 items.filter {
263 (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
264 moveStartupDataToDeviceProtectedStorageIsEnabled) ||
265 it.encryptionType == EncryptionType.DEVICE_PROTECTED
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000266 }
Stefan Andonian54495f32023-09-29 23:41:26 +0000267 if (bootAwareUpdates.isNotEmpty()) {
268 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000269 }
270
271 return itemsPerFile.map { (prefs, items) ->
272 prefs.edit().also { editor ->
273 items.forEach { item -> editor.remove(item.sharedPrefKey) }
274 }
275 }
276 }
277
278 fun migrateStartupDataToDeviceProtectedStorage() {
Stefan Andonian54495f32023-09-29 23:41:26 +0000279 if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000280
281 Log.d(
282 TAG,
283 "Migrating data to unencrypted shared preferences to enable preloading " +
284 "while the user is locked the next time the device reboots."
285 )
286
287 with(bootAwarePrefs.edit()) {
Stefan Andonian54495f32023-09-29 23:41:26 +0000288 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000289 putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
290 apply()
291 }
292 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000293
294 companion object {
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000295 private const val TAG = "LauncherPrefs"
296 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
297
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000298 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
299
300 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
301
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700302 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700303 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian54495f32023-09-29 23:41:26 +0000304 @JvmField
305 val ICON_STATE =
Charlie Andersoned98c482024-01-09 16:04:10 -0500306 nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Andy Wickhamaf5bb302023-05-17 14:13:44 -0700307 @JvmField
308 val ALL_APPS_OVERVIEW_THRESHOLD =
Stefan Andonian54495f32023-09-29 23:41:26 +0000309 nonRestorableItem(
Charlie Andersoned98c482024-01-09 16:04:10 -0500310 "pref_all_apps_overview_threshold",
Stefan Andonian54495f32023-09-29 23:41:26 +0000311 180,
312 EncryptionType.MOVE_TO_DEVICE_PROTECTED
313 )
314 @JvmField
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700315 val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
Anushree Ganjamcb571da2023-10-24 14:47:09 -0700316 nonRestorableItem(
317 "pref_long_press_nav_handle_slop_percentage",
Charlie Andersoned98c482024-01-09 16:04:10 -0500318 LPNH_SLOP_PERCENTAGE.get(),
319 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Anushree Ganjamcb571da2023-10-24 14:47:09 -0700320 )
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700321 @JvmField
322 val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
Charlie Andersoned98c482024-01-09 16:04:10 -0500323 nonRestorableItem(
324 "pref_long_press_nav_handle_timeout_ms",
325 LPNH_TIMEOUT_MS.get(),
326 EncryptionType.MOVE_TO_DEVICE_PROTECTED
327 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200328 @JvmField
329 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
Charlie Andersoned98c482024-01-09 16:04:10 -0500330 nonRestorableItem(
331 "pref_long_press_nav_handle_haptic_hint_start_scale_percent",
332 LPNH_HAPTIC_HINT_START_SCALE_PERCENT.get(),
333 EncryptionType.MOVE_TO_DEVICE_PROTECTED
334 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200335 @JvmField
336 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
Charlie Andersoned98c482024-01-09 16:04:10 -0500337 nonRestorableItem(
338 "pref_long_press_nav_handle_haptic_hint_end_scale_percent",
339 LPNH_HAPTIC_HINT_END_SCALE_PERCENT.get(),
340 EncryptionType.MOVE_TO_DEVICE_PROTECTED
341 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200342 @JvmField
343 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
Charlie Andersoned98c482024-01-09 16:04:10 -0500344 nonRestorableItem(
345 "pref_long_press_nav_handle_haptic_hint_scale_exponent",
346 LPNH_HAPTIC_HINT_SCALE_EXPONENT.get(),
347 EncryptionType.MOVE_TO_DEVICE_PROTECTED
348 )
Andreas Agvardd1674982023-10-16 15:49:56 +0200349 @JvmField
350 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
Charlie Andersoned98c482024-01-09 16:04:10 -0500351 nonRestorableItem(
352 "pref_long_press_nav_handle_haptic_hint_iterations",
353 LPNH_HAPTIC_HINT_ITERATIONS.get(),
354 EncryptionType.MOVE_TO_DEVICE_PROTECTED
355 )
Andy Wickham9fbc52f2023-09-22 17:44:23 -0700356 @JvmField
Andreas Agvardfc74c092023-11-16 15:06:46 +0100357 val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
Charlie Andersoned98c482024-01-09 16:04:10 -0500358 nonRestorableItem(
359 "pref_long_press_nav_handle_haptic_hint_delay",
360 LPNH_HAPTIC_HINT_DELAY.get(),
361 EncryptionType.MOVE_TO_DEVICE_PROTECTED
362 )
Andreas Agvardfc74c092023-11-16 15:06:46 +0100363 @JvmField
Brandon Dayauon96901c42023-11-17 15:14:49 -0800364 val PRIVATE_SPACE_APPS =
Charlie Andersoned98c482024-01-09 16:04:10 -0500365 nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Brandon Dayauon96901c42023-11-17 15:14:49 -0800366 @JvmField
Stefan Andonian54495f32023-09-29 23:41:26 +0000367 val THEMED_ICONS =
368 backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000369 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
Sunny Goyal576f7a52023-10-06 11:28:25 -0700370 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
Stefan Andonian54495f32023-09-29 23:41:26 +0000371 @JvmField
372 val WORKSPACE_SIZE =
373 backedUpItem(
374 DeviceGridState.KEY_WORKSPACE_SIZE,
375 "",
376 EncryptionType.MOVE_TO_DEVICE_PROTECTED
377 )
378 @JvmField
379 val HOTSEAT_COUNT =
380 backedUpItem(
381 DeviceGridState.KEY_HOTSEAT_COUNT,
382 -1,
383 EncryptionType.MOVE_TO_DEVICE_PROTECTED
384 )
385 @JvmField
386 val TASKBAR_PINNING =
387 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700388
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000389 @JvmField
390 val DEVICE_TYPE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000391 backedUpItem(
392 DeviceGridState.KEY_DEVICE_TYPE,
393 InvariantDeviceProfile.TYPE_PHONE,
394 EncryptionType.MOVE_TO_DEVICE_PROTECTED
395 )
396 @JvmField
397 val DB_FILE =
398 backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
fbarone58aaf12023-09-25 11:34:56 -0700399 @JvmField
400 val SHOULD_SHOW_SMARTSPACE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000401 backedUpItem(
402 SHOULD_SHOW_SMARTSPACE_KEY,
403 WIDGET_ON_FIRST_SCREEN,
404 EncryptionType.DEVICE_PROTECTED
405 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000406 @JvmField
407 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000408 backedUpItem(
409 RestoreDbTask.RESTORED_DEVICE_TYPE,
410 InvariantDeviceProfile.TYPE_PHONE,
Stefan Andonian54495f32023-09-29 23:41:26 +0000411 EncryptionType.MOVE_TO_DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000412 )
Charlie Anderson511421c2023-10-26 11:22:26 -0400413 @JvmField
414 val IS_FIRST_LOAD_AFTER_RESTORE =
Charlie Andersoned98c482024-01-09 16:04:10 -0500415 nonRestorableItem(
Charlie Anderson511421c2023-10-26 11:22:26 -0400416 FIRST_LOAD_AFTER_RESTORE_KEY,
417 false,
418 EncryptionType.MOVE_TO_DEVICE_PROTECTED
419 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000420 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
421 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000422 @JvmField
423 val GRID_NAME =
424 ConstantItem(
425 "idp_grid_name",
426 isBackedUp = true,
427 defaultValue = null,
Stefan Andonian54495f32023-09-29 23:41:26 +0000428 encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000429 type = String::class.java
430 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000431 @JvmField
432 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000433 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000434 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
435 }
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000436 @JvmField
437 val IS_STARTUP_DATA_MIGRATED =
438 ConstantItem(
439 "is_startup_data_boot_aware",
440 isBackedUp = false,
441 defaultValue = false,
Stefan Andonian54495f32023-09-29 23:41:26 +0000442 encryptionType = EncryptionType.DEVICE_PROTECTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000443 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000444
Sunny Goyal576f7a52023-10-06 11:28:25 -0700445 // Preferences for widget configurations
446 @JvmField
447 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
448 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
449 @JvmField
450 val WIDGETS_EDUCATION_DIALOG_SEEN =
451 backedUpItem("launcher.widgets_education_dialog_seen", false)
452 @JvmField
453 val WIDGETS_EDUCATION_TIP_SEEN = backedUpItem("launcher.widgets_education_tip_seen", false)
454
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000455 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000456 fun <T> backedUpItem(
457 sharedPrefKey: String,
458 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000459 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000460 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000461 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000462
463 @JvmStatic
464 fun <T> backedUpItem(
465 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000466 type: Class<out T>,
Stefan Andonian54495f32023-09-29 23:41:26 +0000467 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000468 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000469 ): ContextualItem<T> =
470 ContextualItem(
471 sharedPrefKey,
472 isBackedUp = true,
473 defaultValueFromContext,
Stefan Andonian54495f32023-09-29 23:41:26 +0000474 encryptionType,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000475 type
476 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000477
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000478 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000479 fun <T> nonRestorableItem(
480 sharedPrefKey: String,
481 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000482 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000483 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000484 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000485
486 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
487 @JvmStatic
488 fun getPrefs(context: Context): SharedPreferences {
489 // Use application context for shared preferences, so we use single cached instance
490 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000491 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000492 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000493 )
494 }
495
496 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
497 @JvmStatic
498 fun getDevicePrefs(context: Context): SharedPreferences {
499 // Use application context for shared preferences, so we use a single cached instance
500 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000501 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000502 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000503 )
504 }
505 }
506}
507
Stefan Andoniandbc8ec52023-09-21 19:05:07 +0000508// It is a var because the unit tests are setting this to true so they can run.
Stefan Andonian54495f32023-09-29 23:41:26 +0000509var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
510 com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000511
Stefan Andonian54495f32023-09-29 23:41:26 +0000512private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000513
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000514abstract class Item {
515 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000516 abstract val isBackedUp: Boolean
517 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000518 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000519 val sharedPrefFile: String
520 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000521
522 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
523}
524
525data class ConstantItem<T>(
526 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000527 override val isBackedUp: Boolean,
528 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000529 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000530 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
531 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000532) : Item() {
533 init {
Stefan Andonian54495f32023-09-29 23:41:26 +0000534 if (
535 encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
536 moveStartupDataToDeviceProtectedStorageIsEnabled
537 ) {
538 ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000539 }
540 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700541
542 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000543}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000544
545data class ContextualItem<T>(
546 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000547 override val isBackedUp: Boolean,
548 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000549 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000550 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000551) : Item() {
552 private var default: T? = null
553
554 fun defaultValueFromContext(context: Context): T {
555 if (default == null) {
556 default = defaultSupplier(context)
557 }
558 return default!!
559 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700560
561 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000562}
Stefan Andonian54495f32023-09-29 23:41:26 +0000563
564enum class EncryptionType {
565 ENCRYPTED,
566 DEVICE_PROTECTED,
567 MOVE_TO_DEVICE_PROTECTED
568}