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

Commit f96173d7 authored by Vinit Nayak's avatar Vinit Nayak Committed by Automerger Merge Worker
Browse files

Merge "Pass back split bounds and meta info with GroupedTaskInfo" into sc-v2-dev am: b63153fa

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16135327

Change-Id: I930a6dcfad02d524858017365802befbbf063d2b
parents f56d99f7 b63153fa
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -41,10 +41,13 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.StagedSplitBounds;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Manages the recent task list from the system, caching it as necessary.
@@ -62,6 +65,13 @@ public class RecentTasksController implements TaskStackListenerCallback,
    // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
    // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
    private final SparseIntArray mSplitTasks = new SparseIntArray();
    /**
     * Maps taskId to {@link StagedSplitBounds} for both taskIDs.
     * Meaning there will be two taskId integers mapping to the same object.
     * If there's any ordering to the pairing than we can probably just get away with only one
     * taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now.
     */
    private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>();

    /**
     * Creates {@link RecentTasksController}, returns {@code null} if the feature is not
@@ -97,15 +107,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
    /**
     * Adds a split pair. This call does not validate the taskIds, only that they are not the same.
     */
    public void addSplitPair(int taskId1, int taskId2) {
    public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) {
        if (taskId1 == taskId2) {
            return;
        }
        // Remove any previous pairs
        removeSplitPair(taskId1);
        removeSplitPair(taskId2);
        mTaskSplitBoundsMap.remove(taskId1);
        mTaskSplitBoundsMap.remove(taskId2);

        mSplitTasks.put(taskId1, taskId2);
        mSplitTasks.put(taskId2, taskId1);
        mTaskSplitBoundsMap.put(taskId1, splitBounds);
        mTaskSplitBoundsMap.put(taskId2, splitBounds);
    }

    /**
@@ -116,6 +131,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
        if (pairedTaskId != INVALID_TASK_ID) {
            mSplitTasks.delete(taskId);
            mSplitTasks.delete(pairedTaskId);
            mTaskSplitBoundsMap.remove(taskId);
            mTaskSplitBoundsMap.remove(pairedTaskId);
        }
    }

@@ -203,7 +220,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
            if (pairedTaskId != INVALID_TASK_ID) {
                final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
                rawMapping.remove(pairedTaskId);
                recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo));
                recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
                        mTaskSplitBoundsMap.get(pairedTaskId)));
            } else {
                recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
            }
+16 −1
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.StagedSplitBounds;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -691,11 +692,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }

        mRecentTasks.ifPresent(recentTasks -> {
            Rect topLeftBounds = mSplitLayout.getBounds1();
            Rect bottomRightBounds = mSplitLayout.getBounds2();
            int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
            int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId();
            boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
            int leftTopTaskId;
            int rightBottomTaskId;
            if (sideStageTopLeft) {
                leftTopTaskId = sideStageTopTaskId;
                rightBottomTaskId = mainStageTopTaskId;
            } else {
                leftTopTaskId = mainStageTopTaskId;
                rightBottomTaskId = sideStageTopTaskId;
            }
            StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds,
                    leftTopTaskId, rightBottomTaskId);
            if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
                // Update the pair for the top tasks
                recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId);
                recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
            }
        });
    }
+13 −3
Original line number Diff line number Diff line
@@ -30,25 +30,34 @@ import androidx.annotation.Nullable;
public class GroupedRecentTaskInfo implements Parcelable {
    public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
    public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
    public @Nullable StagedSplitBounds mStagedSplitBounds;

    public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
        this(task1, null);
        this(task1, null, null);
    }

    public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
            @Nullable ActivityManager.RecentTaskInfo task2) {
            @Nullable ActivityManager.RecentTaskInfo task2,
            @Nullable StagedSplitBounds stagedSplitBounds) {
        mTaskInfo1 = task1;
        mTaskInfo2 = task2;
        mStagedSplitBounds = stagedSplitBounds;
    }

    GroupedRecentTaskInfo(Parcel parcel) {
        mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
        mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
        mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR);
    }

    @Override
    public String toString() {
        return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2);
        String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
                + ", Task2: " + getTaskInfo(mTaskInfo2);
        if (mStagedSplitBounds != null) {
            taskString += ", SplitBounds: " + mStagedSplitBounds.toString();
        }
        return taskString;
    }

    private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -67,6 +76,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeTypedObject(mTaskInfo1, flags);
        parcel.writeTypedObject(mTaskInfo2, flags);
        parcel.writeTypedObject(mStagedSplitBounds, flags);
    }

    @Override
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.wm.shell.util;

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

/**
 * Container of various information needed to display split screen
 * tasks/leashes/etc in Launcher
 */
