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

Commit 6c97f898 authored by Simon (Qiong) Sun's avatar Simon (Qiong) Sun Committed by Android (Google) Code Review
Browse files

Merge "Support multiple tasks per split screen in SplitBounds" into main

parents a3528680 20360acb
Loading
Loading
Loading
Loading
+67 −16
Original line number Diff line number Diff line
@@ -15,12 +15,18 @@
 */
package com.android.wm.shell.shared.split;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;

import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
@@ -55,13 +61,34 @@ public class SplitBounds implements Parcelable {
     * From seascape, it is the rightBottom task that expands slightly.
     */
    public final boolean initiatedFromSeascape;
    /** @deprecated Use {@link #leftTopTaskIds} instead. */
    @Deprecated
    public final int leftTopTaskId;
    /** @deprecated Use {@link #rightBottomTaskIds} instead. */
    @Deprecated
    public final int rightBottomTaskId;
    @NonNull
    public final List<Integer> leftTopTaskIds;
    @NonNull
    public final List<Integer> rightBottomTaskIds;

    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
            int rightBottomTaskId, @PersistentSnapPosition int snapPosition) {
    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
            int leftTopTaskId, int rightBottomTaskId,
            @NonNull List<Integer> leftTopTaskIds, @NonNull List<Integer> rightBottomTaskIds,
            @PersistentSnapPosition int snapPosition) {
        if (leftTopTaskId == INVALID_TASK_ID || rightBottomTaskId == INVALID_TASK_ID
                || leftTopTaskId == rightBottomTaskId
                || leftTopTaskIds.isEmpty() || rightBottomTaskIds.isEmpty()) {
            throw new IllegalArgumentException("The Split task ids are invalid:"
                    + " leftTopTaskId: " + leftTopTaskId
                    + " rightBottomTaskId: " + rightBottomTaskId
                    + " leftTopTaskId size: "  + leftTopTaskIds.size()
                    + " rightBottomTaskId size: " + rightBottomTaskIds.size());
        }
        this.leftTopBounds = leftTopBounds;
        this.rightBottomBounds = rightBottomBounds;
        this.leftTopTaskIds = List.copyOf(leftTopTaskIds);
        this.rightBottomTaskIds = List.copyOf(rightBottomTaskIds);
        this.leftTopTaskId = leftTopTaskId;
        this.rightBottomTaskId = rightBottomTaskId;
        this.snapPosition = snapPosition;
@@ -82,11 +109,7 @@ public class SplitBounds implements Parcelable {
            // all our current uses, but should be refactored.
            // TODO: Create a more reliable check, or refactor how splitting works on devices
            //  with insets.
            if (rightBottomBounds.width() > leftTopBounds.width()) {
                initiatedFromSeascape = true;
            } else {
                initiatedFromSeascape = false;
            }
            initiatedFromSeascape = rightBottomBounds.width() > leftTopBounds.width();
        }

        float totalWidth = rightBottomBounds.right - leftTopBounds.left;
@@ -97,6 +120,13 @@ public class SplitBounds implements Parcelable {
        dividerHeightPercent = visualDividerBounds.height() / totalHeight;
    }

    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
            int rightBottomTaskId, @PersistentSnapPosition int snapPosition) {
        this(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId,
                Collections.singletonList(leftTopTaskId),
                Collections.singletonList(rightBottomTaskId), snapPosition);
    }

    /**
     * Returns the percentage size of the left/top task (compared to the full width/height of
     * the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and the
@@ -138,11 +168,21 @@ public class SplitBounds implements Parcelable {
        leftTaskPercent = parcel.readFloat();
        appsStackedVertically = parcel.readBoolean();
        initiatedFromSeascape = parcel.readBoolean();
        leftTopTaskId = parcel.readInt();
        rightBottomTaskId = parcel.readInt();
        dividerWidthPercent = parcel.readFloat();
        dividerHeightPercent = parcel.readFloat();
        snapPosition = parcel.readInt();
        leftTopTaskId = parcel.readInt();
        rightBottomTaskId = parcel.readInt();
        int size = parcel.readInt();
        leftTopTaskIds = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            leftTopTaskIds.add(parcel.readInt());
        }
        size = parcel.readInt();
        rightBottomTaskIds = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            rightBottomTaskIds.add(parcel.readInt());
        }
    }

    @Override
@@ -154,11 +194,19 @@ public class SplitBounds implements Parcelable {
        parcel.writeFloat(leftTaskPercent);
        parcel.writeBoolean(appsStackedVertically);
        parcel.writeBoolean(initiatedFromSeascape);
        parcel.writeInt(leftTopTaskId);
        parcel.writeInt(rightBottomTaskId);
        parcel.writeFloat(dividerWidthPercent);
        parcel.writeFloat(dividerHeightPercent);
        parcel.writeInt(snapPosition);
        parcel.writeInt(leftTopTaskId);
        parcel.writeInt(rightBottomTaskId);
        parcel.writeInt(leftTopTaskIds.size());
        for (Integer id : leftTopTaskIds) {
            parcel.writeInt(id); // Write each Integer in the List
        }
        parcel.writeInt(rightBottomTaskIds.size());
        for (Integer id : rightBottomTaskIds) {
            parcel.writeInt(id); // Write each Integer in the List
        }
    }

    @Override
@@ -175,20 +223,23 @@ public class SplitBounds implements Parcelable {
        final SplitBounds other = (SplitBounds) obj;
        return Objects.equals(leftTopBounds, other.leftTopBounds)
                && Objects.equals(rightBottomBounds, other.rightBottomBounds)
                && leftTopTaskId == other.leftTopTaskId
                && rightBottomTaskId == other.rightBottomTaskId
                && leftTopTaskIds.equals(other.leftTopTaskIds)
                && rightBottomTaskIds.equals(other.rightBottomTaskIds)
                && snapPosition == other.snapPosition;
    }

    @Override
    public int hashCode() {
        return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
        return Objects.hash(leftTopBounds, rightBottomBounds,
                leftTopTaskId, rightBottomTaskId, leftTopTaskIds, rightBottomTaskIds);
    }

    @Override
    public String toString() {
        return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
                + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
        return "LeftTop: " + leftTopBounds + " taskId: " + leftTopTaskId
                + ", taskIds: " + leftTopTaskIds + "\n"
                + "RightBottom: " + rightBottomBounds + " taskId: " + rightBottomTaskId
                + ", taskIds: " + rightBottomTaskIds +  "\n"
                + "Divider: " + visualDividerBounds + "\n"
                + "AppsVertical? " + appsStackedVertically + "\n"
                + "snapPosition: " + snapPosition;
+105 −94

File changed.

Preview size limit exceeded, changes collapsed.

+25 −0
Original line number Diff line number Diff line
@@ -57,9 +57,12 @@ import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Base class that handle common task org. related for split-screen stages.
@@ -156,6 +159,16 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
        return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
    }

    /**
     * Returns all visible child task's ids.
     */
    List<Integer> getAllVisibleChildTaskIds() {
        return getAllChildTaskInfos(t -> t.isVisible
                && t.isVisibleRequested && t.taskId != INVALID_TASK_ID).stream()
                .map(runningTaskInfo -> runningTaskInfo.taskId)
                .collect(Collectors.toList());
    }

    /**
     * Returns the top activity uid for the top child task.
     */
