Converting LauncherPrefs to dagger

Fixing ENABLE_TWOLINE_ALLAPPS_TOGGLE not properly tied to IDP:
http://recall/-/ep7WJ8pKwCEklUN5J1mAkM

Bug: 361850561
Flag: EXEMPT dagger-migration
Test: atest LauncherPrefsTest FakeLauncherPrefsTest
Change-Id: Iba63d060f4a8c2e31033fca2a4638c559c161338
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index c78666e..61297ce 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -161,7 +161,7 @@
     };
 
     private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
-    private final ActivityContext mActivity;
+    protected final ActivityContext mActivity;
     private FastBitmapDrawable mIcon;
     private DeviceProfile mDeviceProfile;
     private boolean mCenterVertically;
@@ -190,7 +190,6 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private DotInfo mDotInfo;
     private DotRenderer mDotRenderer;
-    private String mCurrentLanguage;
     @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
     protected DotRenderer.DrawParams mDotParams;
     private Animator mDotScaleAnim;
@@ -302,7 +301,6 @@
 
         mDotParams = new DotRenderer.DrawParams();
 
-        mCurrentLanguage = context.getResources().getConfiguration().locale.getLanguage();
         setEllipsize(TruncateAt.END);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
         setTextAlpha(1f);
@@ -492,13 +490,8 @@
      * Only if actual text can be displayed in two line, the {@code true} value will be effective.
      */
     protected boolean shouldUseTwoLine() {
-        return isCurrentLanguageEnglish() && (mDisplay == DISPLAY_ALL_APPS
-                || mDisplay == DISPLAY_PREDICTION_ROW) && (Flags.enableTwolineToggle()
-                && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()));
-    }
-
-    protected boolean isCurrentLanguageEnglish() {
-        return mCurrentLanguage.equals(Locale.ENGLISH.getLanguage());
+        return mDeviceProfile.inv.enableTwoLinesInAllApps
+                && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW);
     }
 
     @UiThread
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5387815..9ebae9f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,7 +24,6 @@
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
 import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.Utilities.isEnglishLanguage;
 import static com.android.launcher3.Utilities.pxFromSp;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
@@ -1362,16 +1361,9 @@
         if (isVerticalLayout && !mIsResponsiveGrid) {
             hideWorkspaceLabelsIfNotEnoughSpace();
         }
-        if ((Flags.enableTwolineToggle()
-                && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context))) {
-            if (!isEnglishLanguage(context)) {
-                // Set toggle preference value to false if not english here as it's possible the
-                // preference is stale after language change.
-                LauncherPrefs.get(context).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, false);
-            } else {
-                // Add extra textHeight to the existing allAppsCellHeight.
-                allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
-            }
+        if (inv.enableTwoLinesInAllApps) {
+            // Add extra textHeight to the existing allAppsCellHeight.
+            allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
         }
 
         updateHotseatSizes(iconSizePx);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 28293d1..5cca990 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.LauncherPrefs.DB_FILE;
+import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE;
 import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
 import static com.android.launcher3.LauncherPrefs.GRID_NAME;
 import static com.android.launcher3.Utilities.dpiFromPx;
@@ -30,6 +31,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -57,14 +59,15 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.settings.SettingsActivity;
 import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Partner;
 import com.android.launcher3.util.ResourceHelper;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.util.window.CachedDisplayInfo;
 import com.android.launcher3.util.window.WindowManagerProxy;
@@ -123,6 +126,8 @@
     private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
     private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
 
+    private final RunnableList mCloseActions = new RunnableList();
+
     /**
      * Number of icons per row and column in the workspace.
      */
@@ -218,12 +223,12 @@
     @XmlRes
     public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
 
-
+    private String mLocale = "";
+    public boolean enableTwoLinesInAllApps = false;
     /**
      * Fixed landscape mode is the landscape on the phones.
      */
     public boolean isFixedLandscape = false;
-    private LauncherPrefChangeListener mLandscapeModePreferenceListener;
 
     public String dbFile;
     public int defaultLayoutId;
