Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7805d497 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

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
parent c78308a9
Loading
Loading
Loading
Loading
+13 −14
Original line number Diff line number Diff line
@@ -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;
+312 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
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;
@@ -33,11 +34,12 @@ 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.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;
@@ -58,28 +60,11 @@ 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;
    }
public interface TaskShortcutFactory {

    protected TaskSystemShortcut(int iconResId, int labelResId) {
        super(iconResId, labelResId);
    }
    SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);

    @Override
    public View.OnClickListener getOnClickListener(
            BaseDraggingActivity activity, ItemInfo itemInfo) {
        return null;
    }

    public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
    static WorkspaceItemInfo dummyInfo(TaskView view) {
        Task task = view.getTask();

        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
@@ -87,29 +72,20 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
        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);
        dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
        return dummyInfo;
    }

    protected View.OnClickListener getOnClickListenerForTask(
            BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
        return mSystemShortcut.getOnClickListener(activity, dummyInfo);
    }
    TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));

    public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
        public AppInfo() {
            super(new SystemShortcut.AppInfo());
        }
    }
    abstract class MultiWindowFactory implements TaskShortcutFactory {

    public static abstract class MultiWindow extends TaskSystemShortcut {
        private final int mIconRes;
        private final int mTextRes;

        private Handler mHandler;

        public MultiWindow(int iconRes, int textRes) {
            super(iconRes, textRes);
            mHandler = new Handler(Looper.getMainLooper());
        MultiWindowFactory(int iconRes, int textRes) {
            mIconRes = iconRes;
            mTextRes = textRes;
        }

        protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
@@ -117,31 +93,53 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
        protected abstract boolean onActivityStarted(BaseDraggingActivity activity);

        @Override
        public View.OnClickListener getOnClickListener(
                BaseDraggingActivity activity, TaskView taskView) {
        public SystemShortcut getShortcut(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)) {
            if (!isAvailable(activity, task.key.displayId)) {
                return null;
            }
            final RecentsView recentsView = activity.getOverviewPanel();
            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 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);
                            mTaskView.getRootView().removeOnLayoutChangeListener(this);
                            mRecentsView.clearIgnoreResetTask(taskId);

                            // Start animating in the side pages once launcher has been resized
                                recentsView.dismissTask(taskView, false, false);
                            mRecentsView.dismissTask(mTaskView, false, false);
                        }
                    };

@@ -149,49 +147,49 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
                    new DeviceProfile.OnDeviceProfileChangeListener() {
                        @Override
                        public void onDeviceProfileChanged(DeviceProfile dp) {
                                activity.removeOnDeviceProfileChangeListener(this);
                            mTarget.removeOnDeviceProfileChangeListener(this);
                            if (dp.isMultiWindowMode) {
                                    taskView.getRootView().addOnLayoutChangeListener(
                                mTaskView.getRootView().addOnLayoutChangeListener(
                                        onLayoutChangeListener);
                            }
                        }
                    };

                dismissTaskMenuView(activity);
            dismissTaskMenuView(mTarget);

                ActivityOptions options = makeLaunchOptions(activity);
            ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
            if (options != null
                    && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
                            options)) {
                    if (!onActivityStarted(activity)) {
                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
                    activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
                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
                        recentsView.setIgnoreResetTask(taskId);
                        taskView.setAlpha(0f);
                    mRecentsView.setIgnoreResetTask(taskId);
                    mTaskView.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());
                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 = thumbnailView.getDimAlpha();
                    thumbnailView.setDimAlpha(0);
                float alpha = mThumbnailView.getDimAlpha();
                mThumbnailView.setDimAlpha(0);
                Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
                            taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
                        taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
                        Color.BLACK);
                    thumbnailView.setDimAlpha(alpha);
                mThumbnailView.setDimAlpha(alpha);

                AppTransitionAnimationSpecsFuture future =
                        new AppTransitionAnimationSpecsFuture(mHandler) {
@@ -202,16 +200,14 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
                    }
                };
                WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
                            future, animStartedListener, mHandler, true /* scaleUp */, displayId);
                        future, animStartedListener, mHandler, true /* scaleUp */,
                        taskKey.displayId);
            }
            });
        }
    }

    public static class SplitScreen extends MultiWindow {
        public SplitScreen() {
            super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
        }
    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) {
@@ -242,12 +238,10 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
                    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);
        }
    TaskShortcutFactory FREE_FORM = new MultiWindowFactory(
            R.drawable.ic_split_screen, R.string.recent_task_option_freeform) {

        @Override
        protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
@@ -268,22 +262,9 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
            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) {
    TaskShortcutFactory PIN = (activity, tv) -> {
        if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
            return null;
        }
@@ -294,34 +275,38 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
            // We shouldn't be able to pin while an app is locked.
            return null;
        }
            return view -> {
        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(activity).startScreenPinning(
                                taskView.getTask().key.id);
                    SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
                            mTaskView.getTask().key.id);
                } else {
                        taskView.notifyTaskLaunchFailed(TAG);
                    mTaskView.notifyTaskLaunchFailed(TAG);
                }
            };
                taskView.launchTask(true, resultCallback, mHandler);
                dismissTaskMenuView(activity);
            };
            mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
            dismissTaskMenuView(mTarget);
        }
    }

    public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
        public Install() {
            super(new SystemShortcut.Install());
        }
    TaskShortcutFactory INSTALL = (activity, view) ->
            InstantAppResolver.newInstance(activity).isInstantApp(activity,
                 view.getTask().getTopComponent().getPackageName())
                    ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;

        @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;
        }
    }
}
+5 −16
Original line number Diff line number Diff line
@@ -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);
    }

+8 −20
Original line number Diff line number Diff line
@@ -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;
            }
        }
+4 −6
Original line number Diff line number Diff line
@@ -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);
    }

    /**
Loading