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

Commit 7caf5f0e authored by Vinit Nayak's avatar Vinit Nayak
Browse files

Show distinct TaskMenus for each task in GroupedTaskView

* For now we only show the App Info option
for each task icon clicked for GroupedTaskView
* Have TaskMenuView operate on a specific task instead
of only a TaskView
* NOTE: getItemInfo() in TaskIdAttributeContainer needs to
dynamically call getItemInfo(Task) because at the time of
creation of the container in TaskView#bind(), the task
object provided initially is a palceholder task which only
has a taskId and no other attributes set.
getItemInfo() needs a non-null baseIntent, which the
placeholder task doesn't have.
Right fix for this is to have GestureState hold onto
multiple running tasks when in split screen that are
provided by ActivityManagerWrapper when the gesture starts
(but that change is extensive and out of scope w/ this
workaround available)

Bug: 181704764
Test: Open TaskMenuView's w/ either task icon for
GroupedTaskView, opens up corresponding tasks menu.

Change-Id: I70b7c13394ad4980cabbd611cb928bb03d8a2924
parent 03c13ad1
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;

@@ -63,11 +64,16 @@ import java.util.List;
public class TaskOverlayFactory implements ResourceBasedOverride {

    public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
            DeviceProfile deviceProfile) {
            DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) {
        final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
        final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
        boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1;
        for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
            SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
            if (hasMultipleTasks && !menuOption.showForSplitscreen()) {
                continue;
            }

            SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer);
            if (shortcut == null) {
                continue;
            }
