diff --git a/OWNERS b/OWNERS index dd2d00eb048a688a80b9a54ed4df97c1698ad0b2..654493f80762e34b8a9b23cf2f7e55a6bbd5c6ec 100644 --- a/OWNERS +++ b/OWNERS @@ -45,3 +45,6 @@ zakcohen@google.com per-file FeatureFlags.java, globs = set noparent per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com + +per-file DeviceConfigWrapper.java, globs = set noparent +per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index be532b4047b52767c87aaf4af9a0761114e6fcdd..0697f47cda65dd6cdbf69c1e3b1918a3f6243d2e 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -173,6 +173,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map.Entry; /** * Manages the opening and closing app transitions from Launcher diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..6b44ca78a287c7a2299aaacf8ff370b486d94771 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.uioverrides.flags + +import android.os.Handler +import android.provider.DeviceConfig +import android.text.Html +import android.view.inputmethod.EditorInfo +import androidx.preference.Preference +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceViewHolder +import androidx.preference.SwitchPreference +import com.android.launcher3.ExtendedEditText +import com.android.launcher3.R +import com.android.quickstep.util.DeviceConfigHelper +import com.android.quickstep.util.DeviceConfigHelper.Companion.NAMESPACE_LAUNCHER +import com.android.quickstep.util.DeviceConfigHelper.DebugInfo + +/** Helper class to generate UI for Device Config */ +class DevOptionsUiHelper { + + /** Inflates preferences for all server flags in the provider PreferenceGroup */ + fun inflateServerFlags(parent: PreferenceGroup) { + val prefs = DeviceConfigHelper.prefs + // Sort the keys in the order of modified first followed by natural order + val allProps = + DeviceConfigHelper.allProps.values + .toList() + .sortedWith( + Comparator.comparingInt { prop: DebugInfo<*> -> + if (prefs.contains(prop.key)) 0 else 1 + } + .thenComparing { prop: DebugInfo<*> -> prop.key } + ) + + // First add boolean flags + allProps.forEach { + if (it.isInt) return@forEach + val info = it as DebugInfo<Boolean> + + val preference = + object : SwitchPreference(parent.context) { + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + holder.itemView.setOnLongClickListener { + prefs.edit().remove(key).apply() + setChecked(info.getBoolValue()) + summary = info.getSummary() + true + } + } + } + preference.key = info.key + preference.isPersistent = false + preference.title = info.key + preference.summary = info.getSummary() + preference.setChecked(prefs.getBoolean(info.key, info.getBoolValue())) + preference.setOnPreferenceChangeListener { _, newVal -> + DeviceConfigHelper.prefs.edit().putBoolean(info.key, newVal as Boolean).apply() + preference.summary = info.getSummary() + true + } + parent.addPreference(preference) + } + + // Apply Int flags + allProps.forEach { + if (!it.isInt) return@forEach + val info = it as DebugInfo<Int> + + val preference = + object : Preference(parent.context) { + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + val textView = holder.findViewById(R.id.pref_edit_text) as ExtendedEditText + textView.setText(info.getIntValueAsString()) + textView.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + DeviceConfigHelper.prefs + .edit() + .putInt(key, textView.text.toString().toInt()) + .apply() + Handler().post { summary = info.getSummary() } + true + } + false + } + textView.setOnBackKeyListener { + textView.setText(info.getIntValueAsString()) + true + } + + holder.itemView.setOnLongClickListener { + prefs.edit().remove(key).apply() + textView.setText(info.getIntValueAsString()) + summary = info.getSummary() + true + } + } + } + preference.key = info.key + preference.isPersistent = false + preference.title = info.key + preference.summary = info.getSummary() + preference.widgetLayoutResource = R.layout.develop_options_edit_text + parent.addPreference(preference) + } + } + + /** + * Returns the summary to show the description and whether the flag overrides the default value. + */ + private fun DebugInfo<*>.getSummary() = + Html.fromHtml( + (if (DeviceConfigHelper.prefs.contains(this.key)) + "<font color='red'><b>[OVERRIDDEN]</b></font><br>" + else "") + this.desc + ) + + private fun DebugInfo<Boolean>.getBoolValue() = + DeviceConfigHelper.prefs.getBoolean( + this.key, + DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode) + ) + + private fun DebugInfo<Int>.getIntValueAsString() = + DeviceConfigHelper.prefs + .getInt(this.key, DeviceConfig.getInt(NAMESPACE_LAUNCHER, this.key, this.valueInCode)) + .toString() + + companion object { + const val TAG = "DeviceConfigUIHelper" + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java index 6713964ada5ab9b7c21a432019ef7bad193ff05b..fd6bf4daf4c64bf4002031e2baf64515f7c543e4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java @@ -20,19 +20,8 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; -import static com.android.launcher3.LauncherPrefs.PRIVATE_SPACE_APPS; -import static com.android.launcher3.config.FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_SCALE_EXPONENT; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_START_SCALE_PERCENT; -import static com.android.launcher3.config.FeatureFlags.LPNH_SLOP_PERCENTAGE; -import static com.android.launcher3.config.FeatureFlags.LPNH_TIMEOUT_MS; import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_HIGHLIGHT_KEY; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED; -import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey; import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT; import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT; import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN; @@ -51,7 +40,6 @@ import android.provider.Settings; import android.text.Editable; import android.text.TextWatcher; import android.util.ArrayMap; -import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -61,21 +49,18 @@ import android.widget.Toast; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceDataStore; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; -import androidx.preference.SeekBarPreference; import androidx.preference.SwitchPreference; -import com.android.launcher3.ConstantItem; -import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.systemui.shared.plugins.PluginEnabler; import java.util.ArrayList; import java.util.List; @@ -96,8 +81,6 @@ public class DeveloperOptionsUI { private final PreferenceFragmentCompat mFragment; private final PreferenceScreen mPreferenceScreen; - private final FlagTogglerPrefUi mFlagTogglerPrefUi; - private PreferenceCategory mPluginsCategory; public DeveloperOptionsUI(PreferenceFragmentCompat fragment, PreferenceCategory flags) { @@ -112,20 +95,14 @@ public class DeveloperOptionsUI { parent.addView(topBar, parent.indexOfChild(listView)); initSearch(topBar.findViewById(R.id.filter_box)); - mFlagTogglerPrefUi = new FlagTogglerPrefUi(mFragment.requireActivity(), - topBar.findViewById(R.id.flag_apply_btn)); - mFlagTogglerPrefUi.applyTo(flags); + new FlagTogglerPrefUi(mFragment.requireActivity(), topBar.findViewById(R.id.flag_apply_btn)) + .applyTo(flags); + DevOptionsUiHelper uiHelper = new DevOptionsUiHelper(); + uiHelper.inflateServerFlags(newCategory("Server flags")); loadPluginPrefs(); maybeAddSandboxCategory(); addOnboardingPrefsCatergory(); - if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) { - addAllAppsFromOverviewCatergory(); - } - addCustomLpnhCategory(); - if (Flags.enablePrivateSpace()) { - addCustomPrivateAppsCategory(); - } } private void filterPreferences(String query, PreferenceGroup pg) { @@ -205,7 +182,7 @@ public class DeveloperOptionsUI { new ArrayMap<>(); Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions( - new String[]{PLUGIN_PERMISSION}, MATCH_DISABLED_COMPONENTS) + new String[]{PLUGIN_PERMISSION}, MATCH_DISABLED_COMPONENTS) .stream() .map(pi -> pi.packageName) .collect(Collectors.toSet()); @@ -228,7 +205,7 @@ public class DeveloperOptionsUI { } } - PreferenceDataStore enabler = manager.getPluginEnabler(); + PluginEnabler enabler = manager.getPluginEnabler(); plugins.forEach((key, si) -> { String packageName = key.first; List<ComponentName> componentNames = si.stream() @@ -347,111 +324,10 @@ public class DeveloperOptionsUI { return onboardingPref; } - private void addAllAppsFromOverviewCatergory() { - PreferenceCategory category = newCategory("All Apps from Overview Config"); - category.addPreference(createSeekBarPreference("Threshold to open All Apps from Overview", - 105, 500, 100, ALL_APPS_OVERVIEW_THRESHOLD)); - } - - private void addCustomLpnhCategory() { - PreferenceCategory category = newCategory("Long Press Nav Handle Config"); - if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) { - category.addPreference(createSeekBarPreference( - "Slop multiplier (applied to edge slop, " - + "which is generally already 50% higher than touch slop)", - 25, 200, 100, LPNH_SLOP_PERCENTAGE)); - category.addPreference(createSeekBarPreference( - "Extra width DP (how far outside the sides of the nav bar to trigger)", - // Stashed taskbar is currently 220dp; -86 (x2) would result in 48dp touch area. - -86, 100, 1, LPNH_EXTRA_TOUCH_WIDTH_DP)); - category.addPreference(createSeekBarPreference("LPNH timeout", - 100, 500, 1, LPNH_TIMEOUT_MS)); - } - if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) { - category.addPreference( - createSeekBarPreference("Haptic hint start scale", - 0, 100, 100, LPNH_HAPTIC_HINT_START_SCALE_PERCENT)); - category.addPreference(createSeekBarPreference("Haptic hint end scale", - 0, 100, 100, LPNH_HAPTIC_HINT_END_SCALE_PERCENT)); - category.addPreference( - createSeekBarPreference("Haptic hint scale exponent", - 1, 5, 1, LPNH_HAPTIC_HINT_SCALE_EXPONENT)); - category.addPreference( - createSeekBarPreference("Haptic hint iterations (12 ms each)", - 0, 200, 1, LPNH_HAPTIC_HINT_ITERATIONS)); - category.addPreference(createSeekBarPreference("Haptic hint delay (ms)", - 0, 400, 1, LPNH_HAPTIC_HINT_DELAY)); - } - } - - private void addCustomPrivateAppsCategory() { - PreferenceCategory category = newCategory("Apps in Private Space Config"); - category.addPreference(createSeekBarPreference( - "Number of Apps to put in private region", 0, 100, 1, PRIVATE_SPACE_APPS)); - } - - private SeekBarPreference createSeekBarPreference(String title, int min, - int max, int scale, FeatureFlags.IntFlag flag) { - if (!(flag instanceof IntDebugFlag)) { - Log.e(TAG, "Cannot create seekbar preference with IntFlag. Use a launcher preference " - + "flag or pref-backed IntDebugFlag instead"); - return null; - } - IntDebugFlag debugflag = (IntDebugFlag) flag; - if (debugflag.launcherPrefFlag == null) { - Log.e(TAG, "Cannot create seekbar preference with IntDebugFlag. Use a launcher " - + "preference flag or pref-backed IntDebugFlag instead"); - return null; - } - SeekBarPreference seekBarPref = createSeekBarPreference(title, min, max, scale, - debugflag.launcherPrefFlag); - int value = flag.get(); - seekBarPref.setValue(value); - // For some reason the initial value is not triggering the summary update, so call manually. - seekBarPref.setSummary(String.valueOf(scale == 1 ? value - : value / (float) scale)); - return seekBarPref; - } - - /** - * Create a preference with text and a seek bar. Should be added to a PreferenceCategory. - * - * @param title text to show for this seek bar - * @param min min value for the seek bar - * @param max max value for the seek bar - * @param scale how much to divide the value to convert int to float - * @param launcherPref used to store the current value - */ - private SeekBarPreference createSeekBarPreference(String title, int min, int max, int scale, - ConstantItem<Integer> launcherPref) { - SeekBarPreference seekBarPref = new SeekBarPreference(getContext()); - seekBarPref.setTitle(title); - seekBarPref.setSingleLineTitle(false); - - seekBarPref.setMax(max); - seekBarPref.setMin(min); - seekBarPref.setUpdatesContinuously(true); - seekBarPref.setIconSpaceReserved(false); - // Don't directly save to shared prefs, use LauncherPrefs instead. - seekBarPref.setPersistent(false); - seekBarPref.setOnPreferenceChangeListener((preference, newValue) -> { - LauncherPrefs.get(getContext()).put(launcherPref, newValue); - preference.setSummary(String.valueOf(scale == 1 ? newValue - : (int) newValue / (float) scale)); - mFlagTogglerPrefUi.updateMenu(); - return true; - }); - int value = LauncherPrefs.get(getContext()).get(launcherPref); - seekBarPref.setValue(value); - // For some reason the initial value is not triggering the summary update, so call manually. - seekBarPref.setSummary(String.valueOf(scale == 1 ? value - : value / (float) scale)); - return seekBarPref; - } - private String toName(String action) { String str = action.replace("com.android.systemui.action.PLUGIN_", "") .replace("com.android.launcher3.action.PLUGIN_", ""); + StringBuilder b = new StringBuilder(); for (String s : str.split("_")) { if (b.length() != 0) { @@ -466,11 +342,11 @@ public class DeveloperOptionsUI { private static class PluginPreference extends SwitchPreference { private final String mPackageName; private final ResolveInfo mSettingsInfo; - private final PreferenceDataStore mPluginEnabler; + private final PluginEnabler mPluginEnabler; private final List<ComponentName> mComponentNames; PluginPreference(Context prefContext, ResolveInfo pluginInfo, - PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) { + PluginEnabler pluginEnabler, List<ComponentName> componentNames) { super(prefContext); PackageManager pm = prefContext.getPackageManager(); mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName; @@ -495,14 +371,9 @@ public class DeveloperOptionsUI { setWidgetLayoutResource(R.layout.switch_preference_with_settings); } - private boolean isEnabled(ComponentName cn) { - return mPluginEnabler.getBoolean(pluginEnabledKey(cn), true); - - } - private boolean isPluginEnabled() { for (ComponentName componentName : mComponentNames) { - if (!isEnabled(componentName)) { + if (!mPluginEnabler.isEnabled(componentName)) { return false; } } @@ -513,8 +384,9 @@ public class DeveloperOptionsUI { protected boolean persistBoolean(boolean isEnabled) { boolean shouldSendBroadcast = false; for (ComponentName componentName : mComponentNames) { - if (isEnabled(componentName) != isEnabled) { - mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled); + if (mPluginEnabler.isEnabled(componentName) != isEnabled) { + mPluginEnabler.setDisabled(componentName, + isEnabled ? PluginEnabler.ENABLED : PluginEnabler.DISABLED_MANUALLY); shouldSendBroadcast = true; } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java index 4326c67162559e15e8f2206b1c4e383382ae2455..fc39ce42a2e2fa18136886f249a9f90e3febb51e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java @@ -34,7 +34,6 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceViewHolder; import androidx.preference.SwitchPreference; -import com.android.launcher3.LauncherPrefs; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; @@ -162,22 +161,12 @@ public final class FlagTogglerPrefUi implements ActivityLifecycleCallbacksAdapte return mDataStore.getBoolean(flag.key, defaultValue); } - private int getIntFlagStateFromSharedPrefs(IntDebugFlag flag) { - LauncherPrefs prefs = LauncherPrefs.get(mContext); - return flag.launcherPrefFlag == null ? flag.get() : prefs.get(flag.launcherPrefFlag); - } - private boolean anyChanged() { for (DebugFlag flag : FlagsFactory.getDebugFlags()) { if (getFlagStateFromSharedPrefs(flag) != flag.get()) { return true; } } - for (IntDebugFlag flag : FlagsFactory.getIntDebugFlags()) { - if (getIntFlagStateFromSharedPrefs(flag) != flag.get()) { - return true; - } - } return false; } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java index 686ed64e4f3484cba9f052a9dec57645111aa280..7fd63445bb3c6c5a31d5345dfaef52dfcf2430ed 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java @@ -23,8 +23,6 @@ import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED; import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static java.util.Collections.unmodifiableList; - import android.content.Context; import android.content.SharedPreferences; import android.provider.DeviceConfig; @@ -32,13 +30,9 @@ import android.provider.DeviceConfig.Properties; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.android.launcher3.ConstantItem; -import com.android.launcher3.LauncherPrefs; import com.android.launcher3.config.FeatureFlags.BooleanFlag; import com.android.launcher3.config.FeatureFlags.FlagState; -import com.android.launcher3.config.FeatureFlags.IntFlag; import com.android.launcher3.util.ScreenOnTracker; import java.io.PrintWriter; @@ -62,7 +56,6 @@ public class FlagsFactory { public static final String NAMESPACE_LAUNCHER = "launcher"; private static final List<DebugFlag> sDebugFlags = new ArrayList<>(); - private static final List<IntDebugFlag> sIntDebugFlags = new ArrayList<>(); private static SharedPreferences sSharedPreferences; static final BooleanFlag TEAMFOOD_FLAG = getReleaseFlag( @@ -132,42 +125,6 @@ public class FlagsFactory { } } - /** - * Creates a new integer flag. Integer flags are always release flags - */ - public static IntFlag getIntFlag( - int bugId, String key, int defaultValueInCode, String description) { - return getIntFlag(bugId, key, defaultValueInCode, description, null); - } - - /** - * Creates a new integer flag. - * - * @param launcherPrefFlag Set launcherPrefFlag to non-null if you want - * to modify the int flag in Launcher Developer Options and IntDebugFlag - * will be backed up by LauncherPrefs. Modified int value will be saved - * in LauncherPrefs. - */ - public static IntFlag getIntFlag( - int bugId, String key, int defaultValueInCode, String description, - @Nullable ConstantItem<Integer> launcherPrefFlag) { - INSTANCE.mKeySet.add(key); - int defaultValue = DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode); - if (IS_DEBUG_DEVICE) { - int currentValue; - if (launcherPrefFlag == null) { - currentValue = defaultValue; - } else { - currentValue = LauncherPrefs.get(currentApplication()).get(launcherPrefFlag); - } - IntDebugFlag flag = new IntDebugFlag(key, currentValue, defaultValueInCode, - launcherPrefFlag); - sIntDebugFlags.add(flag); - return flag; - } else { - return new IntFlag(defaultValue); - } - } static List<DebugFlag> getDebugFlags() { if (!IS_DEBUG_DEVICE) { @@ -178,15 +135,6 @@ public class FlagsFactory { } } - static List<IntDebugFlag> getIntDebugFlags() { - if (!IS_DEBUG_DEVICE) { - return unmodifiableList(Collections.emptyList()); - } - synchronized (sIntDebugFlags) { - return unmodifiableList(sIntDebugFlags); - } - } - /** Returns the SharedPreferences instance backing Debug FeatureFlags. */ @NonNull static SharedPreferences getSharedPreferences() { @@ -214,12 +162,6 @@ public class FlagsFactory { } } } - pw.println(" IntFlags:"); - synchronized (sIntDebugFlags) { - for (IntFlag flag : sIntDebugFlags) { - pw.println(" " + flag); - } - } pw.println(" DebugFlags:"); synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java deleted file mode 100644 index 1350aa87a84facf586fdeb33913f3ecb66c71415..0000000000000000000000000000000000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.uioverrides.flags; - -import androidx.annotation.Nullable; - -import com.android.launcher3.ConstantItem; -import com.android.launcher3.config.FeatureFlags.IntFlag; - -public class IntDebugFlag extends IntFlag { - public final String key; - private final int mDefaultValueInCode; - @Nullable - public final ConstantItem<Integer> launcherPrefFlag; - - public IntDebugFlag(String key, int currentValue, int defaultValueInCode, - @Nullable ConstantItem<Integer> launcherPrefFlag) { - super(currentValue); - this.key = key; - mDefaultValueInCode = defaultValueInCode; - this.launcherPrefFlag = launcherPrefFlag; - } - - @Override - public String toString() { - return key + ": mCurrentValue=" + get() + ", defaultValueInCode=" + mDefaultValueInCode; - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java index faa900b714fd8ce1572304673caf2c6211deb695..4e09f1fc59f8d19496415869ef6f3c410a5121a6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java @@ -18,12 +18,10 @@ import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; -import androidx.preference.PreferenceDataStore; - import com.android.launcher3.LauncherPrefs; import com.android.systemui.shared.plugins.PluginEnabler; -public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler { +public class PluginEnablerImpl implements PluginEnabler { private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_"; @@ -44,12 +42,12 @@ public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnab } private void setState(ComponentName component, boolean enabled) { - putBoolean(pluginEnabledKey(component), enabled); + mSharedPrefs.edit().putBoolean(pluginEnabledKey(component), enabled).apply(); } @Override public boolean isEnabled(ComponentName component) { - return getBoolean(pluginEnabledKey(component), true); + return mSharedPrefs.getBoolean(pluginEnabledKey(component), true); } @Override @@ -57,17 +55,7 @@ public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnab return isEnabled(componentName) ? ENABLED : DISABLED_MANUALLY; } - @Override - public void putBoolean(String key, boolean value) { - mSharedPrefs.edit().putBoolean(key, value).apply(); - } - - @Override - public boolean getBoolean(String key, boolean defValue) { - return mSharedPrefs.getBoolean(key, defValue); - } - - static String pluginEnabledKey(ComponentName cn) { + private static String pluginEnabledKey(ComponentName cn) { return PREFIX_PLUGIN_ENABLED + cn.flattenToString(); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java index 7f7871380d2f0d549df9406a662c12c254d77a70..a09d0a1df1c71966b0fa1ff76431df156133fd5a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java @@ -98,13 +98,6 @@ public class PluginManagerWrapper { return new PluginPrefs(mContext).getPluginList(); } - /** - * Returns the string key used to store plugin enabled/disabled setting - */ - public static String pluginEnabledKey(ComponentName cn) { - return PluginEnablerImpl.pluginEnabledKey(cn); - } - public static boolean hasPlugins(Context context) { return PluginPrefs.hasPlugins(context); } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 62e823aea14d848f767aaa3f051578582c4eb493..7ee77513629d93fff9b54640ff911534e24d526e 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -22,8 +22,8 @@ import static android.view.Surface.ROTATION_90; import static android.widget.Toast.LENGTH_SHORT; import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; -import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.app.animation.Interpolators.DECELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.app.animation.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; @@ -31,7 +31,6 @@ import static com.android.launcher3.BaseActivity.EVENT_STARTED; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableGridOnlyOverview; -import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.launcher3.PagedView.INVALID_PAGE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; @@ -100,7 +99,6 @@ import com.android.internal.jank.Cuj; import com.android.internal.util.LatencyTracker; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; @@ -892,7 +890,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, @UiThread @Override public void onCurrentShiftUpdated() { - float threshold = LauncherPrefs.get(mContext).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f; + float threshold = DeviceConfigWrapper.get().getAllAppsOverviewThreshold() / 100f; setIsInAllAppsRegion(mCurrentShift.value >= threshold); updateSysUiFlags(mCurrentShift.value); applyScrollAndTransform(); diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..678709c2e208b32b7cbc50630c4e90a81b5ca2c3 --- /dev/null +++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep + +import com.android.quickstep.util.DeviceConfigHelper +import com.android.quickstep.util.DeviceConfigHelper.PropReader +import java.io.PrintWriter + +/** Various configurations specific to nav-bar functionalities */ +class DeviceConfigWrapper private constructor(propReader: PropReader) { + + val customLpnhThresholds = + propReader.get( + "CUSTOM_LPNH_THRESHOLDS", + true, + "Add dev options and server side control to customize the LPNH trigger slop and milliseconds" + ) + + val customLphThresholds = + propReader.get( + "CUSTOM_LPH_THRESHOLDS", + false, + "Server side control to customize LPH timeout and touch slop" + ) + + val overrideLpnhLphThresholds = + propReader.get( + "OVERRIDE_LPNH_LPH_THRESHOLDS", + false, + "Enable AGSA override for LPNH and LPH timeout and touch slop" + ) + + val lpnhSlopPercentage = + propReader.get("LPNH_SLOP_PERCENTAGE", 100, "Controls touch slop percentage for lpnh") + + val animateLpnh = propReader.get("ANIMATE_LPNH", false, "Animates navbar when long pressing") + + val shrinkNavHandleOnPress = + propReader.get( + "SHRINK_NAV_HANDLE_ON_PRESS", + false, + "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled" + ) + + val lpnhTimeoutMs = + propReader.get("LPNH_TIMEOUT_MS", 450, "Controls lpnh timeout in milliseconds") + + val enableLongPressNavHandle = + propReader.get( + "ENABLE_LONG_PRESS_NAV_HANDLE", + true, + "Enables long pressing on the bottom bar nav handle to trigger events." + ) + + val enableSearchHapticHint = + propReader.get( + "ENABLE_SEARCH_HAPTIC_HINT", + true, + "Enables haptic hint while long pressing on the bottom bar nav handle." + ) + + val enableSearchHapticCommit = + propReader.get( + "ENABLE_SEARCH_HAPTIC_COMMIT", + true, + "Enables haptic hint at end of long pressing on the bottom bar nav handle." + ) + + val lpnhHapticHintStartScalePercent = + propReader.get("LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0, "Haptic hint start scale.") + + val lpnhHapticHintEndScalePercent = + propReader.get("LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100, "Haptic hint end scale.") + + val lpnhHapticHintScaleExponent = + propReader.get("LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1, "Haptic hint scale exponent.") + + val lpnhHapticHintIterations = + propReader.get("LPNH_HAPTIC_HINT_ITERATIONS", 50, "Haptic hint number of iterations.") + + val enableLpnhDeepPress = + propReader.get( + "ENABLE_LPNH_DEEP_PRESS", + true, + "Long press of nav handle is instantly triggered if deep press is detected." + ) + + val lpnhHapticHintDelay = + propReader.get("LPNH_HAPTIC_HINT_DELAY", 0, "Delay before haptic hint starts.") + + val lpnhExtraTouchWidthDp = + propReader.get( + "LPNH_EXTRA_TOUCH_WIDTH_DP", + 0, + "Controls extra dp on the nav bar sides to trigger LPNH. Can be negative for a smaller touch region." + ) + + val allAppsOverviewThreshold = + propReader.get( + "ALL_APPS_OVERVIEW_THRESHOLD", + 180, + "Threshold to open All Apps from Overview" + ) + + /** Dump config values. */ + fun dump(prefix: String, writer: PrintWriter) { + writer.println("$prefix DeviceConfigWrapper:") + writer.println("$prefix\tcustomLpnhThresholds=$customLpnhThresholds") + writer.println("$prefix\tcustomLphThresholds=$customLphThresholds") + writer.println("$prefix\toverrideLpnhLphThresholds=$overrideLpnhLphThresholds") + writer.println("$prefix\tlpnhSlopPercentage=$lpnhSlopPercentage") + writer.println("$prefix\tanimateLpnh=$animateLpnh") + writer.println("$prefix\tshrinkNavHandleOnPress=$shrinkNavHandleOnPress") + writer.println("$prefix\tlpnhTimeoutMs=$lpnhTimeoutMs") + writer.println("$prefix\tenableLongPressNavHandle=$enableLongPressNavHandle") + writer.println("$prefix\tenableSearchHapticHint=$enableSearchHapticHint") + writer.println("$prefix\tenableSearchHapticCommit=$enableSearchHapticCommit") + writer.println("$prefix\tlpnhHapticHintStartScalePercent=$lpnhHapticHintStartScalePercent") + writer.println("$prefix\tlpnhHapticHintEndScalePercent=$lpnhHapticHintEndScalePercent") + writer.println("$prefix\tlpnhHapticHintScaleExponent=$lpnhHapticHintScaleExponent") + writer.println("$prefix\tlpnhHapticHintIterations=$lpnhHapticHintIterations") + writer.println("$prefix\tenableLpnhDeepPress=$enableLpnhDeepPress") + writer.println("$prefix\tlpnhHapticHintDelay=$lpnhHapticHintDelay") + writer.println("$prefix\tlpnhExtraTouchWidthDp=$lpnhExtraTouchWidthDp") + writer.println("$prefix\tallAppsOverviewThreshold=$allAppsOverviewThreshold") + } + + companion object { + val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) } + + @JvmStatic fun get() = configHelper.config + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index c56a6219b212fa31a234c6075ce78ba475bd5016..43e564cadfce2fe8c7d3f9dfd1808b7f2fdbb42c 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -60,7 +60,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; @@ -588,9 +587,8 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON; float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) { - float customSlopMultiplier = - FeatureFlags.LPNH_SLOP_PERCENTAGE.get() / 100f; + if (DeviceConfigWrapper.get().getCustomLpnhThresholds()) { + float customSlopMultiplier = DeviceConfigWrapper.get().getLpnhSlopPercentage() / 100f; return customSlopMultiplier * slopMultiplier * touchSlop; } else { return slopMultiplier * touchSlop; diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 66d7144ae085bd9f8c54f53eed361f4a34e2c086..f2ee3f1483e71b4f855518e3ec092a8c0aea200d 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -1456,6 +1456,7 @@ public class TouchInteractionService extends Service { pw.println("AssistStateManager:"); AssistStateManager.INSTANCE.get(this).dump(" ", pw); SystemUiProxy.INSTANCE.get(this).dump(pw); + DeviceConfigWrapper.get().dump(" ", pw); } private AbsSwipeUpHandler createLauncherSwipeHandler( diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java index e22703b65914495d9b362d9eed5e1e92d3f4bde2..5ab2fcc1544ae963e4a31e2e9dd7859bf2e52a12 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java @@ -27,9 +27,9 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.util.DisplayController; +import com.android.quickstep.DeviceConfigWrapper; import com.android.quickstep.InputConsumer; import com.android.quickstep.NavHandle; import com.android.quickstep.RecentsAnimationDeviceState; @@ -64,7 +64,7 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { NavHandle navHandle) { super(delegate, inputMonitor); mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x; - mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get(); + mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress(); AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(context); if (assistStateManager.getLPNHDurationMillis().isPresent()) { mLongPressTimeout = assistStateManager.getLPNHDurationMillis().get().intValue(); @@ -181,8 +181,9 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { private boolean isInNavBarHorizontalArea(float x) { float areaFromMiddle = mNavHandleWidth / 2.0f; - if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) { - areaFromMiddle += Utilities.dpToPx(FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP.get()); + if (DeviceConfigWrapper.get().getCustomLpnhThresholds()) { + areaFromMiddle += Utilities.dpToPx( + DeviceConfigWrapper.get().getLpnhExtraTouchWidthDp()); } int minAccessibleSize = Utilities.dpToPx(24); // Half of 48dp because this is per side. if (areaFromMiddle < minAccessibleSize) { diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java index 16f206556f5c80b1b351bd33b6b17154ed16771b..561e951362df70b1735b4ae24451c74880b4b68c 100644 --- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java @@ -18,7 +18,6 @@ package com.android.quickstep.util; import static com.android.app.animation.Interpolators.DECELERATE; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Flags.enableGridOnlyOverview; -import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; @@ -35,7 +34,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -44,6 +42,7 @@ import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.AllAppsSwipeController; +import com.android.quickstep.DeviceConfigWrapper; import com.android.quickstep.orientation.RecentsPagedOrientationHandler; import com.android.quickstep.views.RecentsView; @@ -191,7 +190,7 @@ public class AnimatorControllerWithResistance { recentsOrientedState.getOrientationHandler()); float dragLengthFactor = (float) dp.heightPx / transitionDragLength; // -1s are because 0-1 is reserved for the normal transition. - float threshold = LauncherPrefs.get(context).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f; + float threshold = DeviceConfigWrapper.get().getAllAppsOverviewThreshold() / 100f; return (threshold - 1) / (dragLengthFactor - 1); } diff --git a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..f601fee954c7331afe3820493636a28a4f8c9b20 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util + +import android.app.ActivityThread +import android.content.Context +import android.content.SharedPreferences +import android.content.SharedPreferences.* +import android.provider.DeviceConfig +import android.provider.DeviceConfig.OnPropertiesChangedListener +import android.provider.DeviceConfig.Properties +import androidx.annotation.WorkerThread +import com.android.launcher3.BuildConfig +import com.android.launcher3.uioverrides.flags.FlagsFactory +import com.android.launcher3.util.Executors + +/** Utility class to manage a set of device configurations */ +class DeviceConfigHelper<ConfigType>(private val factory: (PropReader) -> ConfigType) { + + var config: ConfigType + private set + private val allKeys: Set<String> + private val propertiesListener = OnPropertiesChangedListener { onDevicePropsChanges(it) } + private val sharedPrefChangeListener = OnSharedPreferenceChangeListener { _, _ -> + recreateConfig() + } + + private val changeListeners = mutableListOf<Runnable>() + + init { + // Initialize the default config once. + allKeys = HashSet() + config = + factory( + PropReader( + object : PropProvider { + override fun <T : Any> get(key: String, fallback: T): T { + if (fallback is Int) + return DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, fallback) as T + else if (fallback is Boolean) + return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, fallback) + as T + else return fallback + } + } + ) + ) + + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_LAUNCHER, + Executors.UI_HELPER_EXECUTOR, + propertiesListener + ) + if (BuildConfig.IS_DEBUG_DEVICE) { + prefs.registerOnSharedPreferenceChangeListener(sharedPrefChangeListener) + } + } + + @WorkerThread + private fun onDevicePropsChanges(properties: Properties) { + if (NAMESPACE_LAUNCHER != properties.namespace) return + if (!allKeys.any(properties.keyset::contains)) return + recreateConfig() + } + + private fun recreateConfig() { + val myProps = + DeviceConfig.getProperties( + FlagsFactory.NAMESPACE_LAUNCHER, + *allKeys.toTypedArray<String>() + ) + config = + factory( + PropReader( + object : PropProvider { + override fun <T : Any> get(key: String, fallback: T): T { + if (fallback is Int) return myProps.getInt(key, fallback) as T + else if (fallback is Boolean) + return myProps.getBoolean(key, fallback) as T + else return fallback + } + } + ) + ) + Executors.MAIN_EXECUTOR.execute { changeListeners.forEach(Runnable::run) } + } + + /** Adds a listener for property changes */ + fun addChangeListener(r: Runnable) = changeListeners.add(r) + + /** Removes a previously added listener */ + fun removeChangeListener(r: Runnable) = changeListeners.remove(r) + + fun close() { + DeviceConfig.removeOnPropertiesChangedListener(propertiesListener) + if (BuildConfig.IS_DEBUG_DEVICE) { + prefs.unregisterOnSharedPreferenceChangeListener(sharedPrefChangeListener) + } + } + + internal interface PropProvider { + fun <T : Any> get(key: String, fallback: T): T + } + + /** The reader is sent to the config for initialization */ + class PropReader internal constructor(private val f: PropProvider) { + + @JvmOverloads + fun <T : Any> get(key: String, fallback: T, desc: String? = null): T { + val v = f.get(key, fallback) + if (BuildConfig.IS_DEBUG_DEVICE && desc != null) { + if (v is Int) { + allProps[key] = DebugInfo(key, desc, true, fallback) + return prefs.getInt(key, v) as T + } else if (v is Boolean) { + allProps[key] = DebugInfo(key, desc, false, fallback) + return prefs.getBoolean(key, v) as T + } + } + return v + } + } + + class DebugInfo<T>( + val key: String, + val desc: String, + val isInt: Boolean, + val valueInCode: T, + ) + + companion object { + const val NAMESPACE_LAUNCHER = "launcher" + + val allProps = mutableMapOf<String, DebugInfo<*>>() + + private const val FLAGS_PREF_NAME = "featureFlags" + + val prefs: SharedPreferences by lazy { + ActivityThread.currentApplication() + .createDeviceProtectedStorageContext() + .getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE) + } + } +} diff --git a/res/layout/develop_options_edit_text.xml b/res/layout/develop_options_edit_text.xml new file mode 100644 index 0000000000000000000000000000000000000000..5e44228982bd492b3a37bd61ccff42c48c951351 --- /dev/null +++ b/res/layout/develop_options_edit_text.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.launcher3.ExtendedEditText + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:minWidth="100dp" + android:inputType="numberSigned" + android:id="@+id/pref_edit_text" + android:selectAllOnFocus="true" + android:imeOptions="actionDone" + android:maxLines="1" /> \ No newline at end of file diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index cb19b143a418c1a163b16a1101ef28e03994aeb9..875c407d5dd9ce7072bdfc056bfd097a76136e69 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -297,64 +297,7 @@ class LauncherPrefs(private val encryptedContext: Context) { @JvmField val ICON_STATE = nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED) - @JvmField - val ALL_APPS_OVERVIEW_THRESHOLD = - nonRestorableItem( - "pref_all_apps_overview_threshold", - 180, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE = - nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED) - @JvmField - val LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP = - nonRestorableItem( - "LPNH_EXTRA_TOUCH_WIDTH_DP", - 0, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS = - nonRestorableItem( - "LPNH_TIMEOUT_MS", - 450, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT = - nonRestorableItem( - "LPNH_HAPTIC_HINT_START_SCALE_PERCENT", - 0, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT = - nonRestorableItem( - "LPNH_HAPTIC_HINT_END_SCALE_PERCENT", - 100, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT = - nonRestorableItem( - "LPNH_HAPTIC_HINT_SCALE_EXPONENT", - 1, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS = - nonRestorableItem( - "LPNH_HAPTIC_HINT_ITERATIONS", - 50, - EncryptionType.MOVE_TO_DEVICE_PROTECTED - ) - @JvmField - val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY = - nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED) - @JvmField - val PRIVATE_SPACE_APPS = - nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED) + @JvmField val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false) @JvmField diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index e47613848746b79377d9c061feed44879a914c7f..d6ce2b364385c260b7926cf4accd8af6b29812b2 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -17,14 +17,6 @@ package com.android.launcher3.config; import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE; -import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS; import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED; import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED; import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD; @@ -38,10 +30,8 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.BuildConfig; import com.android.launcher3.Flags; -import com.android.launcher3.uioverrides.flags.FlagsFactory; import java.util.function.Predicate; -import java.util.function.ToIntFunction; /** * Defines a set of flags used to control various launcher behaviors. @@ -52,8 +42,6 @@ public final class FeatureFlags { @VisibleForTesting public static Predicate<BooleanFlag> sBooleanReader = f -> f.mCurrentValue; - @VisibleForTesting - public static ToIntFunction<IntFlag> sIntReader = f -> f.mCurrentValue; private FeatureFlags() { } @@ -130,42 +118,6 @@ public final class FeatureFlags { getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED, "Allow entering All Apps from Overview (e.g. long swipe up from app)"); - public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS = - getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", ENABLED, - "Add dev options and server side control to customize the LPNH " - + "trigger slop and milliseconds"); - - public static final BooleanFlag CUSTOM_LPH_THRESHOLDS = getReleaseFlag(331800576, - "CUSTOM_LPH_THRESHOLDS", DISABLED, - "Server side control to customize LPH timeout and touch slop"); - - public static final BooleanFlag OVERRIDE_LPNH_LPH_THRESHOLDS = getReleaseFlag(331799727, - "OVERRIDE_LPNH_LPH_THRESHOLDS", DISABLED, - "Enable AGSA override for LPNH and LPH timeout and touch slop"); - - public static final BooleanFlag ANIMATE_LPNH = - getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD, - "Animates navbar when long pressing"); - - public static final BooleanFlag SHRINK_NAV_HANDLE_ON_PRESS = - getReleaseFlag(314158312, "SHRINK_NAV_HANDLE_ON_PRESS", DISABLED, - "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled"); - - public static final IntFlag LPNH_SLOP_PERCENTAGE = - FlagsFactory.getIntFlag(301680992, "LPNH_SLOP_PERCENTAGE", 100, - "Controls touch slop percentage for lpnh", - LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE); - - public static final IntFlag LPNH_EXTRA_TOUCH_WIDTH_DP = - FlagsFactory.getIntFlag(301680992, "LPNH_EXTRA_TOUCH_WIDTH_DP", 0, - "Controls extra dp on the nav bar sides to trigger LPNH." - + " Can be negative for a smaller touch region.", - LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP); - - public static final IntFlag LPNH_TIMEOUT_MS = - FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS", 450, - "Controls lpnh timeout in milliseconds", LONG_PRESS_NAV_HANDLE_TIMEOUT_MS); - public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag( 270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED, "Enable option to show keyboard when going to all-apps"); @@ -296,49 +248,6 @@ public final class FeatureFlags { "INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED, "Inject fallback app corpus result when AiAi fails to return it."); - public static final BooleanFlag ENABLE_LONG_PRESS_NAV_HANDLE = - getReleaseFlag(299682306, "ENABLE_LONG_PRESS_NAV_HANDLE", ENABLED, - "Enables long pressing on the bottom bar nav handle to trigger events."); - - public static final BooleanFlag ENABLE_SEARCH_HAPTIC_HINT = - getReleaseFlag(314005131, "ENABLE_SEARCH_HAPTIC_HINT", ENABLED, - "Enables haptic hint while long pressing on the bottom bar nav handle."); - - public static final BooleanFlag ENABLE_SEARCH_HAPTIC_COMMIT = - getReleaseFlag(314005577, "ENABLE_SEARCH_HAPTIC_COMMIT", ENABLED, - "Enables haptic hint at end of long pressing on the bottom bar nav handle."); - - public static final IntFlag LPNH_HAPTIC_HINT_START_SCALE_PERCENT = - FlagsFactory.getIntFlag(309972570, - "LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0, - "Haptic hint start scale.", - LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT); - - public static final IntFlag LPNH_HAPTIC_HINT_END_SCALE_PERCENT = - FlagsFactory.getIntFlag(309972570, - "LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100, - "Haptic hint end scale.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT); - - public static final IntFlag LPNH_HAPTIC_HINT_SCALE_EXPONENT = - FlagsFactory.getIntFlag(309972570, - "LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1, - "Haptic hint scale exponent.", - LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT); - - public static final IntFlag LPNH_HAPTIC_HINT_ITERATIONS = - FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_ITERATIONS", - 50, - "Haptic hint number of iterations.", - LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS); - - public static final BooleanFlag ENABLE_LPNH_DEEP_PRESS = - getReleaseFlag(310952290, "ENABLE_LPNH_DEEP_PRESS", ENABLED, - "Long press of nav handle is instantly triggered if deep press is detected."); - - public static final IntFlag LPNH_HAPTIC_HINT_DELAY = - FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_DELAY", 0, - "Delay before haptic hint starts.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY); - // TODO(Block 17): Clean up flags // Aconfig migration complete for ENABLE_TASKBAR_PINNING. private static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(296231746, @@ -514,22 +423,6 @@ public final class FeatureFlags { } } - /** - * Class representing an integer flag - */ - public static class IntFlag { - - private final int mCurrentValue; - - public IntFlag(int currentValue) { - mCurrentValue = currentValue; - } - - public int get() { - return sIntReader.applyAsInt(this); - } - } - /** * Enabled state for a flag */ diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java index e1695e96c3d06cf6cde5e63a27f42b1cf7a6e78a..cd60c1d1936530692d5bb37f62df5eacd2439e6e 100644 --- a/src/com/android/launcher3/util/VibratorWrapper.java +++ b/src/com/android/launcher3/util/VibratorWrapper.java @@ -19,11 +19,6 @@ import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static android.os.VibrationEffect.createPredefined; import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_SCALE_EXPONENT; -import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_START_SCALE_PERCENT; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; @@ -40,7 +35,6 @@ import android.provider.Settings; import androidx.annotation.Nullable; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; /** * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary. @@ -71,9 +65,6 @@ public class VibratorWrapper { @Nullable private final VibrationEffect mBumpEffect; - @Nullable - private final VibrationEffect mSearchEffect; - private long mLastDragTime; private final int mThresholdUntilNextDragCallMillis; @@ -133,25 +124,6 @@ public class VibratorWrapper { mBumpEffect = null; mThresholdUntilNextDragCallMillis = 0; } - - if (mVibrator.areAllPrimitivesSupported( - VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, - VibrationEffect.Composition.PRIMITIVE_TICK)) { - if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) { - mSearchEffect = VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f) - .compose(); - } else { - // quiet ramp, short pause, then sharp tick - mSearchEffect = VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) - .compose(); - } - } else { - // fallback for devices without composition support - mSearchEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK); - } } /** @@ -233,13 +205,6 @@ public class VibratorWrapper { } } - /** Indicates that search has been invoked. */ - public void vibrateForSearch() { - if (mSearchEffect != null) { - vibrate(mSearchEffect); - } - } - /** Indicates that Taskbar has been invoked. */ public void vibrateForTaskbarUnstash() { if (Utilities.ATLEAST_S && mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) { @@ -251,32 +216,4 @@ public class VibratorWrapper { vibrate(primitiveLowTickEffect); } } - - /** Indicates that search will be invoked if the current gesture is maintained. */ - public void vibrateForSearchHint() { - if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get() && Utilities.ATLEAST_S - && mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) { - float startScale = LPNH_HAPTIC_HINT_START_SCALE_PERCENT.get() / 100f; - float endScale = LPNH_HAPTIC_HINT_END_SCALE_PERCENT.get() / 100f; - int scaleExponent = LPNH_HAPTIC_HINT_SCALE_EXPONENT.get(); - int iterations = LPNH_HAPTIC_HINT_ITERATIONS.get(); - int delayMs = LPNH_HAPTIC_HINT_DELAY.get(); - - VibrationEffect.Composition composition = VibrationEffect.startComposition(); - for (int i = 0; i < iterations; i++) { - float t = i / (iterations - 1f); - float scale = (float) Math.pow((1 - t) * startScale + t * endScale, - scaleExponent); - if (i == 0) { - // Adds a delay before the ramp starts - composition.addPrimitive(PRIMITIVE_LOW_TICK, scale, - delayMs); - } else { - composition.addPrimitive(PRIMITIVE_LOW_TICK, scale); - } - } - - vibrate(composition.compose()); - } - } } diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java index b193d3789597b26db2cb160c67650ca22db78fdd..02e0de02c40c57cf50a885f44df3fa49428fd0c5 100644 --- a/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java +++ b/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java @@ -18,12 +18,8 @@ package com.android.launcher3.uioverrides.flags; import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED; -import androidx.annotation.Nullable; - -import com.android.launcher3.ConstantItem; import com.android.launcher3.config.FeatureFlags.BooleanFlag; import com.android.launcher3.config.FeatureFlags.FlagState; -import com.android.launcher3.config.FeatureFlags.IntFlag; import java.io.PrintWriter; @@ -49,23 +45,6 @@ public class FlagsFactory { return new BooleanFlag(flagState == ENABLED); } - /** - * Creates a new integer flag. Integer flags are always release flags - */ - public static IntFlag getIntFlag( - int bugId, String key, int defaultValueInCode, String description) { - return new IntFlag(defaultValueInCode); - } - - /** - * Creates a new debug integer flag and it is saved in LauncherPrefs. - */ - public static IntFlag getIntFlag( - int bugId, String key, int defaultValueInCode, String description, - @Nullable ConstantItem<Integer> launcherPrefFlag) { - return new IntFlag(defaultValueInCode); - } - /** * Dumps the current flags state to the print writer */ diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java index 95444ba3f24f3ec743d18035e9ea8e9bf0dab2db..2a0f614ae0b5964da2ab3ef653d0a1c031660787 100644 --- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java @@ -48,7 +48,6 @@ import androidx.test.uiautomator.UiDevice; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags.BooleanFlag; -import com.android.launcher3.config.FeatureFlags.IntFlag; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.util.rule.TestStabilityRule; @@ -67,7 +66,6 @@ import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; -import java.util.function.ToIntFunction; public class TestUtil { private static final String TAG = "TestUtil"; @@ -169,21 +167,6 @@ public class TestUtil { }; } - /** - * Utility class to override a int flag during test. Note that the returned SafeCloseable - * must be closed to restore the original state - */ - public static SafeCloseable overrideFlag(IntFlag flag, int value) { - ToIntFunction<IntFlag> originalProxy = FeatureFlags.sIntReader; - ToIntFunction<IntFlag> testProxy = f -> f == flag ? value : originalProxy.applyAsInt(f); - FeatureFlags.sIntReader = testProxy; - return () -> { - if (FeatureFlags.sIntReader == testProxy) { - FeatureFlags.sIntReader = originalProxy; - } - }; - } - public static void uninstallDummyApp() throws IOException { UiDevice.getInstance(getInstrumentation()).executeShellCommand( "pm uninstall " + DUMMY_PACKAGE);