Fix ActivityEmbedding placeholder launching above fullscreen activity
Before, we always place the new created TaskFragment on the top of the Task. Now, when creating TaskFragment for placeholder, we place the new TasKFragment right above the primary TaskFragment. In case there is a fullscreen transparent Activity on top of the primary Activity, this prevents us from launching the placeholder on top of the transparent Activity. Bug: 261550242 Test: atest WmTests:TaskFragmentOrganizerControllerTest Change-Id: I163e9ca1637db45e8695d40a31f2a2c6a4f05189
This commit is contained in:
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WindowingMode;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
@@ -57,14 +58,33 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
|
||||
/** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */
|
||||
@WindowingMode
|
||||
private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
|
||||
private final int mWindowingMode;
|
||||
|
||||
/**
|
||||
* The fragment token of the paired primary TaskFragment.
|
||||
* When it is set, the new TaskFragment will be positioned right above the paired TaskFragment.
|
||||
* Otherwise, the new TaskFragment will be positioned on the top of the Task by default.
|
||||
*
|
||||
* This is different from {@link WindowContainerTransaction#setAdjacentTaskFragments} as we may
|
||||
* set this when the pair of TaskFragments are stacked, while adjacent is only set on the pair
|
||||
* of TaskFragments that are in split.
|
||||
*
|
||||
* This is needed in case we need to launch a placeholder Activity to split below a transparent
|
||||
* always-expand Activity.
|
||||
*/
|
||||
@Nullable
|
||||
private final IBinder mPairedPrimaryFragmentToken;
|
||||
|
||||
private TaskFragmentCreationParams(
|
||||
@NonNull TaskFragmentOrganizerToken organizer,
|
||||
@NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) {
|
||||
@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken,
|
||||
@NonNull IBinder ownerToken, @NonNull Rect initialBounds,
|
||||
@WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken) {
|
||||
mOrganizer = organizer;
|
||||
mFragmentToken = fragmentToken;
|
||||
mOwnerToken = ownerToken;
|
||||
mInitialBounds.set(initialBounds);
|
||||
mWindowingMode = windowingMode;
|
||||
mPairedPrimaryFragmentToken = pairedPrimaryFragmentToken;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -92,12 +112,22 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
return mWindowingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(b/232476698): remove the hide with adding CTS for this in next release.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public IBinder getPairedPrimaryFragmentToken() {
|
||||
return mPairedPrimaryFragmentToken;
|
||||
}
|
||||
|
||||
private TaskFragmentCreationParams(Parcel in) {
|
||||
mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in);
|
||||
mFragmentToken = in.readStrongBinder();
|
||||
mOwnerToken = in.readStrongBinder();
|
||||
mInitialBounds.readFromParcel(in);
|
||||
mWindowingMode = in.readInt();
|
||||
mPairedPrimaryFragmentToken = in.readStrongBinder();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -108,6 +138,7 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
dest.writeStrongBinder(mOwnerToken);
|
||||
mInitialBounds.writeToParcel(dest, flags);
|
||||
dest.writeInt(mWindowingMode);
|
||||
dest.writeStrongBinder(mPairedPrimaryFragmentToken);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -132,6 +163,7 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
+ " ownerToken=" + mOwnerToken
|
||||
+ " initialBounds=" + mInitialBounds
|
||||
+ " windowingMode=" + mWindowingMode
|
||||
+ " pairedFragmentToken=" + mPairedPrimaryFragmentToken
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@@ -159,6 +191,9 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
@WindowingMode
|
||||
private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
@Nullable
|
||||
private IBinder mPairedPrimaryFragmentToken;
|
||||
|
||||
public Builder(@NonNull TaskFragmentOrganizerToken organizer,
|
||||
@NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) {
|
||||
mOrganizer = organizer;
|
||||
@@ -180,14 +215,29 @@ public final class TaskFragmentCreationParams implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fragment token of the paired primary TaskFragment.
|
||||
* When it is set, the new TaskFragment will be positioned right above the paired
|
||||
* TaskFragment. Otherwise, the new TaskFragment will be positioned on the top of the Task
|
||||
* by default.
|
||||
*
|
||||
* This is needed in case we need to launch a placeholder Activity to split below a
|
||||
* transparent always-expand Activity.
|
||||
*
|
||||
* TODO(b/232476698): remove the hide with adding CTS for this in next release.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setPairedPrimaryFragmentToken(@Nullable IBinder fragmentToken) {
|
||||
mPairedPrimaryFragmentToken = fragmentToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Constructs the options to create TaskFragment with. */
|
||||
@NonNull
|
||||
public TaskFragmentCreationParams build() {
|
||||
final TaskFragmentCreationParams result = new TaskFragmentCreationParams(
|
||||
mOrganizer, mFragmentToken, mOwnerToken);
|
||||
result.mInitialBounds.set(mInitialBounds);
|
||||
result.mWindowingMode = mWindowingMode;
|
||||
return result;
|
||||
return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken,
|
||||
mInitialBounds, mWindowingMode, mPairedPrimaryFragmentToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,8 +133,18 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
}
|
||||
|
||||
// Create a TaskFragment for the secondary activity.
|
||||
createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken,
|
||||
secondaryFragmentBounds, windowingMode, activityIntent,
|
||||
final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder(
|
||||
getOrganizerToken(), secondaryFragmentToken, ownerToken)
|
||||
.setInitialBounds(secondaryFragmentBounds)
|
||||
.setWindowingMode(windowingMode)
|
||||
// Make sure to set the paired fragment token so that the new TaskFragment will be
|
||||
// positioned right above the paired TaskFragment.
|
||||
// This is needed in case we need to launch a placeholder Activity to split below a
|
||||
// transparent always-expand Activity.
|
||||
.setPairedPrimaryFragmentToken(launchingFragmentToken)
|
||||
.build();
|
||||
createTaskFragment(wct, fragmentOptions);
|
||||
wct.startActivityInTaskFragment(secondaryFragmentToken, ownerToken, activityIntent,
|
||||
activityOptions);
|
||||
|
||||
// Set adjacent to each other so that the containers below will be invisible.
|
||||
@@ -173,8 +183,21 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
*/
|
||||
void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
|
||||
@NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
|
||||
final TaskFragmentCreationParams fragmentOptions =
|
||||
createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
|
||||
final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder(
|
||||
getOrganizerToken(), fragmentToken, ownerToken)
|
||||
.setInitialBounds(bounds)
|
||||
.setWindowingMode(windowingMode)
|
||||
.build();
|
||||
createTaskFragment(wct, fragmentOptions);
|
||||
}
|
||||
|
||||
void createTaskFragment(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull TaskFragmentCreationParams fragmentOptions) {
|
||||
if (mFragmentInfos.containsKey(fragmentOptions.getFragmentToken())) {
|
||||
throw new IllegalArgumentException(
|
||||
"There is an existing TaskFragment with fragmentToken="
|
||||
+ fragmentOptions.getFragmentToken());
|
||||
}
|
||||
wct.createTaskFragment(fragmentOptions);
|
||||
}
|
||||
|
||||
@@ -189,18 +212,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
wct.reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ownerToken The token of the activity that creates this task fragment. It does not
|
||||
* have to be a child of this task fragment, but must belong to the same task.
|
||||
*/
|
||||
private void createTaskFragmentAndStartActivity(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds,
|
||||
@WindowingMode int windowingMode, @NonNull Intent activityIntent,
|
||||
@Nullable Bundle activityOptions) {
|
||||
createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
|
||||
wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions);
|
||||
}
|
||||
|
||||
void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) {
|
||||
WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null;
|
||||
@@ -238,22 +249,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
wct.setCompanionTaskFragment(secondary, finishSecondaryWithPrimary ? primary : null);
|
||||
}
|
||||
|
||||
TaskFragmentCreationParams createFragmentOptions(@NonNull IBinder fragmentToken,
|
||||
@NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
|
||||
if (mFragmentInfos.containsKey(fragmentToken)) {
|
||||
throw new IllegalArgumentException(
|
||||
"There is an existing TaskFragment with fragmentToken=" + fragmentToken);
|
||||
}
|
||||
|
||||
return new TaskFragmentCreationParams.Builder(
|
||||
getOrganizerToken(),
|
||||
fragmentToken,
|
||||
ownerToken)
|
||||
.setInitialBounds(bounds)
|
||||
.setWindowingMode(windowingMode)
|
||||
.build();
|
||||
}
|
||||
|
||||
void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
|
||||
@Nullable Rect bounds) {
|
||||
if (!mFragmentInfos.containsKey(fragmentToken)) {
|
||||
|
||||
@@ -1218,14 +1218,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
|
||||
@NonNull Activity activityInTask, int taskId) {
|
||||
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
|
||||
activityInTask, taskId);
|
||||
activityInTask, taskId, null /* pairedPrimaryContainer */);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
|
||||
@NonNull Activity activityInTask, int taskId) {
|
||||
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
|
||||
activityInTask, taskId);
|
||||
activityInTask, taskId, null /* pairedPrimaryContainer */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1237,10 +1237,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
* @param activityInTask activity in the same Task so that we can get the Task bounds
|
||||
* if needed.
|
||||
* @param taskId parent Task of the new TaskFragment.
|
||||
* @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
|
||||
* set, the new container will be added right above it.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
|
||||
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) {
|
||||
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
|
||||
@Nullable TaskFragmentContainer pairedPrimaryContainer) {
|
||||
if (activityInTask == null) {
|
||||
throw new IllegalArgumentException("activityInTask must not be null,");
|
||||
}
|
||||
@@ -1249,7 +1252,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
|
||||
pendingAppearedIntent, taskContainer, this);
|
||||
pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer);
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.util.Size;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowMetrics;
|
||||
import android.window.TaskFragmentCreationParams;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
@@ -307,10 +308,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
}
|
||||
|
||||
final int taskId = primaryContainer.getTaskId();
|
||||
final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
|
||||
launchingActivity, taskId);
|
||||
final int windowingMode = mController.getTaskContainer(taskId)
|
||||
.getWindowingModeForSplitTaskFragment(primaryRectBounds);
|
||||
final TaskFragmentContainer secondaryContainer = mController.newContainer(
|
||||
null /* pendingAppearedActivity */, activityIntent, launchingActivity, taskId,
|
||||
// Pass in the primary container to make sure it is added right above the primary.
|
||||
primaryContainer);
|
||||
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
|
||||
final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
|
||||
primaryRectBounds);
|
||||
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
|
||||
rule, splitAttributes);
|
||||
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
|
||||
@@ -412,17 +416,18 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
}
|
||||
|
||||
@Override
|
||||
void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
|
||||
@NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
|
||||
final TaskFragmentContainer container = mController.getContainer(fragmentToken);
|
||||
void createTaskFragment(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull TaskFragmentCreationParams fragmentOptions) {
|
||||
final TaskFragmentContainer container = mController.getContainer(
|
||||
fragmentOptions.getFragmentToken());
|
||||
if (container == null) {
|
||||
throw new IllegalStateException(
|
||||
"Creating a task fragment that is not registered with controller.");
|
||||
}
|
||||
|
||||
container.setLastRequestedBounds(bounds);
|
||||
container.setLastRequestedWindowingMode(windowingMode);
|
||||
super.createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
|
||||
container.setLastRequestedBounds(fragmentOptions.getInitialBounds());
|
||||
container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode());
|
||||
super.createTaskFragment(wct, fragmentOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -118,10 +118,12 @@ class TaskFragmentContainer {
|
||||
/**
|
||||
* Creates a container with an existing activity that will be re-parented to it in a window
|
||||
* container transaction.
|
||||
* @param pairedPrimaryContainer when it is set, the new container will be add right above it
|
||||
*/
|
||||
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
|
||||
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
|
||||
@NonNull SplitController controller) {
|
||||
@NonNull SplitController controller,
|
||||
@Nullable TaskFragmentContainer pairedPrimaryContainer) {
|
||||
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|
||||
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
|
||||
throw new IllegalArgumentException(
|
||||
@@ -130,7 +132,16 @@ class TaskFragmentContainer {
|
||||
mController = controller;
|
||||
mToken = new Binder("TaskFragmentContainer");
|
||||
mTaskContainer = taskContainer;
|
||||
taskContainer.mContainers.add(this);
|
||||
if (pairedPrimaryContainer != null) {
|
||||
if (pairedPrimaryContainer.getTaskContainer() != taskContainer) {
|
||||
throw new IllegalArgumentException(
|
||||
"pairedPrimaryContainer must be in the same Task");
|
||||
}
|
||||
final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer);
|
||||
taskContainer.mContainers.add(primaryIndex + 1, this);
|
||||
} else {
|
||||
taskContainer.mContainers.add(this);
|
||||
}
|
||||
if (pendingAppearedActivity != null) {
|
||||
addPendingAppearedActivity(pendingAppearedActivity);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class JetpackTaskFragmentOrganizerTest {
|
||||
public void testExpandTaskFragment() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
new Intent(), taskContainer, mSplitController);
|
||||
new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */);
|
||||
final TaskFragmentInfo info = createMockInfo(container);
|
||||
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
|
||||
container.setInfo(mTransaction, info);
|
||||
|
||||
@@ -169,7 +169,7 @@ public class SplitControllerTest {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
// tf1 has no running activity so is not active.
|
||||
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
|
||||
new Intent(), taskContainer, mSplitController);
|
||||
new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */);
|
||||
// tf2 has running activity so is active.
|
||||
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
|
||||
doReturn(1).when(tf2).getRunningActivityCount();
|
||||
@@ -375,7 +375,7 @@ public class SplitControllerTest {
|
||||
final Intent intent = new Intent();
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
intent, taskContainer, mSplitController);
|
||||
intent, taskContainer, mSplitController, null /* pairedPrimaryContainer */);
|
||||
final SplitController.ActivityStartMonitor monitor =
|
||||
mSplitController.getActivityStartMonitor();
|
||||
|
||||
@@ -609,7 +609,7 @@ public class SplitControllerTest {
|
||||
false /* isOnReparent */);
|
||||
|
||||
assertFalse(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -771,7 +771,7 @@ public class SplitControllerTest {
|
||||
false /* isOnReparent */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
|
||||
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@@ -813,7 +813,7 @@ public class SplitControllerTest {
|
||||
false /* isOnReparent */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
|
||||
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
|
||||
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ public class TaskContainerTest {
|
||||
assertTrue(taskContainer.isEmpty());
|
||||
|
||||
final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
|
||||
new Intent(), taskContainer, mController);
|
||||
new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
|
||||
assertFalse(taskContainer.isEmpty());
|
||||
|
||||
@@ -138,11 +138,11 @@ public class TaskContainerTest {
|
||||
assertNull(taskContainer.getTopTaskFragmentContainer());
|
||||
|
||||
final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
|
||||
new Intent(), taskContainer, mController);
|
||||
new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
assertEquals(tf0, taskContainer.getTopTaskFragmentContainer());
|
||||
|
||||
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
|
||||
new Intent(), taskContainer, mController);
|
||||
new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
assertEquals(tf1, taskContainer.getTopTaskFragmentContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -94,18 +94,21 @@ public class TaskFragmentContainerTest {
|
||||
|
||||
// One of the activity and the intent must be non-null
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new TaskFragmentContainer(null, null, taskContainer, mController));
|
||||
() -> new TaskFragmentContainer(null, null, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */));
|
||||
|
||||
// One of the activity and the intent must be null.
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController));
|
||||
() -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFinish() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
|
||||
null /* pendingAppearedIntent */, taskContainer, mController);
|
||||
null /* pendingAppearedIntent */, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */);
|
||||
doReturn(container).when(mController).getContainerWithActivity(mActivity);
|
||||
|
||||
// Only remove the activity, but not clear the reference until appeared.
|
||||
@@ -137,12 +140,14 @@ public class TaskFragmentContainerTest {
|
||||
public void testFinish_notFinishActivityThatIsReparenting() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
|
||||
null /* pendingAppearedIntent */, taskContainer, mController);
|
||||
null /* pendingAppearedIntent */, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */);
|
||||
final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
|
||||
container0.setInfo(mTransaction, info);
|
||||
// Request to reparent the activity to a new TaskFragment.
|
||||
final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
|
||||
null /* pendingAppearedIntent */, taskContainer, mController);
|
||||
null /* pendingAppearedIntent */, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */);
|
||||
doReturn(container1).when(mController).getContainerWithActivity(mActivity);
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
|
||||
@@ -159,7 +164,8 @@ public class TaskFragmentContainerTest {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
// Pending activity should be cleared when it has appeared on server side.
|
||||
final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
|
||||
null /* pendingAppearedIntent */, taskContainer, mController);
|
||||
null /* pendingAppearedIntent */, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */);
|
||||
|
||||
assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains(
|
||||
mActivity.getActivityToken()));
|
||||
@@ -172,7 +178,8 @@ public class TaskFragmentContainerTest {
|
||||
|
||||
// Pending intent should be cleared when the container becomes non-empty.
|
||||
final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer(
|
||||
null /* pendingAppearedActivity */, mIntent, taskContainer, mController);
|
||||
null /* pendingAppearedActivity */, mIntent, taskContainer, mController,
|
||||
null /* pairedPrimaryContainer */);
|
||||
|
||||
assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent());
|
||||
|
||||
@@ -187,7 +194,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testIsWaitingActivityAppear() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
|
||||
assertTrue(container.isWaitingActivityAppear());
|
||||
|
||||
@@ -209,7 +216,7 @@ public class TaskFragmentContainerTest {
|
||||
doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any());
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
|
||||
assertNull(container.mAppearEmptyTimeout);
|
||||
|
||||
@@ -249,7 +256,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testCollectNonFinishingActivities() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
List<Activity> activities = container.collectNonFinishingActivities();
|
||||
|
||||
assertTrue(activities.isEmpty());
|
||||
@@ -277,7 +284,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testAddPendingActivity() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertEquals(1, container.collectNonFinishingActivities().size());
|
||||
@@ -291,9 +298,9 @@ public class TaskFragmentContainerTest {
|
||||
public void testIsAbove() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
|
||||
assertTrue(container1.isAbove(container0));
|
||||
assertFalse(container0.isAbove(container1));
|
||||
@@ -303,7 +310,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testGetBottomMostActivity() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertEquals(mActivity, container.getBottomMostActivity());
|
||||
@@ -320,7 +327,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testOnActivityDestroyed() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer(mController);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
final List<IBinder> activities = new ArrayList<>();
|
||||
activities.add(mActivity.getActivityToken());
|
||||
@@ -340,7 +347,7 @@ public class TaskFragmentContainerTest {
|
||||
// True if no info set.
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
spyOn(taskContainer);
|
||||
doReturn(true).when(taskContainer).isVisible();
|
||||
|
||||
@@ -403,7 +410,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testHasAppearedActivity() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertFalse(container.hasAppearedActivity(mActivity.getActivityToken()));
|
||||
@@ -420,7 +427,7 @@ public class TaskFragmentContainerTest {
|
||||
public void testHasPendingAppearedActivity() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertTrue(container.hasPendingAppearedActivity(mActivity.getActivityToken()));
|
||||
@@ -437,9 +444,9 @@ public class TaskFragmentContainerTest {
|
||||
public void testHasActivity() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer(mController);
|
||||
final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
final TaskFragmentContainer container2 = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
|
||||
|
||||
// Activity is pending appeared on container2.
|
||||
container2.addPendingAppearedActivity(mActivity);
|
||||
@@ -472,6 +479,27 @@ public class TaskFragmentContainerTest {
|
||||
assertTrue(container2.hasActivity(mActivity.getActivityToken()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewContainerWithPairedPrimaryContainer() {
|
||||
final TaskContainer taskContainer = createTestTaskContainer();
|
||||
final TaskFragmentContainer tf0 = new TaskFragmentContainer(
|
||||
null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
|
||||
null /* pairedPrimaryTaskFragment */);
|
||||
final TaskFragmentContainer tf1 = new TaskFragmentContainer(
|
||||
null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
|
||||
null /* pairedPrimaryTaskFragment */);
|
||||
taskContainer.mContainers.add(tf0);
|
||||
taskContainer.mContainers.add(tf1);
|
||||
|
||||
// When tf2 is created with using tf0 as pairedPrimaryContainer, tf2 should be inserted
|
||||
// right above tf0.
|
||||
final TaskFragmentContainer tf2 = new TaskFragmentContainer(
|
||||
null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, tf0);
|
||||
assertEquals(0, taskContainer.indexOf(tf0));
|
||||
assertEquals(1, taskContainer.indexOf(tf2));
|
||||
assertEquals(2, taskContainer.indexOf(tf1));
|
||||
}
|
||||
|
||||
/** Creates a mock activity in the organizer process. */
|
||||
private Activity createMockActivity() {
|
||||
final Activity activity = mock(Activity.class);
|
||||
|
||||
@@ -1889,7 +1889,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
// actions.
|
||||
taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
|
||||
ownerActivity.getUid(), ownerActivity.info.processName);
|
||||
ownerTask.addChild(taskFragment, POSITION_TOP);
|
||||
final int position;
|
||||
if (creationParams.getPairedPrimaryFragmentToken() != null) {
|
||||
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
|
||||
// right above the paired one to make sure there is no other window in between.
|
||||
final TaskFragment pairedPrimaryTaskFragment = getTaskFragment(
|
||||
creationParams.getPairedPrimaryFragmentToken());
|
||||
final int pairedPosition = ownerTask.mChildren.indexOf(pairedPrimaryTaskFragment);
|
||||
position = pairedPosition != -1 ? pairedPosition + 1 : POSITION_TOP;
|
||||
} else {
|
||||
position = POSITION_TOP;
|
||||
}
|
||||
ownerTask.addChild(taskFragment, position);
|
||||
taskFragment.setWindowingMode(creationParams.getWindowingMode());
|
||||
taskFragment.setBounds(creationParams.getInitialBounds());
|
||||
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
|
||||
|
||||
@@ -552,10 +552,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
|
||||
final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
|
||||
final IBinder fragmentToken = new Binder();
|
||||
|
||||
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
|
||||
createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken);
|
||||
createTaskFragmentFromOrganizer(mTransaction, ownerActivity, mFragmentToken);
|
||||
mTransaction.startActivityInTaskFragment(
|
||||
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
|
||||
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
|
||||
@@ -564,7 +563,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// Successfully created a TaskFragment.
|
||||
final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
|
||||
final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(
|
||||
mFragmentToken);
|
||||
assertNotNull(taskFragment);
|
||||
assertEquals(ownerActivity.getTask(), taskFragment.getTask());
|
||||
}
|
||||
@@ -702,6 +702,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_createTaskFragment_withPairedPrimaryFragmentToken() {
|
||||
final Task task = createTask(mDisplayContent);
|
||||
mTaskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.setFragmentToken(mFragmentToken)
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
|
||||
final ActivityRecord activityOnTop = createActivityRecord(task);
|
||||
final int uid = Binder.getCallingUid();
|
||||
activityOnTop.info.applicationInfo.uid = uid;
|
||||
activityOnTop.getTask().effectiveUid = uid;
|
||||
final IBinder fragmentToken1 = new Binder();
|
||||
final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
|
||||
mOrganizerToken, fragmentToken1, activityOnTop.token)
|
||||
.setPairedPrimaryFragmentToken(mFragmentToken)
|
||||
.build();
|
||||
mTransaction.setTaskFragmentOrganizer(mIOrganizer);
|
||||
mTransaction.createTaskFragment(params);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// Successfully created a TaskFragment.
|
||||
final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(
|
||||
fragmentToken1);
|
||||
assertNotNull(taskFragment);
|
||||
// The new TaskFragment should be positioned right above the paired TaskFragment.
|
||||
assertEquals(task.mChildren.indexOf(mTaskFragment) + 1,
|
||||
task.mChildren.indexOf(taskFragment));
|
||||
// The top activity should remain on top.
|
||||
assertEquals(task.mChildren.indexOf(taskFragment) + 1,
|
||||
task.mChildren.indexOf(activityOnTop));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_reparentChildren() {
|
||||
doReturn(true).when(mTaskFragment).isAttached();
|
||||
|
||||
Reference in New Issue
Block a user