summaryrefslogtreecommitdiff
path: root/packages/SystemUI/shared/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/shared/src')
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java25
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java40
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java441
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java60
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java401
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java61
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java148
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java154
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java124
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java236
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java213
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java417
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java51
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java27
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java71
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java60
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java395
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java229
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java294
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java136
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java16
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java65
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java63
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java64
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl)19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java45
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java40
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java31
40 files changed, 1577 insertions, 2471 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
new file mode 100644
index 000000000000..74fd13f9564e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
@@ -0,0 +1,25 @@
+/*
+ * 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.systemui.shared.plugins;
+
+import android.content.ComponentName;
+
+/**
+ * Enables and disables plugins.
+ */
+public interface PluginEnabler {
+ void setEnabled(ComponentName component, boolean enabled);
+ boolean isEnabled(ComponentName component);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
new file mode 100644
index 000000000000..c3815e4cee78
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.systemui.shared.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * Provides necessary components for initializing {@link PluginManagerImpl}.
+ */
+public interface PluginInitializer {
+
+ Looper getBgLooper();
+
+ /**
+ * Called from the bg looper during initialization of {@link PluginManagerImpl}.
+ */
+ void onPluginManagerInit();
+
+ String[] getWhitelistedPlugins(Context context);
+
+ PluginEnabler getPluginEnabler(Context context);
+
+ /**
+ * Called from {@link PluginManagerImpl#handleWtfs()}.
+ */
+ void handleWtfs();
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
new file mode 100644
index 000000000000..8e7fadb5c7cb
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2016 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.shared.plugins;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginFragment;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class PluginInstanceManager<T extends Plugin> {
+
+ private static final boolean DEBUG = false;
+
+ private static final String TAG = "PluginInstanceManager";
+ public static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
+
+ private final Context mContext;
+ private final PluginListener<T> mListener;
+ private final String mAction;
+ private final boolean mAllowMultiple;
+ private final VersionInfo mVersion;
+
+ @VisibleForTesting
+ final MainHandler mMainHandler;
+ @VisibleForTesting
+ final PluginHandler mPluginHandler;
+ private final boolean isDebuggable;
+ private final PackageManager mPm;
+ private final PluginManagerImpl mManager;
+ private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
+
+ PluginInstanceManager(Context context, String action, PluginListener<T> listener,
+ boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
+ this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
+ manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
+ }
+
+ @VisibleForTesting
+ PluginInstanceManager(Context context, PackageManager pm, String action,
+ PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
+ PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
+ mMainHandler = new MainHandler(Looper.getMainLooper());
+ mPluginHandler = new PluginHandler(looper);
+ mManager = manager;
+ mContext = context;
+ mPm = pm;
+ mAction = action;
+ mListener = listener;
+ mAllowMultiple = allowMultiple;
+ mVersion = version;
+ mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist));
+ isDebuggable = debuggable;
+ }
+
+ public PluginInfo<T> getPlugin() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("Must be called from UI thread");
+ }
+ mPluginHandler.handleQueryPlugins(null /* All packages */);
+ if (mPluginHandler.mPlugins.size() > 0) {
+ mMainHandler.removeMessages(MainHandler.PLUGIN_CONNECTED);
+ PluginInfo<T> info = mPluginHandler.mPlugins.get(0);
+ PluginPrefs.setHasPlugins(mContext);
+ info.mPlugin.onCreate(mContext, info.mPluginContext);
+ return info;
+ }
+ return null;
+ }
+
+ public void loadAll() {
+ if (DEBUG) Log.d(TAG, "startListening");
+ mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
+ }
+
+ public void destroy() {
+ if (DEBUG) Log.d(TAG, "stopListening");
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+ for (PluginInfo plugin : plugins) {
+ mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
+ plugin.mPlugin).sendToTarget();
+ }
+ }
+
+ public void onPackageRemoved(String pkg) {
+ mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+ }
+
+ public void onPackageChange(String pkg) {
+ mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+ mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
+ }
+
+ public boolean checkAndDisable(String className) {
+ boolean disableAny = false;
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+ for (PluginInfo info : plugins) {
+ if (className.startsWith(info.mPackage)) {
+ disable(info);
+ disableAny = true;
+ }
+ }
+ return disableAny;
+ }
+
+ public boolean disableAll() {
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+ for (int i = 0; i < plugins.size(); i++) {
+ disable(plugins.get(i));
+ }
+ return plugins.size() != 0;
+ }
+
+ private void disable(PluginInfo info) {
+ // Live by the sword, die by the sword.
+ // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
+
+ // If a plugin is detected in the stack of a crash then this will be called for that
+ // plugin, if the plugin causing a crash cannot be identified, they are all disabled
+ // assuming one of them must be bad.
+ if (mWhitelistedPlugins.contains(info.mPackage)) {
+ // Don't disable whitelisted plugins as they are a part of the OS.
+ return;
+ }
+ Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
+ mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
+ false);
+ }
+
+ public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+ for (PluginInfo info : plugins) {
+ if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
+ return info.mVersion != null && info.mVersion.hasClass(cls);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s@%s (action=%s)",
+ getClass().getSimpleName(), hashCode(), mAction);
+ }
+
+ private class MainHandler extends Handler {
+ private static final int PLUGIN_CONNECTED = 1;
+ private static final int PLUGIN_DISCONNECTED = 2;
+
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case PLUGIN_CONNECTED:
+ if (DEBUG) Log.d(TAG, "onPluginConnected");
+ PluginPrefs.setHasPlugins(mContext);
+ PluginInfo<T> info = (PluginInfo<T>) msg.obj;
+ mManager.handleWtfs();
+ if (!(msg.obj instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onCreate as part of the fragment lifecycle.
+ info.mPlugin.onCreate(mContext, info.mPluginContext);
+ }
+ mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
+ break;
+ case PLUGIN_DISCONNECTED:
+ if (DEBUG) Log.d(TAG, "onPluginDisconnected");
+ mListener.onPluginDisconnected((T) msg.obj);
+ if (!(msg.obj instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onDestroy as part of the fragment lifecycle.
+ ((T) msg.obj).onDestroy();
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+ }
+
+ private class PluginHandler extends Handler {
+ private static final int QUERY_ALL = 1;
+ private static final int QUERY_PKG = 2;
+ private static final int REMOVE_PKG = 3;
+
+ private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
+
+ public PluginHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case QUERY_ALL:
+ if (DEBUG) Log.d(TAG, "queryAll " + mAction);
+ for (int i = mPlugins.size() - 1; i >= 0; i--) {
+ PluginInfo<T> plugin = mPlugins.get(i);
+ mListener.onPluginDisconnected(plugin.mPlugin);
+ if (!(plugin.mPlugin instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onDestroy as part of the fragment lifecycle.
+ plugin.mPlugin.onDestroy();
+ }
+ }
+ mPlugins.clear();
+ handleQueryPlugins(null);
+ break;
+ case REMOVE_PKG:
+ String pkg = (String) msg.obj;
+ for (int i = mPlugins.size() - 1; i >= 0; i--) {
+ final PluginInfo<T> plugin = mPlugins.get(i);
+ if (plugin.mPackage.equals(pkg)) {
+ mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
+ plugin.mPlugin).sendToTarget();
+ mPlugins.remove(i);
+ }
+ }
+ break;
+ case QUERY_PKG:
+ String p = (String) msg.obj;
+ if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction);
+ if (mAllowMultiple || (mPlugins.size() == 0)) {
+ handleQueryPlugins(p);
+ } else {
+ if (DEBUG) Log.d(TAG, "Too many of " + mAction);
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ private void handleQueryPlugins(String pkgName) {
+ // This isn't actually a service and shouldn't ever be started, but is
+ // a convenient PM based way to manage our plugins.
+ Intent intent = new Intent(mAction);
+ if (pkgName != null) {
+ intent.setPackage(pkgName);
+ }
+ List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
+ if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
+ if (result.size() > 1 && !mAllowMultiple) {
+ // TODO: Show warning.
+ Log.w(TAG, "Multiple plugins found for " + mAction);
+ return;
+ }
+ for (ResolveInfo info : result) {
+ ComponentName name = new ComponentName(info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ PluginInfo<T> t = handleLoadPlugin(name);
+ if (t == null) continue;
+ mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
+ mPlugins.add(t);
+ }
+ }
+
+ protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
+ // This was already checked, but do it again here to make extra extra sure, we don't
+ // use these on production builds.
+ if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) {
+ // Never ever ever allow these on production builds, they are only for prototyping.
+ Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
+ return null;
+ }
+ if (!mManager.getPluginEnabler().isEnabled(component)) {
+ if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
+ return null;
+ }
+ String pkg = component.getPackageName();
+ String cls = component.getClassName();
+ try {
+ ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
+ // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
+ if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.d(TAG, "Plugin doesn't have permission: " + pkg);
+ return null;
+ }
+ // Create our own ClassLoader so we can use our own code as the parent.
+ ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
+ Context pluginContext = new PluginContextWrapper(
+ mContext.createApplicationContext(info, 0), classLoader);
+ Class<?> pluginClass = Class.forName(cls, true, classLoader);
+ // TODO: Only create the plugin before version check if we need it for
+ // legacy version check.
+ T plugin = (T) pluginClass.newInstance();
+ try {
+ VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
+ if (DEBUG) Log.d(TAG, "createPlugin");
+ return new PluginInfo(pkg, cls, plugin, pluginContext, version);
+ } catch (InvalidVersionException e) {
+ final int icon = mContext.getResources().getIdentifier("tuner", "drawable",
+ mContext.getPackageName());
+ final int color = Resources.getSystem().getIdentifier(
+ "system_notification_accent_color", "color", "android");
+ final Notification.Builder nb = new Notification.Builder(mContext,
+ PluginManager.NOTIFICATION_CHANNEL_ID)
+ .setStyle(new Notification.BigTextStyle())
+ .setSmallIcon(icon)
+ .setWhen(0)
+ .setShowWhen(false)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setColor(mContext.getColor(color));
+ String label = cls;
+ try {
+ label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
+ } catch (NameNotFoundException e2) {
+ }
+ if (!e.isTooNew()) {
+ // Localization not required as this will never ever appear in a user build.
+ nb.setContentTitle("Plugin \"" + label + "\" is too old")
+ .setContentText("Contact plugin developer to get an updated"
+ + " version.\n" + e.getMessage());
+ } else {
+ // Localization not required as this will never ever appear in a user build.
+ nb.setContentTitle("Plugin \"" + label + "\" is too new")
+ .setContentText("Check to see if an OTA is available.\n"
+ + e.getMessage());
+ }
+ Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
+ Uri.parse("package://" + component.flattenToString()));
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
+ mContext.getSystemService(NotificationManager.class)
+ .notifyAsUser(cls, SystemMessage.NOTE_PLUGIN, nb.build(),
+ UserHandle.ALL);
+ // TODO: Warn user.
+ Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
+ + ", expected " + mVersion);
+ return null;
+ }
+ } catch (Throwable e) {
+ Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+ return null;
+ }
+ }
+
+ private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version)
+ throws InvalidVersionException {
+ VersionInfo pv = new VersionInfo().addClass(pluginClass);
+ if (pv.hasVersionInfo()) {
+ version.checkVersion(pv);
+ } else {
+ int fallbackVersion = plugin.getVersion();
+ if (fallbackVersion != version.getDefaultVersion()) {
+ throw new InvalidVersionException("Invalid legacy version", false);
+ }
+ return null;
+ }
+ return pv;
+ }
+ }
+
+ public static class PluginContextWrapper extends ContextWrapper {
+ private final ClassLoader mClassLoader;
+ private LayoutInflater mInflater;
+
+ public PluginContextWrapper(Context base, ClassLoader classLoader) {
+ super(base);
+ mClassLoader = classLoader;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return mClassLoader;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+ if (mInflater == null) {
+ mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
+ }
+ return mInflater;
+ }
+ return getBaseContext().getSystemService(name);
+ }
+ }
+
+ static class PluginInfo<T> {
+ private final Context mPluginContext;
+ private final VersionInfo mVersion;
+ private String mClass;
+ T mPlugin;
+ String mPackage;
+
+ public PluginInfo(String pkg, String cls, T plugin, Context pluginContext,
+ VersionInfo info) {
+ mPlugin = plugin;
+ mClass = cls;
+ mPackage = pkg;
+ mPluginContext = pluginContext;
+ mVersion = info;
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
new file mode 100644
index 000000000000..3f907a8aa348
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.shared.plugins;
+
+import android.text.TextUtils;
+
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+public interface PluginManager {
+
+ String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
+
+ // must be one of the channels created in NotificationChannels.java
+ String NOTIFICATION_CHANNEL_ID = "ALR";
+
+ String[] getWhitelistedPlugins();
+
+ <T extends Plugin> T getOneShotPlugin(Class<T> cls);
+ <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
+
+ <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
+ <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+ boolean allowMultiple);
+ <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class<?> cls);
+ <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class cls, boolean allowMultiple);
+
+ void removePluginListener(PluginListener<?> listener);
+
+ <T> boolean dependsOn(Plugin p, Class<T> cls);
+
+ class Helper {
+ public static <P> String getAction(Class<P> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return info.action();
+ }
+ }
+
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
new file mode 100644
index 000000000000..dc2a9bd5105b
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2016 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.shared.plugins;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+
+import dalvik.system.PathClassLoader;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Arrays;
+import java.util.Map;
+/**
+ * @see Plugin
+ */
+public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {
+
+ private static final String TAG = PluginManagerImpl.class.getSimpleName();
+ static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+
+ private static PluginManager sInstance;
+
+ private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+ = new ArrayMap<>();
+ private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
+ private final ArraySet<String> mOneShotPackages = new ArraySet<>();
+ private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
+ private final Context mContext;
+ private final PluginInstanceManagerFactory mFactory;
+ private final boolean isDebuggable;
+ private final PluginPrefs mPluginPrefs;
+ private final PluginEnabler mPluginEnabler;
+ private final PluginInitializer mPluginInitializer;
+ private ClassLoaderFilter mParentClassLoader;
+ private boolean mListening;
+ private boolean mHasOneShot;
+ private Looper mLooper;
+
+ public PluginManagerImpl(Context context, PluginInitializer initializer) {
+ this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
+ Thread.getUncaughtExceptionPreHandler(), initializer);
+ }
+
+ @VisibleForTesting
+ PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
+ UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) {
+ mContext = context;
+ mFactory = factory;
+ mLooper = initializer.getBgLooper();
+ isDebuggable = debuggable;
+ mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
+ mPluginPrefs = new PluginPrefs(mContext);
+ mPluginEnabler = initializer.getPluginEnabler(mContext);
+ mPluginInitializer = initializer;
+
+ PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
+ defaultHandler);
+ Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
+
+ new Handler(mLooper).post(new Runnable() {
+ @Override
+ public void run() {
+ initializer.onPluginManagerInit();
+ }
+ });
+ }
+
+ public String[] getWhitelistedPlugins() {
+ return mWhitelistedPlugins.toArray(new String[0]);
+ }
+
+ public PluginEnabler getPluginEnabler() {
+ return mPluginEnabler;
+ }
+
+ public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return getOneShotPlugin(info.action(), cls);
+ }
+
+ public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("Must be called from UI thread");
+ }
+ // Passing null causes compiler to complain about incompatible (generic) types.
+ PluginListener<Plugin> dummy = null;
+ PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
+ false, mLooper, cls, this);
+ mPluginPrefs.addAction(action);
+ PluginInfo<T> info = p.getPlugin();
+ if (info != null) {
+ mOneShotPackages.add(info.mPackage);
+ mHasOneShot = true;
+ startListening();
+ return info.mPlugin;
+ }
+ return null;
+ }
+
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+ addPluginListener(listener, cls, false);
+ }
+
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+ boolean allowMultiple) {
+ addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
+ }
+
+ public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class<?> cls) {
+ addPluginListener(action, listener, cls, false);
+ }
+
+ public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class cls, boolean allowMultiple) {
+ mPluginPrefs.addAction(action);
+ PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
+ allowMultiple, mLooper, cls, this);
+ p.loadAll();
+ mPluginMap.put(listener, p);
+ startListening();
+ }
+
+ public void removePluginListener(PluginListener<?> listener) {
+ if (!mPluginMap.containsKey(listener)) return;
+ mPluginMap.remove(listener).destroy();
+ if (mPluginMap.size() == 0) {
+ stopListening();
+ }
+ }
+
+ private void startListening() {
+ if (mListening) return;
+ mListening = true;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(PLUGIN_CHANGED);
+ filter.addAction(DISABLE_PLUGIN);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiver(this, filter);
+ }
+
+ private void stopListening() {
+ // Never stop listening if a one-shot is present.
+ if (!mListening || mHasOneShot) return;
+ mListening = false;
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.loadAll();
+ }
+ } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ ComponentName component = ComponentName.unflattenFromString(
+ uri.toString().substring(10));
+ if (mWhitelistedPlugins.contains(component.getPackageName())) {
+ // Don't disable whitelisted plugins as they are a part of the OS.
+ return;
+ }
+ getPluginEnabler().setEnabled(component, false);
+ mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
+ SystemMessage.NOTE_PLUGIN);
+ } else {
+ Uri data = intent.getData();
+ String pkg = data.getEncodedSchemeSpecificPart();
+ if (mOneShotPackages.contains(pkg)) {
+ int icon = mContext.getResources().getIdentifier("tuner", "drawable",
+ mContext.getPackageName());
+ int color = Resources.getSystem().getIdentifier(
+ "system_notification_accent_color", "color", "android");
+ String label = pkg;
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
+ } catch (NameNotFoundException e) {
+ }
+ // Localization not required as this will never ever appear in a user build.
+ final Notification.Builder nb =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(icon)
+ .setWhen(0)
+ .setShowWhen(false)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setColor(mContext.getColor(color))
+ .setContentTitle("Plugin \"" + label + "\" has updated")
+ .setContentText("Restart SysUI for changes to take effect.");
+ Intent i = new Intent("com.android.systemui.action.RESTART").setData(
+ Uri.parse("package://" + pkg));
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
+ mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
+ SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
+ }
+ if (clearClassLoader(pkg)) {
+ if (Build.IS_ENG) {
+ Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
+ } else {
+ Log.v(TAG, "Reloading " + pkg);
+ }
+ }
+ if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageChange(pkg);
+ }
+ } else {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageRemoved(pkg);
+ }
+ }
+ }
+ }
+
+ public ClassLoader getClassLoader(String sourceDir, String pkg) {
+ if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) {
+ Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir +
+ ", pkg: " + pkg);
+ return null;
+ }
+ if (mClassLoaders.containsKey(pkg)) {
+ return mClassLoaders.get(pkg);
+ }
+ ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+ mClassLoaders.put(pkg, classLoader);
+ return classLoader;
+ }
+
+ private boolean clearClassLoader(String pkg) {
+ return mClassLoaders.remove(pkg) != null;
+ }
+
+ ClassLoader getParentClassLoader() {
+ if (mParentClassLoader == null) {
+ // Lazily load this so it doesn't have any effect on devices without plugins.
+ mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+ "com.android.systemui.plugin");
+ }
+ return mParentClassLoader;
+ }
+
+ public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
+ ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
+ return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
+ }
+
+ public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+ for (int i = 0; i < mPluginMap.size(); i++) {
+ if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void handleWtfs() {
+ mPluginInitializer.handleWtfs();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(String.format(" plugin map (%d):", mPluginMap.size()));
+ for (PluginListener listener: mPluginMap.keySet()) {
+ pw.println(String.format(" %s -> %s",
+ listener, mPluginMap.get(listener)));
+ }
+ }
+
+ @VisibleForTesting
+ public static class PluginInstanceManagerFactory {
+ public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
+ String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
+ Class<?> cls, PluginManagerImpl manager) {
+ return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
+ new VersionInfo().addClass(cls), manager);
+ }
+ }
+
+ // This allows plugins to include any libraries or copied code they want by only including
+ // classes from the plugin library.
+ private static class ClassLoaderFilter extends ClassLoader {
+ private final String mPackage;
+ private final ClassLoader mBase;
+
+ public ClassLoaderFilter(ClassLoader base, String pkg) {
+ super(ClassLoader.getSystemClassLoader());
+ mBase = base;
+ mPackage = pkg;
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+ return mBase.loadClass(name);
+ }
+ }
+
+ private class PluginExceptionHandler implements UncaughtExceptionHandler {
+ private final UncaughtExceptionHandler mHandler;
+
+ private PluginExceptionHandler(UncaughtExceptionHandler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+ if (SystemProperties.getBoolean("plugin.debugging", false)) {
+ mHandler.uncaughtException(thread, throwable);
+ return;
+ }
+ // Search for and disable plugins that may have been involved in this crash.
+ boolean disabledAny = checkStack(throwable);
+ if (!disabledAny) {
+ // We couldn't find any plugins involved in this crash, just to be safe
+ // disable all the plugins, so we can be sure that SysUI is running as
+ // best as possible.
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ disabledAny |= manager.disableAll();
+ }
+ }
+ if (disabledAny) {
+ throwable = new CrashWhilePluginActiveException(throwable);
+ }
+
+ // Run the normal exception handler so we can crash and cleanup our state.
+ mHandler.uncaughtException(thread, throwable);
+ }
+
+ private boolean checkStack(Throwable throwable) {
+ if (throwable == null) return false;
+ boolean disabledAny = false;
+ for (StackTraceElement element : throwable.getStackTrace()) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ disabledAny |= manager.checkAndDisable(element.getClassName());
+ }
+ }
+ return disabledAny | checkStack(throwable.getCause());
+ }
+ }
+
+ public static class CrashWhilePluginActiveException extends RuntimeException {
+ public CrashWhilePluginActiveException(Throwable throwable) {
+ super(throwable);
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
new file mode 100644
index 000000000000..c0c5d7051cea
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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.shared.plugins;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Storage for all plugin actions in SharedPreferences.
+ *
+ * This allows the list of actions that the Tuner needs to search for to be generated
+ * instead of hard coded.
+ */
+public class PluginPrefs {
+
+ private static final String PREFS = "plugin_prefs";
+
+ private static final String PLUGIN_ACTIONS = "actions";
+ private static final String HAS_PLUGINS = "plugins";
+
+ private final Set<String> mPluginActions;
+ private final SharedPreferences mSharedPrefs;
+
+ public PluginPrefs(Context context) {
+ mSharedPrefs = context.getSharedPreferences(PREFS, 0);
+ mPluginActions = new ArraySet<>(mSharedPrefs.getStringSet(PLUGIN_ACTIONS, null));
+ }
+
+ public Set<String> getPluginList() {
+ return mPluginActions;
+ }
+
+ public synchronized void addAction(String action) {
+ if (mPluginActions.add(action)){
+ mSharedPrefs.edit().putStringSet(PLUGIN_ACTIONS, mPluginActions).commit();
+ }
+ }
+
+ public static boolean hasPlugins(Context context) {
+ return context.getSharedPreferences(PREFS, 0).getBoolean(HAS_PLUGINS, false);
+ }
+
+ public static void setHasPlugins(Context context) {
+ context.getSharedPreferences(PREFS, 0).edit().putBoolean(HAS_PLUGINS, true).commit();
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
new file mode 100644
index 000000000000..bb845cd87923
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
@@ -0,0 +1,148 @@
+/*
+ * 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.systemui.shared.plugins;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.plugins.annotations.Dependencies;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.annotations.Requirements;
+import com.android.systemui.plugins.annotations.Requires;
+
+import java.util.function.BiConsumer;
+
+public class VersionInfo {
+
+ private final ArrayMap<Class<?>, Version> mVersions = new ArrayMap<>();
+ private Class<?> mDefault;
+
+ public boolean hasVersionInfo() {
+ return !mVersions.isEmpty();
+ }
+
+ public int getDefaultVersion() {
+ return mVersions.get(mDefault).mVersion;
+ }
+
+ public VersionInfo addClass(Class<?> cls) {
+ if (mDefault == null) {
+ // The legacy default version is from the first class we add.
+ mDefault = cls;
+ }
+ addClass(cls, false);
+ return this;
+ }
+
+ private void addClass(Class<?> cls, boolean required) {
+ if (mVersions.containsKey(cls)) return;
+ ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (provider != null) {
+ mVersions.put(cls, new Version(provider.version(), true));
+ }
+ Requires requires = cls.getDeclaredAnnotation(Requires.class);
+ if (requires != null) {
+ mVersions.put(requires.target(), new Version(requires.version(), required));
+ }
+ Requirements requirements = cls.getDeclaredAnnotation(Requirements.class);
+ if (requirements != null) {
+ for (Requires r : requirements.value()) {
+ mVersions.put(r.target(), new Version(r.version(), required));
+ }
+ }
+ DependsOn depends = cls.getDeclaredAnnotation(DependsOn.class);
+ if (depends != null) {
+ addClass(depends.target(), true);
+ }
+ Dependencies dependencies = cls.getDeclaredAnnotation(Dependencies.class);
+ if (dependencies != null) {
+ for (DependsOn d : dependencies.value()) {
+ addClass(d.target(), true);
+ }
+ }
+ }
+
+ public void checkVersion(VersionInfo plugin) throws InvalidVersionException {
+ final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
+ plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ Version v = versions.remove(aClass);
+ if (v == null) {
+ v = VersionInfo.this.createVersion(aClass);
+ }
+ if (v == null) {
+ throw new InvalidVersionException(aClass.getSimpleName()
+ + " does not provide an interface", false);
+ }
+ if (v.mVersion != version.mVersion) {
+ throw new InvalidVersionException(aClass, v.mVersion < version.mVersion,
+ v.mVersion,
+ version.mVersion);
+ }
+ }
+ });
+ versions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ if (version.mRequired) {
+ throw new InvalidVersionException("Missing required dependency "
+ + aClass.getSimpleName(), false);
+ }
+ }
+ });
+ }
+
+ private Version createVersion(Class<?> cls) {
+ ProvidesInterface provider = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (provider != null) {
+ return new Version(provider.version(), false);
+ }
+ return null;
+ }
+
+ public <T> boolean hasClass(Class<T> cls) {
+ return mVersions.containsKey(cls);
+ }
+
+ public static class InvalidVersionException extends RuntimeException {
+ private final boolean mTooNew;
+
+ public InvalidVersionException(String str, boolean tooNew) {
+ super(str);
+ mTooNew = tooNew;
+ }
+
+ public InvalidVersionException(Class<?> cls, boolean tooNew, int expected, int actual) {
+ super(cls.getSimpleName() + " expected version " + expected + " but had " + actual);
+ mTooNew = tooNew;
+ }
+
+ public boolean isTooNew() {
+ return mTooNew;
+ }
+ }
+
+ private static class Version {
+
+ private final int mVersion;
+ private final boolean mRequired;
+
+ public Version(int version, boolean required) {
+ mVersion = version;
+ mRequired = required;
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index ebfadd881c19..ece2bb9a9507 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -17,7 +17,7 @@
package com.android.systemui.shared.recents;
import android.graphics.Rect;
-import com.android.systemui.shared.system.GraphicBufferCompat;
+import android.view.MotionEvent;
/**
* Temporary callbacks into SystemUI.
@@ -26,9 +26,10 @@ interface ISystemUiProxy {
/**
* Proxies SurfaceControl.screenshotToBuffer().
+ * @Removed
+ * GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
+ * int maxLayer, boolean useIdentityTransform, int rotation) = 0;
*/
- GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
- int maxLayer, boolean useIdentityTransform, int rotation) = 0;
/**
* Begins screen pinning on the provided {@param taskId}.
@@ -60,4 +61,14 @@ interface ISystemUiProxy {
* needed from current value
*/
void setBackButtonAlpha(float alpha, boolean animate) = 8;
+
+ /**
+ * Proxies motion events from the homescreen UI to the status bar. Only called when
+ * swipe down is detected on WORKSPACE. The sender guarantees the following order of events on
+ * the tracking pointer.
+ *
+ * Normal gesture: DOWN, MOVE/POINTER_DOWN/POINTER_UP)*, UP or CANCLE
+ */
+ void onStatusBarMotionEvent(in MotionEvent event) = 9;
+
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
deleted file mode 100644
index a0e7752e13fa..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-/**
- * Background task resource loader
- */
-class BackgroundTaskLoader implements Runnable {
- static String TAG = "BackgroundTaskLoader";
- static boolean DEBUG = false;
-
- private Context mContext;
- private final HandlerThread mLoadThread;
- private final Handler mLoadThreadHandler;
- private final Handler mMainThreadHandler;
-
- private final TaskResourceLoadQueue mLoadQueue;
- private final IconLoader mIconLoader;
-
- private boolean mStarted;
- private boolean mCancelled;
- private boolean mWaitingOnLoadQueue;
-
- private final OnIdleChangedListener mOnIdleChangedListener;
-
- /** Constructor, creates a new loading thread that loads task resources in the background */
- public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
- mLoadQueue = loadQueue;
- mIconLoader = iconLoader;
- mMainThreadHandler = new Handler();
- mOnIdleChangedListener = onIdleChangedListener;
- mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- mLoadThread.start();
- mLoadThreadHandler = new Handler(mLoadThread.getLooper());
- }
-
- /** Restarts the loader thread */
- void start(Context context) {
- mContext = context;
- mCancelled = false;
- if (!mStarted) {
- // Start loading on the load thread
- mStarted = true;
- mLoadThreadHandler.post(this);
- } else {
- // Notify the load thread to start loading again
- synchronized (mLoadThread) {
- mLoadThread.notifyAll();
- }
- }
- }
-
- /** Requests the loader thread to stop after the current iteration */
- void stop() {
- // Mark as cancelled for the thread to pick up
- mCancelled = true;
- // If we are waiting for the load queue for more tasks, then we can just reset the
- // Context now, since nothing is using it
- if (mWaitingOnLoadQueue) {
- mContext = null;
- }
- }
-
- @Override
- public void run() {
- while (true) {
- if (mCancelled) {
- // We have to unset the context here, since the background thread may be using it
- // when we call stop()
- mContext = null;
- // If we are cancelled, then wait until we are started again
- synchronized(mLoadThread) {
- try {
- mLoadThread.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- } else {
- // If we've stopped the loader, then fall through to the above logic to wait on
- // the load thread
- processLoadQueueItem();
-
- // If there are no other items in the list, then just wait until something is added
- if (!mCancelled && mLoadQueue.isEmpty()) {
- synchronized(mLoadQueue) {
- try {
- mWaitingOnLoadQueue = true;
- mMainThreadHandler.post(
- () -> mOnIdleChangedListener.onIdleChanged(true));
- mLoadQueue.wait();
- mMainThreadHandler.post(
- () -> mOnIdleChangedListener.onIdleChanged(false));
- mWaitingOnLoadQueue = false;
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- }
- }
- }
- }
-
- /**
- * This needs to be in a separate method to work around an surprising interpreter behavior:
- * The register will keep the local reference to cachedThumbnailData even if it falls out of
- * scope. Putting it into a method fixes this issue.
- */
- private void processLoadQueueItem() {
- // Load the next item from the queue
- final Task t = mLoadQueue.nextTask();
- if (t != null) {
- final Drawable icon = mIconLoader.getIcon(t);
- if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
- final ThumbnailData thumbnailData =
- ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
- true /* reducedResolution */);
-
- if (!mCancelled) {
- // Notify that the task data has changed
- mMainThreadHandler.post(
- () -> t.notifyTaskDataLoaded(thumbnailData, icon));
- }
- }
- }
-
- interface OnIdleChangedListener {
- void onIdleChanged(boolean idle);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
deleted file mode 100644
index 898d64a1ea1a..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A list of filtered tasks.
- */
-class FilteredTaskList {
-
- private final ArrayList<Task> mTasks = new ArrayList<>();
- private final ArrayList<Task> mFilteredTasks = new ArrayList<>();
- private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>();
- private TaskFilter mFilter;
-
- /** Sets the task filter, and returns whether the set of filtered tasks have changed. */
- boolean setFilter(TaskFilter filter) {
- ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
- mFilter = filter;
- updateFilteredTasks();
- return !prevFilteredTasks.equals(mFilteredTasks);
- }
-
- /** Adds a new task to the task list */
- void add(Task t) {
- mTasks.add(t);
- updateFilteredTasks();
- }
-
- /** Sets the list of tasks */
- void set(List<Task> tasks) {
- mTasks.clear();
- mTasks.addAll(tasks);
- updateFilteredTasks();
- }
-
- /** Removes a task from the base list only if it is in the filtered list */
- boolean remove(Task t) {
- if (mFilteredTasks.contains(t)) {
- boolean removed = mTasks.remove(t);
- updateFilteredTasks();
- return removed;
- }
- return false;
- }
-
- /** Returns the index of this task in the list of filtered tasks */
- int indexOf(Task t) {
- if (t != null && mFilteredTaskIndices.containsKey(t.key)) {
- return mFilteredTaskIndices.get(t.key);
- }
- return -1;
- }
-
- /** Returns the size of the list of filtered tasks */
- int size() {
- return mFilteredTasks.size();
- }
-
- /** Returns whether the filtered list contains this task */
- boolean contains(Task t) {
- return mFilteredTaskIndices.containsKey(t.key);
- }
-
- /** Updates the list of filtered tasks whenever the base task list changes */
- private void updateFilteredTasks() {
- mFilteredTasks.clear();
- if (mFilter != null) {
- // Create a sparse array from task id to Task
- SparseArray<Task> taskIdMap = new SparseArray<>();
- int taskCount = mTasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- taskIdMap.put(t.key.id, t);
- }
-
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- if (mFilter.acceptTask(taskIdMap, t, i)) {
- mFilteredTasks.add(t);
- }
- }
- } else {
- mFilteredTasks.addAll(mTasks);
- }
- updateFilteredTaskIndices();
- }
-
- /** Updates the mapping of tasks to indices. */
- private void updateFilteredTaskIndices() {
- int taskCount = mFilteredTasks.size();
- mFilteredTaskIndices.clear();
- for (int i = 0; i < taskCount; i++) {
- Task t = mFilteredTasks.get(i);
- mFilteredTaskIndices.put(t.key, i);
- }
- }
-
- /** Returns the list of filtered tasks */
- ArrayList<Task> getTasks() {
- return mFilteredTasks;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
deleted file mode 100644
index 24ba99840165..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import static android.os.Process.setThreadPriority;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-
-/**
- * Loader class that loads full-resolution thumbnails when appropriate.
- */
-public class HighResThumbnailLoader implements TaskCallbacks {
-
- private final ActivityManagerWrapper mActivityManager;
-
- @GuardedBy("mLoadQueue")
- private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
- @GuardedBy("mLoadQueue")
- private final ArraySet<Task> mLoadingTasks = new ArraySet<>();
- @GuardedBy("mLoadQueue")
- private boolean mLoaderIdling;
-
- private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
-
- private final Thread mLoadThread;
- private final Handler mMainThreadHandler;
- private final boolean mIsLowRamDevice;
- private boolean mLoading;
- private boolean mVisible;
- private boolean mFlingingFast;
- private boolean mTaskLoadQueueIdle;
-
- public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper,
- boolean isLowRamDevice) {
- mActivityManager = activityManager;
- mMainThreadHandler = new Handler(looper);
- mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
- mLoadThread.start();
- mIsLowRamDevice = isLowRamDevice;
- }
-
- public void setVisible(boolean visible) {
- if (mIsLowRamDevice) {
- return;
- }
- mVisible = visible;
- updateLoading();
- }
-
- public void setFlingingFast(boolean flingingFast) {
- if (mFlingingFast == flingingFast || mIsLowRamDevice) {
- return;
- }
- mFlingingFast = flingingFast;
- updateLoading();
- }
-
- /**
- * Sets whether the other task load queue is idling. Avoid double-loading bitmaps by not
- * starting this queue until the other queue is idling.
- */
- public void setTaskLoadQueueIdle(boolean idle) {
- if (mIsLowRamDevice) {
- return;
- }
- mTaskLoadQueueIdle = idle;
- updateLoading();
- }
-
- @VisibleForTesting
- boolean isLoading() {
- return mLoading;
- }
-
- private void updateLoading() {
- setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle);
- }
-
- private void setLoading(boolean loading) {
- if (loading == mLoading) {
- return;
- }
- synchronized (mLoadQueue) {
- mLoading = loading;
- if (!loading) {
- stopLoading();
- } else {
- startLoading();
- }
- }
- }
-
- @GuardedBy("mLoadQueue")
- private void startLoading() {
- for (int i = mVisibleTasks.size() - 1; i >= 0; i--) {
- Task t = mVisibleTasks.get(i);
- if ((t.thumbnail == null || t.thumbnail.reducedResolution)
- && !mLoadQueue.contains(t) && !mLoadingTasks.contains(t)) {
- mLoadQueue.add(t);
- }
- }
- mLoadQueue.notifyAll();
- }
-
- @GuardedBy("mLoadQueue")
- private void stopLoading() {
- mLoadQueue.clear();
- mLoadQueue.notifyAll();
- }
-
- /**
- * Needs to be called when a task becomes visible. Note that this is different from
- * {@link TaskCallbacks#onTaskDataLoaded} as this method should only be called once when it
- * becomes visible, whereas onTaskDataLoaded can be called multiple times whenever some data
- * has been updated.
- */
- public void onTaskVisible(Task t) {
- t.addCallback(this);
- mVisibleTasks.add(t);
- if ((t.thumbnail == null || t.thumbnail.reducedResolution) && mLoading) {
- synchronized (mLoadQueue) {
- mLoadQueue.add(t);
- mLoadQueue.notifyAll();
- }
- }
- }
-
- /**
- * Needs to be called when a task becomes visible. See {@link #onTaskVisible} why this is
- * different from {@link TaskCallbacks#onTaskDataUnloaded()}
- */
- public void onTaskInvisible(Task t) {
- t.removeCallback(this);
- mVisibleTasks.remove(t);
- synchronized (mLoadQueue) {
- mLoadQueue.remove(t);
- }
- }
-
- @VisibleForTesting
- void waitForLoaderIdle() {
- while (true) {
- synchronized (mLoadQueue) {
- if (mLoadQueue.isEmpty() && mLoaderIdling) {
- return;
- }
- }
- SystemClock.sleep(100);
- }
- }
-
- @Override
- public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
- if (thumbnailData != null && !thumbnailData.reducedResolution) {
- synchronized (mLoadQueue) {
- mLoadQueue.remove(task);
- }
- }
- }
-
- @Override
- public void onTaskDataUnloaded() {
- }
-
- @Override
- public void onTaskWindowingModeChanged() {
- }
-
- private final Runnable mLoader = new Runnable() {
-
- @Override
- public void run() {
- setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND + 1);
- while (true) {
- Task next = null;
- synchronized (mLoadQueue) {
- if (!mLoading || mLoadQueue.isEmpty()) {
- try {
- mLoaderIdling = true;
- mLoadQueue.wait();
- mLoaderIdling = false;
- } catch (InterruptedException e) {
- // Don't care.
- }
- } else {
- next = mLoadQueue.poll();
- if (next != null) {
- mLoadingTasks.add(next);
- }
- }
- }
- if (next != null) {
- loadTask(next);
- }
- }
- }
-
- private void loadTask(Task t) {
- ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id,
- false /* reducedResolution */);
- mMainThreadHandler.post(() -> {
- synchronized (mLoadQueue) {
- mLoadingTasks.remove(t);
- }
- if (mVisibleTasks.contains(t)) {
- t.notifyTaskDataLoaded(thumbnail, t.icon);
- }
- });
- }
- };
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
deleted file mode 100644
index a04a6a3fe0c5..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2014 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.shared.recents.model;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.SparseBooleanArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class stores the loading state as it goes through multiple stages of loading:
- * 1) preloadRawTasks() will load the raw set of recents tasks from the system
- * 2) preloadPlan() will construct a new task stack with all metadata and only icons and
- * thumbnails that are currently in the cache
- * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load
- * options specified, such that we can transition into the Recents activity seamlessly
- */
-public class RecentsTaskLoadPlan {
-
- /** The set of conditions to preload tasks. */
- public static class PreloadOptions {
- public boolean loadTitles = true;
- }
-
- /** The set of conditions to load tasks. */
- public static class Options {
- public int runningTaskId = -1;
- public boolean loadIcons = true;
- public boolean loadThumbnails = false;
- public boolean onlyLoadForCache = false;
- public boolean onlyLoadPausedActivities = false;
- public int numVisibleTasks = 0;
- public int numVisibleTaskThumbnails = 0;
- }
-
- private final Context mContext;
- private final KeyguardManager mKeyguardManager;
-
- private List<ActivityManager.RecentTaskInfo> mRawTasks;
- private TaskStack mStack;
-
- private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray();
-
- public RecentsTaskLoadPlan(Context context) {
- mContext = context;
- mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- }
-
- /**
- * Preloads the list of recent tasks from the system. After this call, the TaskStack will
- * have a list of all the recent tasks with their metadata, not including icons or
- * thumbnails which were not cached and have to be loaded.
- *
- * The tasks will be ordered by:
- * - least-recent to most-recent stack tasks
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
- int currentUserId) {
- Resources res = mContext.getResources();
- ArrayList<Task> allTasks = new ArrayList<>();
- if (mRawTasks == null) {
- mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
- ActivityManager.getMaxRecentTasksStatic(), currentUserId);
-
- // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
- Collections.reverse(mRawTasks);
- }
-
- int taskCount = mRawTasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-
- // Compose the task key
- final ComponentName sourceComponent = t.origActivity != null
- // Activity alias if there is one
- ? t.origActivity
- // The real activity if there is no alias (or the target if there is one)
- : t.realActivity;
- final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
- TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
- sourceComponent, t.userId, t.lastActiveTime);
-
- boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
- boolean isStackTask = !isFreeformTask;
- boolean isLaunchTarget = taskKey.id == runningTaskId;
-
- ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
- if (info == null) {
- continue;
- }
-
- // Load the title, icon, and color
- String title = opts.loadTitles
- ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
- : "";
- String titleDescription = opts.loadTitles
- ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
- : "";
- Drawable icon = isStackTask
- ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
- : null;
- ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
- false /* loadIfNotCached */, false /* storeInCache */);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
- int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
- boolean isSystemApp = (info != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
-
- // TODO: Refactor to not do this every preload
- if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
- mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
- }
- boolean isLocked = mTmpLockedUsers.get(t.userId);
-
- // Add the task to the stack
- Task task = new Task(taskKey, icon,
- thumbnail, title, titleDescription, activityColor, backgroundColor,
- isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
- t.taskDescription, t.resizeMode, t.topActivity, isLocked);
-
- allTasks.add(task);
- }
-
- // Initialize the stacks
- mStack = new TaskStack();
- mStack.setTasks(allTasks, false /* notifyStackChanges */);
- }
-
- /**
- * Called to apply the actual loading based on the specified conditions.
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- public void executePlan(Options opts, RecentsTaskLoader loader) {
- Resources res = mContext.getResources();
-
- // Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- TaskKey taskKey = task.key;
-
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
-
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
- true);
- }
- }
- if (opts.loadThumbnails && isVisibleThumbnail) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
- true /* loadIfNotCached */, true /* storeInCache */);
- }
- }
- }
-
- /**
- * Returns the TaskStack from the preloaded list of recent tasks.
- */
- public TaskStack getTaskStack() {
- return mStack;
- }
-
- /** Returns whether there are any tasks in any stacks. */
- public boolean hasTasks() {
- if (mStack != null) {
- return mStack.getTaskCount() > 0;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
deleted file mode 100644
index 1309a6065c8a..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright (C) 2014 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.shared.recents.model;
-
-import android.app.ActivityManager;
-import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.Trace;
-import android.util.Log;
-import android.util.LruCache;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.Map;
-
-
-/**
- * Recents task loader
- */
-public class RecentsTaskLoader {
- private static final String TAG = "RecentsTaskLoader";
- private static final boolean DEBUG = false;
-
- /** Levels of svelte in increasing severity/austerity. */
- // No svelting.
- public static final int SVELTE_NONE = 0;
- // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
- // caching thumbnails as you scroll.
- public static final int SVELTE_LIMIT_CACHE = 1;
- // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
- // evict all thumbnails when hidden.
- public static final int SVELTE_DISABLE_CACHE = 2;
- // Disable all thumbnail loading.
- public static final int SVELTE_DISABLE_LOADING = 3;
-
- // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
- // for many tasks, which we use to get the activity labels and icons. Unlike the other caches
- // below, this is per-package so we can't invalidate the items in the cache based on the last
- // active time. Instead, we rely on the PackageMonitor to keep us informed whenever a
- // package in the cache has been updated, so that we may remove it.
- private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<String> mActivityLabelCache;
- private final TaskKeyLruCache<String> mContentDescriptionCache;
- private final TaskResourceLoadQueue mLoadQueue;
- private final IconLoader mIconLoader;
- private final BackgroundTaskLoader mLoader;
- private final HighResThumbnailLoader mHighResThumbnailLoader;
- @GuardedBy("this")
- private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
- @GuardedBy("this")
- private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
- private final int mMaxThumbnailCacheSize;
- private final int mMaxIconCacheSize;
- private int mNumVisibleTasksLoaded;
- private int mSvelteLevel;
-
- private int mDefaultTaskBarBackgroundColor;
- private int mDefaultTaskViewBackgroundColor;
-
- private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
- @Override
- public void onEntryEvicted(TaskKey key) {
- if (key != null) {
- mActivityInfoCache.remove(key.getComponent());
- }
- }
- };
-
- public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
- int svelteLevel) {
- mMaxThumbnailCacheSize = maxThumbnailCacheSize;
- mMaxIconCacheSize = maxIconCacheSize;
- mSvelteLevel = svelteLevel;
-
- // Initialize the proxy, cache and loaders
- int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
- mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
- Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
- mLoadQueue = new TaskResourceLoadQueue();
- mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction);
- mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
- mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
- mClearActivityInfoOnEviction);
- mActivityInfoCache = new LruCache<>(numRecentTasks);
-
- mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
- mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader,
- mHighResThumbnailLoader::setTaskLoadQueueIdle);
- }
-
- protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
- LruCache<ComponentName, ActivityInfo> activityInfoCache) {
- return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
- }
-
- /**
- * Sets the default task bar/view colors if none are provided by the app.
- */
- public void setDefaultColors(int defaultTaskBarBackgroundColor,
- int defaultTaskViewBackgroundColor) {
- mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor;
- mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor;
- }
-
- /** Returns the size of the app icon cache. */
- public int getIconCacheSize() {
- return mMaxIconCacheSize;
- }
-
- /** Returns the size of the thumbnail cache. */
- public int getThumbnailCacheSize() {
- return mMaxThumbnailCacheSize;
- }
-
- public HighResThumbnailLoader getHighResThumbnailLoader() {
- return mHighResThumbnailLoader;
- }
-
- /** Preloads recents tasks using the specified plan to store the output. */
- public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
- preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
- }
-
- /** Preloads recents tasks using the specified plan to store the output. */
- public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
- int currentUserId) {
- try {
- Trace.beginSection("preloadPlan");
- plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
- } finally {
- Trace.endSection();
- }
- }
-
- /** Begins loading the heavy task data according to the specified options. */
- public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) {
- if (opts == null) {
- throw new RuntimeException("Requires load options");
- }
- if (opts.onlyLoadForCache && opts.loadThumbnails) {
- // If we are loading for the cache, we'd like to have the real cache only include the
- // visible thumbnails. However, we also don't want to reload already cached thumbnails.
- // Thus, we copy over the current entries into a second cache, and clear the real cache,
- // such that the real cache only contains visible thumbnails.
- mTempCache.copyEntries(mThumbnailCache);
- mThumbnailCache.evictAll();
- }
- plan.executePlan(opts, this);
- mTempCache.evictAll();
- if (!opts.onlyLoadForCache) {
- mNumVisibleTasksLoaded = opts.numVisibleTasks;
- }
- }
-
- /**
- * Acquires the task resource data directly from the cache, loading if necessary.
- */
- public void loadTaskData(Task t) {
- Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
- icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
- mLoadQueue.addTask(t);
- t.notifyTaskDataLoaded(t.thumbnail, icon);
- }
-
- /** Releases the task resource data back into the pool. */
- public void unloadTaskData(Task t) {
- mLoadQueue.removeTask(t);
- t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
- }
-
- /** Completely removes the resource data from the pool. */
- public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
- mLoadQueue.removeTask(t);
- mIconCache.remove(t.key);
- mActivityLabelCache.remove(t.key);
- mContentDescriptionCache.remove(t.key);
- if (notifyTaskDataUnloaded) {
- t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
- }
- }
-
- /**
- * Handles signals from the system, trimming memory when requested to prevent us from running
- * out of memory.
- */
- public synchronized void onTrimMemory(int level) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- // Stop the loader immediately when the UI is no longer visible
- stopLoader();
- mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
- mMaxIconCacheSize / 2));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- // We are leaving recents, so trim the data a bit
- mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
- mActivityInfoCache.trimToSize(Math.max(1,
- ActivityManager.getMaxRecentTasksStatic() / 2));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- // We are going to be low on memory
- mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
- mActivityInfoCache.trimToSize(Math.max(1,
- ActivityManager.getMaxRecentTasksStatic() / 4));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- // We are low on memory, so release everything
- mIconCache.evictAll();
- mActivityInfoCache.evictAll();
- // The cache is small, only clear the label cache when we are critical
- mActivityLabelCache.evictAll();
- mContentDescriptionCache.evictAll();
- mThumbnailCache.evictAll();
- break;
- default:
- break;
- }
- }
-
- public void onPackageChanged(String packageName) {
- // Remove all the cached activity infos for this package. The other caches do not need to
- // be pruned at this time, as the TaskKey expiration checks will flush them next time their
- // cached contents are requested
- Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
- for (ComponentName cn : activityInfoCache.keySet()) {
- if (cn.getPackageName().equals(packageName)) {
- if (DEBUG) {
- Log.d(TAG, "Removing activity info from cache: " + cn);
- }
- mActivityInfoCache.remove(cn);
- }
- }
- }
-
- /**
- * Returns the cached task label if the task key is not expired, updating the cache if it is.
- */
- String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) {
- // Return the task description label if it exists
- if (td != null && td.getLabel() != null) {
- return td.getLabel();
- }
- // Return the cached activity label if it exists
- String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
- // All short paths failed, load the label from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
- if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo,
- taskKey.userId);
- mActivityLabelCache.put(taskKey, label);
- return label;
- }
- // If the activity info does not exist or fails to load, return an empty label for now,
- // but do not cache it
- return "";
- }
-
- /**
- * Returns the cached task content description if the task key is not expired, updating the
- * cache if it is.
- */
- String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) {
- // Return the cached content description if it exists
- String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
-
- // All short paths failed, load the label from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
- if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(
- activityInfo, taskKey.userId, td);
- if (td == null) {
- // Only add to the cache if the task description is null, otherwise, it is possible
- // for the task description to change between calls without the last active time
- // changing (ie. between preloading and Overview starting) which would lead to stale
- // content descriptions
- // TODO: Investigate improving this
- mContentDescriptionCache.put(taskKey, label);
- }
- return label;
- }
- // If the content description does not exist, return an empty label for now, but do not
- // cache it
- return "";
- }
-
- /**
- * Returns the cached task icon if the task key is not expired, updating the cache if it is.
- */
- Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
- boolean loadIfNotCached) {
- return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
- }
-
- /**
- * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
- */
- synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached,
- boolean storeInCache) {
- ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (cached != null) {
- return cached;
- }
-
- cached = mTempCache.getAndInvalidateIfModified(taskKey);
- if (cached != null) {
- mThumbnailCache.put(taskKey, cached);
- return cached;
- }
-
- if (loadIfNotCached) {
- if (mSvelteLevel < SVELTE_DISABLE_LOADING) {
- // Load the thumbnail from the system
- ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
- taskKey.id, true /* reducedResolution */);
- if (thumbnailData.thumbnail != null) {
- if (storeInCache) {
- mThumbnailCache.put(taskKey, thumbnailData);
- }
- return thumbnailData;
- }
- }
- }
-
- // We couldn't load any thumbnail
- return null;
- }
-
- /**
- * Returns the task's primary color if possible, defaulting to the default color if there is
- * no specified primary color.
- */
- int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
- if (td != null && td.getPrimaryColor() != 0) {
- return td.getPrimaryColor();
- }
- return mDefaultTaskBarBackgroundColor;
- }
-
- /**
- * Returns the task's background color if possible.
- */
- int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
- if (td != null && td.getBackgroundColor() != 0) {
- return td.getBackgroundColor();
- }
- return mDefaultTaskViewBackgroundColor;
- }
-
- /**
- * Returns the activity info for the given task key, retrieving one from the system if the
- * task key is expired.
- */
- ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
- return mIconLoader.getAndUpdateActivityInfo(taskKey);
- }
-
- /**
- * Starts loading tasks.
- */
- public void startLoader(Context ctx) {
- mLoader.start(ctx);
- }
-
- /**
- * Stops the task loader and clears all queued, pending task loads.
- */
- private void stopLoader() {
- mLoader.stop();
- mLoadQueue.clearTasks();
- }
-
- public synchronized void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.println(TAG);
- writer.print(prefix); writer.println("Icon Cache");
- mIconCache.dump(innerPrefix, writer);
- writer.print(prefix); writer.println("Thumbnail Cache");
- mThumbnailCache.dump(innerPrefix, writer);
- writer.print(prefix); writer.println("Temp Thumbnail Cache");
- mTempCache.dump(innerPrefix, writer);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index b51004bf6d56..7558efae6c23 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.recents.model;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.content.ComponentName;
import android.content.Intent;
@@ -31,13 +32,14 @@ import java.util.ArrayList;
import java.util.Objects;
/**
- * A task represents the top most task in the system's task stack.
+ * A task in the recent tasks list.
*/
public class Task {
public static final String TAG = "Task";
/* Task callbacks */
+ @Deprecated
public interface TaskCallbacks {
/* Notifies when a task has been bound */
void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
@@ -65,6 +67,21 @@ public class Task {
private int mHashCode;
+ public TaskKey(ActivityManager.RecentTaskInfo t) {
+ ComponentName sourceComponent = t.origActivity != null
+ // Activity alias if there is one
+ ? t.origActivity
+ // The real activity if there is no alias (or the target if there is one)
+ : t.realActivity;
+ this.id = t.taskId;
+ this.windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+ this.baseIntent = t.baseIntent;
+ this.sourceComponent = sourceComponent;
+ this.userId = t.userId;
+ this.lastActiveTime = t.lastActiveTime;
+ updateHashCode();
+ }
+
public TaskKey(int id, int windowingMode, Intent intent,
ComponentName sourceComponent, int userId, long lastActiveTime) {
this.id = id;
@@ -125,6 +142,7 @@ public class Task {
/**
* The temporary sort index in the stack, used when ordering the stack.
*/
+ @Deprecated
public int temporarySortIndexInStack;
/**
@@ -134,6 +152,7 @@ public class Task {
public Drawable icon;
public ThumbnailData thumbnail;
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public String title;
@ViewDebug.ExportedProperty(category="recents")
public String titleDescription;
@@ -142,6 +161,7 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public int colorBackground;
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public boolean useLightOnPrimaryColor;
/**
@@ -153,10 +173,13 @@ public class Task {
* The state isLaunchTarget will be set for the correct task upon launching Recents.
*/
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public boolean isLaunchTarget;
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public boolean isStackTask;
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public boolean isSystemApp;
@ViewDebug.ExportedProperty(category="recents")
public boolean isDockable;
@@ -165,6 +188,7 @@ public class Task {
* Resize mode. See {@link ActivityInfo#resizeMode}.
*/
@ViewDebug.ExportedProperty(category="recents")
+ @Deprecated
public int resizeMode;
@ViewDebug.ExportedProperty(category="recents")
@@ -173,12 +197,31 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public boolean isLocked;
+ @Deprecated
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
public Task() {
// Do nothing
}
+ public Task(TaskKey key) {
+ this.key = key;
+ this.taskDescription = new TaskDescription();
+ }
+
+ public Task(TaskKey key, int colorPrimary, int colorBackground,
+ boolean isDockable, boolean isLocked, TaskDescription taskDescription,
+ ComponentName topActivity) {
+ this.key = key;
+ this.colorPrimary = colorPrimary;
+ this.colorBackground = colorBackground;
+ this.taskDescription = taskDescription;
+ this.isDockable = isDockable;
+ this.isLocked = isLocked;
+ this.topActivity = topActivity;
+ }
+
+ @Deprecated
public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
boolean isStackTask, boolean isSystemApp, boolean isDockable,
@@ -206,6 +249,7 @@ public class Task {
/**
* Copies the metadata from another task, but retains the current callbacks.
*/
+ @Deprecated
public void copyFrom(Task o) {
this.key = o.key;
this.icon = o.icon;
@@ -228,6 +272,7 @@ public class Task {
/**
* Add a callback.
*/
+ @Deprecated
public void addCallback(TaskCallbacks cb) {
if (!mCallbacks.contains(cb)) {
mCallbacks.add(cb);
@@ -237,11 +282,13 @@ public class Task {
/**
* Remove a callback.
*/
+ @Deprecated
public void removeCallback(TaskCallbacks cb) {
mCallbacks.remove(cb);
}
/** Updates the task's windowing mode. */
+ @Deprecated
public void setWindowingMode(int windowingMode) {
key.setWindowingMode(windowingMode);
int callbackCount = mCallbacks.size();
@@ -251,6 +298,7 @@ public class Task {
}
/** Notifies the callback listeners that this task has been loaded */
+ @Deprecated
public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
this.icon = applicationIcon;
this.thumbnail = thumbnailData;
@@ -261,6 +309,7 @@ public class Task {
}
/** Notifies the callback listeners that this task has been unloaded */
+ @Deprecated
public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) {
icon = defaultApplicationIcon;
thumbnail = null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
deleted file mode 100644
index 5f3dcd16e074..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import android.util.SparseArray;
-
-/**
- * An interface for a task filter to query whether a particular task should show in a stack.
- */
-public interface TaskFilter {
- /** Returns whether the filter accepts the specified task */
- boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
index 4bf3500a3405..342cb75b2c14 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
@@ -34,7 +34,7 @@ public abstract class TaskKeyCache<V> {
* Gets a specific entry in the cache with the specified key, regardless of whether the cached
* value is valid or not.
*/
- final V get(TaskKey key) {
+ public final synchronized V get(TaskKey key) {
return getCacheEntry(key.id);
}
@@ -42,7 +42,7 @@ public abstract class TaskKeyCache<V> {
* Returns the value only if the key is valid (has not been updated since the last time it was
* in the cache)
*/
- final V getAndInvalidateIfModified(TaskKey key) {
+ public final synchronized V getAndInvalidateIfModified(TaskKey key) {
TaskKey lastKey = mKeys.get(key.id);
if (lastKey != null) {
if ((lastKey.windowingMode != key.windowingMode) ||
@@ -59,7 +59,7 @@ public abstract class TaskKeyCache<V> {
}
/** Puts an entry in the cache for a specific key. */
- final void put(TaskKey key, V value) {
+ public final synchronized void put(TaskKey key, V value) {
if (key == null || value == null) {
Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
return;
@@ -70,14 +70,14 @@ public abstract class TaskKeyCache<V> {
/** Removes a cache entry for a specific key. */
- final void remove(TaskKey key) {
+ public final synchronized void remove(TaskKey key) {
// Remove the key after the cache value because we need it to make the callback
removeCacheEntry(key.id);
mKeys.remove(key.id);
}
/** Removes all the entries in the cache. */
- final void evictAll() {
+ public final synchronized void evictAll() {
evictAllCache();
mKeys.clear();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
index 0ba2c3bf6e3c..b2c79a4c0a32 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
@@ -58,7 +58,7 @@ public class TaskKeyLruCache<V> extends TaskKeyCache<V> {
}
/** Trims the cache to a specific size */
- final void trimToSize(int cacheSize) {
+ public final void trimToSize(int cacheSize) {
mCache.trimToSize(cacheSize);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java
deleted file mode 100644
index 4408eced3e93..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.io.PrintWriter;
-
-/**
- * Like {@link TaskKeyLruCache}, but without LRU functionality.
- */
-public class TaskKeyStrongCache<V> extends TaskKeyCache<V> {
-
- private static final String TAG = "TaskKeyCache";
-
- private final ArrayMap<Integer, V> mCache = new ArrayMap<>();
-
- final void copyEntries(TaskKeyStrongCache<V> other) {
- for (int i = other.mKeys.size() - 1; i >= 0; i--) {
- TaskKey key = other.mKeys.valueAt(i);
- put(key, other.mCache.get(key.id));
- }
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
- writer.print(prefix); writer.print(TAG);
- writer.print(" numEntries="); writer.print(mKeys.size());
- writer.println();
- int keyCount = mKeys.size();
- for (int i = 0; i < keyCount; i++) {
- writer.print(innerPrefix); writer.println(mKeys.get(mKeys.keyAt(i)));
- }
- }
-
- @Override
- protected V getCacheEntry(int id) {
- return mCache.get(id);
- }
-
- @Override
- protected void putCacheEntry(int id, V value) {
- mCache.put(id, value);
- }
-
- @Override
- protected void removeCacheEntry(int id) {
- mCache.remove(id);
- }
-
- @Override
- protected void evictAllCache() {
- mCache.clear();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
deleted file mode 100644
index fbb6acebc8e0..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.systemui.shared.recents.model;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * A Task load queue
- */
-class TaskResourceLoadQueue {
-
- private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
-
- /** Adds a new task to the load queue */
- void addTask(Task t) {
- if (!mQueue.contains(t)) {
- mQueue.add(t);
- }
- synchronized(this) {
- notifyAll();
- }
- }
-
- /**
- * Retrieves the next task from the load queue, as well as whether we want that task to be
- * force reloaded.
- */
- Task nextTask() {
- return mQueue.poll();
- }
-
- /** Removes a task from the load queue */
- void removeTask(Task t) {
- mQueue.remove(t);
- }
-
- /** Clears all the tasks from the load queue */
- void clearTasks() {
- mQueue.clear();
- }
-
- /** Returns whether the load queue is empty */
- boolean isEmpty() {
- return mQueue.isEmpty();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
deleted file mode 100644
index a36939769477..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2014 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.shared.recents.model;
-
-import android.content.ComponentName;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * The task stack contains a list of multiple tasks.
- */
-public class TaskStack {
-
- private static final String TAG = "TaskStack";
-
- /** Task stack callbacks */
- public interface TaskStackCallbacks {
- /**
- * Notifies when a new task has been added to the stack.
- */
- void onStackTaskAdded(TaskStack stack, Task newTask);
-
- /**
- * Notifies when a task has been removed from the stack.
- */
- void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
- AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved);
-
- /**
- * Notifies when all tasks have been removed from the stack.
- */
- void onStackTasksRemoved(TaskStack stack);
-
- /**
- * Notifies when tasks in the stack have been updated.
- */
- void onStackTasksUpdated(TaskStack stack);
- }
-
- private final ArrayList<Task> mRawTaskList = new ArrayList<>();
- private final FilteredTaskList mStackTaskList = new FilteredTaskList();
- private TaskStackCallbacks mCb;
-
- public TaskStack() {
- // Ensure that we only show stack tasks
- mStackTaskList.setFilter((taskIdMap, t, index) -> t.isStackTask);
- }
-
- /** Sets the callbacks for this task stack. */
- public void setCallbacks(TaskStackCallbacks cb) {
- mCb = cb;
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
- removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved) {
- if (mStackTaskList.contains(t)) {
- mStackTaskList.remove(t);
- Task newFrontMostTask = getFrontMostTask();
- if (mCb != null) {
- // Notify that a task has been removed
- mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
- fromDockGesture, dismissRecentsIfAllRemoved);
- }
- }
- mRawTaskList.remove(t);
- }
-
- /**
- * Removes all tasks from the stack.
- */
- public void removeAllTasks(boolean notifyStackChanges) {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task t = tasks.get(i);
- mStackTaskList.remove(t);
- mRawTaskList.remove(t);
- }
- if (mCb != null && notifyStackChanges) {
- // Notify that all tasks have been removed
- mCb.onStackTasksRemoved(this);
- }
- }
-
-
- /**
- * @see #setTasks(List, boolean)
- */
- public void setTasks(TaskStack stack, boolean notifyStackChanges) {
- setTasks(stack.mRawTaskList, notifyStackChanges);
- }
-
- /**
- * Sets a few tasks in one go, without calling any callbacks.
- *
- * @param tasks the new set of tasks to replace the current set.
- * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
- */
- public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
- // Compute a has set for each of the tasks
- ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
- ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
- ArrayList<Task> addedTasks = new ArrayList<>();
- ArrayList<Task> removedTasks = new ArrayList<>();
- ArrayList<Task> allTasks = new ArrayList<>();
-
- // Disable notifications if there are no callbacks
- if (mCb == null) {
- notifyStackChanges = false;
- }
-
- // Remove any tasks that no longer exist
- int taskCount = mRawTaskList.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task task = mRawTaskList.get(i);
- if (!newTasksMap.containsKey(task.key)) {
- if (notifyStackChanges) {
- removedTasks.add(task);
- }
- }
- }
-
- // Add any new tasks
- taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task newTask = tasks.get(i);
- Task currentTask = currentTasksMap.get(newTask.key);
- if (currentTask == null && notifyStackChanges) {
- addedTasks.add(newTask);
- } else if (currentTask != null) {
- // The current task has bound callbacks, so just copy the data from the new task
- // state and add it back into the list
- currentTask.copyFrom(newTask);
- newTask = currentTask;
- }
- allTasks.add(newTask);
- }
-
- // Sort all the tasks to ensure they are ordered correctly
- for (int i = allTasks.size() - 1; i >= 0; i--) {
- allTasks.get(i).temporarySortIndexInStack = i;
- }
-
- mStackTaskList.set(allTasks);
- mRawTaskList.clear();
- mRawTaskList.addAll(allTasks);
-
- // Only callback for the removed tasks after the stack has updated
- int removedTaskCount = removedTasks.size();
- Task newFrontMostTask = getFrontMostTask();
- for (int i = 0; i < removedTaskCount; i++) {
- mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
- AnimationProps.IMMEDIATE, false /* fromDockGesture */,
- true /* dismissRecentsIfAllRemoved */);
- }
-
- // Only callback for the newly added tasks after this stack has been updated
- int addedTaskCount = addedTasks.size();
- for (int i = 0; i < addedTaskCount; i++) {
- mCb.onStackTaskAdded(this, addedTasks.get(i));
- }
-
- // Notify that the task stack has been updated
- if (notifyStackChanges) {
- mCb.onStackTasksUpdated(this);
- }
- }
-
- /**
- * Gets the front-most task in the stack.
- */
- public Task getFrontMostTask() {
- ArrayList<Task> stackTasks = mStackTaskList.getTasks();
- if (stackTasks.isEmpty()) {
- return null;
- }
- return stackTasks.get(stackTasks.size() - 1);
- }
-
- /** Gets the task keys */
- public ArrayList<TaskKey> getTaskKeys() {
- ArrayList<TaskKey> taskKeys = new ArrayList<>();
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- taskKeys.add(task.key);
- }
- return taskKeys;
- }
-
- /**
- * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
- */
- public ArrayList<Task> getTasks() {
- return mStackTaskList.getTasks();
- }
-
- /**
- * Computes a set of all the active and historical tasks.
- */
- public ArrayList<Task> computeAllTasksList() {
- ArrayList<Task> tasks = new ArrayList<>();
- tasks.addAll(mStackTaskList.getTasks());
- return tasks;
- }
-
- /**
- * Returns the number of stack tasks.
- */
- public int getTaskCount() {
- return mStackTaskList.size();
- }
-
- /**
- * Returns the task in stack tasks which is the launch target.
- */
- public Task getLaunchTarget() {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.isLaunchTarget) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Returns whether the next launch target should actually be the PiP task.
- */
- public boolean isNextLaunchTargetPip(long lastPipTime) {
- Task launchTarget = getLaunchTarget();
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null && lastPipTime > 0) {
- // If the PiP time is more recent than the next launch target, then launch the PiP task
- return lastPipTime > nextLaunchTarget.key.lastActiveTime;
- } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
- // Otherwise, if there is no next launch target, but there is a PiP, then launch
- // the PiP task
- return true;
- }
- return false;
- }
-
- /**
- * Returns the task in stack tasks which should be launched next if Recents are toggled
- * again, or null if there is no task to be launched. Callers should check
- * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
- * stack.
- */
- public Task getNextLaunchTarget() {
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null) {
- return nextLaunchTarget;
- }
- return getTasks().get(getTaskCount() - 1);
- }
-
- private Task getNextLaunchTargetRaw() {
- int taskCount = getTaskCount();
- if (taskCount == 0) {
- return null;
- }
- int launchTaskIndex = indexOfTask(getLaunchTarget());
- if (launchTaskIndex != -1 && launchTaskIndex > 0) {
- return getTasks().get(launchTaskIndex - 1);
- }
- return null;
- }
-
- /** Returns the index of this task in this current task stack */
- public int indexOfTask(Task t) {
- return mStackTaskList.indexOf(t);
- }
-
- /** Finds the task with the specified task id. */
- public Task findTaskWithId(int taskId) {
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == taskId) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Computes the components of tasks in this stack that have been removed as a result of a change
- * in the specified package.
- */
- public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
- // Identify all the tasks that should be removed as a result of the package being removed.
- // Using a set to ensure that we callback once per unique component.
- ArraySet<ComponentName> existingComponents = new ArraySet<>();
- ArraySet<ComponentName> removedComponents = new ArraySet<>();
- ArrayList<TaskKey> taskKeys = getTaskKeys();
- int taskKeyCount = taskKeys.size();
- for (int i = 0; i < taskKeyCount; i++) {
- TaskKey t = taskKeys.get(i);
-
- // Skip if this doesn't apply to the current user
- if (t.userId != userId) continue;
-
- ComponentName cn = t.getComponent();
- if (cn.getPackageName().equals(packageName)) {
- if (existingComponents.contains(cn)) {
- // If we know that the component still exists in the package, then skip
- continue;
- }
- if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) {
- existingComponents.add(cn);
- } else {
- removedComponents.add(cn);
- }
- }
- }
- return removedComponents;
- }
-
- @Override
- public String toString() {
- String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- str += " " + tasks.get(i).toString() + "\n";
- }
- return str;
- }
-
- /**
- * Given a list of tasks, returns a map of each task's key to the task.
- */
- private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
- ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size());
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- map.put(task.key, task);
- }
- return map;
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
- writer.println();
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- tasks.get(i).dump(innerPrefix, writer);
- }
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
deleted file mode 100644
index 2de7f74ba477..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2016 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.shared.recents.utilities;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.util.SparseArray;
-import android.util.SparseLongArray;
-import android.view.View;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * The generic set of animation properties to animate a {@link View}. The animation can have
- * different interpolators, start delays and durations for each of the different properties.
- */
-public class AnimationProps {
-
- private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR);
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
- public @interface PropType {}
-
- public static final int ALL = 0;
- public static final int TRANSLATION_X = 1;
- public static final int TRANSLATION_Y = 2;
- public static final int TRANSLATION_Z = 3;
- public static final int ALPHA = 4;
- public static final int SCALE = 5;
- public static final int BOUNDS = 6;
- public static final int DIM_ALPHA = 7;
-
- private SparseLongArray mPropStartDelay;
- private SparseLongArray mPropDuration;
- private SparseArray<Interpolator> mPropInterpolators;
- private Animator.AnimatorListener mListener;
-
- /**
- * The builder constructor.
- */
- public AnimationProps() {}
-
- /**
- * Creates an animation with a default {@param duration} and {@param interpolator} for all
- * properties in this animation.
- */
- public AnimationProps(int duration, Interpolator interpolator) {
- this(0, duration, interpolator, null);
- }
-
- /**
- * Creates an animation with a default {@param duration} and {@param interpolator} for all
- * properties in this animation.
- */
- public AnimationProps(int duration, Interpolator interpolator,
- Animator.AnimatorListener listener) {
- this(0, duration, interpolator, listener);
- }
-
- /**
- * Creates an animation with a default {@param startDelay}, {@param duration} and
- * {@param interpolator} for all properties in this animation.
- */
- public AnimationProps(int startDelay, int duration, Interpolator interpolator) {
- this(startDelay, duration, interpolator, null);
- }
-
- /**
- * Creates an animation with a default {@param startDelay}, {@param duration} and
- * {@param interpolator} for all properties in this animation.
- */
- public AnimationProps(int startDelay, int duration, Interpolator interpolator,
- Animator.AnimatorListener listener) {
- setStartDelay(ALL, startDelay);
- setDuration(ALL, duration);
- setInterpolator(ALL, interpolator);
- setListener(listener);
- }
-
- /**
- * Creates a new {@link AnimatorSet} that will animate the given animators. Callers need to
- * manually apply the individual animation properties for each of the animators respectively.
- */
- public AnimatorSet createAnimator(List<Animator> animators) {
- AnimatorSet anim = new AnimatorSet();
- if (mListener != null) {
- anim.addListener(mListener);
- }
- anim.playTogether(animators);
- return anim;
- }
-
- /**
- * Applies the specific start delay, duration and interpolator to the given {@param animator}
- * for the specified {@param propertyType}.
- */
- public <T extends ValueAnimator> T apply(@PropType int propertyType, T animator) {
- animator.setStartDelay(getStartDelay(propertyType));
- animator.setDuration(getDuration(propertyType));
- animator.setInterpolator(getInterpolator(propertyType));
- return animator;
- }
-
- /**
- * Sets a start delay for a specific property.
- */
- public AnimationProps setStartDelay(@PropType int propertyType, int startDelay) {
- if (mPropStartDelay == null) {
- mPropStartDelay = new SparseLongArray();
- }
- mPropStartDelay.append(propertyType, startDelay);
- return this;
- }
-
- /**
- * Returns the start delay for a specific property.
- */
- public long getStartDelay(@PropType int propertyType) {
- if (mPropStartDelay != null) {
- long startDelay = mPropStartDelay.get(propertyType, -1);
- if (startDelay != -1) {
- return startDelay;
- }
- return mPropStartDelay.get(ALL, 0);
- }
- return 0;
- }
-
- /**
- * Sets a duration for a specific property.
- */
- public AnimationProps setDuration(@PropType int propertyType, int duration) {
- if (mPropDuration == null) {
- mPropDuration = new SparseLongArray();
- }
- mPropDuration.append(propertyType, duration);
- return this;
- }
-
- /**
- * Returns the duration for a specific property.
- */
- public long getDuration(@PropType int propertyType) {
- if (mPropDuration != null) {
- long duration = mPropDuration.get(propertyType, -1);
- if (duration != -1) {
- return duration;
- }
- return mPropDuration.get(ALL, 0);
- }
- return 0;
- }
-
- /**
- * Sets an interpolator for a specific property.
- */
- public AnimationProps setInterpolator(@PropType int propertyType, Interpolator interpolator) {
- if (mPropInterpolators == null) {
- mPropInterpolators = new SparseArray<>();
- }
- mPropInterpolators.append(propertyType, interpolator);
- return this;
- }
-
- /**
- * Returns the interpolator for a specific property, falling back to the general interpolator
- * if there is no specific property interpolator.
- */
- public Interpolator getInterpolator(@PropType int propertyType) {
- if (mPropInterpolators != null) {
- Interpolator interp = mPropInterpolators.get(propertyType);
- if (interp != null) {
- return interp;
- }
- return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR);
- }
- return LINEAR_INTERPOLATOR;
- }
-
- /**
- * Sets an animator listener for this animation.
- */
- public AnimationProps setListener(Animator.AnimatorListener listener) {
- mListener = listener;
- return this;
- }
-
- /**
- * Returns the animator listener for this animation.
- */
- public Animator.AnimatorListener getListener() {
- return mListener;
- }
-
- /**
- * Returns whether this animation has any duration.
- */
- public boolean isImmediate() {
- int count = mPropDuration.size();
- for (int i = 0; i < count; i++) {
- if (mPropDuration.valueAt(i) > 0) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7d159b745254..7dffc2613956 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -16,158 +16,19 @@
package com.android.systemui.shared.recents.utilities;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.RectEvaluator;
-import android.annotation.FloatRange;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
-import android.os.Trace;
-import android.util.ArraySet;
-import android.util.IntProperty;
-import android.util.Property;
-import android.util.TypedValue;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ViewRootImpl;
-import android.view.ViewStub;
-
-import java.util.ArrayList;
-import java.util.Collections;
/* Common code */
public class Utilities {
- public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
- new IntProperty<Drawable>("drawableAlpha") {
- @Override
- public void setValue(Drawable object, int alpha) {
- object.setAlpha(alpha);
- }
-
- @Override
- public Integer get(Drawable object) {
- return object.getAlpha();
- }
- };
-
- public static final Property<Drawable, Rect> DRAWABLE_RECT =
- new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
- @Override
- public void set(Drawable object, Rect bounds) {
- object.setBounds(bounds);
- }
-
- @Override
- public Rect get(Drawable object) {
- return object.getBounds();
- }
- };
-
- public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
- public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
-
- /**
- * @return the first parent walking up the view hierarchy that has the given class type.
- *
- * @param parentClass must be a class derived from {@link View}
- */
- public static <T extends View> T findParent(View v, Class<T> parentClass) {
- ViewParent parent = v.getParent();
- while (parent != null) {
- if (parentClass.isAssignableFrom(parent.getClass())) {
- return (T) parent;
- }
- parent = parent.getParent();
- }
- return null;
- }
-
- /**
- * Initializes the {@param setOut} with the given object.
- */
- public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
- setOut.clear();
- if (obj != null) {
- setOut.add(obj);
- }
- return setOut;
- }
-
/**
- * Replaces the contents of {@param setOut} with the contents of the {@param array}.
- */
- public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
- setOut.clear();
- if (array != null) {
- Collections.addAll(setOut, array);
- }
- return setOut;
- }
-
- /**
- * @return the clamped {@param value} between the provided {@param min} and {@param max}.
- */
- public static float clamp(float value, float min, float max) {
- return Math.max(min, Math.min(max, value));
- }
-
- /**
- * @return the clamped {@param value} between the provided {@param min} and {@param max}.
- */
- public static int clamp(int value, int min, int max) {
- return Math.max(min, Math.min(max, value));
- }
-
- /**
- * @return the clamped {@param value} between 0 and 1.
- */
- public static float clamp01(float value) {
- return Math.max(0f, Math.min(1f, value));
- }
-
- /**
- * Scales the {@param value} to be proportionally between the {@param min} and
- * {@param max} values.
- *
- * @param value must be between 0 and 1
- */
- public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
- return min + (value * (max - min));
- }
-
- /**
- * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
- *
- * @param value must be between {@param min} and {@param max}
+ * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
*/
- public static float unmapRange(float value, float min, float max) {
- return (value - min) / (max - min);
- }
-
- /** Scales a rect about its centroid */
- public static void scaleRectAboutCenter(RectF r, float scale) {
- if (scale != 1.0f) {
- float cx = r.centerX();
- float cy = r.centerY();
- r.offset(-cx, -cy);
- r.left *= scale;
- r.top *= scale;
- r.right *= scale;
- r.bottom *= scale;
- r.offset(cx, cy);
- }
+ public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
+ Message msg = h.obtainMessage().setCallback(r);
+ h.sendMessageAtFrontOfQueue(msg);
}
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
@@ -179,7 +40,7 @@ public class Utilities {
bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
-
+
float fgR = Color.red(fg) / 255f;
float fgG = Color.green(fg) / 255f;
float fgB = Color.blue(fg) / 255f;
@@ -191,147 +52,10 @@ public class Utilities {
return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
}
- /** Returns the base color overlaid with another overlay color with a specified alpha. */
- public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
- return Color.rgb(
- (int) (overlayAlpha * Color.red(baseColor) +
- (1f - overlayAlpha) * Color.red(overlayColor)),
- (int) (overlayAlpha * Color.green(baseColor) +
- (1f - overlayAlpha) * Color.green(overlayColor)),
- (int) (overlayAlpha * Color.blue(baseColor) +
- (1f - overlayAlpha) * Color.blue(overlayColor)));
- }
-
- /**
- * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
- * are not called.
- */
- public static void cancelAnimationWithoutCallbacks(Animator animator) {
- if (animator != null && animator.isStarted()) {
- removeAnimationListenersRecursive(animator);
- animator.cancel();
- }
- }
-
/**
- * Recursively removes all the listeners of all children of this animator
- */
- public static void removeAnimationListenersRecursive(Animator animator) {
- if (animator instanceof AnimatorSet) {
- ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
- for (int i = animators.size() - 1; i >= 0; i--) {
- removeAnimationListenersRecursive(animators.get(i));
- }
- }
- animator.removeAllListeners();
- }
-
- /**
- * Sets the given {@link View}'s frame from its current translation.
- */
- public static void setViewFrameFromTranslation(View v) {
- RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
- v.setTranslationX(0);
- v.setTranslationY(0);
- v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
- (int) taskViewRect.right, (int) taskViewRect.bottom);
- }
-
- /**
- * Returns a view stub for the given view id.
- */
- public static ViewStub findViewStubById(View v, int stubId) {
- return (ViewStub) v.findViewById(stubId);
- }
-
- /**
- * Returns a view stub for the given view id.
- */
- public static ViewStub findViewStubById(Activity a, int stubId) {
- return (ViewStub) a.findViewById(stubId);
- }
-
- /**
- * Used for debugging, converts DP to PX.
- */
- public static float dpToPx(Resources res, float dp) {
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
- }
-
- /**
- * Adds a trace event for debugging.
- */
- public static void addTraceEvent(String event) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
-
- /**
- * Returns whether this view, or one of its descendants have accessibility focus.
- */
- public static boolean isDescendentAccessibilityFocused(View v) {
- if (v.isAccessibilityFocused()) {
- return true;
- }
-
- if (v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) v;
- int childCount = vg.getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns the application configuration, which is independent of the activity's current
- * configuration in multiwindow.
- */
- public static Configuration getAppConfiguration(Context context) {
- return context.getApplicationContext().getResources().getConfiguration();
- }
-
- /**
- * @return The next frame name for the specified surface or -1 if the surface is no longer
- * valid.
- */
- public static long getNextFrameNumber(Surface s) {
- return s != null && s.isValid()
- ? s.getNextFrameNumber()
- : -1;
-
- }
-
- /**
- * @return The surface for the specified view.
- */
- public static @Nullable Surface getSurface(View v) {
- ViewRootImpl viewRoot = v.getViewRootImpl();
- if (viewRoot == null) {
- return null;
- }
- return viewRoot.mSurface;
- }
-
- /**
- * Returns a lightweight dump of a rect.
- */
- public static String dumpRect(Rect r) {
- if (r == null) {
- return "N:0,0-0,0";
- }
- return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
- }
-
- /**
- * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
+ * @return the clamped {@param value} between the provided {@param min} and {@param max}.
*/
- public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
- Message msg = h.obtainMessage().setCallback(r);
- h.sendMessageAtFrontOfQueue(msg);
+ public static float clamp(float value, float min, float max) {
+ return Math.max(min, Math.min(max, value));
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
deleted file mode 100644
index 45728c403ac4..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2014 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.shared.recents.view;
-
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewOutlineProvider;
-
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-/**
- * An outline provider that has a clip and outline that can be animated.
- */
-public class AnimateableViewBounds extends ViewOutlineProvider {
-
- private static final float MIN_ALPHA = 0.1f;
- private static final float MAX_ALPHA = 0.8f;
-
- protected View mSourceView;
- protected Rect mClipRect = new Rect();
- protected Rect mClipBounds = new Rect();
- protected Rect mLastClipBounds = new Rect();
- protected int mCornerRadius;
- protected float mAlpha = 1f;
-
- public AnimateableViewBounds(View source, int cornerRadius) {
- mSourceView = source;
- mCornerRadius = cornerRadius;
- }
-
- /**
- * Resets the right and bottom clip for this view.
- */
- public void reset() {
- mClipRect.set(0, 0, 0, 0);
- updateClipBounds();
- }
-
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setAlpha(Utilities.mapRange(mAlpha, MIN_ALPHA, MAX_ALPHA));
- if (mCornerRadius > 0) {
- outline.setRoundRect(mClipRect.left, mClipRect.top,
- mSourceView.getWidth() - mClipRect.right,
- mSourceView.getHeight() - mClipRect.bottom,
- mCornerRadius);
- } else {
- outline.setRect(mClipRect.left, mClipRect.top,
- mSourceView.getWidth() - mClipRect.right,
- mSourceView.getHeight() - mClipRect.bottom);
- }
- }
-
- /**
- * Sets the view outline alpha.
- */
- public void setAlpha(float alpha) {
- if (Float.compare(alpha, mAlpha) != 0) {
- mAlpha = alpha;
- // TODO, If both clip and alpha change in the same frame, only invalidate once
- mSourceView.invalidateOutline();
- }
- }
-
- /**
- * @return the outline alpha.
- */
- public float getAlpha() {
- return mAlpha;
- }
-
- /**
- * Sets the top clip.
- */
- public void setClipTop(int top) {
- mClipRect.top = top;
- updateClipBounds();
- }
-
- /**
- * @return the top clip.
- */
- public int getClipTop() {
- return mClipRect.top;
- }
-
- /**
- * Sets the bottom clip.
- */
- public void setClipBottom(int bottom) {
- mClipRect.bottom = bottom;
- updateClipBounds();
- }
-
- /**
- * @return the bottom clip.
- */
- public int getClipBottom() {
- return mClipRect.bottom;
- }
-
- /**
- * @return the clip bounds.
- */
- public Rect getClipBounds() {
- return mClipBounds;
- }
-
- protected void updateClipBounds() {
- mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
- mSourceView.getWidth() - Math.max(0, mClipRect.right),
- mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
- if (!mLastClipBounds.equals(mClipBounds)) {
- mSourceView.setClipBounds(mClipBounds);
- // TODO, If both clip and alpha change in the same frame, only invalidate once
- mSourceView.invalidateOutline();
- mLastClipBounds.set(mClipBounds);
- }
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
index ab890439a381..e253360a5364 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
@@ -21,13 +21,11 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
+import android.graphics.Picture;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
import android.view.View;
import java.util.function.Consumer;
@@ -108,12 +106,10 @@ public class RecentsTransition {
* null if we were unable to allocate a hardware bitmap.
*/
public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) {
- RenderNode node = RenderNode.create("RecentsTransition", null);
- node.setLeftTopRightBottom(0, 0, width, height);
- node.setClipToBounds(false);
- DisplayListCanvas c = node.start(width, height);
- consumer.accept(c);
- node.end(c);
- return ThreadedRenderer.createHardwareBitmap(node, width, height);
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
+ consumer.accept(canvas);
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 63a4cd497bc1..c7910f97675e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration.ActivityType;
@@ -113,7 +114,7 @@ public class ActivityManagerWrapper {
// Note: The set of running tasks from the system is ordered by recency
try {
List<ActivityManager.RunningTaskInfo> tasks =
- ActivityManager.getService().getFilteredTasks(1, ignoreActivityType,
+ ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType,
WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
if (tasks.isEmpty()) {
return null;
@@ -129,7 +130,7 @@ public class ActivityManagerWrapper {
*/
public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
try {
- return ActivityManager.getService().getRecentTasks(numTasks,
+ return ActivityTaskManager.getService().getRecentTasks(numTasks,
RECENT_IGNORE_UNAVAILABLE, userId).getList();
} catch (RemoteException e) {
Log.e(TAG, "Failed to get recent tasks", e);
@@ -143,7 +144,7 @@ public class ActivityManagerWrapper {
public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) {
ActivityManager.TaskSnapshot snapshot = null;
try {
- snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
+ snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, reducedResolution);
} catch (RemoteException e) {
Log.w(TAG, "Failed to retrieve task snapshot", e);
}
@@ -200,8 +201,8 @@ public class ActivityManagerWrapper {
/**
* Starts the recents activity. The caller should manage the thread on which this is called.
*/
- public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver,
- RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback,
+ public void startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver,
+ final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
try {
IAssistDataReceiver receiver = null;
@@ -234,7 +235,7 @@ public class ActivityManagerWrapper {
}
};
}
- ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
+ ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner);
if (resultCallback != null) {
resultCallbackHandler.post(new Runnable() {
@Override
@@ -260,7 +261,7 @@ public class ActivityManagerWrapper {
*/
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
try {
- ActivityManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
+ ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
} catch (RemoteException e) {
Log.e(TAG, "Failed to cancel recents animation", e);
}
@@ -283,9 +284,9 @@ public class ActivityManagerWrapper {
* @param resultCallback The result success callback
* @param resultCallbackHandler The handler to receive the result callback
*/
- public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
- int windowingMode, int activityType, Consumer<Boolean> resultCallback,
- Handler resultCallbackHandler) {
+ public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options,
+ int windowingMode, int activityType, final Consumer<Boolean> resultCallback,
+ final Handler resultCallbackHandler) {
if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// We show non-visible docked tasks in Recents, but we always want to launch
// them in the fullscreen stack.
@@ -333,7 +334,7 @@ public class ActivityManagerWrapper {
public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
try {
Bundle optsBundle = options == null ? null : options.toBundle();
- ActivityManager.getService().startActivityFromRecents(taskId, optsBundle);
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle);
return true;
} catch (Exception e) {
return false;
@@ -341,6 +342,20 @@ public class ActivityManagerWrapper {
}
/**
+ * Moves an already resumed task to the side of the screen to initiate split screen.
+ */
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+ Rect initialBounds) {
+ try {
+ return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
+ createMode, true /* onTop */, false /* animate */, initialBounds,
+ true /* showRecents */);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
@@ -363,7 +378,7 @@ public class ActivityManagerWrapper {
/**
* Requests that the system close any open system windows (including other SystemUI).
*/
- public void closeSystemWindows(String reason) {
+ public void closeSystemWindows(final String reason) {
mBackgroundExecutor.submit(new Runnable() {
@Override
public void run() {
@@ -379,12 +394,12 @@ public class ActivityManagerWrapper {
/**
* Removes a task by id.
*/
- public void removeTask(int taskId) {
+ public void removeTask(final int taskId) {
mBackgroundExecutor.submit(new Runnable() {
@Override
public void run() {
try {
- ActivityManager.getService().removeTask(taskId);
+ ActivityTaskManager.getService().removeTask(taskId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to remove task=" + taskId, e);
}
@@ -393,11 +408,27 @@ public class ActivityManagerWrapper {
}
/**
+ * Removes all the recent tasks.
+ */
+ public void removeAllRecentTasks() {
+ mBackgroundExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove all tasks", e);
+ }
+ }
+ });
+ }
+
+ /**
* Cancels the current window transtion to/from Recents for the given task id.
*/
public void cancelWindowTransition(int taskId) {
try {
- ActivityManager.getService().cancelTaskWindowTransition(taskId);
+ ActivityTaskManager.getService().cancelTaskWindowTransition(taskId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
}
@@ -408,7 +439,7 @@ public class ActivityManagerWrapper {
*/
public boolean isScreenPinningActive() {
try {
- return ActivityManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
+ return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
} catch (RemoteException e) {
return false;
}
@@ -427,7 +458,7 @@ public class ActivityManagerWrapper {
*/
public boolean isLockToAppActive() {
try {
- return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 712cca67c5d6..7154f5396fbd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -16,11 +16,13 @@
package com.android.systemui.shared.system;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityOptions;
+import android.content.Context;
+import android.os.Handler;
/**
* Wrapper around internal ActivityOptions creation.
@@ -43,4 +45,17 @@ public abstract class ActivityOptionsCompat {
RemoteAnimationAdapterCompat remoteAnimationAdapter) {
return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
}
+
+ public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
+ int exitResId, final Runnable callback, final Handler callbackHandler) {
+ return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+ new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ if (callback != null) {
+ callbackHandler.post(callback);
+ }
+ }
+ });
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java
new file mode 100644
index 000000000000..bb319e625b44
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java
@@ -0,0 +1,63 @@
+/*
+ * 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.systemui.shared.system;
+
+import android.view.IDockedStackListener;
+
+/**
+ * An interface to track docked stack changes.
+ */
+public class DockedStackListenerCompat {
+
+ IDockedStackListener.Stub mListener = new IDockedStackListener.Stub() {
+ @Override
+ public void onDividerVisibilityChanged(boolean visible) {}
+
+ @Override
+ public void onDockedStackExistsChanged(boolean exists) {
+ DockedStackListenerCompat.this.onDockedStackExistsChanged(exists);
+ }
+
+ @Override
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+ boolean isHomeStackResizable) {
+ DockedStackListenerCompat.this.onDockedStackMinimizedChanged(minimized, animDuration,
+ isHomeStackResizable);
+ }
+
+ @Override
+ public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {}
+
+ @Override
+ public void onDockSideChanged(final int newDockSide) {
+ DockedStackListenerCompat.this.onDockSideChanged(newDockSide);
+ }
+ };
+
+ public void onDockedStackExistsChanged(boolean exists) {
+ // To be overridden
+ }
+
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+ boolean isHomeStackResizable) {
+ // To be overridden
+ }
+
+ public void onDockSideChanged(final int newDockSide) {
+ // To be overridden
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
deleted file mode 100644
index 66b8fed1a48f..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.systemui.shared.system;
-
-import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wraps the internal graphic buffer.
- */
-public class GraphicBufferCompat implements Parcelable {
-
- private GraphicBuffer mBuffer;
-
- public GraphicBufferCompat(GraphicBuffer buffer) {
- mBuffer = buffer;
- }
-
- public GraphicBufferCompat(Parcel in) {
- mBuffer = GraphicBuffer.CREATOR.createFromParcel(in);
- }
-
- public Bitmap toBitmap() {
- return mBuffer != null
- ? Bitmap.createHardwareBitmap(mBuffer)
- : null;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- mBuffer.writeToParcel(dest, flags);
- }
-
- public static final Parcelable.Creator<GraphicBufferCompat> CREATOR
- = new Parcelable.Creator<GraphicBufferCompat>() {
- public GraphicBufferCompat createFromParcel(Parcel in) {
- return new GraphicBufferCompat(in);
- }
-
- public GraphicBufferCompat[] newArray(int size) {
- return new GraphicBufferCompat[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index 38b8ae8418af..0cde9daa81b3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -67,7 +68,7 @@ public class InputConsumerController {
}
@Override
- public void onInputEvent(InputEvent event, int displayId) {
+ public void onInputEvent(InputEvent event) {
boolean handled = true;
try {
if (mListener != null && event instanceof MotionEvent) {
@@ -146,8 +147,9 @@ public class InputConsumerController {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
- mWindowManager.destroyInputConsumer(mName);
- mWindowManager.createInputConsumer(mToken, mName, inputChannel);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
@@ -164,7 +166,8 @@ public class InputConsumerController {
public void unregisterInputConsumer() {
if (mInputEventReceiver != null) {
try {
- mWindowManager.destroyInputConsumer(mName);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
} catch (RemoteException e) {
Log.e(TAG, "Failed to destroy input consumer", e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
index f9450adcdf30..c42e7e3fd216 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,9 +11,22 @@
* 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.
+ * limitations under the License
*/
package com.android.systemui.shared.system;
-parcelable GraphicBufferCompat; \ No newline at end of file
+import android.app.KeyguardManager;
+import android.content.Context;
+
+public class KeyguardManagerCompat {
+ private final KeyguardManager mKeyguardManager;
+
+ public KeyguardManagerCompat(Context context) {
+ mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ }
+
+ public boolean isDeviceLocked(int userId) {
+ return mKeyguardManager.isDeviceLocked(userId);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java
new file mode 100644
index 000000000000..a5299038d3a9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.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.systemui.shared.system;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+
+public class RecentTaskInfoCompat {
+
+ private ActivityManager.RecentTaskInfo mInfo;
+
+ public RecentTaskInfoCompat(ActivityManager.RecentTaskInfo info) {
+ mInfo = info;
+ }
+
+ public int getUserId() {
+ return mInfo.userId;
+ }
+
+ public boolean supportsSplitScreenMultiWindow() {
+ return mInfo.supportsSplitScreenMultiWindow;
+ }
+
+ public ComponentName getTopActivity() {
+ return mInfo.topActivity;
+ }
+
+ public ActivityManager.TaskDescription getTaskDescription() {
+ return mInfo.taskDescription;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 625b1de74290..61be076ac48a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -41,11 +41,11 @@ public class RemoteAnimationAdapterCompat {
}
private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
- RemoteAnimationRunnerCompat remoteAnimationAdapter) {
+ final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
- IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final Runnable animationFinishedCallback = new Runnable() {
@@ -63,7 +63,7 @@ public class RemoteAnimationAdapterCompat {
}
@Override
- public void onAnimationCancelled() throws RemoteException {
+ public void onAnimationCancelled() {
remoteAnimationAdapter.onAnimationCancelled();
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
index 5a28a5e28d91..7c8c23eba73b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
@@ -48,7 +48,7 @@ public abstract class RotationWatcher {
if (!mIsWatching) {
try {
WindowManagerGlobal.getWindowManagerService().watchRotation(mWatcher,
- mContext.getDisplay().getDisplayId());
+ mContext.getDisplayId());
mIsWatching = true;
} catch (RemoteException e) {
Log.w(TAG, "Failed to set rotation watcher", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
new file mode 100644
index 000000000000..5bc1f2511411
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
@@ -0,0 +1,35 @@
+/*
+ * 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.systemui.shared.system;
+
+import android.util.StatsLog;
+
+/**
+ * Wrapper class to make StatsLog hidden API accessible.
+ */
+public class StatsLogCompat {
+
+ /**
+ * StatsLog.write(StatsLog.LAUNCHER_EVENT, int action, int src_state, int dst_state,
+ * byte[] extension, boolean is_swipe_up_enabled);
+ */
+ public static void write(int action, int srcState, int dstState, byte [] extension,
+ boolean swipeUpEnabled) {
+ StatsLog.write(19, action, srcState, dstState, extension,
+ swipeUpEnabled);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index 217e0aaac709..dc4eb3b71faa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -21,6 +21,7 @@ import android.graphics.Rect;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewRootImpl;
@@ -47,11 +48,13 @@ public class SyncRtSurfaceTransactionApplier {
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(SurfaceParams... params) {
+ public void scheduleApply(final SurfaceParams... params) {
if (mTargetViewRootImpl == null) {
return;
}
- mTargetViewRootImpl.registerRtFrameCallback(frame -> {
+ mTargetViewRootImpl.registerRtFrameCallback(new ThreadedRenderer.FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
if (mTargetSurface == null || !mTargetSurface.isValid()) {
return;
}
@@ -64,6 +67,7 @@ public class SyncRtSurfaceTransactionApplier {
}
t.setEarlyWakeup();
t.apply();
+ }
});
// Make sure a frame gets scheduled.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
new file mode 100644
index 000000000000..eaf8d9b57398
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
@@ -0,0 +1,40 @@
+/*
+ * 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.systemui.shared.system;
+
+import android.app.ActivityManager;
+
+public class TaskDescriptionCompat {
+
+ private ActivityManager.TaskDescription mTaskDescription;
+
+ public TaskDescriptionCompat(ActivityManager.TaskDescription td) {
+ mTaskDescription = td;
+ }
+
+ public int getPrimaryColor() {
+ return mTaskDescription != null
+ ? mTaskDescription.getPrimaryColor()
+ : 0;
+ }
+
+ public int getBackgroundColor() {
+ return mTaskDescription != null
+ ? mTaskDescription.getBackgroundColor()
+ : 0;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 5e293c61c35a..628b3c6e21dc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import android.app.ActivityTaskManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.IActivityManager;
import android.app.TaskStackListener;
@@ -57,7 +58,7 @@ public class TaskStackChangeListeners extends TaskStackListener {
if (!mRegistered) {
// Register mTaskStackListener to IActivityManager only once if needed.
try {
- am.registerTaskStackListener(this);
+ ActivityTaskManager.getService().registerTaskStackListener(this);
mRegistered = true;
} catch (Exception e) {
Log.w(TAG, "Failed to call registerTaskStackListener", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 67afee076efe..70258c20538d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -83,11 +83,6 @@ public class TransactionCompat {
return this;
}
- public TransactionCompat setFinalCrop(SurfaceControlCompat surfaceControl, Rect crop) {
- mTransaction.setFinalCrop(surfaceControl.mSurfaceControl, crop);
- return this;
- }
-
public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
IBinder handle, long frameNumber) {
mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, handle, frameNumber);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
index b2b140e4b0a9..de2a3e44841e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
@@ -17,7 +17,7 @@ package com.android.systemui.shared.system;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.view.DisplayListCanvas;
+import android.graphics.RecordingCanvas;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowCallbacks;
@@ -55,7 +55,7 @@ public class WindowCallbacksCompat {
}
@Override
- public void onPostDraw(DisplayListCanvas canvas) {
+ public void onPostDraw(RecordingCanvas canvas) {
WindowCallbacksCompat.this.onPostDraw(canvas);
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index ed2f8310eff4..3191d14c5a83 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -151,6 +151,25 @@ public class WindowManagerWrapper {
}
}
+ public void setPipVisibility(final boolean visible) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setPipVisibility(visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
+ }
+ }
+
+ /**
+ * @return whether there is a soft nav bar.
+ */
+ public boolean hasSoftNavigationBar() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* @return The side of the screen where navigation bar is positioned.
* @see #NAV_BAR_POS_RIGHT
@@ -166,4 +185,16 @@ public class WindowManagerWrapper {
}
return NAV_BAR_POS_INVALID;
}
+
+ /**
+ * Registers a docked stack listener with the system.
+ */
+ public void registerDockedStackListener(DockedStackListenerCompat listener) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
+ listener.mListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register docked stack listener");
+ }
+ }
}