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

Commit 538281db authored by Jeremy Sim's avatar Jeremy Sim Committed by Android (Google) Code Review
Browse files

Merge "Use already-running app instances when splitting" into tm-qpr-dev

parents 97db6097 2cbd7bbf
Loading
Loading
Loading
Loading
+26 −10
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
@@ -41,6 +43,9 @@ import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;

import java.util.function.Consumer;

public interface QuickstepSystemShortcut {

@@ -93,22 +98,30 @@ public interface QuickstepSystemShortcut {
            }

            StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
            RecentsView recentsView = mTarget.getOverviewPanel();
            // Check if there is already an instance of this app running, if so, initiate the split
            // using that.
            recentsView.findLastActiveTaskAndDoSplitOperation(
                    intent.getComponent(),
                    (Consumer<Task>) foundTask -> {
                        SplitSelectSource source = new SplitSelectSource(mOriginalView,
                    new BitmapDrawable(bitmap), intent, mPosition, mItemInfo, splitEvent);
                                new BitmapDrawable(bitmap), intent, mPosition, mItemInfo,
                                splitEvent, foundTask);
                        if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
                            startSplitToHome(source);
                        } else {
                RecentsView recentsView = mTarget.getOverviewPanel();
                            recentsView.initiateSplitSelect(source);
                        }
                    }
            );
        }

        private void startSplitToHome(SplitSelectSource source) {
            AbstractFloatingView.closeAllOpenViews(mTarget);

            SplitSelectStateController controller = mTarget.getSplitSelectStateController();
            controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
                    source.itemInfo, source.splitEvent);
                    source.itemInfo, source.splitEvent, source.alreadyRunningTask);

            RecentsView recentsView = mTarget.getOverviewPanel();
            recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
@@ -142,16 +155,19 @@ public interface QuickstepSystemShortcut {
        public final SplitPositionOption position;
        public final ItemInfo itemInfo;
        public final StatsLogManager.EventEnum splitEvent;
        @Nullable
        public final Task alreadyRunningTask;

        public SplitSelectSource(View view, Drawable drawable, Intent intent,
                SplitPositionOption position, ItemInfo itemInfo,
                StatsLogManager.EventEnum splitEvent) {
                StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) {
            this.view = view;
            this.drawable = drawable;
            this.intent = intent;
            this.position = position;
            this.itemInfo = itemInfo;
            this.splitEvent = splitEvent;
            this.alreadyRunningTask = foundTask;
        }
    }
}
+27 −18
Original line number Diff line number Diff line
@@ -28,8 +28,10 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;

import java.io.PrintWriter;
import java.util.function.Consumer;

/**
 * Base class for providing different taskbar UI
@@ -160,23 +162,30 @@ public class TaskbarUIController {
     */
    public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
        RecentsView recents = getRecentsView();
        TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
        if (foundTaskView != null) {
        recents.findLastActiveTaskAndDoSplitOperation(
                info.getTargetComponent(),
                (Consumer<Task>) foundTask -> {
                    if (foundTask != null) {
                        TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
                        // There is already a running app of this type, use that as second app.
                        recents.confirmSplitSelect(
                                foundTaskView,
                                foundTaskView.getTask(),
                                foundTaskView.getIconView().getDrawable(),
                                foundTaskView.getThumbnail(),
                                foundTaskView.getThumbnail().getThumbnail(),
                    /* intent */ null);
                                null /* intent */);
                    } else {
                        // No running app of that type, create a new instance as second app.
                        recents.confirmSplitSelect(
                    /* containerTaskView */ null,
                    /* task */ null,
                                null /* containerTaskView */,
                                null /* task */,
                                new BitmapDrawable(info.bitmap.icon),
                                startingView,
                    /* thumbnail */ null,
                                null /* thumbnail */,
                                intent);
                    }
                }
        );
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -96,7 +96,8 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
    }

    /**
     * Fetches the list of recent tasks.
     * Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
     * at the end of the list.
     *
     * @param callback The callback to receive the task plan once its complete or null. This is
     *                always called on the UI thread.
+9 −5
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.RemoteAnimationAdapter;
@@ -124,10 +123,15 @@ public class SplitSelectStateController {
     * To be called after first task selected from home or all apps.
     */
    public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
            @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
            @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent,
            @Nullable Task alreadyRunningTask) {
        if (alreadyRunningTask != null) {
            mInitialTaskId = alreadyRunningTask.key.id;
        } else {
            mInitialTaskIntent = intent;
            mUser = itemInfo.user;
        mItemInfo = itemInfo;
        }

        setInitialData(stagePosition, splitEvent, itemInfo);
    }

