diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index db8c7f23b9..6722f3de4e 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -22,7 +22,7 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; +import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI; import static com.android.quickstep.BaseActivityInterface.getTaskDimension; @@ -116,7 +116,6 @@ public class RecentsOrientedState implements | FLAG_SWIPE_UP_NOT_RUNNING; private final Context mContext; - private final SharedPreferences mSharedPrefs; private final OrientationEventListener mOrientationListener; private final SettingsCache mSettingsCache; private final SettingsCache.OnChangeListener mRotationChangeListener = @@ -139,7 +138,6 @@ public class RecentsOrientedState implements public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy, IntConsumer rotationChangeListener) { mContext = context; - mSharedPrefs = LauncherPrefs.getPrefs(context); mOrientationListener = new OrientationEventListener(context) { @Override public void onOrientationChanged(int degrees) { @@ -278,7 +276,7 @@ public class RecentsOrientedState implements @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { - if (ALLOW_ROTATION_PREFERENCE_KEY.equals(s)) { + if (LauncherPrefs.ALLOW_ROTATION.getSharedPrefKey().equals(s)) { updateHomeRotationSetting(); } } @@ -289,7 +287,7 @@ public class RecentsOrientedState implements } private void updateHomeRotationSetting() { - boolean homeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); + boolean homeRotationEnabled = LauncherPrefs.get(mContext).get(ALLOW_ROTATION); setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled); SystemUiProxy.INSTANCE.get(mContext).setHomeRotationEnabled(homeRotationEnabled); } @@ -303,13 +301,13 @@ public class RecentsOrientedState implements } private void initMultipleOrientationListeners() { - mSharedPrefs.registerOnSharedPreferenceChangeListener(this); + LauncherPrefs.get(mContext).addListener(this, ALLOW_ROTATION); mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener); updateAutoRotateSetting(); } private void destroyMultipleOrientationListeners() { - mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); + LauncherPrefs.get(mContext).removeListener(this, ALLOW_ROTATION); mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener); } diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index 1fb2dce254..2e07e3022a 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -8,6 +8,8 @@ import com.android.launcher3.allapps.WorkProfileManager import com.android.launcher3.model.DeviceGridState import com.android.launcher3.pm.InstallSessionHelper import com.android.launcher3.provider.RestoreDbTask +import com.android.launcher3.states.RotationHelper +import com.android.launcher3.util.DisplayController import com.android.launcher3.util.MainThreadInitializedObject import com.android.launcher3.util.Themes @@ -17,31 +19,36 @@ import com.android.launcher3.util.Themes */ class LauncherPrefs(private val context: Context) { + /** Wrapper around `getInner` for a `ContextualItem` */ + fun get(item: ContextualItem): T = + getInner(item, item.defaultValueFromContext(context)) + + /** Wrapper around `getInner` for an `Item` */ + fun get(item: ConstantItem): T = getInner(item, item.defaultValue) + /** * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the * default value type, and will throw an error if the type of the item provided is not a * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set`. */ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") - fun get(item: Item): T { + private fun getInner(item: Item, default: T): T { val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) - return when (item.defaultValue::class.java) { - String::class.java -> sp.getString(item.sharedPrefKey, item.defaultValue as String) + return when (default::class.java) { + String::class.java -> sp.getString(item.sharedPrefKey, default as String) Boolean::class.java, - java.lang.Boolean::class.java -> - sp.getBoolean(item.sharedPrefKey, item.defaultValue as Boolean) + java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean) Int::class.java, - java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, item.defaultValue as Int) + java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int) Float::class.java, - java.lang.Float::class.java -> - sp.getFloat(item.sharedPrefKey, item.defaultValue as Float) + java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float) Long::class.java, - java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, item.defaultValue as Long) - Set::class.java -> sp.getStringSet(item.sharedPrefKey, item.defaultValue as Set) + java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long) + Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set) else -> throw IllegalArgumentException( - "item type: ${item.defaultValue::class.java}" + + "item type: ${default::class.java}" + " is not compatible with sharedPref methods" ) } @@ -57,14 +64,14 @@ class LauncherPrefs(private val context: Context) { * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the * provided item configurations. */ - fun put(vararg itemsToValues: Pair, Any>): Unit = + fun put(vararg itemsToValues: Pair): Unit = prepareToPutValues(itemsToValues).forEach { it.apply() } /** * Stores the value provided in `SharedPreferences` according to the item configuration provided * It is asynchronous, so the caller can't assume that the value put is immediately available. */ - fun put(item: Item, value: T): Unit = + fun put(item: Item, value: T): Unit = context .getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) .edit() @@ -75,7 +82,7 @@ class LauncherPrefs(private val context: Context) { * Synchronously stores all the values provided according to their associated Item * configuration. */ - fun putSync(vararg itemsToValues: Pair, Any>): Unit = + fun putSync(vararg itemsToValues: Pair): Unit = prepareToPutValues(itemsToValues).forEach { it.commit() } /** @@ -86,7 +93,7 @@ class LauncherPrefs(private val context: Context) { * items given as part of the itemsToValues parameter */ private fun prepareToPutValues( - itemsToValues: Array, Any>> + itemsToValues: Array> ): List = itemsToValues .groupBy { it.first.sharedPrefFile } @@ -108,7 +115,7 @@ class LauncherPrefs(private val context: Context) { */ @Suppress("UNCHECKED_CAST") private fun SharedPreferences.Editor.putValue( - item: Item<*>, + item: Item, value: Any ): SharedPreferences.Editor = when (value::class.java) { @@ -124,8 +131,7 @@ class LauncherPrefs(private val context: Context) { Set::class.java -> putStringSet(item.sharedPrefKey, value as Set) else -> throw IllegalArgumentException( - "item type: " + - "${item.defaultValue!!::class} is not compatible with sharedPref methods" + "item type: ${value::class} is not compatible with sharedPref methods" ) } @@ -134,7 +140,7 @@ class LauncherPrefs(private val context: Context) { * `SharedPreferences` files associated with the provided list of items. The listener will need * to filter update notifications so they don't activate for non-relevant updates. */ - fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) { + fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) { items .map { it.sharedPrefFile } .distinct() @@ -149,7 +155,7 @@ class LauncherPrefs(private val context: Context) { * Stops the listener from getting notified of any more updates to any of the * `SharedPreferences` files associated with any of the provided list of [Item]. */ - fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) { + fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) { // If a listener is not registered to a SharedPreference, unregistering it does nothing items .map { it.sharedPrefFile } @@ -165,7 +171,7 @@ class LauncherPrefs(private val context: Context) { * Checks if all the provided [Item] have values stored in their corresponding * `SharedPreferences` files. */ - fun has(vararg items: Item<*>): Boolean { + fun has(vararg items: Item): Boolean { items .groupBy { it.sharedPrefFile } .forEach { (file, itemsSublist) -> @@ -179,17 +185,17 @@ class LauncherPrefs(private val context: Context) { /** * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */ - fun remove(vararg items: Item<*>) = prepareToRemove(items).forEach { it.apply() } + fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() } /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */ - fun removeSync(vararg items: Item<*>) = prepareToRemove(items).forEach { it.commit() } + fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() } /** * Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values * from their respective `SharedPreferences` files. These returned `Editors` can then be * committed or applied for synchronous or async behavior. */ - private fun prepareToRemove(items: Array>): List = + private fun prepareToRemove(items: Array): List = items .groupBy { it.sharedPrefFile } .map { (file, items) -> @@ -218,16 +224,32 @@ class LauncherPrefs(private val context: Context) { backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "") @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "") + @JvmField + val ALLOW_ROTATION = + backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) { + RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info) + } @VisibleForTesting @JvmStatic - fun backedUpItem(sharedPrefKey: String, defaultValue: T): Item = - Item(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) + fun backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem = + ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) + + @JvmStatic + fun backedUpItem( + sharedPrefKey: String, + defaultValueFromContext: (c: Context) -> T + ): ContextualItem = + ContextualItem( + sharedPrefKey, + LauncherFiles.SHARED_PREFERENCES_KEY, + defaultValueFromContext + ) @VisibleForTesting @JvmStatic - fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): Item = - Item(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) + fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem = + ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") @JvmStatic @@ -251,6 +273,30 @@ class LauncherPrefs(private val context: Context) { } } -data class Item(val sharedPrefKey: String, val sharedPrefFile: String, val defaultValue: T) { - fun to(value: T): Pair, T> = Pair(this, value) +abstract class Item { + abstract val sharedPrefKey: String + abstract val sharedPrefFile: String + + fun to(value: T): Pair = Pair(this, value) +} + +data class ConstantItem( + override val sharedPrefKey: String, + override val sharedPrefFile: String, + val defaultValue: T +) : Item() + +data class ContextualItem( + override val sharedPrefKey: String, + override val sharedPrefFile: String, + private val defaultSupplier: (c: Context) -> T +) : Item() { + private var default: T? = null + + fun defaultValueFromContext(context: Context): T { + if (default == null) { + default = defaultSupplier(context) + } + return default!! + } } diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index e5b4ebae1d..7b4e2485ce 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -20,6 +20,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; +import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH; @@ -63,7 +64,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, @Nullable private BaseActivity mActivity; - private SharedPreferences mSharedPrefs = null; private final Handler mRequestOrientationHandler; private boolean mIgnoreAutoRotateSettings; @@ -103,17 +103,10 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, // On large devices we do not handle auto-rotate differently. mIgnoreAutoRotateSettings = ignoreAutoRotateSettings; if (!mIgnoreAutoRotateSettings) { - if (mSharedPrefs == null) { - mSharedPrefs = LauncherPrefs.getPrefs(mActivity); - mSharedPrefs.registerOnSharedPreferenceChangeListener(this); - } - mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, - getAllowRotationDefaultValue(info)); + mHomeRotationEnabled = LauncherPrefs.get(mActivity).get(ALLOW_ROTATION); + LauncherPrefs.get(mActivity).addListener(this, ALLOW_ROTATION); } else { - if (mSharedPrefs != null) { - mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - mSharedPrefs = null; - } + LauncherPrefs.get(mActivity).removeListener(this, ALLOW_ROTATION); } } @@ -121,8 +114,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { if (mDestroyed || mIgnoreAutoRotateSettings) return; boolean wasRotationEnabled = mHomeRotationEnabled; - mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, - getAllowRotationDefaultValue(mActivity.getDeviceProfile().getDisplayInfo())); + mHomeRotationEnabled = LauncherPrefs.get(mActivity).get(ALLOW_ROTATION); if (mHomeRotationEnabled != wasRotationEnabled) { notifyChange(); } @@ -179,10 +171,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, if (!mDestroyed) { mDestroyed = true; DisplayController.INSTANCE.get(mActivity).removeChangeListener(this); + LauncherPrefs.get(mActivity).removeListener(this, ALLOW_ROTATION); mActivity = null; - if (mSharedPrefs != null) { - mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - } } } diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt index 151abf1040..31e8d3099b 100644 --- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -13,6 +13,7 @@ import org.junit.runner.RunWith private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) +private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true } @SmallTest @RunWith(AndroidJUnit4::class) @@ -145,4 +146,9 @@ class LauncherPrefsTest { assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue) } } + + @Test + fun get_contextualItem_returnsCorrectDefault() { + assertThat(launcherPrefs.get(TEST_CONTEXTUAL_ITEM)).isTrue() + } }