blob: 13181e87f29f3f2e2273922338f549201610671f [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
22import androidx.annotation.VisibleForTesting
fbarone58aaf12023-09-25 11:34:56 -070023import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
Stefan Andonian4c9612b2023-02-22 00:00:03 +000024import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
25import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
Stefan Andoniand1b33b32022-12-16 21:22:27 +000026import com.android.launcher3.model.DeviceGridState
27import com.android.launcher3.pm.InstallSessionHelper
28import com.android.launcher3.provider.RestoreDbTask
Charlie Anderson511421c2023-10-26 11:22:26 -040029import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +000030import com.android.launcher3.states.RotationHelper
31import com.android.launcher3.util.DisplayController
Stefan Andoniand1b33b32022-12-16 21:22:27 +000032import com.android.launcher3.util.MainThreadInitializedObject
Sunny Goyal10fa0162024-04-21 00:13:35 -070033import com.android.launcher3.util.SafeCloseable
Stefan Andoniand1b33b32022-12-16 21:22:27 +000034import 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.
40 */
Sunny Goyal10fa0162024-04-21 00:13:35 -070041class LauncherPrefs(private val encryptedContext: Context) : SafeCloseable {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000042 private val deviceProtectedStorageContext =
43 encryptedContext.createDeviceProtectedStorageContext()
44
45 private val bootAwarePrefs
46 get() =
47 deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
48
49 private val Item.encryptedPrefs
50 get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
51
Stefan Andonian5bd9a222023-02-23 00:58:33 +000052 private fun chooseSharedPreferences(item: Item): SharedPreferences =
Stefan Andonian1a626d22024-04-03 18:36:16 +000053 if (item.encryptionType == EncryptionType.DEVICE_PROTECTED) bootAwarePrefs
Stefan Andonian5bd9a222023-02-23 00:58:33 +000054 else item.encryptedPrefs
Stefan Andonian146701c2022-11-10 23:07:40 +000055
Stefan Andonian1d7f7032023-01-23 21:55:04 +000056 /** Wrapper around `getInner` for a `ContextualItem` */
Stefan Andonian5bd9a222023-02-23 00:58:33 +000057 fun <T> get(item: ContextualItem<T>): T =
58 getInner(item, item.defaultValueFromContext(encryptedContext))
Stefan Andonian1d7f7032023-01-23 21:55:04 +000059
60 /** Wrapper around `getInner` for an `Item` */
Stefan Andonian4c9612b2023-02-22 00:00:03 +000061 fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
Stefan Andonian1d7f7032023-01-23 21:55:04 +000062
Stefan Andoniand1b33b32022-12-16 21:22:27 +000063 /**
64 * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
65 * default value type, and will throw an error if the type of the item provided is not a
66 * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
67 */
68 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
Stefan Andonian4c9612b2023-02-22 00:00:03 +000069 private fun <T> getInner(item: Item, default: T): T {
Stefan Andonian5bd9a222023-02-23 00:58:33 +000070 val sp = chooseSharedPreferences(item)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000071
Stefan Andonian4c9612b2023-02-22 00:00:03 +000072 return when (item.type) {
73 String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000074 Boolean::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000075 java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000076 Int::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000077 java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000078 Float::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000079 java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000080 Long::class.java,
Stefan Andonian1d7f7032023-01-23 21:55:04 +000081 java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
Stefan Andonian4c9612b2023-02-22 00:00:03 +000082 Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +000083 else ->
84 throw IllegalArgumentException(
Stefan Andonian4c9612b2023-02-22 00:00:03 +000085 "item type: ${item.type}" + " is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +000086 )
87 }
88 as T
Stefan Andonian146701c2022-11-10 23:07:40 +000089 }
90
Stefan Andoniand1b33b32022-12-16 21:22:27 +000091 /**
92 * Stores each of the values provided in `SharedPreferences` according to the configuration
93 * contained within the associated items provided. Internally, it uses apply, so the caller
94 * cannot assume that the values that have been put are immediately available for use.
95 *
96 * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
97 * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
98 * provided item configurations.
99 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000100 fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000101 prepareToPutValues(itemsToValues).forEach { it.apply() }
102
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000103 /** See referenced `put` method above. */
104 fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000105
106 /**
107 * Synchronously stores all the values provided according to their associated Item
108 * configuration.
109 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000110 fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000111 prepareToPutValues(itemsToValues).forEach { it.commit() }
112
113 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000114 * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
115 * the item is boot aware, this method updates both the boot aware and the encrypted files. This
116 * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
117 * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
118 * already points to encrypted storage.
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000119 *
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000120 * Returns a list of editors with all transactions added so that the caller can determine to use
121 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000122 */
123 private fun prepareToPutValues(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000124 updates: Array<out Pair<Item, Any>>
125 ): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000126 val updatesPerPrefFile =
127 updates
128 .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
129 .groupBy { it.first.encryptedPrefs }
130 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000131
Stefan Andonian54495f32023-09-29 23:41:26 +0000132 val bootAwareUpdates =
Stefan Andonian1a626d22024-04-03 18:36:16 +0000133 updates.filter { it.first.encryptionType == EncryptionType.DEVICE_PROTECTED }
Stefan Andonian54495f32023-09-29 23:41:26 +0000134 if (bootAwareUpdates.isNotEmpty()) {
135 updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000136 }
137
138 return updatesPerPrefFile.map { prefToItemValueList ->
139 prefToItemValueList.key.edit().apply {
140 prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
141 putValue(itemToValue.first, itemToValue.second)
142 }
143 }
144 }
145 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000146
147 /**
148 * Handles adding values to `SharedPreferences` regardless of type. This method is especially
149 * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
150 * types of Item values.
151 */
152 @Suppress("UNCHECKED_CAST")
153 private fun SharedPreferences.Editor.putValue(
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000154 item: Item,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000155 value: Any?
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000156 ): SharedPreferences.Editor =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000157 when (item.type) {
158 String::class.java -> putString(item.sharedPrefKey, value as? String)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000159 Boolean::class.java,
160 java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
161 Int::class.java,
162 java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
163 Float::class.java,
164 java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
165 Long::class.java,
166 java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000167 Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000168 else ->
169 throw IllegalArgumentException(
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000170 "item type: ${item.type} is not compatible with sharedPref methods"
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000171 )
172 }
173
174 /**
175 * After calling this method, the listener will be notified of any future updates to the
176 * `SharedPreferences` files associated with the provided list of items. The listener will need
177 * to filter update notifications so they don't activate for non-relevant updates.
178 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000179 fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000180 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000181 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000182 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000183 .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
Thales Lima03ac3772023-01-06 15:16:41 +0000184 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000185
186 /**
187 * Stops the listener from getting notified of any more updates to any of the
188 * `SharedPreferences` files associated with any of the provided list of [Item].
189 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000190 fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000191 // If a listener is not registered to a SharedPreference, unregistering it does nothing
192 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000193 .map { chooseSharedPreferences(it) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000194 .distinct()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000195 .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000196 }
197
198 /**
199 * Checks if all the provided [Item] have values stored in their corresponding
200 * `SharedPreferences` files.
201 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000202 fun has(vararg items: Item): Boolean {
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000203 items
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000204 .groupBy { chooseSharedPreferences(it) }
205 .forEach { (prefs, itemsSublist) ->
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000206 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
207 }
208 return true
209 }
210
211 /**
212 * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
213 */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000214 fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000215
216 /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000217 fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000218
219 /**
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000220 * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
221 * item is boot aware, this method removes the data from both the boot aware and encrypted
222 * files.
223 *
224 * @return a list of editors with all transactions added so that the caller can determine to use
Stefan Andonian956b88e2023-03-20 20:38:46 +0000225 * .apply() or .commit()
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000226 */
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000227 private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
Stefan Andonian54495f32023-09-29 23:41:26 +0000228 val itemsPerFile =
229 items
230 .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
231 .groupBy { it.encryptedPrefs }
232 .toMutableMap()
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000233
Stefan Andonian1a626d22024-04-03 18:36:16 +0000234 val bootAwareUpdates = items.filter { it.encryptionType == EncryptionType.DEVICE_PROTECTED }
Stefan Andonian54495f32023-09-29 23:41:26 +0000235 if (bootAwareUpdates.isNotEmpty()) {
236 itemsPerFile[bootAwarePrefs] = bootAwareUpdates
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000237 }
238
239 return itemsPerFile.map { (prefs, items) ->
240 prefs.edit().also { editor ->
241 items.forEach { item -> editor.remove(item.sharedPrefKey) }
242 }
243 }
244 }
245
Sunny Goyal10fa0162024-04-21 00:13:35 -0700246 override fun close() {}
247
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000248 companion object {
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000249 @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
250
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000251 @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
252
253 @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
254
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700255 const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
Jagrut Desai2e8ca872024-04-17 15:27:30 -0700256 const val TASKBAR_PINNING_DESKTOP_MODE_KEY = "TASKBAR_PINNING_DESKTOP_MODE_KEY"
fbarone58aaf12023-09-25 11:34:56 -0700257 const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
Stefan Andonian54495f32023-09-29 23:41:26 +0000258 @JvmField
Stefan Andonian1a626d22024-04-03 18:36:16 +0000259 val ICON_STATE = nonRestorableItem("pref_icon_shape_path", "", EncryptionType.ENCRYPTED)
Sunny Goyal899313162024-04-05 12:54:32 -0700260
Andy Wickhame92f25d2023-10-03 12:48:57 -0700261 @JvmField
262 val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
Brandon Dayauon96901c42023-11-17 15:14:49 -0800263 @JvmField
Stefan Andonian1a626d22024-04-03 18:36:16 +0000264 val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.ENCRYPTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000265 @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
Sunny Goyal576f7a52023-10-06 11:28:25 -0700266 @JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
Stefan Andonian54495f32023-09-29 23:41:26 +0000267 @JvmField
268 val WORKSPACE_SIZE =
Stefan Andonian1a626d22024-04-03 18:36:16 +0000269 backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", EncryptionType.ENCRYPTED)
Stefan Andonian54495f32023-09-29 23:41:26 +0000270 @JvmField
271 val HOTSEAT_COUNT =
Stefan Andonian1a626d22024-04-03 18:36:16 +0000272 backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, EncryptionType.ENCRYPTED)
Stefan Andonian54495f32023-09-29 23:41:26 +0000273 @JvmField
274 val TASKBAR_PINNING =
275 backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
Jagrut Desai2e8ca872024-04-17 15:27:30 -0700276 @JvmField
277 val TASKBAR_PINNING_IN_DESKTOP_MODE =
278 backedUpItem(TASKBAR_PINNING_DESKTOP_MODE_KEY, true, EncryptionType.DEVICE_PROTECTED)
Jagrut Desaic6d625b2023-04-03 16:57:32 -0700279
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000280 @JvmField
281 val DEVICE_TYPE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000282 backedUpItem(
283 DeviceGridState.KEY_DEVICE_TYPE,
284 InvariantDeviceProfile.TYPE_PHONE,
Stefan Andonian1a626d22024-04-03 18:36:16 +0000285 EncryptionType.ENCRYPTED
Stefan Andonian54495f32023-09-29 23:41:26 +0000286 )
287 @JvmField
Stefan Andonian1a626d22024-04-03 18:36:16 +0000288 val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.ENCRYPTED)
fbarone58aaf12023-09-25 11:34:56 -0700289 @JvmField
290 val SHOULD_SHOW_SMARTSPACE =
Stefan Andonian54495f32023-09-29 23:41:26 +0000291 backedUpItem(
292 SHOULD_SHOW_SMARTSPACE_KEY,
293 WIDGET_ON_FIRST_SCREEN,
294 EncryptionType.DEVICE_PROTECTED
295 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000296 @JvmField
297 val RESTORE_DEVICE =
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000298 backedUpItem(
299 RestoreDbTask.RESTORED_DEVICE_TYPE,
300 InvariantDeviceProfile.TYPE_PHONE,
Stefan Andonian1a626d22024-04-03 18:36:16 +0000301 EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000302 )
Charlie Anderson511421c2023-10-26 11:22:26 -0400303 @JvmField
304 val IS_FIRST_LOAD_AFTER_RESTORE =
Stefan Andonian1a626d22024-04-03 18:36:16 +0000305 nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000306 @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
307 @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000308 @JvmField
309 val GRID_NAME =
310 ConstantItem(
311 "idp_grid_name",
312 isBackedUp = true,
313 defaultValue = null,
Stefan Andonian1a626d22024-04-03 18:36:16 +0000314 encryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000315 type = String::class.java
316 )
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000317 @JvmField
318 val ALLOW_ROTATION =
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000319 backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000320 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
321 }
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000322
Sunny Goyal576f7a52023-10-06 11:28:25 -0700323 // Preferences for widget configurations
324 @JvmField
325 val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
326 backedUpItem("launcher.reconfigurable_widget_education_tip_seen", false)
Sunny Goyal576f7a52023-10-06 11:28:25 -0700327
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000328 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000329 fun <T> backedUpItem(
330 sharedPrefKey: String,
331 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000332 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000333 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000334 ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000335
336 @JvmStatic
337 fun <T> backedUpItem(
338 sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000339 type: Class<out T>,
Stefan Andonian54495f32023-09-29 23:41:26 +0000340 encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000341 defaultValueFromContext: (c: Context) -> T
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000342 ): ContextualItem<T> =
343 ContextualItem(
344 sharedPrefKey,
345 isBackedUp = true,
346 defaultValueFromContext,
Stefan Andonian54495f32023-09-29 23:41:26 +0000347 encryptionType,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000348 type
349 )
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000350
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000351 @JvmStatic
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000352 fun <T> nonRestorableItem(
353 sharedPrefKey: String,
354 defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000355 encryptionType: EncryptionType = EncryptionType.ENCRYPTED
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000356 ): ConstantItem<T> =
Stefan Andonian54495f32023-09-29 23:41:26 +0000357 ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000358
359 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
360 @JvmStatic
361 fun getPrefs(context: Context): SharedPreferences {
362 // Use application context for shared preferences, so we use single cached instance
363 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000364 SHARED_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000365 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000366 )
367 }
368
369 @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
370 @JvmStatic
371 fun getDevicePrefs(context: Context): SharedPreferences {
372 // Use application context for shared preferences, so we use a single cached instance
373 return context.applicationContext.getSharedPreferences(
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000374 DEVICE_PREFERENCES_KEY,
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000375 MODE_PRIVATE
Stefan Andoniand1b33b32022-12-16 21:22:27 +0000376 )
377 }
378 }
379}
380
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000381abstract class Item {
382 abstract val sharedPrefKey: String
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000383 abstract val isBackedUp: Boolean
384 abstract val type: Class<*>
Stefan Andonian54495f32023-09-29 23:41:26 +0000385 abstract val encryptionType: EncryptionType
Stefan Andonian246eeaa2023-02-21 22:14:47 +0000386 val sharedPrefFile: String
387 get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000388
389 fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
390}
391
392data class ConstantItem<T>(
393 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000394 override val isBackedUp: Boolean,
395 val defaultValue: T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000396 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000397 // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
398 override val type: Class<out T> = defaultValue!!::class.java
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000399) : Item() {
Sunny Goyalcd447402023-10-06 13:47:50 -0700400
401 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Stefan Andonian5bd9a222023-02-23 00:58:33 +0000402}
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000403
404data class ContextualItem<T>(
405 override val sharedPrefKey: String,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000406 override val isBackedUp: Boolean,
407 private val defaultSupplier: (c: Context) -> T,
Stefan Andonian54495f32023-09-29 23:41:26 +0000408 override val encryptionType: EncryptionType,
Stefan Andonian4c9612b2023-02-22 00:00:03 +0000409 override val type: Class<out T>
Stefan Andonian1d7f7032023-01-23 21:55:04 +0000410) : Item() {
411 private var default: T? = null
412
413 fun defaultValueFromContext(context: Context): T {
414 if (default == null) {
415 default = defaultSupplier(context)
416 }
417 return default!!
418 }
Sunny Goyalcd447402023-10-06 13:47:50 -0700419
420 fun get(c: Context): T = LauncherPrefs.get(c).get(this)
Thales Lima03ac3772023-01-06 15:16:41 +0000421}
Stefan Andonian54495f32023-09-29 23:41:26 +0000422
423enum class EncryptionType {
424 ENCRYPTED,
425 DEVICE_PROTECTED,
Stefan Andonian54495f32023-09-29 23:41:26 +0000426}