Moving various nav-bar configs out of feature flags

Bug: 332588958
Test: Presubmit
Flag: None
Change-Id: Ia4dc5b69bcf4d461e3444b9e37bb7334bb74aae4
This commit is contained in:
Sunny Goyal
2024-04-05 12:54:32 -07:00
parent 962b3d43ea
commit 89931316a2
22 changed files with 514 additions and 556 deletions
+3
View File
@@ -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
@@ -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
@@ -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"
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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) {
@@ -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;
}
}
@@ -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();
}
}
@@ -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);
}
@@ -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();
@@ -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
}
}
@@ -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;
@@ -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(
@@ -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) {
@@ -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);
}
@@ -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)
}
}
}
+26
View File
@@ -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" />
+1 -58
View File
@@ -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
@@ -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
*/
@@ -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());
}
}
}
@@ -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
*/
@@ -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);