@@ -87,7 +93,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
        if (!canLauncherRotate && isInLandscape) {
            // Add screenshot action to task menu.
            SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
                    .getShortcut(activity, taskView);
                    .getShortcut(activity, taskContainer);
            if (screenshotShortcut != null) {
                shortcuts.add(screenshotShortcut);
            }
@@ -95,7 +101,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
            // Add modal action only if display orientation is the same as the device orientation.
            if (orientedState.getDisplayRotation() == ROTATION_0) {
                SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
                        .getShortcut(activity, taskView);
                        .getShortcut(activity, taskContainer);
                if (modalShortcut != null) {
                    shortcuts.add(modalShortcut);
                }
@@ -105,7 +111,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
    }


    public static void addSplitOptions(List<SystemShortcut> outShortcuts,
    private static void addSplitOptions(List<SystemShortcut> outShortcuts,
            BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
        int[] taskViewTaskIds = taskView.getTaskIds();
        boolean alreadyHasMultipleTasks = taskViewTaskIds[0] != -1 &&
+51 −26
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -61,9 +62,25 @@ import java.util.List;
 * Represents a system shortcut that can be shown for a recent task.
 */
public interface TaskShortcutFactory {
    SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
    SystemShortcut getShortcut(BaseDraggingActivity activity,
            TaskIdAttributeContainer taskContainer);

    TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
    default boolean showForSplitscreen() {
        return false;
    }

    TaskShortcutFactory APP_INFO = new TaskShortcutFactory() {
        @Override
        public SystemShortcut getShortcut(BaseDraggingActivity activity,
                TaskIdAttributeContainer taskContainer) {
            return new AppInfo(activity, taskContainer.getItemInfo());
        }

        @Override
        public boolean showForSplitscreen() {
            return true;
        }
    };

    abstract class MultiWindowFactory implements TaskShortcutFactory {

@@ -82,15 +99,16 @@ public interface TaskShortcutFactory {
        protected abstract boolean onActivityStarted(BaseDraggingActivity activity);

        @Override
        public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
            final Task task  = taskView.getTask();
        public SystemShortcut getShortcut(BaseDraggingActivity activity,
                TaskIdAttributeContainer taskContainer) {
            final Task task  = taskContainer.getTask();
            if (!task.isDockable) {
                return null;
            }
            if (!isAvailable(activity, task.key.displayId)) {
                return null;
            }
            return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this,
            return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this,
                    mLauncherEvent);
        }
    }
@@ -123,13 +141,14 @@ public interface TaskShortcutFactory {
        private final LauncherEvent mLauncherEvent;

        public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
                TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
            super(iconRes, textRes, activity, taskView.getItemInfo());
                TaskIdAttributeContainer taskContainer, MultiWindowFactory factory,
                LauncherEvent launcherEvent) {
            super(iconRes, textRes, activity, taskContainer.getItemInfo());
            mLauncherEvent = launcherEvent;
            mHandler = new Handler(Looper.getMainLooper());
            mTaskView = taskView;
            mTaskView = taskContainer.getTaskView();
            mRecentsView = activity.getOverviewPanel();
            mThumbnailView = taskView.getThumbnail();
            mThumbnailView = taskContainer.getThumbnailView();
            mFactory = factory;
        }

@@ -233,11 +252,13 @@ public interface TaskShortcutFactory {
        }

        @Override
        public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
            SystemShortcut shortcut = super.getShortcut(activity, taskView);
        public SystemShortcut getShortcut(BaseDraggingActivity activity,
                TaskIdAttributeContainer taskContainer) {
            SystemShortcut shortcut = super.getShortcut(activity, taskContainer);
            if (shortcut != null && FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
                // Disable if there's only one recent app for split screen
                shortcut.setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
                shortcut.setEnabled(taskContainer.getTaskView().
                        getRecentsView().getTaskViewCount() > 1);
            }
            return shortcut;
        }
@@ -284,7 +305,7 @@ public interface TaskShortcutFactory {
        }
    };

    TaskShortcutFactory PIN = (activity, tv) -> {
    TaskShortcutFactory PIN = (activity, taskContainer) -> {
        if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
            return null;
        }
@@ -295,7 +316,7 @@ public interface TaskShortcutFactory {
            // We shouldn't be able to pin while an app is locked.
            return null;
        }
        return new PinSystemShortcut(activity, tv);
        return new PinSystemShortcut(activity, taskContainer);
    };

    class PinSystemShortcut extends SystemShortcut {
@@ -304,9 +325,11 @@ public interface TaskShortcutFactory {

        private final TaskView mTaskView;

        public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
            mTaskView = tv;
        public PinSystemShortcut(BaseDraggingActivity target,
                TaskIdAttributeContainer taskContainer) {
            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target,
                    taskContainer.getItemInfo());
            mTaskView = taskContainer.getTaskView();
        }

        @Override
@@ -320,20 +343,22 @@ public interface TaskShortcutFactory {
        }
    }

    TaskShortcutFactory INSTALL = (activity, view) ->
    TaskShortcutFactory INSTALL = (activity, taskContainer) ->
            InstantAppResolver.newInstance(activity).isInstantApp(activity,
                 view.getTask().getTopComponent().getPackageName())
                    ? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
                 taskContainer.getTask().getTopComponent().getPackageName())
                    ? new SystemShortcut.Install(activity, taskContainer.getItemInfo()) : null;

    TaskShortcutFactory WELLBEING = (activity, view) ->
            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
    TaskShortcutFactory WELLBEING = (activity, taskContainer) ->
            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo());

    TaskShortcutFactory SCREENSHOT = (activity, tv) -> tv.getThumbnail().getTaskOverlay()
            .getScreenshotShortcut(activity, tv.getItemInfo());
    TaskShortcutFactory SCREENSHOT = (activity, taskContainer) ->
            taskContainer.getThumbnailView().getTaskOverlay()
                    .getScreenshotShortcut(activity, taskContainer.getItemInfo());

    TaskShortcutFactory MODAL = (activity, tv) -> {
    TaskShortcutFactory MODAL = (activity, taskContainer) -> {
        if (ENABLE_OVERVIEW_SELECTIONS.get()) {
            return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
            return taskContainer.getThumbnailView()
                    .getTaskOverlay().getModalStateSystemShortcut(taskContainer.getItemInfo());
        }
        return null;
    };
+12 −4
Original line number Diff line number Diff line
package com.android.quickstep.views;

import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -11,7 +14,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
@@ -72,7 +74,9 @@ public class GroupedTaskView extends TaskView {
        super.bind(primary, orientedState);
        mSecondaryTask = secondary;
        mTaskIdContainer[1] = secondary.key.id;
        mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2);
        mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
                STAGE_POSITION_BOTTOM_OR_RIGHT);
        mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
        mSnapshotView2.bind(secondary);
        mSplitBoundsConfig = splitBoundsConfig;
    }
@@ -112,6 +116,10 @@ public class GroupedTaskView extends TaskView {
        }
    }

    protected boolean showTaskMenuWithContainer(IconView iconView) {
        return TaskMenuView.showForTask(mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1]);
    }

    public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
        mSplitBoundsConfig = stagedSplitBounds;
        invalidate();
@@ -143,14 +151,14 @@ public class GroupedTaskView extends TaskView {
    @Override
    public RunnableList launchTaskAnimated() {
        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, null /*callback*/);
                STAGE_POSITION_TOP_OR_LEFT, null /*callback*/);
        return null;
    }

    @Override
    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback);
                STAGE_POSITION_TOP_OR_LEFT, callback);
    }

    @Override
+50 −22
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.quickstep.views;

import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;

import android.animation.Animator;
@@ -39,6 +41,7 @@ import android.widget.TextView;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
@@ -49,6 +52,7 @@ import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;