@@ -247,7 +252,9 @@
     private InvariantDeviceProfile(Context context) {
         String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
-        DisplayController.INSTANCE.get(context).setPriorityListener(
+
+        DisplayController dc = DisplayController.INSTANCE.get(context);
+        dc.setPriorityListener(
                 (displayContext, info, flags) -> {
                     if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
                             | CHANGE_NAVIGATION_MODE | CHANGE_TASKBAR_PINNING
@@ -255,25 +262,28 @@
                         onConfigChanged(displayContext);
                     }
                 });
-        if (Flags.oneGridSpecs()) {
-            mLandscapeModePreferenceListener = (String preference_name) -> {
-                // Here we need both conditions even though they might seem redundant but because
-                // the update happens in the executable there can be race conditions and this avoids
-                // it.
-                if (isFixedLandscape != FIXED_LANDSCAPE_MODE.get(context)
-                        && SettingsActivity.FIXED_LANDSCAPE_MODE.equals(preference_name)) {
-                    MAIN_EXECUTOR.execute(() -> {
-                        Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
-                        onConfigChanged(context.getApplicationContext());
-                        Trace.endSection();
-                    });
-                }
-            };
-            LauncherPrefs.INSTANCE.get(context).addListener(
-                    mLandscapeModePreferenceListener,
-                    FIXED_LANDSCAPE_MODE
-            );
-        }
+        mCloseActions.add(() -> dc.setPriorityListener(null));
+
+        LauncherPrefChangeListener prefListener = key -> {
+            if (FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(key)
+                    && isFixedLandscape != FIXED_LANDSCAPE_MODE.get(context)) {
+                Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
+                onConfigChanged(context);
+                Trace.endSection();
+            } else if (ENABLE_TWOLINE_ALLAPPS_TOGGLE.getSharedPrefKey().equals(key)
+                    && enableTwoLinesInAllApps != ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context)) {
+                onConfigChanged(context);
+            }
+        };
+        LauncherPrefs prefs = LauncherPrefs.INSTANCE.get(context);
+        prefs.addListener(prefListener, FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE);
+        mCloseActions.add(() -> prefs.removeListener(prefListener,
+                FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE));
+
+        SimpleBroadcastReceiver localeReceiver = new SimpleBroadcastReceiver(
+                MAIN_EXECUTOR, i -> onConfigChanged(context));
+        localeReceiver.register(context, Intent.ACTION_LOCALE_CHANGED);
+        mCloseActions.add(() -> localeReceiver.unregisterReceiverSafely(context));
     }
 
     /**
@@ -341,12 +351,7 @@
 
     @Override
     public void close() {
-        DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
-        if (mLandscapeModePreferenceListener != null) {
-            LauncherPrefs.INSTANCE.executeIfCreated(
-                    lp -> lp.removeListener(mLandscapeModePreferenceListener, FIXED_LANDSCAPE_MODE)
-            );
-        }
+        mCloseActions.executeAllAndDestroy();
     }
 
     public static String getCurrentGridName(Context context) {
@@ -413,6 +418,11 @@
     }
 
     private void initGrid(Context context, Info displayInfo, DisplayOption displayOption) {
+        enableTwoLinesInAllApps = Flags.enableTwolineToggle()
+                && Utilities.isEnglishLanguage(context)
+                && ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context);
+        mLocale = context.getResources().getConfiguration().locale.toString();
+
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
@@ -564,7 +574,7 @@
     private Object[] toModelState() {
         return new Object[]{
                 numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
-                iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile};
+                iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile, mLocale};
     }
 
     /** Updates IDP using the provided context. Notifies listeners of change. */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b38efc2..3fbf888 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -417,7 +417,6 @@
 
     private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>();
     private boolean mIsColdStartupAfterReboot;
-    private boolean mForceConfigUpdate;
 
     private boolean mIsNaturalScrollingEnabled;
 
