diff options
author | Tony Wickham <twickham@google.com> | 2018-10-05 15:11:21 -0700 |
---|---|---|
committer | Tony Wickham <twickham@google.com> | 2018-10-10 14:02:56 -0700 |
commit | 76cce29d66b2c3bd411d7b0f27e350cdb6fb6575 (patch) | |
tree | 683fb9fcf45318d709927ed666c81b3a55d6f021 | |
parent | 4548a7d48d431aa960bf81494351662a4146ceac (diff) | |
download | packages_apps_Trebuchet-76cce29d66b2c3bd411d7b0f27e350cdb6fb6575.tar.gz packages_apps_Trebuchet-76cce29d66b2c3bd411d7b0f27e350cdb6fb6575.tar.bz2 packages_apps_Trebuchet-76cce29d66b2c3bd411d7b0f27e350cdb6fb6575.zip |
Add plugin support
- Add libs/plugin_core.jar
- Include plugin_core in Launcher3 build (it is already present other
builds as part of the updated shared lib)
- Add PluginEnablerImpl that uses SharedPrefs to enable/disable plugin
components
- Add src_plugins, where plugin interfaces will live. It has a build
rule to create a jar that plugin projects will depend on.
- Copy PluginPreferencesFragment from sysui but using our implementation
for PluginEnabler
Bug: 115877296
Change-Id: I3db54677eaceb10f92018c0f9d18920ad9ffac39
-rw-r--r-- | Android.mk | 31 | ||||
-rw-r--r-- | libs/plugin_core.jar | bin | 0 -> 5057 bytes | |||
-rw-r--r-- | quickstep/libs/sysui_shared.jar | bin | 142780 -> 1576098 bytes | |||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java | 45 | ||||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java | 47 | ||||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java | 52 | ||||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java | 217 | ||||
-rw-r--r-- | res/layout/switch_preference_with_settings.xml | 46 | ||||
-rw-r--r-- | src_plugins/README.md | 3 | ||||
-rw-r--r-- | src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java | 36 |
10 files changed, 477 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk index 727c541c4..b8e6c858f 100644 --- a/Android.mk +++ b/Android.mk @@ -28,6 +28,35 @@ LOCAL_UNINSTALLABLE_MODULE := true LOCAL_SDK_VERSION := current include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := libPluginCore +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := libs/plugin_core.jar +LOCAL_UNINSTALLABLE_MODULE := true +LOCAL_SDK_VERSION := current +include $(BUILD_PREBUILT) + +# +# Build rule for plugin lib (needed to write a plugin). +# +include $(CLEAR_VARS) +LOCAL_USE_AAPT2 := true +LOCAL_AAPT2_ONLY := true +LOCAL_MODULE_TAGS := optional + +LOCAL_STATIC_JAVA_LIBRARIES := libPluginCore + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src_plugins) + +LOCAL_SDK_VERSION := current +LOCAL_MIN_SDK_VERSION := 28 +LOCAL_MODULE := LauncherPluginLib +LOCAL_PRIVILEGED_MODULE := true + +include $(BUILD_STATIC_JAVA_LIBRARY) + # # Build rule for Launcher3 dependencies lib. # @@ -40,6 +69,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.recyclerview_recyclerview \ androidx.dynamicanimation_dynamicanimation +LOCAL_STATIC_JAVA_LIBRARIES := libPluginCore + LOCAL_SRC_FILES := \ $(call all-proto-files-under, protos) \ $(call all-proto-files-under, proto_overrides) diff --git a/libs/plugin_core.jar b/libs/plugin_core.jar Binary files differnew file mode 100644 index 000000000..dd27f86fa --- /dev/null +++ b/libs/plugin_core.jar diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar Binary files differindex 7ff664d41..c5a7c051e 100644 --- a/quickstep/libs/sysui_shared.jar +++ b/quickstep/libs/sysui_shared.jar diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java new file mode 100644 index 000000000..e9fac26bd --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java @@ -0,0 +1,45 @@ +/* + * 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.uioverrides.plugins; + +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; + +import com.android.launcher3.Utilities; +import com.android.systemui.shared.plugins.PluginEnabler; + +public class PluginEnablerImpl implements PluginEnabler { + + final private SharedPreferences mSharedPrefs; + + public PluginEnablerImpl(Context context) { + mSharedPrefs = Utilities.getDevicePrefs(context); + } + + @Override + public void setEnabled(ComponentName component, boolean enabled) { + mSharedPrefs.edit().putBoolean(toPrefString(component), enabled).apply(); + } + + @Override + public boolean isEnabled(ComponentName component) { + return mSharedPrefs.getBoolean(toPrefString(component), true); + } + + private String toPrefString(ComponentName component) { + return "PLUGIN_ENABLED_" + component.flattenToString(); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java new file mode 100644 index 000000000..8a6aa050c --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java @@ -0,0 +1,47 @@ +/* + * 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.uioverrides.plugins; + +import android.content.Context; +import android.os.Looper; + +import com.android.launcher3.LauncherModel; +import com.android.systemui.shared.plugins.PluginEnabler; +import com.android.systemui.shared.plugins.PluginInitializer; + +public class PluginInitializerImpl implements PluginInitializer { + @Override + public Looper getBgLooper() { + return LauncherModel.getWorkerLooper(); + } + + @Override + public void onPluginManagerInit() { + } + + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[0]; + } + + @Override + public PluginEnabler getPluginEnabler(Context context) { + return new PluginEnablerImpl(context); + } + + @Override + public void handleWtfs() { + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java new file mode 100644 index 000000000..ca12951c7 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java @@ -0,0 +1,52 @@ +/* + * 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.uioverrides.plugins; + +import android.content.Context; + +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.shared.plugins.PluginEnabler; +import com.android.systemui.shared.plugins.PluginInitializer; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManagerImpl; + +public class PluginManagerWrapper { + + public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE = + new MainThreadInitializedObject<>(PluginManagerWrapper::new); + + private final PluginManager mPluginManager; + private final PluginEnabler mPluginEnabler; + + private PluginManagerWrapper(Context c) { + PluginInitializer pluginInitializer = new PluginInitializerImpl(); + mPluginManager = new PluginManagerImpl(c, pluginInitializer); + mPluginEnabler = pluginInitializer.getPluginEnabler(c); + } + + PluginEnabler getPluginEnabler() { + return mPluginEnabler; + } + + public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) { + mPluginManager.addPluginListener(listener, pluginClass); + } + + public void removePluginListener(PluginListener<? extends Plugin> listener) { + mPluginManager.removePluginListener(listener); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java new file mode 100644 index 000000000..3da4f8427 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java @@ -0,0 +1,217 @@ +/* + * 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.uioverrides.plugins; + +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.Bundle; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.view.View; + +import com.android.launcher3.R; +import com.android.systemui.shared.plugins.PluginEnabler; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginPrefs; + +import java.util.List; +import java.util.Set; + +/** + * This class is copied from System UI Tuner, except using our PluginEnablerImpl. The reason we + * can't share a common base class in the shared lib is because the androidx preference dependency + * interferes with our recyclerview and fragment dependencies. + */ +public class PluginPreferencesFragment extends PreferenceFragment { + public static final String ACTION_PLUGIN_SETTINGS + = "com.android.systemui.action.PLUGIN_SETTINGS"; + + private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN"; + + private PluginPrefs mPluginPrefs; + private PluginEnabler mPluginEnabler; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + 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(mReceiver, filter); + filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); + getContext().registerReceiver(mReceiver, filter); + + mPluginEnabler = PluginManagerWrapper.INSTANCE.get(getContext()).getPluginEnabler(); + loadPrefs(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + getContext().unregisterReceiver(mReceiver); + } + + private void loadPrefs() { + PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext()); + screen.setOrderingAsAdded(false); + Context prefContext = getContext(); + mPluginPrefs = new PluginPrefs(getContext()); + PackageManager pm = getContext().getPackageManager(); + + Set<String> pluginActions = mPluginPrefs.getPluginList(); + 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); + apps.forEach(app -> { + if (!plugins.containsKey(app.packageName)) return; + SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler); + pref.setSummary("Plugins: " + toString(plugins.get(app.packageName))); + screen.addPreference(pref); + }); + setPreferenceScreen(screen); + } + + 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 final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + loadPrefs(); + } + }; + + private static class PluginPreference extends SwitchPreference { + private final boolean mHasSettings; + private final PackageInfo mInfo; + private final PluginEnabler mPluginEnabler; + + public PluginPreference(Context prefContext, PackageInfo info, PluginEnabler 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 isPluginEnabled() { + for (int i = 0; i < mInfo.services.length; i++) { + ComponentName componentName = new ComponentName(mInfo.packageName, + mInfo.services[i].name); + if (!mPluginEnabler.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 (mPluginEnabler.isEnabled(componentName) != isEnabled) { + mPluginEnabler.setEnabled(componentName, isEnabled); + shouldSendBroadcast = true; + } + } + if (shouldSendBroadcast) { + final String pkg = mInfo.packageName; + final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED, + pkg != null ? Uri.fromParts("package", pkg, null) : null); + getContext().sendBroadcast(intent); + } + setChecked(isEnabled); + return true; + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + view.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE + : View.GONE); + view.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE + : View.GONE); + view.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))); + } + }); + view.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/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml new file mode 100644 index 000000000..d3195610d --- /dev/null +++ b/res/layout/switch_preference_with_settings.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <ImageView + android:id="@+id/settings" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_setting" + android:tint="@android:color/black" + android:padding="12dp" + android:background="?android:attr/selectableItemBackgroundBorderless" /> + + <View + android:id="@+id/divider" + android:layout_width="1dp" + android:layout_height="30dp" + android:layout_marginEnd="8dp" + android:background="?android:attr/listDivider" /> + + <!-- Note: seems we need focusable="false" and clickable="false" when moving to androidx --> + <Switch + android:id="@android:id/switch_widget" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@null" /> +</LinearLayout>
\ No newline at end of file diff --git a/src_plugins/README.md b/src_plugins/README.md new file mode 100644 index 000000000..c7ce1daca --- /dev/null +++ b/src_plugins/README.md @@ -0,0 +1,3 @@ +This directory contains plugin interfaces that launcher listens for and plugins implement. In other words, these are the hooks that specify what plugins launcher currently supports. + +Details about how to create a new plugin interface, or to use existing interfaces to write a plugin can be found at go/gnl/plugins. diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java new file mode 100644 index 000000000..fcb2abe3e --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java @@ -0,0 +1,36 @@ +/* + * 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.uioverrides.plugins; + +import android.content.Context; + +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; + +public class PluginManagerWrapper { + + public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE = + new MainThreadInitializedObject<>(PluginManagerWrapper::new); + + private PluginManagerWrapper(Context c) { + } + + public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) { + } + + public void removePluginListener(PluginListener<? extends Plugin> listener) { + } +} |