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/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 22e491f..b329156 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -35,7 +35,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
@@ -150,8 +149,7 @@
int totalHeight = iconHeight + iconPadding + textHeight + mVerticalPadding * 2;
// Prediction row height will be 4dp bigger than the regular apps in A-Z list when two line
// is not enabled. Otherwise, the extra height will increase by just the textHeight.
- int extraHeight = (Flags.enableTwolineToggle() &&
- LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()))
+ int extraHeight = deviceProfile.inv.enableTwoLinesInAllApps
? (textHeight + mTopRowExtraHeight) : mTopRowExtraHeight;
totalHeight += extraHeight;
return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 0b94dfd..31c5a4c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -23,7 +23,6 @@
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.FakeLauncherPrefs
-import com.android.launcher3.LauncherPrefs
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppModule
import com.android.launcher3.dagger.LauncherAppSingleton
@@ -74,8 +73,6 @@
.bindSettingsCache(settingsCacheSandbox.cache)
componentBinder?.invoke(context, builder)
base.initDaggerComponent(builder)
-
- putObject(LauncherPrefs.INSTANCE, FakeLauncherPrefs(context))
}
}
@@ -119,6 +116,9 @@
@LauncherAppSingleton
@Component(modules = [LauncherAppModule::class])
interface TaskbarSandboxComponent : LauncherAppComponent {
+
+ override fun getLauncherPrefs(): FakeLauncherPrefs
+
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
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 {
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 8598917..6676766 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -284,7 +284,6 @@
isFixedLandscape: Boolean = false,
) {
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
- LauncherPrefs.get(testContext).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, true)
val windowsBounds = perDisplayBoundsCache[displayInfo]!!
val realBounds = windowsBounds[rotation]
whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
@@ -310,10 +309,11 @@
val configurationContext = runningContext.createConfigurationContext(config)
context = SandboxContext(configurationContext)
context.initDaggerComponent(
- DaggerAbsDPTestSandboxComponent.builder().bindWMProxy(windowManagerProxy)
+ DaggerAbsDPTestSandboxComponent.builder()
+ .bindWMProxy(windowManagerProxy)
+ .bindLauncherPrefs(launcherPrefs)
)
context.putObject(DisplayController.INSTANCE, displayController)
- context.putObject(LauncherPrefs.INSTANCE, launcherPrefs)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
@@ -322,6 +322,7 @@
whenever(launcherPrefs.get(LauncherPrefs.DEVICE_TYPE)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.WORKSPACE_SIZE)).thenReturn("")
whenever(launcherPrefs.get(LauncherPrefs.DB_FILE)).thenReturn("")
+ whenever(launcherPrefs.get(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE)).thenReturn(true)
val info = spy(DisplayController.Info(context, windowManagerProxy, perDisplayBoundsCache))
whenever(displayController.info).thenReturn(info)
whenever(info.isTransientTaskbar).thenReturn(isGestureMode)
@@ -375,6 +376,8 @@
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
+ @BindsInstance fun bindLauncherPrefs(prefs: LauncherPrefs): Builder
+
override fun build(): AbsDPTestSandboxComponent
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
index 946bbc5..7573d2f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
@@ -17,61 +17,24 @@
package com.android.launcher3
import android.content.Context
-import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import android.content.Context.MODE_PRIVATE
+import android.content.SharedPreferences
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppSingleton
+import java.io.File
+import javax.inject.Inject
/** Emulates Launcher preferences for a test environment. */
-class FakeLauncherPrefs(private val context: Context) : LauncherPrefs() {
- private val prefsMap = mutableMapOf<String, Any>()
- private val listeners = mutableSetOf<LauncherPrefChangeListener>()
+@LauncherAppSingleton
+class FakeLauncherPrefs @Inject constructor(@ApplicationContext context: Context) :
+ LauncherPrefs(context) {
- @Suppress("UNCHECKED_CAST")
- override fun <T> get(item: ContextualItem<T>): T {
- return prefsMap.getOrDefault(item.sharedPrefKey, item.defaultValueFromContext(context)) as T
- }
+ private val backingPrefs =
+ context.getSharedPreferences(
+ File.createTempFile("fake-pref", ".xml", context.filesDir),
+ MODE_PRIVATE,
+ )
- @Suppress("UNCHECKED_CAST")
- override fun <T> get(item: ConstantItem<T>): T {
- return prefsMap.getOrDefault(item.sharedPrefKey, item.defaultValue) as T
- }
-
- override fun put(vararg itemsToValues: Pair<Item, Any>) = putSync(*itemsToValues)
-
- override fun <T : Any> put(item: Item, value: T) = putSync(item to value)
-
- override fun putSync(vararg itemsToValues: Pair<Item, Any>) {
- itemsToValues
- .map { (i, v) -> i.sharedPrefKey to v }
- .forEach { (k, v) ->
- prefsMap[k] = v
- notifyChange(k)
- }
- }
-
- override fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- listeners.add(listener)
- }
-
- override fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- listeners.remove(listener)
- }
-
- override fun has(vararg items: Item) = items.all { it.sharedPrefKey in prefsMap }
-
- override fun remove(vararg items: Item) = removeSync(*items)
-
- override fun removeSync(vararg items: Item) {
- items
- .filter { it.sharedPrefKey in prefsMap }
- .forEach {
- prefsMap.remove(it.sharedPrefKey)
- notifyChange(it.sharedPrefKey)
- }
- }
-
- override fun close() = Unit
-
- private fun notifyChange(key: String) {
- // Mimics SharedPreferencesImpl#notifyListeners main thread dispatching.
- MAIN_EXECUTOR.execute { listeners.forEach { it.onPrefChanged(key) } }
- }
+ override val Item.sharedPrefs: SharedPreferences
+ get() = backingPrefs
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
index 2463c93..c57c86f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
@@ -128,7 +128,7 @@
val listener = LauncherPrefChangeListener { changedKey = it }
launcherPrefs.addListener(listener, TEST_CONSTANT_ITEM)
- launcherPrefs.removeListener(listener)
+ launcherPrefs.removeListener(listener, TEST_CONSTANT_ITEM)
getInstrumentation().runOnMainSync { launcherPrefs.put(TEST_CONSTANT_ITEM, true) }
assertThat(changedKey).isNull()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index b933ed2..f51871b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -20,8 +20,6 @@
import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.text.style.DynamicDrawableSpan.ALIGN_CENTER;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
import static com.android.launcher3.BubbleTextView.DISPLAY_ALL_APPS;
import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT;
@@ -44,6 +42,7 @@
import android.graphics.Typeface;
import android.os.Build;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.SpannedString;
@@ -64,8 +63,10 @@
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.views.BaseDragLayer;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -111,19 +112,22 @@
private static final float SPACE_MULTIPLIER = 1;
private static final float SPACE_EXTRA = 0;
+ private SandboxModelContext mModelContext;
+
private BubbleTextView mBubbleTextView;
private ItemInfoWithIcon mItemInfoWithIcon;
private Context mContext;
private int mLimitedWidth;
private AppInfo mGmailAppInfo;
- private LauncherPrefs mLauncherPrefs;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Utilities.enableRunningInTestHarnessForTests();
- mContext = new ActivityContextWrapper(getApplicationContext());
- mLauncherPrefs = LauncherPrefs.get(mContext);
+ mModelContext = new SandboxModelContext();
+ LauncherPrefs.get(mModelContext).put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+
+ mContext = new ActivityContextWrapper(mModelContext);
mBubbleTextView = new BubbleTextView(mContext);
mBubbleTextView.reset();
@@ -149,10 +153,14 @@
mGmailAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent());
}
+ @After
+ public void tearDown() {
+ mModelContext.onDestroy();
+ }
+
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEmptyString_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
mItemInfoWithIcon.title = EMPTY_STRING;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -165,8 +173,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEmptyString_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
mItemInfoWithIcon.title = EMPTY_STRING;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -179,9 +187,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -195,8 +202,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -210,9 +217,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringNoSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "flutterappflorafy"
mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -226,8 +232,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringNoSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "flutterappflorafy"
mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -241,9 +247,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "System UWB Field Test"
mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -257,8 +262,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "System UWB Field Test"
mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -272,9 +277,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringSymbolLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -288,8 +292,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringSymbolLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -359,9 +363,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEnsurePredictionRowIsTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW);
@@ -375,9 +378,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void modifyTitleToSupportMultiLine_whenLimitedHeight_shouldBeOneLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -390,9 +392,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void modifyTitleToSupportMultiLine_whenUnlimitedHeight_shouldBeTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index d0cf610..e6e156f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -43,7 +43,6 @@
import dagger.Component
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
-import kotlin.math.min
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -58,6 +57,7 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.stubbing.Answer
+import kotlin.math.min
/** Unit tests for {@link DisplayController} */
@SmallTest
@@ -97,9 +97,10 @@
@Before
fun setUp() {
context.initDaggerComponent(
- DaggerDisplayControllerTestComponent.builder().bindWMProxy(windowManagerProxy)
+ DaggerDisplayControllerTestComponent.builder()
+ .bindWMProxy(windowManagerProxy)
+ .bindLauncherPrefs(launcherPrefs)
)
- context.putObject(LauncherPrefs.INSTANCE, launcherPrefs)
displayManager = context.spyService(DisplayManager::class.java)
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
@@ -243,6 +244,8 @@
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
+ @BindsInstance fun bindLauncherPrefs(prefs: LauncherPrefs): Builder
+
override fun build(): DisplayControllerTestComponent
}
}