[automerge] Reset embedded TaskFragment override when entering PIP 2p: 103a6dc7f7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17651825 Bug: 225371112 Change-Id: I1180c21fba0a57e4096e6ef482fb8d396a48bdd0 Merged-In: I03bd679b77f7ed8a65ba5fc59723b83a4254442c
This commit is contained in:
@@ -2481,6 +2481,12 @@ class ActivityStarter {
|
||||
if (inTaskFragment == null) {
|
||||
inTaskFragment = TaskFragment.fromTaskFragmentToken(
|
||||
mOptions.getLaunchTaskFragmentToken(), mService);
|
||||
if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) {
|
||||
// Do not start activity in TaskFragment in a PIP Task.
|
||||
Slog.w(TAG, "Can not start activity in TaskFragment in PIP: "
|
||||
+ inTaskFragment);
|
||||
inTaskFragment = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
||||
* @see #mFullConfiguration
|
||||
*/
|
||||
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
|
||||
updateRequestedOverrideConfiguration(overrideConfiguration);
|
||||
// Update full configuration of this container and all its children.
|
||||
final ConfigurationContainer parent = getParent();
|
||||
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
|
||||
}
|
||||
|
||||
/** Updates override configuration without recalculate full config. */
|
||||
void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
|
||||
// Pre-compute this here, so we don't need to go through the entire Configuration when
|
||||
// writing to proto (which has significant cost if we write a lot of empty configurations).
|
||||
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
|
||||
@@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
||||
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
|
||||
mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
|
||||
}
|
||||
// Update full configuration of this container and all its children.
|
||||
final ConfigurationContainer parent = getParent();
|
||||
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
|
||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
|
||||
import static android.content.res.Configuration.EMPTY;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
@@ -2005,7 +2006,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
// of the activity entering PIP
|
||||
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
||||
|
||||
final boolean singleActivity = task.getChildCount() == 1;
|
||||
// TODO: Does it make sense to only count non-finishing activities?
|
||||
final boolean singleActivity = task.getActivityCount() == 1;
|
||||
final Task rootTask;
|
||||
if (singleActivity) {
|
||||
rootTask = task;
|
||||
@@ -2086,6 +2088,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
// TODO(task-org): Figure-out more structured way to do this long term.
|
||||
r.setWindowingMode(intermediateWindowingMode);
|
||||
r.mWaitForEnteringPinnedMode = true;
|
||||
rootTask.forAllTaskFragments(tf -> {
|
||||
// When the Task is entering picture-in-picture, we should clear all override from
|
||||
// the client organizer, so the PIP activity can get the correct config from the
|
||||
// Task, and prevent conflict with the PipTaskOrganizer.
|
||||
if (tf.isOrganizedTaskFragment()) {
|
||||
tf.resetAdjacentTaskFragment();
|
||||
tf.updateRequestedOverrideConfiguration(EMPTY);
|
||||
}
|
||||
});
|
||||
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
// Set the launch bounds for launch-into-pip Activity on the root task.
|
||||
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
|
||||
|
||||
@@ -1380,6 +1380,14 @@ class Task extends TaskFragment {
|
||||
return getActivity(ActivityRecord::canBeTopRunning);
|
||||
}
|
||||
|
||||
int getActivityCount() {
|
||||
final int[] activityCount = new int[1];
|
||||
forAllActivities(ar -> {
|
||||
activityCount[0]++;
|
||||
});
|
||||
return activityCount[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any activities in this task belongs to input uid.
|
||||
*/
|
||||
|
||||
@@ -339,7 +339,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
}
|
||||
}
|
||||
|
||||
private void resetAdjacentTaskFragment() {
|
||||
void resetAdjacentTaskFragment() {
|
||||
// Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
|
||||
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
|
||||
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
|
||||
@@ -2317,6 +2317,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
mMinHeight = minHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config
|
||||
* override for such TaskFragment to prevent flight with PipTaskOrganizer.
|
||||
*/
|
||||
boolean isEmbeddedTaskFragmentInPip() {
|
||||
return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode();
|
||||
}
|
||||
|
||||
boolean shouldRemoveSelfOnLastChildRemoval() {
|
||||
return !mCreatedByOrganizer || mIsRemovalRequested;
|
||||
}
|
||||
|
||||
@@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
boolean canStartChangeTransition() {
|
||||
return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
|
||||
&& getSurfaceControl() != null && !mDisplayContent.inTransition()
|
||||
&& isVisible() && isVisibleRequested() && okToAnimate();
|
||||
&& isVisible() && isVisibleRequested() && okToAnimate()
|
||||
// Pip animation will be handled by PipTaskOrganizer.
|
||||
&& !inPinnedWindowingMode() && getParent() != null
|
||||
&& !getParent().inPinnedWindowingMode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
|
||||
final IBinder fragmentToken = hop.getContainer();
|
||||
if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
|
||||
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
|
||||
if (tf == null) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to operate with invalid fragment token");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
if (tf.isEmbeddedTaskFragmentInPip()) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to start activity in PIP TaskFragment");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
final Intent activityIntent = hop.getActivityIntent();
|
||||
final Bundle activityOptions = hop.getLaunchOptions();
|
||||
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
|
||||
final int result = mService.getActivityStartController()
|
||||
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
|
||||
hop.getCallingActivity(), caller.mUid, caller.mPid);
|
||||
@@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
if (parent.isEmbeddedTaskFragmentInPip()) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to reparent activity to PIP TaskFragment");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
if (!parent.isAllowedToEmbedActivity(activity)) {
|
||||
final Throwable exception = new SecurityException(
|
||||
"The task fragment is not trusted to embed the given activity.");
|
||||
@@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
if (tf1.isEmbeddedTaskFragmentInPip()
|
||||
|| (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to set adjacent on TaskFragment in PIP Task");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
|
||||
@@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
|
||||
+ " organizer root1=" + root1 + " root2=" + root2);
|
||||
}
|
||||
if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) {
|
||||
Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task");
|
||||
return 0;
|
||||
}
|
||||
root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
|
||||
return TRANSACT_EFFECTS_LIFECYCLE;
|
||||
}
|
||||
@@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
private int applyWindowContainerChange(WindowContainer wc,
|
||||
WindowContainerTransaction.Change c) {
|
||||
sanitizeWindowContainer(wc);
|
||||
if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) {
|
||||
// No override from organizer for embedded TaskFragment in a PIP Task.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int effects = applyChanges(wc, c);
|
||||
|
||||
@@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
return;
|
||||
}
|
||||
// The ownerActivity has to belong to the same app as the target Task.
|
||||
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|
||||
|| ownerActivity.getTask().effectiveUid != caller.mUid) {
|
||||
final Task ownerTask = ownerActivity.getTask();
|
||||
if (ownerTask.effectiveUid != ownerActivity.getUid()
|
||||
|| ownerTask.effectiveUid != caller.mUid) {
|
||||
final Throwable exception =
|
||||
new SecurityException("Not allowed to operate with the ownerToken while "
|
||||
+ "the root activity of the target task belong to the different app");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
}
|
||||
if (ownerTask.inPinnedWindowingMode()) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to create TaskFragment in PIP Task");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
}
|
||||
final TaskFragment taskFragment = new TaskFragment(mService,
|
||||
creationParams.getFragmentToken(), true /* createdByOrganizer */);
|
||||
// Set task fragment organizer immediately, since it might have to be notified about further
|
||||
// actions.
|
||||
taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
|
||||
ownerActivity.getUid(), ownerActivity.info.processName);
|
||||
ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
|
||||
ownerTask.addChild(taskFragment, POSITION_TOP);
|
||||
taskFragment.setWindowingMode(creationParams.getWindowingMode());
|
||||
taskFragment.setBounds(creationParams.getInitialBounds());
|
||||
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
|
||||
@@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) {
|
||||
final Throwable exception = new SecurityException(
|
||||
"Not allow to reparent in TaskFragment in PIP Task.");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
}
|
||||
while (oldParent.hasChild()) {
|
||||
oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
|
||||
}
|
||||
@@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return 0;
|
||||
}
|
||||
if (taskFragment.isEmbeddedTaskFragmentInPip()) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to delete TaskFragment in PIP Task");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return 0;
|
||||
}
|
||||
mLaunchTaskFragments.removeAt(index);
|
||||
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
|
||||
return TRANSACT_EFFECTS_LIFECYCLE;
|
||||
|
||||
@@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase {
|
||||
assertTrue(firstActivity.mRequestForceTransition);
|
||||
}
|
||||
|
||||
/**
|
||||
* When there is only one activity in the Task, and the activity is requesting to enter PIP, the
|
||||
* whole Task should enter PIP.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleActivityTaskEnterPip() {
|
||||
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
final ActivityRecord activity = new ActivityBuilder(mAtm)
|
||||
.setTask(fullscreenTask)
|
||||
.build();
|
||||
final Task task = activity.getTask();
|
||||
|
||||
// Move activity to pinned root task.
|
||||
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
|
||||
null /* launchIntoPipHostActivity */, "test");
|
||||
|
||||
// Ensure a task has moved over.
|
||||
ensureTaskPlacement(task, activity);
|
||||
assertTrue(task.inPinnedWindowingMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* When there is only one activity in the Task, and the activity is requesting to enter PIP, the
|
||||
* whole Task should enter PIP even if the activity is in a TaskFragment.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleActivityInTaskFragmentEnterPip() {
|
||||
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(fullscreenTask)
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
final ActivityRecord activity = taskFragment.getTopMostActivity();
|
||||
final Task task = activity.getTask();
|
||||
|
||||
// Move activity to pinned root task.
|
||||
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
|
||||
null /* launchIntoPipHostActivity */, "test");
|
||||
|
||||
// Ensure a task has moved over.
|
||||
ensureTaskPlacement(task, activity);
|
||||
assertTrue(task.inPinnedWindowingMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* When there is one TaskFragment with two activities in the Task, the activity requests to
|
||||
* enter PIP, that activity will be move to PIP root task.
|
||||
*/
|
||||
@Test
|
||||
public void testMultipleActivitiesInTaskFragmentEnterPip() {
|
||||
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(fullscreenTask)
|
||||
.createActivityCount(2)
|
||||
.build();
|
||||
final ActivityRecord firstActivity = taskFragment.getTopMostActivity();
|
||||
final ActivityRecord secondActivity = taskFragment.getBottomMostActivity();
|
||||
|
||||
// Move first activity to pinned root task.
|
||||
mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
|
||||
null /* launchIntoPipHostActivity */, "test");
|
||||
|
||||
final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
|
||||
final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
|
||||
|
||||
// Ensure a task has moved over.
|
||||
ensureTaskPlacement(pinnedRootTask, firstActivity);
|
||||
ensureTaskPlacement(fullscreenTask, secondActivity);
|
||||
assertTrue(pinnedRootTask.inPinnedWindowingMode());
|
||||
assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
|
||||
}
|
||||
|
||||
private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
|
||||
final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||
@@ -28,6 +31,8 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -434,6 +439,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_startActivityInTaskFragment() {
|
||||
setupTaskFragmentInPip();
|
||||
final ActivityRecord activity = mTaskFragment.getTopMostActivity();
|
||||
final IBinder errorToken = new Binder();
|
||||
spyOn(mAtm.getActivityStartController());
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Not allow to start activity in a TaskFragment that is in a PIP Task.
|
||||
mTransaction.startActivityInTaskFragment(
|
||||
mFragmentToken, activity.token, new Intent(), null /* activityOptions */)
|
||||
.setErrorCallbackToken(errorToken);
|
||||
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||
|
||||
verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(),
|
||||
any(), any(), anyInt(), anyInt());
|
||||
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
|
||||
eq(errorToken), any(IllegalArgumentException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_reparentActivityToTaskFragment() {
|
||||
setupTaskFragmentInPip();
|
||||
final ActivityRecord activity = createActivityRecord(mDisplayContent);
|
||||
final IBinder errorToken = new Binder();
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Not allow to reparent activity to a TaskFragment that is in a PIP Task.
|
||||
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
|
||||
.setErrorCallbackToken(errorToken);
|
||||
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||
|
||||
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
|
||||
eq(errorToken), any(IllegalArgumentException.class));
|
||||
assertNull(activity.getOrganizedTaskFragment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_setAdjacentTaskFragment() {
|
||||
setupTaskFragmentInPip();
|
||||
final IBinder errorToken = new Binder();
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Not allow to set adjacent on a TaskFragment that is in a PIP Task.
|
||||
mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */,
|
||||
null /* options */)
|
||||
.setErrorCallbackToken(errorToken);
|
||||
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||
|
||||
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
|
||||
eq(errorToken), any(IllegalArgumentException.class));
|
||||
verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_createTaskFragment() {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED,
|
||||
ACTIVITY_TYPE_STANDARD);
|
||||
final ActivityRecord activity = createActivityRecord(pipTask);
|
||||
final IBinder fragmentToken = new Binder();
|
||||
final IBinder errorToken = new Binder();
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Not allow to create TaskFragment in a PIP Task.
|
||||
createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken);
|
||||
mTransaction.setErrorCallbackToken(errorToken);
|
||||
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||
|
||||
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
|
||||
eq(errorToken), any(IllegalArgumentException.class));
|
||||
assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_deleteTaskFragment() {
|
||||
setupTaskFragmentInPip();
|
||||
final IBinder errorToken = new Binder();
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Not allow to delete a TaskFragment that is in a PIP Task.
|
||||
mTransaction.deleteTaskFragment(mFragmentWindowToken)
|
||||
.setErrorCallbackToken(errorToken);
|
||||
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||
|
||||
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
|
||||
eq(errorToken), any(IllegalArgumentException.class));
|
||||
assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskFragmentInPip_setConfig() {
|
||||
setupTaskFragmentInPip();
|
||||
spyOn(mAtm.mWindowOrganizerController);
|
||||
|
||||
// Set bounds is ignored on a TaskFragment that is in a PIP Task.
|
||||
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
|
||||
|
||||
verify(mTaskFragment, never()).setBounds(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
|
||||
// Task - TaskFragment - Activity.
|
||||
@@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
/** Setups an embedded TaskFragment in a PIP Task. */
|
||||
private void setupTaskFragmentInPip() {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
mTaskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setCreateParentTask()
|
||||
.setFragmentToken(mFragmentToken)
|
||||
.setOrganizer(mOrganizer)
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
|
||||
mAtm.mWindowOrganizerController.mLaunchTaskFragments
|
||||
.put(mFragmentToken, mTaskFragment);
|
||||
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
|
||||
@@ -31,6 +32,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -206,6 +208,39 @@ public class TaskFragmentTest extends WindowTestsBase {
|
||||
assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() {
|
||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
.setCreateParentTask()
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
final Task task = taskFragment.getTask();
|
||||
final ActivityRecord activity = taskFragment.getTopMostActivity();
|
||||
final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000);
|
||||
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
|
||||
taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
|
||||
taskFragment.setBounds(taskFragmentBounds);
|
||||
|
||||
assertEquals(taskFragmentBounds, activity.getBounds());
|
||||
assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
|
||||
|
||||
// Move activity to pinned root task.
|
||||
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
|
||||
null /* launchIntoPipHostActivity */, "test");
|
||||
|
||||
// Ensure taskFragment requested config is reset.
|
||||
assertEquals(taskFragment, activity.getOrganizedTaskFragment());
|
||||
assertEquals(task, activity.getTask());
|
||||
assertTrue(task.inPinnedWindowingMode());
|
||||
assertTrue(taskFragment.inPinnedWindowingMode());
|
||||
final Rect taskBounds = task.getBounds();
|
||||
assertEquals(taskBounds, taskFragment.getBounds());
|
||||
assertEquals(taskBounds, activity.getBounds());
|
||||
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
||||
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
||||
|
||||
Reference in New Issue
Block a user