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

Commit acbe872a authored by Chris Li's avatar Chris Li
Browse files

Allow non-resizable apps in split-screen (6/n)

Trigger TaskInfoChanged when the direct top Activity of the organized
Task enters or exits size compat mode.

This is the prerequisite to move SizeCompatModeActivityController from
system ui to shell. After that, we can render the size compat restart
button on Task Surface similar to split screen divider.

Bug: 176061101
Bug: 178327644
Test: atest WmTests:SizeCompatTests
Test: atest WmTests:WindowOrganizerTests
Change-Id: I0e328796095bc17d40a5ef2f354f291c12ffa04a
parent 544ee86b
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.window.TaskSnapshot;
import android.window.WindowContainerToken;

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

/**
@@ -179,6 +178,19 @@ public class TaskInfo {
    @Nullable
    public ActivityInfo topActivityInfo;

    /**
     * The top activity in this task.
     * @hide
     */
    @Nullable
    public IBinder topActivityToken;

    /**
     * Whether the direct top activity is in size compat mode on foreground.
     * @hide
     */
    public boolean topActivityInSizeCompat;

    /**
     * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
     * supports), this is what the system actually uses for resizability based on other policy and
@@ -356,6 +368,8 @@ public class TaskInfo {
        parentTaskId = source.readInt();
        isFocused = source.readBoolean();
        isVisible = source.readBoolean();
        topActivityToken = source.readStrongBinder();
        topActivityInSizeCompat = source.readBoolean();
    }

    /**
@@ -391,6 +405,8 @@ public class TaskInfo {
        dest.writeInt(parentTaskId);
        dest.writeBoolean(isFocused);
        dest.writeBoolean(isVisible);
        dest.writeStrongBinder(topActivityToken);
        dest.writeBoolean(topActivityInSizeCompat);
    }

    @Override
@@ -415,6 +431,8 @@ public class TaskInfo {
                + " parentTaskId=" + parentTaskId
                + " isFocused=" + isFocused
                + " isVisible=" + isVisible
                + " topActivityToken=" + topActivityToken
                + " topActivityInSizeCompat=" + topActivityInSizeCompat
                + "}";
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -2183,6 +2183,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }

    /** @return Root task of this activity, null if there is no task. */
    @Nullable
    Task getRootTask() {
        return task != null ? task.getRootTask() : null;
    }
