change package name to uiuios
This commit is contained in:
280
src/com/android/uiuios/config/BaseFlags.java
Normal file
280
src/com/android/uiuios/config/BaseFlags.java
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.uiuios.config;
|
||||
|
||||
import static androidx.core.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.uiuios.Utilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Defines a set of flags used to control various launcher behaviors.
|
||||
*
|
||||
* <p>All the flags should be defined here with appropriate default values.
|
||||
*
|
||||
* <p>This class is kept package-private to prevent direct access.
|
||||
*/
|
||||
@Keep
|
||||
abstract class BaseFlags {
|
||||
|
||||
private static final Object sLock = new Object();
|
||||
@GuardedBy("sLock")
|
||||
private static final List<TogglableFlag> sFlags = new ArrayList<>();
|
||||
|
||||
static final String FLAGS_PREF_NAME = "featureFlags";
|
||||
|
||||
BaseFlags() {
|
||||
throw new UnsupportedOperationException("Don't instantiate BaseFlags");
|
||||
}
|
||||
|
||||
public static boolean showFlagTogglerUi(Context context) {
|
||||
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
|
||||
}
|
||||
|
||||
public static final boolean IS_DOGFOOD_BUILD = false;
|
||||
|
||||
// When enabled the promise icon is visible in all apps while installation an app.
|
||||
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
|
||||
|
||||
// Enable moving the QSB on the 0th screen of the workspace
|
||||
public static final boolean QSB_ON_FIRST_SCREEN = true;
|
||||
|
||||
public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true,
|
||||
"An example flag that doesn't do anything. Useful for testing");
|
||||
|
||||
//Feature flag to enable pulling down navigation shade from workspace.
|
||||
public static final boolean PULL_DOWN_STATUS_BAR = true;
|
||||
|
||||
// When true, custom widgets are loaded using CustomWidgetParser.
|
||||
public static final boolean ENABLE_CUSTOM_WIDGETS = false;
|
||||
|
||||
// Features to control Launcher3Go behavior
|
||||
public static final boolean GO_DISABLE_WIDGETS = false;
|
||||
|
||||
// When enabled shows a work profile tab in all apps
|
||||
public static final boolean ALL_APPS_TABS_ENABLED = true;
|
||||
|
||||
// When true, overview shows screenshots in the orientation they were taken rather than
|
||||
// trying to make them fit the orientation the device is in.
|
||||
public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
|
||||
|
||||
/**
|
||||
* Feature flag to handle define config changes dynamically instead of killing the process.
|
||||
*/
|
||||
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
|
||||
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
|
||||
|
||||
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
|
||||
false, "Enable springs for quickstep animations");
|
||||
|
||||
public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
|
||||
"ADAPTIVE_ICON_WINDOW_ANIM", true,
|
||||
"Use adaptive icons for window animations.");
|
||||
|
||||
public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
|
||||
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
|
||||
|
||||
public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
|
||||
"ENABLE_HINTS_IN_OVERVIEW", false,
|
||||
"Show chip hints and gleams on the overview screen");
|
||||
|
||||
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
|
||||
"FAKE_LANDSCAPE_UI", false,
|
||||
"Rotate launcher UI instead of using transposed layout");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
// Avoid the disk read for user builds
|
||||
if (Utilities.IS_DEBUG_DEVICE) {
|
||||
synchronized (sLock) {
|
||||
for (TogglableFlag flag : sFlags) {
|
||||
flag.initialize(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<TogglableFlag> getTogglableFlags() {
|
||||
// By Java Language Spec 12.4.2
|
||||
// https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the
|
||||
// TogglableFlag instances on BaseFlags will be created before those on the FeatureFlags
|
||||
// subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the
|
||||
// FeatureFlags one takes priority.
|
||||
SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
|
||||
synchronized (sLock) {
|
||||
for (TogglableFlag flag : sFlags) {
|
||||
flagsByKey.put(flag.key, flag);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(flagsByKey.values());
|
||||
}
|
||||
|
||||
public static class TogglableFlag {
|
||||
private final String key;
|
||||
private final boolean defaultValue;
|
||||
private final String description;
|
||||
private boolean currentValue;
|
||||
|
||||
TogglableFlag(
|
||||
String key,
|
||||
boolean defaultValue,
|
||||
String description) {
|
||||
this.key = checkNotNull(key);
|
||||
this.currentValue = this.defaultValue = defaultValue;
|
||||
this.description = checkNotNull(description);
|
||||
synchronized (sLock) {
|
||||
sFlags.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the value of this flag. This should only be used in tests. */
|
||||
@VisibleForTesting
|
||||
void setForTests(boolean value) {
|
||||
currentValue = value;
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, defaultValue);
|
||||
}
|
||||
|
||||
public void updateStorage(Context context, boolean value) {
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
|
||||
Context.MODE_PRIVATE).edit();
|
||||
if (value == defaultValue) {
|
||||
editor.remove(key).apply();
|
||||
} else {
|
||||
editor.putBoolean(key, value).apply();
|
||||
}
|
||||
}
|
||||
|
||||
boolean getFromStorage(Context context, boolean defaultValue) {
|
||||
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/** Returns the value of the flag at process start, including any overrides present. */
|
||||
public boolean get() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TogglableFlag{"
|
||||
+ "key=" + key + ", "
|
||||
+ "defaultValue=" + defaultValue + ", "
|
||||
+ "description=" + description
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof TogglableFlag) {
|
||||
TogglableFlag that = (TogglableFlag) o;
|
||||
return (this.key.equals(that.getKey()))
|
||||
&& (this.defaultValue == that.getDefaultValue())
|
||||
&& (this.description.equals(that.getDescription()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h$ = 1;
|
||||
h$ *= 1000003;
|
||||
h$ ^= key.hashCode();
|
||||
h$ *= 1000003;
|
||||
h$ ^= defaultValue ? 1231 : 1237;
|
||||
h$ *= 1000003;
|
||||
h$ ^= description.hashCode();
|
||||
return h$;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
|
||||
* This is useful if we want to be able to control this flag from another process.
|
||||
*/
|
||||
public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
|
||||
private ContentResolver contentResolver;
|
||||
|
||||
ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
|
||||
new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
superInitialize(context);
|
||||
}});
|
||||
superInitialize(context);
|
||||
}
|
||||
|
||||
private void superInitialize(Context context) {
|
||||
super.initialize(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStorage(Context context, boolean value) {
|
||||
if (contentResolver == null) {
|
||||
return;
|
||||
}
|
||||
Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean getFromStorage(Context context, boolean defaultValue) {
|
||||
if (contentResolver == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
149
src/com/android/uiuios/config/FlagTogglerPrefUi.java
Normal file
149
src/com/android/uiuios/config/FlagTogglerPrefUi.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.config;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Process;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.uiuios.R;
|
||||
import com.android.uiuios.config.BaseFlags.TogglableFlag;
|
||||
|
||||
import androidx.preference.PreferenceDataStore;
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
/**
|
||||
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
|
||||
*/
|
||||
public final class FlagTogglerPrefUi {
|
||||
|
||||
private static final String TAG = "FlagTogglerPrefFrag";
|
||||
|
||||
private final PreferenceFragment mFragment;
|
||||
private final Context mContext;
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
|
||||
private final PreferenceDataStore mDataStore = new PreferenceDataStore() {
|
||||
|
||||
@Override
|
||||
public void putBoolean(String key, boolean value) {
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
if (flag.getKey().equals(key)) {
|
||||
boolean prevValue = flag.get();
|
||||
flag.updateStorage(mContext, value);
|
||||
updateMenu();
|
||||
if (flag.get() != prevValue) {
|
||||
Toast.makeText(mContext, "Flag applied", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
if (flag.getKey().equals(key)) {
|
||||
return flag.getFromStorage(mContext, defaultValue);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
public FlagTogglerPrefUi(PreferenceFragment fragment) {
|
||||
mFragment = fragment;
|
||||
mContext = fragment.getActivity();
|
||||
mSharedPreferences = mContext.getSharedPreferences(
|
||||
FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public void applyTo(PreferenceGroup parent) {
|
||||
// For flag overrides we only want to store when the engineer chose to override the
|
||||
// flag with a different value than the default. That way, when we flip flags in
|
||||
// future, engineers will pick up the new value immediately. To accomplish this, we use a
|
||||
// custom preference data store.
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
SwitchPreference switchPreference = new SwitchPreference(mContext);
|
||||
switchPreference.setKey(flag.getKey());
|
||||
switchPreference.setDefaultValue(flag.getDefaultValue());
|
||||
switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
|
||||
switchPreference.setTitle(flag.getKey());
|
||||
updateSummary(switchPreference, flag);
|
||||
switchPreference.setPreferenceDataStore(mDataStore);
|
||||
parent.addPreference(switchPreference);
|
||||
}
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the summary to show the description and whether the flag overrides the default value.
|
||||
*/
|
||||
private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
|
||||
String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
|
||||
String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
|
||||
switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
|
||||
switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.getDescription()));
|
||||
}
|
||||
|
||||
private void updateMenu() {
|
||||
mFragment.setHasOptionsMenu(anyChanged());
|
||||
mFragment.getActivity().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
public void onCreateOptionsMenu(Menu menu) {
|
||||
if (anyChanged()) {
|
||||
menu.add(0, R.id.menu_apply_flags, 0, "Apply")
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
public void onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_apply_flags) {
|
||||
mSharedPreferences.edit().commit();
|
||||
Log.e(TAG,
|
||||
"Killing launcher process " + Process.myPid() + " to apply new flag values");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
if (anyChanged()) {
|
||||
Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
|
||||
return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
|
||||
}
|
||||
|
||||
private boolean anyChanged() {
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user