public class StagedSplitBounds implements Parcelable {
    public final Rect leftTopBounds;
    public final Rect rightBottomBounds;
    /** This rect represents the actual gap between the two apps */
    public final Rect visualDividerBounds;
    // This class is orientation-agnostic, so we compute both for later use
    public final float topTaskPercent;
    public final float leftTaskPercent;
    /**
     * If {@code true}, that means at the time of creation of this object, the
     * split-screened apps were vertically stacked. This is useful in scenarios like
     * rotation where the bounds won't change, but this variable can indicate what orientation
     * the bounds were originally in
     */
    public final boolean appsStackedVertically;
    public final int leftTopTaskId;
    public final int rightBottomTaskId;

    public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
            int leftTopTaskId, int rightBottomTaskId) {
        this.leftTopBounds = leftTopBounds;
        this.rightBottomBounds = rightBottomBounds;
        this.leftTopTaskId = leftTopTaskId;
        this.rightBottomTaskId = rightBottomTaskId;

        if (rightBottomBounds.top > leftTopBounds.top) {
            // vertical apps, horizontal divider
            this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
                    leftTopBounds.right, rightBottomBounds.top);
            appsStackedVertically = true;
        } else {
            // horizontal apps, vertical divider
            this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
                    rightBottomBounds.left, leftTopBounds.bottom);
            appsStackedVertically = false;
        }

        leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
        topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
    }

    public StagedSplitBounds(Parcel parcel) {
        leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
        rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
        visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
        topTaskPercent = parcel.readFloat();
        leftTaskPercent = parcel.readFloat();
        appsStackedVertically = parcel.readBoolean();
        leftTopTaskId = parcel.readInt();
        rightBottomTaskId = parcel.readInt();
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeTypedObject(leftTopBounds, flags);
        parcel.writeTypedObject(rightBottomBounds, flags);
        parcel.writeTypedObject(visualDividerBounds, flags);
        parcel.writeFloat(topTaskPercent);
        parcel.writeFloat(leftTaskPercent);
        parcel.writeBoolean(appsStackedVertically);
        parcel.writeInt(leftTopTaskId);
        parcel.writeInt(rightBottomTaskId);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public String toString() {
        return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
                + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
                + "Divider: " + visualDividerBounds + "\n"
                + "AppsVertical? " + appsStackedVertically;
    }

    public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() {
        @Override
        public StagedSplitBounds createFromParcel(Parcel in) {
            return new StagedSplitBounds(in);
        }

        @Override
        public StagedSplitBounds[] newArray(int size) {
            return new StagedSplitBounds[size];
        }
    };
}
+25 −6
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -31,10 +33,9 @@ import static org.mockito.Mockito.verify;
import static java.lang.Integer.MAX_VALUE;

import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.TaskAppearedInfo;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -45,6 +46,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.StagedSplitBounds;

import org.junit.Before;
import org.junit.Test;
@@ -106,8 +108,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
        setRawList(t1, t2, t3, t4, t5, t6);

        // Mark a couple pairs [t2, t4], [t3, t5]
        mRecentTasksController.addSplitPair(t2.taskId, t4.taskId);
        mRecentTasksController.addSplitPair(t3.taskId, t5.taskId);
        StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4);
        StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5);

        mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
        mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);

        ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
                MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -126,7 +131,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
        setRawList(t1, t2, t3);

        // Add a pair
        mRecentTasksController.addSplitPair(t2.taskId, t3.taskId);
        StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3);
        mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
        reset(mRecentTasksController);

        // Remove one of the tasks and ensure the pair is removed
@@ -201,10 +207,23 @@ public class RecentTasksControllerTest extends ShellTestCase {
        int[] flattenedTaskIds = new int[recentTasks.size() * 2];
        for (int i = 0; i < recentTasks.size(); i++) {
            GroupedRecentTaskInfo pair = recentTasks.get(i);
            flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId;
            int taskId1 = pair.mTaskInfo1.taskId;
            flattenedTaskIds[2 * i] = taskId1;
            flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
                    ? pair.mTaskInfo2.taskId
                    : -1;

            if (pair.mTaskInfo2 != null) {
                assertNotNull(pair.mStagedSplitBounds);
                int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId;
                int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId;
                // Unclear if pairs are ordered by split position, most likely not.
                assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
                assertTrue(bottomRightTaskId == taskId1
                        || bottomRightTaskId == pair.mTaskInfo2.taskId);
            } else {
                assertNull(pair.mStagedSplitBounds);
            }
        }
        assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
                        + " Received: " + Arrays.toString(flattenedTaskIds),
Loading