@@ -2191,6 +2192,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
    }

    /** @return the first organized parent task. */
    @Nullable
    Task getOrganizedTask() {
        return task != null ? task.getOrganizedTask() : null;
    }

    @Override
    @Nullable
    TaskDisplayArea getDisplayArea() {
+9 −1
Original line number Diff line number Diff line
@@ -5471,14 +5471,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

    /** Checks whether the given activity is in size compatibility mode and notifies the change. */
    void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
        if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
        final Task organizedTask = r.getOrganizedTask();
        if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
                || organizedTask == null) {
            // The callback is only interested in the foreground changes of fullscreen activity.
            return;
        }
        if (!r.inSizeCompatMode()) {
            if (mLastCompatModeActivity != null) {
                // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
                mAtmService.getTaskChangeNotificationController()
                        .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
                // This will do nothing until SizeCompatModeActivityController is moved to shell
                organizedTask.onSizeCompatActivityChanged();
            }
            mLastCompatModeActivity = null;
            return;
@@ -5487,8 +5492,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            return;
        }
        mLastCompatModeActivity = r;
        // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
        mAtmService.getTaskChangeNotificationController()
                .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
        // This will do nothing until SizeCompatModeActivityController is moved to shell
        organizedTask.onSizeCompatActivityChanged();
    }

    boolean isUidPresent(int uid) {
+28 −0
Original line number Diff line number Diff line
@@ -3082,6 +3082,20 @@ class Task extends WindowContainer<WindowContainer> {
        return parentTask == null ? this : parentTask.getRootTask();
    }

    /** @return the first organized task. */
    @Nullable
    Task getOrganizedTask() {
        if (isOrganized()) {
            return this;
        }
        final WindowContainer parent = getParent();
        if (parent == null) {
            return null;
        }
        final Task parentTask = parent.asTask();
        return parentTask == null ? null : parentTask.getOrganizedTask();
    }

    // TODO(task-merge): Figure out what's the right thing to do for places that used it.
    boolean isRootTask() {
        return getRootTask() == this;
@@ -4172,6 +4186,14 @@ class Task extends WindowContainer<WindowContainer> {
        info.topActivityInfo = mReuseActivitiesReport.top != null
                ? mReuseActivitiesReport.top.info
                : null;
        info.topActivityToken = mReuseActivitiesReport.top != null
                ? mReuseActivitiesReport.top.appToken
                : null;
        // Whether the direct top activity is in size compat mode on foreground.
        info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
                && mReuseActivitiesReport.top.getOrganizedTask() == this
                && mReuseActivitiesReport.top.inSizeCompatMode()
                && mReuseActivitiesReport.top.isState(RESUMED);
        info.launchCookies.clear();
        info.addLaunchCookie(mLaunchCookie);
        forAllActivities(r -> {
@@ -5221,6 +5243,12 @@ class Task extends WindowContainer<WindowContainer> {
        }
    }

    /** Called when the top activity in the Root Task enters or exits size compat mode. */
    void onSizeCompatActivityChanged() {
        // Trigger TaskInfoChanged to update the size compat restart button.
        dispatchTaskInfoChangedIfNeeded(true /* force */);
    }

    /**
     * See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
     * transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
+43 −0
Original line number Diff line number Diff line
@@ -480,10 +480,13 @@ public class SizeCompatTests extends WindowTestsBase {
    /**
     * Ensures that {@link TaskStackListener} can receive callback about the activity in size
     * compatibility mode.
     *
     * TODO(b/178327644) Remove after update DC#handleActivitySizeCompatModeIfNeeded
     */
    @Test
    public void testHandleActivitySizeCompatMode() {
        setUpDisplaySizeWithApp(1000, 2000);
        doReturn(true).when(mTask).isOrganized();
        ActivityRecord activity = mActivity;
        activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
        prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -520,6 +523,46 @@ public class SizeCompatTests extends WindowTestsBase {
        assertEquals(null, compatTokens.get(0));
    }

    /**
     * Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
     * compatibility mode.
     */
    @Test
    public void testHandleActivitySizeCompatModeChanged() {
        setUpDisplaySizeWithApp(1000, 2000);
        doReturn(true).when(mTask).isOrganized();
        ActivityRecord activity = mActivity;
        activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
        prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
        assertFitted();

        // Resize the display so that the activity exercises size-compat mode.
        resizeDisplay(mTask.mDisplayContent, 1000, 2500);

        // Expect the exact token when the activity is in size compatibility mode.
        verify(mTask).onSizeCompatActivityChanged();
        ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();

        assertEquals(mActivity.appToken, taskInfo.topActivityToken);
        assertTrue(taskInfo.topActivityInSizeCompat);

        // Make the activity resizable again by restarting it
        clearInvocations(mTask);
        activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
        activity.mVisibleRequested = true;
        activity.restartProcessIfVisible();
        // The full lifecycle isn't hooked up so manually set state to resumed
        activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
        mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);

        // Expect null token when switching to non-size-compat mode activity.
        verify(mTask).onSizeCompatActivityChanged();
        taskInfo = mTask.getTaskInfo();

        assertEquals(mActivity.appToken, taskInfo.topActivityToken);
        assertFalse(taskInfo.topActivityInSizeCompat);
    }

    @Test
    public void testShouldUseSizeCompatModeOnResizableTask() {
        setUpDisplaySizeWithApp(1000, 2500);
Loading