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);
+}