@@ -216,6 +229,18 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
        return null;
    }

    private List<ActivityManager.RunningTaskInfo> getAllChildTaskInfos(
            Predicate<ActivityManager.RunningTaskInfo> predicate) {
        List<ActivityManager.RunningTaskInfo> matchingTasks = new ArrayList<>();
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
            final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
            if (predicate.test(taskInfo)) {
                matchingTasks.add(taskInfo);
            }
        }
        return matchingTasks;
    }

    @Override
    @CallSuper
    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+42 −1
Original line number Diff line number Diff line
package com.android.wm.shell.recents;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;

import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

import android.graphics.Rect;
@@ -18,6 +21,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Collections;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class SplitBoundsTest extends ShellTestCase {
@@ -26,6 +31,7 @@ public class SplitBoundsTest extends ShellTestCase {
    private static final int DIVIDER_SIZE = 20;
    private static final int TASK_ID_1 = 4;
    private static final int TASK_ID_2 = 9;
    private static final int TASK_ID_ILLEGAL = INVALID_TASK_ID;

    // Bounds in screen space
    private final Rect mTopRect = new Rect();
@@ -94,4 +100,39 @@ public class SplitBoundsTest extends ShellTestCase {
        float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
        assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
    }

    @Test
    public void testIllegalTaskIds() {
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_ILLEGAL, TASK_ID_2, SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_1, TASK_ID_ILLEGAL, SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_ILLEGAL, TASK_ID_ILLEGAL, SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_1, TASK_ID_1, SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_1, TASK_ID_2, Collections.emptyList(), Collections.emptyList(),
                        SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_1, TASK_ID_2, Collections.singletonList(TASK_ID_1),
                        Collections.emptyList(), SNAP_TO_2_50_50));
        assertThrows(
                IllegalArgumentException.class,
                () -> new SplitBounds(mLeftRect, mRightRect,
                        TASK_ID_1, TASK_ID_2, Collections.emptyList(),
                        Collections.singletonList(TASK_ID_2), SNAP_TO_2_50_50));
    }
}
 No newline at end of file