@@ -763,7 +762,7 @@
     protected void onHandleConfigurationChanged() {
         Trace.beginSection("Launcher#onHandleconfigurationChanged");
         try {
-            if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
+            if (!initDeviceProfile(mDeviceProfile.inv)) {
                 return;
             }
             dispatchDeviceProfileChanged();
@@ -776,7 +775,6 @@
             mModel.rebindCallbacks();
             updateDisallowBack();
         } finally {
-            mForceConfigUpdate = false;
             Trace.endSection();
         }
     }
@@ -3181,13 +3179,6 @@
         return mAnimationCoordinator;
     }
 
-    /**
-     * Set to force config update when set to true next time onHandleConfigurationChanged is called.
-     */
-    public void setForceConfigUpdate(boolean forceConfigUpdate) {
-        mForceConfigUpdate = forceConfigUpdate;
-    }
-
     @Override
     public View.OnLongClickListener getAllAppsItemLongClickListener() {
         return ItemLongClickListener.INSTANCE_ALL_APPS;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a53238d..5989e4c 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -34,8 +34,6 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.pm.LauncherApps;
@@ -70,9 +68,6 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
-import java.util.Locale;
-import java.util.Objects;
-
 public class LauncherAppState implements SafeCloseable {
 
     public static final String TAG = "LauncherAppState";
@@ -128,22 +123,11 @@
 
         SimpleBroadcastReceiver modelChangeReceiver =
                 new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, mModel::onBroadcastIntent);
-        final Locale oldLocale = mContext.getResources().getConfiguration().locale;
         modelChangeReceiver.register(
                 mContext,
-                () -> {
-                    // if local has changed before receiver is registered on bg thread,
-                    // mModel needs to reload.
-                    Locale newLocale = mContext.getResources().getConfiguration().locale;
-                    if (!Objects.equals(oldLocale, newLocale)) {
-                        mModel.forceReload();
-                    }
-                },
-                Intent.ACTION_LOCALE_CHANGED,
                 ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
         if (BuildConfig.IS_STUDIO_BUILD) {
-            mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD),
-                    RECEIVER_EXPORTED);
+            modelChangeReceiver.register(mContext, RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
         }
         mOnTerminateCallback.add(() -> modelChangeReceiver.unregisterReceiverSafely(mContext));
 
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index b56df46..185629b 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -176,7 +176,6 @@
     fun onBroadcastIntent(intent: Intent) {
         if (DEBUG_RECEIVER || sDebugTracing) Log.d(TAG, "onReceive intent=$intent")
         when (intent.action) {
-            Intent.ACTION_LOCALE_CHANGED,
             LauncherAppState.ACTION_FORCE_ROLOAD ->
                 // If we have changed locale we need to clear out the labels in all apps/workspace.
                 forceReload()
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5b9c2fa..ad592d8 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -23,59 +23,212 @@
 import com.android.launcher3.InvariantDeviceProfile.GRID_NAME_PREFS_KEY
 import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
 import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.model.DeviceGridState
 import com.android.launcher3.pm.InstallSessionHelper
 import com.android.launcher3.provider.RestoreDbTask
 import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
 import com.android.launcher3.settings.SettingsActivity
 import com.android.launcher3.states.RotationHelper
+import com.android.launcher3.util.DaggerSingletonObject
 import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.MainThreadInitializedObject
-import com.android.launcher3.util.SafeCloseable
 import com.android.launcher3.util.Themes
+import javax.inject.Inject
 
 /**
  * Manages Launcher [SharedPreferences] through [Item] instances.
  *
  * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
  */
-abstract class LauncherPrefs : SafeCloseable {
+@LauncherAppSingleton
+open class LauncherPrefs
+@Inject
+constructor(@ApplicationContext private val encryptedContext: Context) {
+
+    private val deviceProtectedSharedPrefs: SharedPreferences by lazy {
+        encryptedContext
+            .createDeviceProtectedStorageContext()
+            .getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
+    }
+
+    open val Item.sharedPrefs: SharedPreferences
+        get() =
+            if (encryptionType == EncryptionType.DEVICE_PROTECTED) deviceProtectedSharedPrefs
+            else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
 
     /** Returns the value with type [T] for [item]. */
-    abstract fun <T> get(item: ContextualItem<T>): T
+    fun <T> get(item: ContextualItem<T>): T =
+        getInner(item, item.defaultValueFromContext(encryptedContext))
 
     /** Returns the value with type [T] for [item]. */
-    abstract fun <T> get(item: ConstantItem<T>): T
+    fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
 
-    /** Stores the values for each item in preferences. */
-    abstract fun put(vararg itemsToValues: Pair<Item, Any>)
+    /**
+     * 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<String>`.
+     */
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+    private fun <T> getInner(item: Item, default: T): T {
+        val sp = item.sharedPrefs
 
-    /** Stores the [value] with type [T] for [item] in preferences. */
-    abstract fun <T : Any> put(item: Item, value: T)
+        return when (item.type) {
+            String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
+            Boolean::class.java,
+            java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
+            Int::class.java,
+            java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
+            Float::class.java,
+            java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
+            Long::class.java,
+            java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
+            Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
+            else ->
+                throw IllegalArgumentException(
+                    "item type: ${item.type}" + " is not compatible with sharedPref methods"
+                )
+        }
+            as T
+    }
 
-    /** Synchronous version of [put]. */
-    abstract fun putSync(vararg itemsToValues: Pair<Item, Any>)
+    /**
+     * Stores each of the values provided in `SharedPreferences` according to the configuration
+     * contained within the associated items provided. Internally, it uses apply, so the caller
+     * cannot assume that the values that have been put are immediately available for use.
+     *
+     * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
+     * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
+     * provided item configurations.
+     */
+    fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
+        prepareToPutValues(itemsToValues).forEach { it.apply() }
 
-    /** Registers [listener] for [items]. */
-    abstract fun addListener(listener: LauncherPrefChangeListener, vararg items: Item)
+    /** See referenced `put` method above. */
+    fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
 
-    /** Unregisters [listener] for [items]. */
-    abstract fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item)
+    /**
+     * Synchronously stores all the values provided according to their associated Item
+     * configuration.
+     */
+    fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
+        prepareToPutValues(itemsToValues).forEach { it.commit() }
 
-    /** Returns `true` iff all [items] have a value. */
-    abstract fun has(vararg items: Item): Boolean
+    /**
+     * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
+     * the item is boot aware, this method updates both the boot aware and the encrypted files. This
+     * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
+     * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
+     * already points to encrypted storage.
+     *
+     * Returns a list of editors with all transactions added so that the caller can determine to use
+     * .apply() or .commit()
+     */
+    private fun prepareToPutValues(
+        updates: Array<out Pair<Item, Any>>
+    ): List<SharedPreferences.Editor> {
+        val updatesPerPrefFile = updates.groupBy { it.first.sharedPrefs }.toMap()
 
-    /** Removes the value for each item in [items]. */
-    abstract fun remove(vararg items: Item)
+        return updatesPerPrefFile.map { (sharedPref, itemList) ->
+            sharedPref.edit().apply { itemList.forEach { (item, value) -> putValue(item, value) } }
+        }
+    }
 
-    /** Synchronous version of [remove]. */
-    abstract fun removeSync(vararg items: Item)
+    /**
+     * Handles adding values to `SharedPreferences` regardless of type. This method is especially
+     * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
+     * types of Item values.
+     */
+    @Suppress("UNCHECKED_CAST")
+    private fun SharedPreferences.Editor.putValue(
+        item: Item,
+        value: Any?,
+    ): SharedPreferences.Editor =
+        when (item.type) {
+            String::class.java -> putString(item.sharedPrefKey, value as? String)
+            Boolean::class.java,
+            java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
+            Int::class.java,
+            java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
+            Float::class.java,
+            java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
+            Long::class.java,
+            java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
+            Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
+            else ->
+                throw IllegalArgumentException(
+                    "item type: ${item.type} is not compatible with sharedPref methods"
+                )
+        }
+
+    /**
+     * After calling this method, the listener will be notified of any future updates to the
+     * `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: LauncherPrefChangeListener, vararg items: Item) {
+        items
+            .map { it.sharedPrefs }
+            .distinct()
+            .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
+    }
+
+    /**
+     * 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: LauncherPrefChangeListener, vararg items: Item) {
+        // If a listener is not registered to a SharedPreference, unregistering it does nothing
+        items
+            .map { it.sharedPrefs }
+            .distinct()
+            .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
+    }
+
+    /**
+     * Checks if all the provided [Item] have values stored in their corresponding
+     * `SharedPreferences` files.
+     */
+    fun has(vararg items: Item): Boolean {
+        items
+            .groupBy { it.sharedPrefs }
+            .forEach { (prefs, itemsSublist) ->
+                if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
+            }
+        return true
+    }
+
+    /**
+     * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
+     */
+    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() }
+
+    /**
+     * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
+     * item is boot aware, this method removes the data from both the boot aware and encrypted
+     * files.
+     *
+     * @return a list of editors with all transactions added so that the caller can determine to use
+     *   .apply() or .commit()
+     */
+    private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
+        val itemsPerFile = items.groupBy { it.sharedPrefs }.toMap()
+
+        return itemsPerFile.map { (prefs, items) ->
+            prefs.edit().also { editor ->
+                items.forEach { item -> editor.remove(item.sharedPrefKey) }
+            }
+        }
+    }
 
     companion object {
         @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
 
-        @JvmField
-        var INSTANCE = MainThreadInitializedObject<LauncherPrefs> { LauncherPrefsImpl(it) }
+        @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLauncherPrefs)
 
         @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
 
@@ -212,214 +365,6 @@
     }
 }
 
