Separating SystemShortcut and Factory

> This makes easier to extend the factory and callbacks separately
> Availability checks are only performed once when getting the shortcut

Change-Id: I413541eabfb2b9e987c852d5171c6696b1853958
This commit is contained in:
Sunny Goyal
2019-10-22 15:22:59 -07:00
parent c78308a9a7
commit 7805d49764
9 changed files with 517 additions and 560 deletions
@@ -19,11 +19,11 @@ package com.android.quickstep;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.graphics.Matrix;
import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskThumbnailView;
@@ -40,25 +40,24 @@ import java.util.List;
public class TaskOverlayFactory implements ResourceBasedOverride {
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
new TaskSystemShortcut.AppInfo(),
new TaskSystemShortcut.SplitScreen(),
new TaskSystemShortcut.Pin(),
new TaskSystemShortcut.Install(),
new TaskSystemShortcut.Freeform()
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.APP_INFO,
TaskShortcutFactory.SPLIT_SCREEN,
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM
};
public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
public List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
View.OnClickListener onClickListener =
menuOption.getOnClickListener(activity, taskView);
if (onClickListener != null) {
shortcuts.add(menuOption);
for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
if (shortcut != null) {
shortcuts.add(shortcut);
}
}
return shortcuts;
@@ -0,0 +1,312 @@
/*
* 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.quickstep;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.InstantAppResolver;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* Represents a system shortcut that can be shown for a recent task.
*/
public interface TaskShortcutFactory {
SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
static WorkspaceItemInfo dummyInfo(TaskView view) {
Task task = view.getTask();
WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
dummyInfo.intent = new Intent();
ComponentName component = task.getTopComponent();
dummyInfo.intent.setComponent(component);
dummyInfo.user = UserHandle.of(task.key.userId);
dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
return dummyInfo;
}
TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
abstract class MultiWindowFactory implements TaskShortcutFactory {
private final int mIconRes;
private final int mTextRes;
MultiWindowFactory(int iconRes, int textRes) {
mIconRes = iconRes;
mTextRes = textRes;
}
protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
protected abstract ActivityOptions makeLaunchOptions(Activity activity);
protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
@Override
public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
final Task task = taskView.getTask();
if (!task.isDockable) {
return null;
}
if (!isAvailable(activity, task.key.displayId)) {
return null;
}
return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this);
}
}
class MultiWindowSystemShortcut extends SystemShortcut {
private Handler mHandler;
private final RecentsView mRecentsView;
private final TaskThumbnailView mThumbnailView;
private final TaskView mTaskView;
private final MultiWindowFactory mFactory;
public MultiWindowSystemShortcut(int iconRes, int textRes,
BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) {
super(iconRes, textRes, activity, dummyInfo(taskView));
mHandler = new Handler(Looper.getMainLooper());
mTaskView = taskView;
mRecentsView = activity.getOverviewPanel();
mThumbnailView = taskView.getThumbnail();
mFactory = factory;
}
@Override
public void onClick(View view) {
Task.TaskKey taskKey = mTaskView.getTask().key;
final int taskId = taskKey.id;
final View.OnLayoutChangeListener onLayoutChangeListener =
new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int l, int t, int r, int b,
int oldL, int oldT, int oldR, int oldB) {
mTaskView.getRootView().removeOnLayoutChangeListener(this);
mRecentsView.clearIgnoreResetTask(taskId);
// Start animating in the side pages once launcher has been resized
mRecentsView.dismissTask(mTaskView, false, false);
}
};
final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
new DeviceProfile.OnDeviceProfileChangeListener() {
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mTarget.removeOnDeviceProfileChangeListener(this);
if (dp.isMultiWindowMode) {
mTaskView.getRootView().addOnLayoutChangeListener(
onLayoutChangeListener);
}
}
};
dismissTaskMenuView(mTarget);
ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
if (options != null
&& ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
options)) {
if (!mFactory.onActivityStarted(mTarget)) {
return;
}
// Add a device profile change listener to kick off animating the side tasks
// once we enter multiwindow mode and relayout
mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
final Runnable animStartedListener = () -> {
// Hide the task view and wait for the window to be resized
// TODO: Consider animating in launcher and do an in-place start activity
// afterwards
mRecentsView.setIgnoreResetTask(taskId);
mTaskView.setAlpha(0f);
};
final int[] position = new int[2];
mThumbnailView.getLocationOnScreen(position);
final int width = (int) (mThumbnailView.getWidth() * mTaskView.getScaleX());
final int height = (int) (mThumbnailView.getHeight() * mTaskView.getScaleY());
final Rect taskBounds = new Rect(position[0], position[1],
position[0] + width, position[1] + height);
// Take the thumbnail of the task without a scrim and apply it back after
float alpha = mThumbnailView.getDimAlpha();
mThumbnailView.setDimAlpha(0);
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
Color.BLACK);
mThumbnailView.setDimAlpha(alpha);
AppTransitionAnimationSpecsFuture future =
new AppTransitionAnimationSpecsFuture(mHandler) {
@Override
public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return Collections.singletonList(new AppTransitionAnimationSpecCompat(
taskId, thumbnail, taskBounds));
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
}
}
}
TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(
R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) {
@Override
protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
// Don't show menu-item if already in multi-window and the task is from
// the secondary display.
// TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
// implementation is enabled
return !activity.getDeviceProfile().isMultiWindowMode
&& (displayId == -1 || displayId == DEFAULT_DISPLAY);
}
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
final ActivityCompat act = new ActivityCompat(activity);
final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
act.getDisplayId());
if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
return null;
}
boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
}
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
activity.getUserEventDispatcher().logActionOnControl(TAP,
LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
return true;
}
};
TaskShortcutFactory FREE_FORM = new MultiWindowFactory(
R.drawable.ic_split_screen, R.string.recent_task_option_freeform) {
@Override
protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
}
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
// Arbitrary bounds only because freeform is in dev mode right now
Rect r = new Rect(50, 50, 200, 200);
activityOptions.setLaunchBounds(r);
return activityOptions;
}
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
activity.returnToHomescreen();
return true;
}
};
TaskShortcutFactory PIN = (activity, tv) -> {
if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
return null;
}
if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
return null;
}
if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
// We shouldn't be able to pin while an app is locked.
return null;
}
return new PinSystemShortcut(activity, tv);
};
class PinSystemShortcut extends SystemShortcut {
private static final String TAG = "PinSystemShortcut";
private final TaskView mTaskView;
public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
mTaskView = tv;
}
@Override
public void onClick(View view) {
Consumer<Boolean> resultCallback = success -> {
if (success) {
SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
mTaskView.getTask().key.id);
} else {
mTaskView.notifyTaskLaunchFailed(TAG);
}
};
mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
dismissTaskMenuView(mTarget);
}
}
TaskShortcutFactory INSTALL = (activity, view) ->
InstantAppResolver.newInstance(activity).isInstantApp(activity,
view.getTask().getTopComponent().getPackageName())
? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
}
@@ -1,327 +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.quickstep;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.InstantAppResolver;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* Represents a system shortcut that can be shown for a recent task.
*/
public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
private static final String TAG = "TaskSystemShortcut";
protected T mSystemShortcut;
public TaskSystemShortcut(T systemShortcut) {
super(systemShortcut);
mSystemShortcut = systemShortcut;
}
protected TaskSystemShortcut(int iconResId, int labelResId) {
super(iconResId, labelResId);
}
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, ItemInfo itemInfo) {
return null;
}
public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
Task task = view.getTask();
WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
dummyInfo.intent = new Intent();
ComponentName component = task.getTopComponent();
dummyInfo.intent.setComponent(component);
dummyInfo.user = UserHandle.of(task.key.userId);
dummyInfo.title = TaskUtils.getTitle(activity, task);
return getOnClickListenerForTask(activity, task, dummyInfo);
}
protected View.OnClickListener getOnClickListenerForTask(
BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
return mSystemShortcut.getOnClickListener(activity, dummyInfo);
}
public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
public AppInfo() {
super(new SystemShortcut.AppInfo());
}
}
public static abstract class MultiWindow extends TaskSystemShortcut {
private Handler mHandler;
public MultiWindow(int iconRes, int textRes) {
super(iconRes, textRes);
mHandler = new Handler(Looper.getMainLooper());
}
protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
protected abstract ActivityOptions makeLaunchOptions(Activity activity);
protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, TaskView taskView) {
final Task task = taskView.getTask();
final int taskId = task.key.id;
final int displayId = task.key.displayId;
if (!task.isDockable) {
return null;
}
if (!isAvailable(activity, displayId)) {
return null;
}
final RecentsView recentsView = activity.getOverviewPanel();
final TaskThumbnailView thumbnailView = taskView.getThumbnail();
return (v -> {
final View.OnLayoutChangeListener onLayoutChangeListener =
new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int l, int t, int r, int b,
int oldL, int oldT, int oldR, int oldB) {
taskView.getRootView().removeOnLayoutChangeListener(this);
recentsView.clearIgnoreResetTask(taskId);
// Start animating in the side pages once launcher has been resized
recentsView.dismissTask(taskView, false, false);
}
};
final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
new DeviceProfile.OnDeviceProfileChangeListener() {
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
activity.removeOnDeviceProfileChangeListener(this);
if (dp.isMultiWindowMode) {
taskView.getRootView().addOnLayoutChangeListener(
onLayoutChangeListener);
}
}
};
dismissTaskMenuView(activity);
ActivityOptions options = makeLaunchOptions(activity);
if (options != null
&& ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
options)) {
if (!onActivityStarted(activity)) {
return;
}
// Add a device profile change listener to kick off animating the side tasks
// once we enter multiwindow mode and relayout
activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
final Runnable animStartedListener = () -> {
// Hide the task view and wait for the window to be resized
// TODO: Consider animating in launcher and do an in-place start activity
// afterwards
recentsView.setIgnoreResetTask(taskId);
taskView.setAlpha(0f);
};
final int[] position = new int[2];
thumbnailView.getLocationOnScreen(position);
final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
final Rect taskBounds = new Rect(position[0], position[1],
position[0] + width, position[1] + height);
// Take the thumbnail of the task without a scrim and apply it back after
float alpha = thumbnailView.getDimAlpha();
thumbnailView.setDimAlpha(0);
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
Color.BLACK);
thumbnailView.setDimAlpha(alpha);
AppTransitionAnimationSpecsFuture future =
new AppTransitionAnimationSpecsFuture(mHandler) {
@Override
public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return Collections.singletonList(new AppTransitionAnimationSpecCompat(
taskId, thumbnail, taskBounds));
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */, displayId);
}
});
}
}
public static class SplitScreen extends MultiWindow {
public SplitScreen() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
}
@Override
protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
// Don't show menu-item if already in multi-window and the task is from
// the secondary display.
// TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
// implementation is enabled
return !activity.getDeviceProfile().isMultiWindowMode
&& (displayId == -1 || displayId == DEFAULT_DISPLAY);
}
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
final ActivityCompat act = new ActivityCompat(activity);
final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
act.getDisplayId());
if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
return null;
}
boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
}
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
activity.getUserEventDispatcher().logActionOnControl(TAP,
LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
return true;
}
}
public static class Freeform extends MultiWindow {
public Freeform() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
}
@Override
protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
}
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
// Arbitrary bounds only because freeform is in dev mode right now
Rect r = new Rect(50, 50, 200, 200);
activityOptions.setLaunchBounds(r);
return activityOptions;
}
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
activity.returnToHomescreen();
return true;
}
}
public static class Pin extends TaskSystemShortcut {
private static final String TAG = Pin.class.getSimpleName();
private Handler mHandler;
public Pin() {
super(R.drawable.ic_pin, R.string.recent_task_option_pin);
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, TaskView taskView) {
if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
return null;
}
if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
return null;
}
if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
// We shouldn't be able to pin while an app is locked.
return null;
}
return view -> {
Consumer<Boolean> resultCallback = success -> {
if (success) {
SystemUiProxy.INSTANCE.get(activity).startScreenPinning(
taskView.getTask().key.id);
} else {
taskView.notifyTaskLaunchFailed(TAG);
}
};
taskView.launchTask(true, resultCallback, mHandler);
dismissTaskMenuView(activity);
};
}
}
public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
public Install() {
super(new SystemShortcut.Install());
}
@Override
protected View.OnClickListener getOnClickListenerForTask(
BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
task.getTopComponent().getPackageName())) {
return mSystemShortcut.createOnClickListener(activity, itemInfo);
}
return null;
}
}
}
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -26,7 +25,6 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -41,16 +39,13 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.IconView.OnScaleUpdateListener;
import java.util.List;
/**
* Contains options for a recent task when long-pressing its icon.
*/
@@ -197,22 +192,16 @@ public class TaskMenuView extends AbstractFloatingView {
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
final List<TaskSystemShortcut> shortcuts =
TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
final int count = shortcuts.size();
for (int i = 0; i < count; ++i) {
final TaskSystemShortcut menuOption = shortcuts.get(i);
addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
}
TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView)
.forEach(this::addMenuOption);
}
private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
private void addMenuOption(SystemShortcut menuOption) {
ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
menuOptionView.setOnClickListener(onClickListener);
menuOptionView.setOnClickListener(menuOption);
mOptionLayout.addView(menuOptionView);
}
@@ -53,6 +53,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -61,7 +62,6 @@ import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
@@ -713,15 +713,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
final List<TaskSystemShortcut> shortcuts =
TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
final int count = shortcuts.size();
for (int i = 0; i < count; ++i) {
final TaskSystemShortcut menuOption = shortcuts.get(i);
OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
if (onClickListener != null) {
info.addAction(menuOption.createAccessibilityAction(context));
}
for (SystemShortcut s : TaskOverlayFactory.INSTANCE.get(getContext())
.getEnabledShortcuts(this)) {
info.addAction(s.createAccessibilityAction(context));
}
if (mDigitalWellBeingToast.hasLimit()) {
@@ -752,16 +746,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
return true;
}
final List<TaskSystemShortcut> shortcuts =
TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
final int count = shortcuts.size();
for (int i = 0; i < count; ++i) {
final TaskSystemShortcut menuOption = shortcuts.get(i);
if (menuOption.hasHandlerForAction(action)) {
OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
if (onClickListener != null) {
onClickListener.onClick(this);
}
for (SystemShortcut s : TaskOverlayFactory.INSTANCE.get(getContext())
.getEnabledShortcuts(this)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
return true;
}
}
@@ -382,8 +382,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
SystemShortcut widgetInfo = new SystemShortcut.Widgets();
View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo);
SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
@@ -394,7 +393,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
}
}
if (onClickListener != null && widgetsView == null) {
if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
initializeSystemShortcut(
@@ -407,7 +406,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
} else if (onClickListener == null && widgetsView != null) {
} else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
@@ -430,8 +429,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
info.setIconAndContentDescriptionFor((ImageView) view);
}
view.setTag(info);
view.setOnClickListener(info.getOnClickListener(mLauncher,
(ItemInfo) mOriginalIcon.getTag()));
view.setOnClickListener(info);
}
/**
@@ -16,13 +16,19 @@
package com.android.launcher3.popup;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.AbstractFloatingView;
@@ -32,55 +38,75 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
public RemoteActionShortcut(RemoteAction action) {
super(action.getIcon(), action.getTitle(), action.getContentDescription(),
R.id.action_remote_action_shortcut);
public RemoteActionShortcut(RemoteAction action,
BaseDraggingActivity activity, ItemInfo itemInfo) {
super(0, R.id.action_remote_action_shortcut, activity, itemInfo);
mAction = action;
}
@Override
public View.OnClickListener getOnClickListener(
final BaseDraggingActivity activity, final ItemInfo itemInfo) {
return view -> {
AbstractFloatingView.closeAllOpenViews(activity);
public void setIconAndLabelFor(View iconView, TextView labelView) {
mAction.getIcon().loadDrawableAsync(iconView.getContext(),
iconView::setBackground,
MAIN_EXECUTOR.getHandler());
labelView.setText(mAction.getTitle());
}
final String actionIdentity = mAction.getTitle() + ", " +
itemInfo.getTargetComponent().getPackageName();
try {
if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
mAction.getActionIntent().send(
activity,
0,
new Intent().putExtra(
Intent.EXTRA_PACKAGE_NAME,
itemInfo.getTargetComponent().getPackageName()),
(pendingIntent, intent, resultCode, resultData, resultExtras) -> {
if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
if (resultData != null && !resultData.isEmpty()) {
Log.e(TAG, "Remote action returned result: " + actionIdentity
+ " : " + resultData);
Toast.makeText(activity, resultData, Toast.LENGTH_SHORT).show();
}
},
new Handler(Looper.getMainLooper()));
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
Toast.makeText(activity, activity.getString(
R.string.remote_action_failed,
mAction.getTitle()),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void setIconAndContentDescriptionFor(ImageView view) {
mAction.getIcon().loadDrawableAsync(view.getContext(),
view::setImageDrawable,
MAIN_EXECUTOR.getHandler());
view.setContentDescription(mAction.getContentDescription());
}
activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
};
@Override
public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
return new AccessibilityNodeInfo.AccessibilityAction(
R.id.action_remote_action_shortcut, mAction.getContentDescription());
}
@Override
public void onClick(View view) {
AbstractFloatingView.closeAllOpenViews(mTarget);
final String actionIdentity = mAction.getTitle() + ", "
+ mItemInfo.getTargetComponent().getPackageName();
try {
if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
mAction.getActionIntent().send(
mTarget,
0,
new Intent().putExtra(
Intent.EXTRA_PACKAGE_NAME,
mItemInfo.getTargetComponent().getPackageName()),
(pendingIntent, intent, resultCode, resultData, resultExtras) -> {
if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
if (resultData != null && !resultData.isEmpty()) {
Log.e(TAG, "Remote action returned result: " + actionIdentity
+ " : " + resultData);
Toast.makeText(mTarget, resultData, Toast.LENGTH_SHORT).show();
}
},
MAIN_EXECUTOR.getHandler());
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
Toast.makeText(mTarget, mTarget.getString(
R.string.remote_action_failed,
mAction.getTitle()),
Toast.LENGTH_SHORT)
.show();
}
mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
}
@Override
@@ -5,14 +5,13 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
@@ -39,41 +38,30 @@ import java.util.List;
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
* @param <T>
*/
public abstract class SystemShortcut<T extends BaseDraggingActivity>
extends ItemInfo {
public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
implements View.OnClickListener {
private final int mIconResId;
private final int mLabelResId;
private final Icon mIcon;
private final CharSequence mLabel;
private final CharSequence mContentDescription;
private final int mAccessibilityActionId;
public SystemShortcut(int iconResId, int labelResId) {
protected final T mTarget;
protected final ItemInfo mItemInfo;
public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
mIcon = null;
mLabel = null;
mContentDescription = null;
mTarget = target;
mItemInfo = itemInfo;
}
public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
int accessibilityActionId) {
mIcon = icon;
mLabel = label;
mContentDescription = contentDescription;
mAccessibilityActionId = accessibilityActionId;
mIconResId = 0;
mLabelResId = 0;
}
public SystemShortcut(SystemShortcut other) {
public SystemShortcut(SystemShortcut<T> other) {
mIconResId = other.mIconResId;
mLabelResId = other.mLabelResId;
mIcon = other.mIcon;
mLabel = other.mLabel;
mContentDescription = other.mContentDescription;
mAccessibilityActionId = other.mAccessibilityActionId;
mTarget = other.mTarget;
mItemInfo = other.mItemInfo;
}
/**
@@ -84,150 +72,135 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity>
}
public void setIconAndLabelFor(View iconView, TextView labelView) {
if (mIcon != null) {
mIcon.loadDrawableAsync(iconView.getContext(),
iconView::setBackground,
new Handler(Looper.getMainLooper()));
} else {
iconView.setBackgroundResource(mIconResId);
}
if (mLabel != null) {
labelView.setText(mLabel);
} else {
labelView.setText(mLabelResId);
}
iconView.setBackgroundResource(mIconResId);
labelView.setText(mLabelResId);
}
public void setIconAndContentDescriptionFor(ImageView view) {
if (mIcon != null) {
mIcon.loadDrawableAsync(view.getContext(),
view::setImageDrawable,
new Handler(Looper.getMainLooper()));
} else {
view.setImageResource(mIconResId);
}
view.setContentDescription(getContentDescription(view.getContext()));
}
private CharSequence getContentDescription(Context context) {
return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
view.setImageResource(mIconResId);
view.setContentDescription(view.getContext().getText(mLabelResId));
}
public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
getContentDescription(context));
return new AccessibilityNodeInfo.AccessibilityAction(
mAccessibilityActionId, context.getText(mLabelResId));
}
public boolean hasHandlerForAction(int action) {
return mAccessibilityActionId == action;
}
public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
public interface Factory<T extends BaseDraggingActivity> {
@Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
}
public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets == null) {
return null;
}
return new Widgets(launcher, itemInfo);
};
public static class Widgets extends SystemShortcut<Launcher> {
public Widgets() {
super(R.drawable.ic_widget, R.string.widget_button_text);
public Widgets(Launcher target, ItemInfo itemInfo) {
super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
}
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets == null) {
return null;
}
return (view) -> {
AbstractFloatingView.closeAllOpenViews(launcher);
WidgetsBottomSheet widgetsBottomSheet =
(WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
widgetsBottomSheet.populateAndShow(itemInfo);
launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.WIDGETS_BUTTON, view);
};
public void onClick(View view) {
AbstractFloatingView.closeAllOpenViews(mTarget);
WidgetsBottomSheet widgetsBottomSheet =
(WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
widgetsBottomSheet.populateAndShow(mItemInfo);
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.WIDGETS_BUTTON, view);
}
}
public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
public static class AppInfo extends SystemShortcut {
public AppInfo() {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label);
public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
itemInfo);
}
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, ItemInfo itemInfo) {
return (view) -> {
dismissTaskMenuView(activity);
Rect sourceBounds = activity.getViewBounds(view);
new PackageManagerHelper(activity).startDetailsActivityForInfo(
itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
};
public void onClick(View view) {
dismissTaskMenuView(mTarget);
Rect sourceBounds = mTarget.getViewBounds(view);
new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
}
}
public static Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
&& ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
boolean isInstantApp = false;
if (itemInfo instanceof com.android.launcher3.AppInfo) {
com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
return new Install(activity, itemInfo);
};
public static class Install extends SystemShortcut {
public Install() {
super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
target, itemInfo);
}
@Override
public View.OnClickListener getOnClickListener(
BaseDraggingActivity activity, ItemInfo itemInfo) {
boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
boolean isInstantApp = false;
if (itemInfo instanceof com.android.launcher3.AppInfo) {
com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
return createOnClickListener(activity, itemInfo);
}
public View.OnClickListener createOnClickListener(
BaseDraggingActivity activity, ItemInfo itemInfo) {
return view -> {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
itemInfo.getTargetComponent().getPackageName());
activity.startActivitySafely(view, intent, itemInfo, null);
AbstractFloatingView.closeAllOpenViews(activity);
};
public void onClick(View view) {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
mItemInfo.getTargetComponent().getPackageName());
mTarget.startActivitySafely(view, intent, mItemInfo, null);
AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
public static Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
return new DismissPrediction(launcher, itemInfo);
};
public static class DismissPrediction extends SystemShortcut<Launcher> {
public DismissPrediction() {
super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label, launcher,
itemInfo);
}
@Override
public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) {
if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
return (view) -> {
PopupContainerWithArrow.closeAllOpenViews(activity);
activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
AppLaunchTracker.INSTANCE.get(view.getContext())
.onDismissApp(itemInfo.getTargetComponent(),
itemInfo.user,
AppLaunchTracker.CONTAINER_PREDICTIONS);
};
public void onClick(View view) {
PopupContainerWithArrow.closeAllOpenViews(mTarget);
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
mItemInfo.getTargetComponent(),
mItemInfo.user,
AppLaunchTracker.CONTAINER_PREDICTIONS);
}
}
protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
public static void dismissTaskMenuView(BaseDraggingActivity activity) {
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
@@ -34,25 +34,24 @@ public class SystemShortcutFactory implements ResourceBasedOverride {
forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
/** Note that these are in order of priority. */
private final SystemShortcut[] mAllShortcuts;
private final SystemShortcut.Factory[] mAllFactories;
@SuppressWarnings("unused")
public SystemShortcutFactory() {
this(new SystemShortcut.AppInfo(),
new SystemShortcut.Widgets(),
new SystemShortcut.Install(),
new SystemShortcut.DismissPrediction());
this(SystemShortcut.APP_INFO, SystemShortcut.WIDGETS, SystemShortcut.INSTALL,
SystemShortcut.DISMISS_PREDICTION);
}
protected SystemShortcutFactory(SystemShortcut... shortcuts) {
mAllShortcuts = shortcuts;
protected SystemShortcutFactory(SystemShortcut.Factory... factories) {
mAllFactories = factories;
}
public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
List<SystemShortcut> systemShortcuts = new ArrayList<>();
for (SystemShortcut systemShortcut : mAllShortcuts) {
if (systemShortcut.getOnClickListener(launcher, info) != null) {
systemShortcuts.add(systemShortcut);
for (SystemShortcut.Factory factory : mAllFactories) {
SystemShortcut shortcut = factory.getShortcut(launcher, info);
if (shortcut != null) {
systemShortcuts.add(shortcut);
}
}