Adding support for remote app action provider.
> Connect to a predefined content provider to get package specific action
> Add that action to shortcut menu and task menu
Change-Id: Ide5c09d04112e86c8e19c2f9e66c88c15b3fd04e
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 298b0ff..b5441df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -45,13 +45,11 @@
TaskShortcutFactory.SPLIT_SCREEN,
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
- TaskShortcutFactory.FREE_FORM
+ TaskShortcutFactory.FREE_FORM,
+ TaskShortcutFactory.WELLBEING
};
- public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
- forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
-
- public List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
+ public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
@@ -63,6 +61,9 @@
return shortcuts;
}
+ public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+ forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
+
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index a3a1d6d..9ba2e5a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -36,6 +36,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -309,4 +310,6 @@
view.getTask().getTopComponent().getPackageName())
? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
+ TaskShortcutFactory WELLBEING = (activity, view) ->
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index b810c4a..80022b4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -192,8 +192,7 @@
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView)
- .forEach(this::addMenuOption);
+ TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
}
private void addMenuOption(SystemShortcut menuOption) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 3af0f70..0dfc39b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -713,8 +713,7 @@
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
- for (SystemShortcut s : TaskOverlayFactory.INSTANCE.get(getContext())
- .getEnabledShortcuts(this)) {
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
info.addAction(s.createAccessibilityAction(context));
}
@@ -746,8 +745,7 @@
return true;
}
- for (SystemShortcut s : TaskOverlayFactory.INSTANCE.get(getContext())
- .getEnabledShortcuts(this)) {
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
return true;
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 98aaceb..5d9a009 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -33,4 +33,6 @@
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
+
+ <string name="wellbeing_provider_pkg" translatable="false"></string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index f55ef94..fc9cfcd 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -35,6 +35,8 @@
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
@@ -47,6 +49,8 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+import java.util.stream.Stream;
+
/**
* Extension of Launcher activity to provide quickstep specific functionality
*/
@@ -251,4 +255,19 @@
getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
}
}
+
+ @Override
+ public void finishBindingItems(int pageBoundFirst) {
+ super.finishBindingItems(pageBoundFirst);
+ // Instantiate and initialize WellbeingModel now that its loading won't interfere with
+ // populating workspace.
+ // TODO: Find a better place for this
+ WellbeingModel.get(this);
+ }
+
+ @Override
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.concat(super.getSupportedShortcuts(),
+ Stream.of(WellbeingModel.SHORTCUT_FACTORY));
+ }
}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
new file mode 100644
index 0000000..852a08e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import static android.content.ContentResolver.SCHEME_CONTENT;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+
+import android.annotation.TargetApi;
+import android.app.RemoteAction;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.popup.RemoteActionShortcut;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data model for digital wellbeing status of apps.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public final class WellbeingModel {
+ private static final String TAG = "WellbeingModel";
+ private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_PACKAGE_ADDED = 1;
+ private static final int MSG_PACKAGE_REMOVED = 2;
+ private static final int MSG_FULL_REFRESH = 3;
+
+ // Welbeing contract
+ private static final String METHOD_GET_ACTIONS = "get_actions";
+ private static final String EXTRA_ACTIONS = "actions";
+ private static final String EXTRA_ACTION = "action";
+ private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
+ private static final String EXTRA_PACKAGES = "packages";
+
+ private static WellbeingModel sInstance;
+
+ private final Context mContext;
+ private final String mWellbeingProviderPkg;
+ private final Handler mWorkerHandler;
+
+ private final ContentObserver mContentObserver;
+
+ private final Object mModelLock = new Object();
+ // Maps the action Id to the corresponding RemoteAction
+ private final Map<String, RemoteAction> mActionIdMap = new ArrayMap<>();
+ private final Map<String, String> mPackageToActionId = new HashMap<>();
+
+ private boolean mIsInTest;
+
+ private WellbeingModel(final Context context) {
+ mContext = context;
+ mWorkerHandler =
+ new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
+
+ mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
+ mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ // Wellbeing reports that app actions have changed.
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
+ + "], uri = [" + uri + "]");
+ }
+ Preconditions.assertUIThread();
+ updateWellbeingData();
+ }
+ };
+
+ if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
+ context.registerReceiver(
+ new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
+ PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
+ Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
+ Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Intent.ACTION_PACKAGE_RESTARTED));
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
+ filter);
+
+ restartObserver();
+ }
+ }
+
+ public void setInTest(boolean inTest) {
+ mIsInTest = inTest;
+ }
+
+ protected void onWellbeingProviderChanged(Intent intent) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
+ }
+ restartObserver();
+ }
+
+ private void restartObserver() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.unregisterContentObserver(mContentObserver);
+ Uri actionsUri = apiBuilder().path("actions").build();
+ try {
+ resolver.registerContentObserver(
+ actionsUri, true /* notifyForDescendants */, mContentObserver);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ }
+ updateWellbeingData();
+ }
+
+ @MainThread
+ public static WellbeingModel get(@NonNull Context context) {
+ Preconditions.assertUIThread();
+ if (sInstance == null) {
+ sInstance = new WellbeingModel(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ @MainThread
+ private SystemShortcut getShortcutForApp(String packageName, int userId,
+ BaseDraggingActivity activity, ItemInfo info) {
+ Preconditions.assertUIThread();
+ // Work profile apps are not recognized by digital wellbeing.
+ if (userId != UserHandle.myUserId()) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: not current user");
+ }
+ return null;
+ }
+
+ synchronized (mModelLock) {
+ String actionId = mPackageToActionId.get(packageName);
+ final RemoteAction action = actionId != null ? mActionIdMap.get(actionId) : null;
+ if (action == null) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: no action");
+ }
+ return null;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG,
+ "getShortcutForApp [" + packageName + "]: action: '" + action.getTitle()
+ + "'");
+ }
+ return new RemoteActionShortcut(action, activity, info);
+ }
+ }
+
+ private void updateWellbeingData() {
+ mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
+ }
+
+ private Uri.Builder apiBuilder() {
+ return new Uri.Builder()
+ .scheme(SCHEME_CONTENT)
+ .authority(mWellbeingProviderPkg + ".api");
+ }
+
+ private boolean updateActions(String... packageNames) {
+ if (packageNames.length == 0) {
+ return true;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "retrieveActions() called with: packageNames = [" + String.join(", ",
+ packageNames) + "]");
+ }
+ Preconditions.assertNonUiThread();
+
+ Uri contentUri = apiBuilder().build();
+ final Bundle remoteActionBundle;
+ try (ContentProviderClient client = mContext.getContentResolver()
+ .acquireUnstableContentProviderClient(contentUri)) {
+ if (client == null) {
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): null provider");
+ return false;
+ }
+
+ // Prepare wellbeing call parameters.
+ final Bundle params = new Bundle();
+ params.putStringArray(EXTRA_PACKAGES, packageNames);
+ params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1);
+ // Perform wellbeing call .
+ remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params);
+ } catch (DeadObjectException e) {
+ Log.i(TAG, "retrieveActions(): DeadObjectException");
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ return true;
+ }
+
+ synchronized (mModelLock) {
+ // Remove the entries for requested packages, and then update the fist with what we
+ // got from service
+ Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
+
+ // The result consists of sub-bundles, each one is per a remote action. Each sub-bundle
+ // has a RemoteAction and a list of packages to which the action applies.
+ for (String actionId :
+ remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
+ final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
+ mActionIdMap.put(actionId,
+ actionBundle.getParcelable(EXTRA_ACTION));
+
+ final String[] packagesForAction =
+ actionBundle.getStringArray(EXTRA_PACKAGES);
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
+ packagesForAction));
+ }
+ for (String packageName : packagesForAction) {
+ mPackageToActionId.put(packageName, actionId);
+ }
+ }
+ }
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): finished");
+ return true;
+ }
+
+ private boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PACKAGE_REMOVED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ synchronized (mModelLock) {
+ mPackageToActionId.remove(packageName);
+ }
+ return true;
+ }
+ case MSG_PACKAGE_ADDED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ if (!updateActions(packageName)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+
+ case MSG_FULL_REFRESH: {
+ // Remove all existing messages
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ final String[] packageNames = mContext.getSystemService(LauncherApps.class)
+ .getActivityList(null, Process.myUserHandle()).stream()
+ .map(li -> li.getApplicationInfo().packageName).distinct()
+ .toArray(String[]::new);
+ if (!updateActions(packageNames)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void scheduleRefreshRetry(Message originalMsg) {
+ int retryCount = originalMsg.arg1;
+ if (retryCount >= RETRY_TIMES_MS.length) {
+ // To many retries, skip
+ return;
+ }
+
+ Message msg = Message.obtain(originalMsg);
+ msg.arg1 = retryCount + 1;
+ mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
+ }
+
+ private void onAppPackageChanged(Intent intent) {
+ if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
+ Preconditions.assertUIThread();
+
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null || packageName.length() == 0) {
+ // they sent us a bad intent
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
+ }
+ }
+
+ /**
+ * Shortcut factory for generating wellbeing action
+ */
+ public static final SystemShortcut.Factory SHORTCUT_FACTORY = (activity, info) ->
+ WellbeingModel.get(activity).getShortcutForApp(
+ info.getTargetComponent().getPackageName(),
+ info.user.getIdentifier(),
+ activity, info);
+}
diff --git a/res/values/config.xml b/res/values/config.xml
index 0387184..10671c5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,7 +69,6 @@
<string name="app_transition_manager_class" translatable="false"></string>
<string name="instant_app_resolver_class" translatable="false"></string>
<string name="main_process_initializer_class" translatable="false"></string>
- <string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index bb08ba8..3dce9fc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -31,6 +31,10 @@
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
+import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
+import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
+import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.CRASH_ADD_CUSTOM_SHORTCUT;
@@ -114,6 +118,7 @@
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
@@ -170,6 +175,7 @@
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Stream;
/**
* Default launcher application.
@@ -2655,6 +2661,10 @@
getStateManager().goToState(LauncherState.NORMAL);
}
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
+ }
+
public static Launcher getLauncher(Context context) {
return (Launcher) fromContext(context);
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 8e5d852..2034926 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -213,7 +214,7 @@
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher));
+ container.populateAndShow(icon, itemInfo);
return container;
}
@@ -238,8 +239,7 @@
}
}
- protected void populateAndShow(
- BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
+ protected void populateAndShow(BubbleTextView icon, ItemInfo item) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_CONTEXT_MENU, "populateAndShow");
}
@@ -247,7 +247,10 @@
populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
- factory.getEnabledShortcuts(mLauncher, item));
+ mLauncher.getSupportedShortcuts()
+ .map(s -> s.getShortcut(mLauncher, item))
+ .filter(s -> s != null)
+ .collect(Collectors.toList()));
}
public ViewGroup getSystemShortcutContainerForTesting() {
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
deleted file mode 100644
index 8b8a4d0..0000000
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.popup;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SystemShortcutFactory implements ResourceBasedOverride {
-
- public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
- forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
-
- /** Note that these are in order of priority. */
- private final SystemShortcut.Factory[] mAllFactories;
-
- @SuppressWarnings("unused")
- public SystemShortcutFactory() {
- this(SystemShortcut.APP_INFO, SystemShortcut.WIDGETS, SystemShortcut.INSTALL,
- SystemShortcut.DISMISS_PREDICTION);
- }
-
- protected SystemShortcutFactory(SystemShortcut.Factory... factories) {
- mAllFactories = factories;
- }
-
- public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
- List<SystemShortcut> systemShortcuts = new ArrayList<>();
- for (SystemShortcut.Factory factory : mAllFactories) {
- SystemShortcut shortcut = factory.getShortcut(launcher, info);
- if (shortcut != null) {
- systemShortcuts.add(shortcut);
- }
- }
-
- return systemShortcuts;
- }
-}