-private class LauncherPrefsImpl(private val encryptedContext: Context) : LauncherPrefs() {
-    private val deviceProtectedStorageContext =
-        encryptedContext.createDeviceProtectedStorageContext()
-
-    private val bootAwarePrefs
-        get() =
-            deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
-
-    private val Item.encryptedPrefs
-        get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
-
-    private fun chooseSharedPreferences(item: Item): SharedPreferences =
-        if (item.encryptionType == EncryptionType.DEVICE_PROTECTED) bootAwarePrefs
-        else item.encryptedPrefs
-
-    /** Wrapper around `getInner` for a `ContextualItem` */
-    override fun <T> get(item: ContextualItem<T>): T =
-        getInner(item, item.defaultValueFromContext(encryptedContext))
-
-    /** Wrapper around `getInner` for an `Item` */
-    override fun <T> get(item: ConstantItem<T>): 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<String>`.
-     */
-    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
-    private fun <T> getInner(item: Item, default: T): T {
-        val sp = chooseSharedPreferences(item)
-
-        return when (item.type) {
-            String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
-            Boolean::class.java,
-            java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
-            Int::class.java,
-            java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
-            Float::class.java,
-            java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
-            Long::class.java,
-            java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
-            Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
-            else ->
-                throw IllegalArgumentException(
-                    "item type: ${item.type}" + " is not compatible with sharedPref methods"
-                )
-        }
-            as T
-    }
-
-    /**
-     * Stores each of the values provided in `SharedPreferences` according to the configuration
-     * contained within the associated items provided. Internally, it uses apply, so the caller
-     * cannot assume that the values that have been put are immediately available for use.
-     *
-     * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
-     * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
-     * provided item configurations.
-     */
-    override fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
-        prepareToPutValues(itemsToValues).forEach { it.apply() }
-
-    /** See referenced `put` method above. */
-    override fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
-
-    /**
-     * Synchronously stores all the values provided according to their associated Item
-     * configuration.
-     */
-    override fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
-        prepareToPutValues(itemsToValues).forEach { it.commit() }
-
-    /**
-     * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
-     * the item is boot aware, this method updates both the boot aware and the encrypted files. This
-     * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
-     * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
-     * already points to encrypted storage.
-     *
-     * Returns a list of editors with all transactions added so that the caller can determine to use
-     * .apply() or .commit()
-     */
-    private fun prepareToPutValues(
-        updates: Array<out Pair<Item, Any>>
-    ): List<SharedPreferences.Editor> {
-        val updatesPerPrefFile =
-            updates
-                .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
-                .groupBy { it.first.encryptedPrefs }
-                .toMutableMap()
-
-        val bootAwareUpdates =
-            updates.filter { it.first.encryptionType == EncryptionType.DEVICE_PROTECTED }
-        if (bootAwareUpdates.isNotEmpty()) {
-            updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
-        }
-
-        return updatesPerPrefFile.map { prefToItemValueList ->
-            prefToItemValueList.key.edit().apply {
-                prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
-                    putValue(itemToValue.first, itemToValue.second)
-                }
-            }
-        }
-    }
-
-    /**
-     * Handles adding values to `SharedPreferences` regardless of type. This method is especially
-     * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
-     * types of Item values.
-     */
-    @Suppress("UNCHECKED_CAST")
-    private fun SharedPreferences.Editor.putValue(
-        item: Item,
-        value: Any?,
-    ): SharedPreferences.Editor =
-        when (item.type) {
-            String::class.java -> putString(item.sharedPrefKey, value as? String)
-            Boolean::class.java,
-            java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
-            Int::class.java,
-            java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
-            Float::class.java,
-            java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
-            Long::class.java,
-            java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
-            Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
-            else ->
-                throw IllegalArgumentException(
-                    "item type: ${item.type} is not compatible with sharedPref methods"
-                )
-        }
-
-    /**
-     * After calling this method, the listener will be notified of any future updates to the
-     * `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.
-     */
-    override fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
-        items
-            .map { chooseSharedPreferences(it) }
-            .distinct()
-            .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
-    }
-
-    /**
-     * 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].
-     */
-    override fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
-        // If a listener is not registered to a SharedPreference, unregistering it does nothing
-        items
-            .map { chooseSharedPreferences(it) }
-            .distinct()
-            .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
-    }
-
-    /**
-     * Checks if all the provided [Item] have values stored in their corresponding
-     * `SharedPreferences` files.
-     */
-    override fun has(vararg items: Item): Boolean {
-        items
-            .groupBy { chooseSharedPreferences(it) }
-            .forEach { (prefs, itemsSublist) ->
-                if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
-            }
-        return true
-    }
-
-    /**
-     * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
-     */
-    override fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
-
-    /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
-    override fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
-
-    /**
-     * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
-     * item is boot aware, this method removes the data from both the boot aware and encrypted
-     * files.
-     *
-     * @return a list of editors with all transactions added so that the caller can determine to use
-     *   .apply() or .commit()
-     */
-    private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
-        val itemsPerFile =
-            items
-                .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
-                .groupBy { it.encryptedPrefs }
-                .toMutableMap()
-
-        val bootAwareUpdates = items.filter { it.encryptionType == EncryptionType.DEVICE_PROTECTED }
-        if (bootAwareUpdates.isNotEmpty()) {
-            itemsPerFile[bootAwarePrefs] = bootAwareUpdates
-        }
-
-        return itemsPerFile.map { (prefs, items) ->
-            prefs.edit().also { editor ->
-                items.forEach { item -> editor.remove(item.sharedPrefKey) }
-            }
-        }
-    }
-
-    override fun close() {}
-}
-
 abstract class Item {
     abstract val sharedPrefKey: String
     abstract val isBackedUp: Boolean
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 60bf3ea..e8b7247 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -40,8 +40,6 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.model.data.AppInfo;
@@ -219,9 +217,7 @@
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case VIEW_TYPE_ICON:
-                int layout = (Flags.enableTwolineToggle()
-                        && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
-                                mActivityContext.getApplicationContext()))
+                int layout = mActivityContext.getDeviceProfile().inv.enableTwoLinesInAllApps
                         ? R.layout.all_apps_icon_twoline : R.layout.all_apps_icon;
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         layout, parent, false);
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 340fb02..72a97a8 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
 import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.model.ItemInstallQueue;
@@ -62,6 +63,7 @@
     VibratorWrapper getVibratorWrapper();
     MSDLPlayerWrapper getMSDLPlayerWrapper();
     WindowManagerProxy getWmProxy();
+    LauncherPrefs getLauncherPrefs();
 
     /** Builder for LauncherBaseAppComponent. */
     interface Builder {