diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml index bc658e4830..662b86ebbe 100644 --- a/res/values-land/dimens.xml +++ b/res/values-land/dimens.xml @@ -15,9 +15,6 @@ --> - - 12% - 58dp 48dp diff --git a/res/values/config.xml b/res/values/config.xml index 2a1f6f7864..0dfed97ce3 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -114,4 +114,7 @@ + + + diff --git a/res/xml/dynamic_resources.xml b/res/xml/dynamic_resources.xml new file mode 100644 index 0000000000..f5d262868c --- /dev/null +++ b/res/xml/dynamic_resources.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java index a9242f94e0..3668313bdc 100644 --- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.settings; +import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; + import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey; @@ -24,28 +26,21 @@ 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.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; 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.util.Pair; 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; @@ -54,6 +49,16 @@ import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; import androidx.preference.SwitchPreference; +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.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * Dev-build only UI allowing developers to toggle flag settings and plugins. * See {@link FeatureFlags}. @@ -154,44 +159,53 @@ public class DeveloperOptionsFragment extends PreferenceFragment { PackageManager pm = getContext().getPackageManager(); Set pluginActions = manager.getPluginActions(); - ArrayMap> plugins = new ArrayMap<>(); + + ArrayMap, ArrayList>> plugins = + new ArrayMap<>(); + + Set pluginPermissionApps = pm.getPackagesHoldingPermissions( + new String[]{PLUGIN_PERMISSION}, MATCH_DISABLED_COMPONENTS) + .stream() + .map(pi -> pi.packageName) + .collect(Collectors.toSet()); + for (String action : pluginActions) { String name = toName(action); List result = pm.queryIntentServices( - new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS); + new Intent(action), MATCH_DISABLED_COMPONENTS); for (ResolveInfo info : result) { String packageName = info.serviceInfo.packageName; - if (!plugins.containsKey(packageName)) { - plugins.put(packageName, new ArraySet<>()); + if (!pluginPermissionApps.contains(packageName)) { + continue; } - plugins.get(packageName).add(name); + + Pair key = Pair.create(packageName, info.serviceInfo.processName); + if (!plugins.containsKey(key)) { + plugins.put(key, new ArrayList<>()); + } + plugins.get(key).add(Pair.create(name, info.serviceInfo)); } } - List 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); + PreferenceDataStore enabler = manager.getPluginEnabler(); + plugins.forEach((key, si) -> { + String packageName = key.first; + List componentNames = si.stream() + .map(p -> new ComponentName(packageName, p.second.name)) + .collect(Collectors.toList()); + if (!componentNames.isEmpty()) { + SwitchPreference pref = new PluginPreference( + prefContext, si.get(0).second.applicationInfo, enabler, componentNames); + pref.setSummary("Plugins: " + + si.stream().map(p -> p.first).collect(Collectors.joining(", "))); + mPluginsCategory.addPreference(pref); + } }); } - private String toString(ArraySet 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_", ""); + 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) { @@ -205,18 +219,20 @@ public class DeveloperOptionsFragment extends PreferenceFragment { private static class PluginPreference extends SwitchPreference { private final boolean mHasSettings; - private final PackageInfo mInfo; private final PreferenceDataStore mPluginEnabler; + private final String mPackageName; + private final List mComponentNames; - public PluginPreference(Context prefContext, PackageInfo info, - PreferenceDataStore pluginEnabler) { + PluginPreference(Context prefContext, ApplicationInfo info, + PreferenceDataStore pluginEnabler, List componentNames) { super(prefContext); PackageManager pm = prefContext.getPackageManager(); mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS) .setPackage(info.packageName), 0) != null; - mInfo = info; + mPackageName = info.packageName; + mComponentNames = componentNames; mPluginEnabler = pluginEnabler; - setTitle(info.applicationInfo.loadLabel(pm)); + setTitle(info.loadLabel(pm)); setChecked(isPluginEnabled()); setWidgetLayoutResource(R.layout.switch_preference_with_settings); } @@ -227,9 +243,7 @@ public class DeveloperOptionsFragment extends PreferenceFragment { } private boolean isPluginEnabled() { - for (int i = 0; i < mInfo.services.length; i++) { - ComponentName componentName = new ComponentName(mInfo.packageName, - mInfo.services[i].name); + for (ComponentName componentName : mComponentNames) { if (!isEnabled(componentName)) { return false; } @@ -240,17 +254,14 @@ public class DeveloperOptionsFragment extends PreferenceFragment { @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); - + for (ComponentName componentName : mComponentNames) { if (isEnabled(componentName) != isEnabled) { mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled); shouldSendBroadcast = true; } } if (shouldSendBroadcast) { - final String pkg = mInfo.packageName; + final String pkg = mPackageName; final Intent intent = new Intent(PLUGIN_CHANGED, pkg != null ? Uri.fromParts("package", pkg, null) : null); getContext().sendBroadcast(intent); @@ -268,8 +279,7 @@ public class DeveloperOptionsFragment extends PreferenceFragment { : View.GONE); holder.findViewById(R.id.settings).setOnClickListener(v -> { ResolveInfo result = v.getContext().getPackageManager().resolveActivity( - new Intent(ACTION_PLUGIN_SETTINGS).setPackage( - mInfo.packageName), 0); + new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName), 0); if (result != null) { v.getContext().startActivity(new Intent().setComponent( new ComponentName(result.activityInfo.packageName, @@ -278,7 +288,7 @@ public class DeveloperOptionsFragment extends PreferenceFragment { }); holder.itemView.setOnLongClickListener(v -> { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.fromParts("package", mInfo.packageName, null)); + intent.setData(Uri.fromParts("package", mPackageName, null)); getContext().startActivity(intent); return true; }); diff --git a/src/com/android/launcher3/util/DynamicResource.java b/src/com/android/launcher3/util/DynamicResource.java new file mode 100644 index 0000000000..8a75767f42 --- /dev/null +++ b/src/com/android/launcher3/util/DynamicResource.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 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.util; + +import android.content.Context; + +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.FractionRes; +import androidx.annotation.IntegerRes; + +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.ResourceProvider; + +/** + * Utility class to support customizing resource values using plugins + * + * To load resources, call + * DynamicResource.provider(context).getInt(resId) or any other supported methods + * + * To allow customization for a particular resource, add them to dynamic_resources.xml + */ +public class DynamicResource implements ResourceProvider, PluginListener { + + private static final MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(DynamicResource::new); + + private final Context mContext; + private ResourceProvider mPlugin; + + private DynamicResource(Context context) { + mContext = context; + PluginManagerWrapper.INSTANCE.get(context).addPluginListener(this, + ResourceProvider.class, false /* allowedMultiple */); + } + + @Override + public int getInt(@IntegerRes int resId) { + return mContext.getResources().getInteger(resId); + } + + @Override + public float getFraction(@FractionRes int resId) { + return mContext.getResources().getFraction(resId, 1, 1); + } + + @Override + public float getDimension(@DimenRes int resId) { + return mContext.getResources().getDimension(resId); + } + + @Override + public int getColor(@ColorRes int resId) { + return mContext.getResources().getColor(resId, null); + } + + @Override + public void onPluginConnected(ResourceProvider plugin, Context context) { + mPlugin = plugin; + } + + @Override + public void onPluginDisconnected(ResourceProvider plugin) { + mPlugin = null; + } + + /** + * Returns the currently active or default provider + */ + public static ResourceProvider provider(Context context) { + DynamicResource dr = DynamicResource.INSTANCE.get(context); + ResourceProvider plugin = dr.mPlugin; + return plugin == null ? dr : plugin; + } +} diff --git a/src_plugins/com/android/systemui/plugins/ResourceProvider.java b/src_plugins/com/android/systemui/plugins/ResourceProvider.java new file mode 100644 index 0000000000..eaed9e7f1c --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/ResourceProvider.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 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.systemui.plugins; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Plugin to support customizing resource + */ +@ProvidesInterface(action = ResourceProvider.ACTION, version = ResourceProvider.VERSION) +public interface ResourceProvider extends Plugin { + String ACTION = "com.android.launcher3.action.PLUGIN_DYNAMIC_RESOURCE"; + int VERSION = 1; + + /** + * @see android.content.res.Resources#getInteger(int) + */ + int getInt(int resId); + + /** + * @see android.content.res.Resources#getFraction(int, int, int) + */ + float getFraction(int resId); + + /** + * @see android.content.res.Resources#getDimension(int) + */ + float getDimension(int resId); + + /** + * @see android.content.res.Resources#getColor(int) + */ + int getColor(int resId); +}