+73 −13
Original line number Diff line number Diff line
@@ -1221,18 +1221,50 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
    }

    /**
     * Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
     * no match.
     * Pulls the list of active Tasks from RecentModel, and finds the most recently active Task
     * matching a given ComponentName. Then uses that Task (which could be null) with the given
     * callback.
     *
     * Used in various splitscreen operations when we need to check if there is a currently running
     * Task of a certain type and use the most recent one.
     */
    @Nullable
    public TaskView getTaskViewByComponentName(ComponentName componentName) {
        for (int i = 0; i < getTaskViewCount(); i++) {
            TaskView taskView = requireTaskViewAt(i);
            if (taskView.getTask().key.sourceComponent.equals(componentName)) {
                return taskView;
    public void findLastActiveTaskAndDoSplitOperation(ComponentName componentName,
            Consumer<Task> callback) {
        mModel.getTasks(taskGroups -> {
            Task lastActiveTask = null;
            // Loop through tasks in reverse, since they are ordered with most-recent tasks last.
            for (int i = taskGroups.size() - 1; i >= 0; i--) {
                GroupTask groupTask = taskGroups.get(i);
                Task task1 = groupTask.task1;
                if (isInstanceOfComponent(task1, componentName)) {
                    lastActiveTask = task1;
                    break;
                }
                Task task2 = groupTask.task2;
                if (isInstanceOfComponent(task2, componentName)) {
                    lastActiveTask = task2;
                    break;
                }
        return null;
            }

            callback.accept(lastActiveTask);
        });
    }

    /**
     * Checks if a given Task is the most recently-active Task of type componentName. Used for
     * selecting already-running Tasks for splitscreen.
     */
    public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
        if (task == null) {
            return false;
        }
        // Exclude the task that is already staged
        if (mSplitHiddenTaskView != null && mSplitHiddenTaskView.getTask().equals(task)) {
            return false;
        }

        return task.key.baseIntent.getComponent().equals(componentName);
    }

    public void setOverviewStateEnabled(boolean enabled) {
@@ -1509,13 +1541,41 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        int previousCurrentPage = mCurrentPage;
        removeAllViews();

        // Add views as children based on whether it's grouped or single task
        // If we are entering Overview as a result of initiating a split from somewhere else
        // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
        Task stagedTaskToBeRemovedFromGrid =
                mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;

        // Add views as children based on whether it's grouped or single task. Looping through
        // taskGroups backwards populates the thumbnail grid from least recent to most recent.
        for (int i = taskGroups.size() - 1; i >= 0; i--) {
            GroupTask groupTask = taskGroups.get(i);
            TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
            boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
                    && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);

            TaskView taskView;
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
                // to be a temporary container for the remaining task.
                taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
            } else {
                taskView = getTaskViewFromPool(groupTask.taskViewType);
            }

            addView(taskView);

            if (taskView instanceof GroupedTaskView) {
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
                    taskView.bind(groupTask.task2, mOrientationState);
                } else {
                    taskView.bind(groupTask.task1, mOrientationState);
                }
            } else if (isRemovalNeeded) {
                // If the task we need to remove is not part of a pair, bind it to the TaskView
                // first (to prevent problems), then remove the whole thing.
                taskView.bind(groupTask.task1, mOrientationState);
                removeView(taskView);
            } else if (taskView instanceof GroupedTaskView) {
                boolean firstTaskIsLeftTopTask =
                        groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
                Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
@@ -4269,7 +4329,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        mSplitSelectSource = splitSelectSource;
        mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
                splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
                splitSelectSource.splitEvent);
                splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTask);
    }

    /**