diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2018-10-24 16:20:37 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2018-10-25 13:07:42 -0700 |
commit | eae0514f1a8bcb9d498e146e7de78ddc187cbb17 (patch) | |
tree | f61aac6d7bc4871e7c6523a82bc594099023ec4a /src | |
parent | 0d9275deba38a5f650b2323513bbdd5a5a823055 (diff) | |
download | android_packages_apps_Trebuchet-eae0514f1a8bcb9d498e146e7de78ddc187cbb17.tar.gz android_packages_apps_Trebuchet-eae0514f1a8bcb9d498e146e7de78ddc187cbb17.tar.bz2 android_packages_apps_Trebuchet-eae0514f1a8bcb9d498e146e7de78ddc187cbb17.zip |
Moving Plugin and feature flags UI to androidX
> Combining both preference into a single screen
> Adding plugin preference screen so quickstep build
Change-Id: I3d2ff3ebdac3c774659e79ecb81be6f75f765eb0
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/launcher3/config/FlagTogglerPrefUi.java (renamed from src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java) | 118 | ||||
-rw-r--r-- | src/com/android/launcher3/settings/DeveloperOptionsFragment.java | 287 | ||||
-rw-r--r-- | src/com/android/launcher3/settings/SettingsActivity.java | 9 |
3 files changed, 360 insertions, 54 deletions
diff --git a/src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java index 0a1fd2fe0..d3be51dda 100644 --- a/src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java +++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java @@ -18,103 +18,117 @@ package com.android.launcher3.config; import android.content.Context; import android.content.SharedPreferences; -import android.os.Bundle; import android.os.Process; -import android.preference.PreferenceDataStore; -import android.preference.PreferenceFragment; -import android.preference.SwitchPreference; import android.util.Log; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast; import com.android.launcher3.R; import com.android.launcher3.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 FlagTogglerPreferenceFragment extends PreferenceFragment { +public final class FlagTogglerPrefUi { + private static final String TAG = "FlagTogglerPrefFrag"; - private SharedPreferences mSharedPreferences; - private MenuItem saveButton; + private final PreferenceFragment mFragment; + private final Context mContext; + private final SharedPreferences mSharedPreferences; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.flag_preferences); - mSharedPreferences = getContext().getSharedPreferences( + private final PreferenceDataStore mDataStore = new PreferenceDataStore() { + + @Override + public void putBoolean(String key, boolean value) { + for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) { + if (flag.getKey().equals(key)) { + if (value == flag.getDefaultValue()) { + mSharedPreferences.edit().remove(key).apply(); + } else { + mSharedPreferences.edit().putBoolean(key, value).apply(); + } + updateMenu(); + } + } + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return mSharedPreferences.getBoolean(key, defValue); + } + }; + + 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. - getPreferenceManager().setPreferenceDataStore(new PreferenceDataStore() { - @Override - public void putBoolean(String key, boolean value) { - for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) { - if (flag.getKey().equals(key)) { - if (value == flag.getDefaultValue()) { - mSharedPreferences.edit().remove(key).apply(); - } else { - mSharedPreferences.edit().putBoolean(key, value).apply(); - } - } - } - } - }); - for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) { - SwitchPreference switchPreference = new SwitchPreference(getContext()); + SwitchPreference switchPreference = new SwitchPreference(mContext); switchPreference.setKey(flag.getKey()); switchPreference.setDefaultValue(flag.getDefaultValue()); switchPreference.setChecked(getFlagStateFromSharedPrefs(flag)); switchPreference.setTitle(flag.getKey()); switchPreference.setSummaryOn(flag.getDefaultValue() ? "" : "overridden"); switchPreference.setSummaryOff(flag.getDefaultValue() ? "overridden" : ""); - getPreferenceScreen().addPreference(switchPreference); + switchPreference.setPreferenceDataStore(mDataStore); + parent.addPreference(switchPreference); } - setHasOptionsMenu(true); + updateMenu(); } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - saveButton = menu.add("Apply"); - saveButton.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + private void updateMenu() { + mFragment.setHasOptionsMenu(anyChanged()); + mFragment.getActivity().invalidateOptionsMenu(); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item == saveButton) { + 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); } - return super.onOptionsItemSelected(item); } - @Override public void onStop() { - boolean anyChanged = false; - for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) { - anyChanged = anyChanged || - getFlagStateFromSharedPrefs(flag) != flag.get(); - } - - if (anyChanged) { - Toast.makeText( - getContext(), - "Flag won't be applied until you restart launcher", + if (anyChanged()) { + Toast.makeText(mContext, "Flag won't be applied until you restart launcher", Toast.LENGTH_LONG).show(); } - super.onStop(); } private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) { - return mSharedPreferences.getBoolean(flag.getKey(), flag.getDefaultValue()); + return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue()); + } + + private boolean anyChanged() { + for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) { + if (getFlagStateFromSharedPrefs(flag) != flag.get()) { + return true; + } + } + return false; } }
\ No newline at end of file diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java new file mode 100644 index 000000000..a9242f94e --- /dev/null +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -0,0 +1,287 @@ +/* + * 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.launcher3.settings; + +import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED; +import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey; + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; + +import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.config.FlagTogglerPrefUi; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; + +import java.util.List; +import java.util.Set; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceDataStore; +import androidx.preference.PreferenceFragment; +import androidx.preference.PreferenceScreen; +import androidx.preference.PreferenceViewHolder; +import androidx.preference.SwitchPreference; + +/** + * Dev-build only UI allowing developers to toggle flag settings and plugins. + * See {@link FeatureFlags}. + */ +@TargetApi(Build.VERSION_CODES.O) +public class DeveloperOptionsFragment extends PreferenceFragment { + + private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS"; + private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN"; + + private final BroadcastReceiver mPluginReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + loadPluginPrefs(); + } + }; + + private PreferenceScreen mPreferenceScreen; + + private PreferenceCategory mPluginsCategory; + private FlagTogglerPrefUi mFlagTogglerPrefUi; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + getContext().registerReceiver(mPluginReceiver, filter); + getContext().registerReceiver(mPluginReceiver, + new IntentFilter(Intent.ACTION_USER_UNLOCKED)); + + mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext()); + setPreferenceScreen(mPreferenceScreen); + + initFlags(); + loadPluginPrefs(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + getContext().unregisterReceiver(mPluginReceiver); + } + + private PreferenceCategory newCategory(String title) { + PreferenceCategory category = new PreferenceCategory(getContext()); + category.setOrder(Preference.DEFAULT_ORDER); + category.setTitle(title); + mPreferenceScreen.addPreference(category); + return category; + } + + private void initFlags() { + if (!FeatureFlags.showFlagTogglerUi(getContext())) { + return; + } + + mFlagTogglerPrefUi = new FlagTogglerPrefUi(this); + mFlagTogglerPrefUi.applyTo(newCategory("Feature flags")); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (mFlagTogglerPrefUi != null) { + mFlagTogglerPrefUi.onCreateOptionsMenu(menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mFlagTogglerPrefUi != null) { + mFlagTogglerPrefUi.onOptionsItemSelected(item); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onStop() { + if (mFlagTogglerPrefUi != null) { + mFlagTogglerPrefUi.onStop(); + } + super.onStop(); + } + + private void loadPluginPrefs() { + if (mPluginsCategory != null) { + mPreferenceScreen.removePreference(mPluginsCategory); + } + if (!PluginManagerWrapper.hasPlugins(getActivity())) { + mPluginsCategory = null; + return; + } + mPluginsCategory = newCategory("Plugins"); + + PluginManagerWrapper manager = PluginManagerWrapper.INSTANCE.get(getContext()); + Context prefContext = getContext(); + PackageManager pm = getContext().getPackageManager(); + + Set<String> pluginActions = manager.getPluginActions(); + ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>(); + for (String action : pluginActions) { + String name = toName(action); + List<ResolveInfo> result = pm.queryIntentServices( + new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS); + for (ResolveInfo info : result) { + String packageName = info.serviceInfo.packageName; + if (!plugins.containsKey(packageName)) { + plugins.put(packageName, new ArraySet<>()); + } + plugins.get(packageName).add(name); + } + } + + List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{PLUGIN_PERMISSION}, + PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES); + PreferenceDataStore enabled = manager.getPluginEnabler(); + apps.forEach(app -> { + if (!plugins.containsKey(app.packageName)) return; + SwitchPreference pref = new PluginPreference(prefContext, app, enabled); + pref.setSummary("Plugins: " + toString(plugins.get(app.packageName))); + mPluginsCategory.addPreference(pref); + }); + } + + private String toString(ArraySet<String> plugins) { + StringBuilder b = new StringBuilder(); + for (String string : plugins) { + if (b.length() != 0) { + b.append(", "); + } + b.append(string); + } + return b.toString(); + } + + private String toName(String action) { + String str = action.replace("com.android.systemui.action.PLUGIN_", ""); + StringBuilder b = new StringBuilder(); + for (String s : str.split("_")) { + if (b.length() != 0) { + b.append(' '); + } + b.append(s.substring(0, 1)); + b.append(s.substring(1).toLowerCase()); + } + return b.toString(); + } + + private static class PluginPreference extends SwitchPreference { + private final boolean mHasSettings; + private final PackageInfo mInfo; + private final PreferenceDataStore mPluginEnabler; + + public PluginPreference(Context prefContext, PackageInfo info, + PreferenceDataStore pluginEnabler) { + super(prefContext); + PackageManager pm = prefContext.getPackageManager(); + mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS) + .setPackage(info.packageName), 0) != null; + mInfo = info; + mPluginEnabler = pluginEnabler; + setTitle(info.applicationInfo.loadLabel(pm)); + setChecked(isPluginEnabled()); + setWidgetLayoutResource(R.layout.switch_preference_with_settings); + } + + private boolean isEnabled(ComponentName cn) { + return mPluginEnabler.getBoolean(pluginEnabledKey(cn), true); + + } + + private boolean isPluginEnabled() { + for (int i = 0; i < mInfo.services.length; i++) { + ComponentName componentName = new ComponentName(mInfo.packageName, + mInfo.services[i].name); + if (!isEnabled(componentName)) { + return false; + } + } + return true; + } + + @Override + protected boolean persistBoolean(boolean isEnabled) { + boolean shouldSendBroadcast = false; + for (int i = 0; i < mInfo.services.length; i++) { + ComponentName componentName = new ComponentName(mInfo.packageName, + mInfo.services[i].name); + + if (isEnabled(componentName) != isEnabled) { + mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled); + shouldSendBroadcast = true; + } + } + if (shouldSendBroadcast) { + final String pkg = mInfo.packageName; + final Intent intent = new Intent(PLUGIN_CHANGED, + pkg != null ? Uri.fromParts("package", pkg, null) : null); + getContext().sendBroadcast(intent); + } + setChecked(isEnabled); + return true; + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE + : View.GONE); + holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE + : View.GONE); + holder.findViewById(R.id.settings).setOnClickListener(v -> { + ResolveInfo result = v.getContext().getPackageManager().resolveActivity( + new Intent(ACTION_PLUGIN_SETTINGS).setPackage( + mInfo.packageName), 0); + if (result != null) { + v.getContext().startActivity(new Intent().setComponent( + new ComponentName(result.activityInfo.packageName, + result.activityInfo.name))); + } + }); + holder.itemView.setOnLongClickListener(v -> { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", mInfo.packageName, null)); + getContext().startActivity(intent); + return true; + }); + } + } +} diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java index 4c022b438..7c158d9f7 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -24,17 +24,16 @@ import static com.android.launcher3.util.SecureSettingsObserver.newNotificationS import android.app.Activity; import android.app.DialogFragment; import android.app.Fragment; -import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; -import android.util.Log; import com.android.launcher3.LauncherFiles; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.IconShapeOverride; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.SecureSettingsObserver; import androidx.preference.ListPreference; @@ -52,6 +51,7 @@ import androidx.recyclerview.widget.RecyclerView; public class SettingsActivity extends Activity implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback { + private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options"; private static final String FLAGS_PREFERENCE_KEY = "flag_toggler"; private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging"; @@ -204,6 +204,11 @@ public class SettingsActivity extends Activity case FLAGS_PREFERENCE_KEY: // Only show flag toggler UI if this build variant implements that. return FeatureFlags.showFlagTogglerUi(getContext()); + + case DEVELOPER_OPTIONS_KEY: + // Show if plugins are enabled or flag UI is enabled. + return FeatureFlags.showFlagTogglerUi(getContext()) || + PluginManagerWrapper.hasPlugins(getContext()); } return true; |