+58 −0
Original line number Diff line number Diff line
@@ -54,6 +54,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
@@ -187,4 +189,60 @@ public final class StageTaskListenerTests extends ShellTestCase {
        mStageTaskListener.deactivate(mWct);
        assertThat(mStageTaskListener.isActive()).isFalse();
    }

    @Test
    public void testGetAllVisibleChildTaskIds() {
        final ActivityManager.RunningTaskInfo taskVisible1 =
                new TestRunningTaskInfoBuilder()
                        .setTaskId(1)
                        .setVisible(true)
                        .setVisibleRequested(true)
                        .build();
        final ActivityManager.RunningTaskInfo taskInvisible2 =
                new TestRunningTaskInfoBuilder()
                        .setTaskId(2)
                        .setVisible(false)
                        .build();
        final ActivityManager.RunningTaskInfo taskVisible3 =
                new TestRunningTaskInfoBuilder()
                        .setTaskId(3)
                        .setVisible(true)
                        .setVisibleRequested(true)
                        .build();
        final ActivityManager.RunningTaskInfo taskVisible4 =
                new TestRunningTaskInfoBuilder()
                        .setTaskId(4)
                        .setVisible(true)
                        .setVisibleRequested(true)
                        .build();
        final ActivityManager.RunningTaskInfo taskInvisible5 =
                new TestRunningTaskInfoBuilder()
                        .setTaskId(5)
                        .setVisible(false)
                        .build();
        final List<Integer> visibleTaskIds = Arrays.asList(taskVisible1.taskId, taskVisible3.taskId,
                taskVisible4.taskId);

        mStageTaskListener.mChildrenTaskInfo.clear();
        assertThat(mStageTaskListener.mChildrenTaskInfo.size() == 0).isTrue();

        mStageTaskListener.mChildrenTaskInfo.put(taskVisible1.taskId, taskVisible1);
        mStageTaskListener.mChildrenTaskInfo.put(taskInvisible2.taskId, taskInvisible2);
        mStageTaskListener.mChildrenTaskInfo.put(taskVisible3.taskId, taskVisible3);
        mStageTaskListener.mChildrenTaskInfo.put(taskVisible4.taskId, taskVisible4);
        mStageTaskListener.mChildrenTaskInfo.put(taskInvisible5.taskId, taskInvisible5);

        final List<Integer> ids = mStageTaskListener.getAllVisibleChildTaskIds();
        assertThat(ids.size() == 3).isTrue();
        assertTrue("List should contain all visible taskIds",
                ids.containsAll(visibleTaskIds));
        assertFalse("List should not contain invisible taskId2",
                ids.contains(taskInvisible2.taskId));
        assertFalse("List should not contain invisible taskId5",
                ids.contains(taskInvisible5.taskId));

        // Clear the mChildrenTaskInfo.
        mStageTaskListener.mChildrenTaskInfo.clear();
        assertThat(mStageTaskListener.mChildrenTaskInfo.size() == 0).isTrue();
    }
}
Loading