/**
 * Contains options for a recent task when long-pressing its icon.
@@ -65,6 +69,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
    private TextView mTaskName;
    private AnimatorSet mOpenCloseAnimator;
    private TaskView mTaskView;
    private TaskIdAttributeContainer mTaskContainer;
    private LinearLayout mOptionLayout;

    public TaskMenuView(Context context, AttributeSet attrs) {
@@ -129,7 +134,8 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        // Inset due to margin
        PointF additionalInset = pagedOrientationHandler
                .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
        int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
        int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;

        float adjustedY = y + taskTopMargin - additionalInset.y;
        float adjustedX = x - additionalInset.x;
@@ -137,7 +143,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
        // which would render the X and Y position set here incorrect
        setPivotX(0);
        if (mActivity.getDeviceProfile().overviewShowAsGrid) {
        if (deviceProfile.overviewShowAsGrid) {
            // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
            setPivotY(-taskTopMargin);
        } else {
@@ -145,9 +151,26 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        }
        setRotation(pagedOrientationHandler.getDegreesRotated());
        setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
                mTaskView.getThumbnail(), overscrollShift));
                mTaskContainer.getThumbnailView(), overscrollShift));
        setY(pagedOrientationHandler.getTaskMenuY(
                adjustedY, mTaskView.getThumbnail(), overscrollShift));
                adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));

        // TODO(b/193432925) temporary menu placement for split screen task menus
        TaskIdAttributeContainer[] taskIdAttributeContainers =
                mTaskView.getTaskIdAttributeContainers();
        if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
            if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
                return;
            }
            Rect r = new Rect();
            mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
            if (deviceProfile.isLandscape) {
                setX(r.left);
            } else {
                setY(r.top);

            }
        }
    }

    public void onRotationChanged() {
@@ -162,19 +185,21 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        }
    }

    public static boolean showForTask(TaskView taskView) {
        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
    public static boolean showForTask(TaskIdAttributeContainer taskContainer) {
        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(
                taskContainer.getTaskView().getContext());
        final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
                        R.layout.task_menu, activity.getDragLayer(), false);
        return taskMenuView.populateAndShowForTask(taskView);
        return taskMenuView.populateAndShowForTask(taskContainer);
    }

    private boolean populateAndShowForTask(TaskView taskView) {
    private boolean populateAndShowForTask(TaskIdAttributeContainer taskContainer) {
        if (isAttachedToWindow()) {
            return false;
        }
        mActivity.getDragLayer().addView(this);
        mTaskView = taskView;
        mTaskView = taskContainer.getTaskView();
        mTaskContainer = taskContainer;
        if (!populateAndLayoutMenu()) {
            return false;
        }
@@ -192,20 +217,21 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange

    /** @return true if successfully able to populate task view menu, false otherwise */
    private boolean populateAndLayoutMenu() {
        if (mTaskView.getTask().icon == null) {
        if (mTaskContainer.getTask().icon == null) {
            // Icon may not be loaded
            return false;
        }
        addMenuOptions(mTaskView);
        orientAroundTaskView(mTaskView);
        addMenuOptions(mTaskContainer);
        orientAroundTaskView(mTaskContainer);
        return true;
    }

    private void addMenuOptions(TaskView taskView) {
        mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
    private void addMenuOptions(TaskIdAttributeContainer taskContainer) {
        mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
        mTaskName.setOnClickListener(v -> close(true));
        
        TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile())
        TaskOverlayFactory.getEnabledShortcuts(mTaskView, mActivity.getDeviceProfile(),
                taskContainer)
                .forEach(this::addMenuOption);
    }

@@ -223,23 +249,25 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        mOptionLayout.addView(menuOptionView);
    }

    private void orientAroundTaskView(TaskView taskView) {
        PagedOrientationHandler orientationHandler = taskView.getPagedOrientationHandler();
    private void orientAroundTaskView(TaskIdAttributeContainer taskContainer) {
        RecentsView recentsView = mActivity.getOverviewPanel();
        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);

        // Get Position
        mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
        mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
        Rect insets = mActivity.getDragLayer().getInsets();
        BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
        int padding = getResources()
                .getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
        params.width = orientationHandler.getTaskMenuWidth(taskView.getThumbnail()) - (2 * padding);
        params.width = orientationHandler
                .getTaskMenuWidth(taskContainer.getThumbnailView()) - (2 * padding);
        // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
        params.gravity = Gravity.LEFT;
        setLayoutParams(params);
        setScaleX(taskView.getScaleX());
        setScaleY(taskView.getScaleY());
        setScaleX(mTaskView.getScaleX());
        setScaleY(mTaskView.getScaleY());

        // Set divider spacing
        ShapeDrawable divider = new ShapeDrawable(new RectShape());
@@ -272,7 +300,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        revealAnimator.setInterpolator(Interpolators.DEACCEL);
        mOpenCloseAnimator.playTogether(revealAnimator,
                ObjectAnimator.ofFloat(
                        mTaskView.getThumbnail(), DIM_ALPHA,
                        mTaskContainer.getThumbnailView(), DIM_ALPHA,
                        closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
                ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
        mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
+49 −25

File changed.

Preview size limit exceeded, changes collapsed.