Merge "Allow non-resizable apps in split-screen (6/n)" into sc-dev
This commit is contained in:
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2180,6 +2180,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;
|
||||
}
|
||||
@@ -2188,6 +2189,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() {
|
||||
|
||||
@@ -5485,14 +5485,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;
|
||||
@@ -5501,8 +5506,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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -42,6 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
|
||||
import static com.android.server.wm.Task.ActivityState.RESUMED;
|
||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
|
||||
|
||||
@@ -55,6 +56,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
@@ -1244,6 +1246,54 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
assertEquals(splitPrimaryRootTask, activity.getRootTask());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
|
||||
final ITaskOrganizer organizer = registerMockOrganizer();
|
||||
final Task rootTask = createStack();
|
||||
final Task task = createTask(rootTask);
|
||||
final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
|
||||
final ArgumentCaptor<RunningTaskInfo> infoCaptor =
|
||||
ArgumentCaptor.forClass(RunningTaskInfo.class);
|
||||
|
||||
assertTrue(rootTask.isOrganized());
|
||||
|
||||
spyOn(activity);
|
||||
doReturn(true).when(activity).inSizeCompatMode();
|
||||
doReturn(true).when(activity).isState(RESUMED);
|
||||
|
||||
// Ensure task info show top activity in size compat.
|
||||
rootTask.onSizeCompatActivityChanged();
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
|
||||
RunningTaskInfo info = infoCaptor.getValue();
|
||||
assertEquals(rootTask.mTaskId, info.taskId);
|
||||
assertEquals(activity.appToken, info.topActivityToken);
|
||||
assertTrue(info.topActivityInSizeCompat);
|
||||
|
||||
// Ensure task info show top activity that is not in foreground as not in size compat.
|
||||
clearInvocations(organizer);
|
||||
doReturn(false).when(activity).isState(RESUMED);
|
||||
rootTask.onSizeCompatActivityChanged();
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
|
||||
info = infoCaptor.getValue();
|
||||
assertEquals(rootTask.mTaskId, info.taskId);
|
||||
assertEquals(activity.appToken, info.topActivityToken);
|
||||
assertFalse(info.topActivityInSizeCompat);
|
||||
|
||||
// Ensure task info show non size compat top activity as not in size compat.
|
||||
clearInvocations(organizer);
|
||||
doReturn(true).when(activity).isState(RESUMED);
|
||||
doReturn(false).when(activity).inSizeCompatMode();
|
||||
rootTask.onSizeCompatActivityChanged();
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
|
||||
info = infoCaptor.getValue();
|
||||
assertEquals(rootTask.mTaskId, info.taskId);
|
||||
assertEquals(activity.appToken, info.topActivityToken);
|
||||
assertFalse(info.topActivityInSizeCompat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that task vanished is called for a specific task.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user