Merge "Polish ActivityEmbedding enter/exit PiP (1/n)"

This commit is contained in:
Chris Li
2022-10-13 04:40:32 +00:00
committed by Android (Google) Code Review
7 changed files with 105 additions and 9 deletions

View File

@@ -407,6 +407,7 @@ public final class TransitionInfo implements Parcelable {
public static final class Change implements Parcelable {
private final WindowContainerToken mContainer;
private WindowContainerToken mParent;
private WindowContainerToken mLastParent;
private final SurfaceControl mLeash;
private @TransitionMode int mMode = TRANSIT_NONE;
private @ChangeFlags int mFlags = FLAG_NONE;
@@ -435,6 +436,7 @@ public final class TransitionInfo implements Parcelable {
private Change(Parcel in) {
mContainer = in.readTypedObject(WindowContainerToken.CREATOR);
mParent = in.readTypedObject(WindowContainerToken.CREATOR);
mLastParent = in.readTypedObject(WindowContainerToken.CREATOR);
mLeash = new SurfaceControl();
mLeash.readFromParcel(in);
mMode = in.readInt();
@@ -458,6 +460,14 @@ public final class TransitionInfo implements Parcelable {
mParent = parent;
}
/**
* Sets the parent of this change's container before the transition if this change's
* container is reparented in the transition.
*/
public void setLastParent(@Nullable WindowContainerToken lastParent) {
mLastParent = lastParent;
}
/** Sets the transition mode for this change */
public void setMode(@TransitionMode int mode) {
mMode = mode;
@@ -541,6 +551,17 @@ public final class TransitionInfo implements Parcelable {
return mParent;
}
/**
* @return the parent of the changing container before the transition if it is reparented
* in the transition. The parent window may not be collected in the transition as a
* participant, and it may have been detached from the display. {@code null} if the changing
* container has not been reparented in the transition, or if the parent is not organizable.
*/
@Nullable
public WindowContainerToken getLastParent() {
return mLastParent;
}
/** @return which action this change represents. */
public @TransitionMode int getMode() {
return mMode;
@@ -640,6 +661,7 @@ public final class TransitionInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedObject(mContainer, flags);
dest.writeTypedObject(mParent, flags);
dest.writeTypedObject(mLastParent, flags);
mLeash.writeToParcel(dest, flags);
dest.writeInt(mMode);
dest.writeInt(mFlags);
@@ -685,6 +707,7 @@ public final class TransitionInfo implements Parcelable {
+ mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation
+ " endFixedRotation=" + mEndFixedRotation;
if (mSnapshot != null) out += " snapshot=" + mSnapshot;
if (mLastParent != null) out += " lastParent=" + mLastParent;
return out + "}";
}
}

View File

@@ -452,14 +452,17 @@ public class PipTransition extends PipTransitionController {
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TaskInfo taskInfo, @Nullable TransitionInfo.Change pipTaskChange) {
TransitionInfo.Change pipChange = pipTaskChange;
if (pipChange == null) {
if (mCurrentPipTaskToken == null) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: There is no existing PiP Task for TRANSIT_EXIT_PIP", TAG);
} else if (pipChange == null) {
// The pipTaskChange is null, this can happen if we are reparenting the PIP activity
// back to its original Task. In that case, we should animate the activity leash
// instead, which should be the only non-task, independent, TRANSIT_CHANGE window.
// instead, which should be the change whose last parent is the recorded PiP Task.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() == null && change.getMode() == TRANSIT_CHANGE
&& TransitionInfo.isIndependent(change, info)) {
if (mCurrentPipTaskToken.equals(change.getLastParent())) {
// Find the activity that is exiting PiP.
pipChange = change;
break;
}

View File

@@ -2020,7 +2020,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
// to its previous freeform bounds.
rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds);
rootTask.setBounds(task.getBounds());
// When creating a new Task for PiP, set its initial bounds as the TaskFragment in
// case the activity is embedded, so that it can be animated to PiP window from the
// current bounds.
// Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
// will be updated later after this is collected in transition.
rootTask.setBoundsUnchecked(r.getTaskFragment().getBounds());
// Move the last recents animation transaction from original task to the new one.
if (task.mLastRecentsAnimationTransaction != null) {

View File

@@ -2605,6 +2605,13 @@ class Task extends TaskFragment {
return boundsChange;
}
/** Sets the requested bounds regardless of the windowing mode. */
int setBoundsUnchecked(@NonNull Rect bounds) {
final int boundsChange = super.setBounds(bounds);
updateSurfaceBounds();
return boundsChange;
}
@Override
public boolean isCompatible(int windowingMode, int activityType) {
// TODO: Should we just move this to ConfigurationContainer?
@@ -5852,10 +5859,7 @@ class Task extends TaskFragment {
return BOUNDS_CHANGE_NONE;
}
final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
updateSurfaceBounds();
return result;
return setBoundsUnchecked(!inMultiWindowMode() ? null : bounds);
}
@Override

View File

@@ -1561,6 +1561,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (info.mEndParent != null) {
change.setParent(info.mEndParent.mRemoteToken.toWindowContainerToken());
}
if (info.mStartParent != null && info.mStartParent.mRemoteToken != null
&& target.getParent() != info.mStartParent) {
change.setLastParent(info.mStartParent.mRemoteToken.toWindowContainerToken());
}
change.setMode(info.getTransitMode(target));
change.setStartAbsBounds(info.mAbsoluteBounds);
change.setFlags(info.getChangeFlags(target));

View File

@@ -72,6 +72,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.PowerManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -391,6 +392,33 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
}
@Test
public void testMovingEmbeddedActivityToPip() {
final Rect taskBounds = new Rect(0, 0, 800, 1000);
final Rect taskFragmentBounds = new Rect(0, 0, 400, 1000);
final Task task = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
task.setBounds(taskBounds);
assertEquals(taskBounds, task.getBounds());
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.createActivityCount(2)
.setBounds(taskFragmentBounds)
.build();
assertEquals(taskFragmentBounds, taskFragment.getBounds());
final ActivityRecord topActivity = taskFragment.getTopMostActivity();
// Move the top activity to pinned root task.
mRootWindowContainer.moveActivityToPinnedRootTask(topActivity,
null /* launchIntoPipHostActivity */, "test");
final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask();
// Ensure the initial bounds of the PiP Task is the same as the TaskFragment.
ensureTaskPlacement(pinnedRootTask, topActivity);
assertEquals(taskFragmentBounds, pinnedRootTask.getBounds());
}
private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();

View File

@@ -1324,6 +1324,35 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
}
@Test
public void testReparentChangeLastParent() {
final Transition transition = createTestTransition(TRANSIT_CHANGE);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
final ArraySet<WindowContainer> participants = transition.mParticipants;
// Reparent activity in transition.
final Task lastParent = createTask(mDisplayContent);
final Task newParent = createTask(mDisplayContent);
final ActivityRecord activity = createActivityRecord(lastParent);
activity.mVisibleRequested = true;
// Skip manipulate the SurfaceControl.
doNothing().when(activity).setDropInputMode(anyInt());
changes.put(activity, new Transition.ChangeInfo(activity));
activity.reparent(newParent, POSITION_TOP);
activity.mVisibleRequested = false;
participants.add(activity);
final ArrayList<WindowContainer> targets = Transition.calculateTargets(
participants, changes);
final TransitionInfo info = Transition.calculateTransitionInfo(
transition.mType, 0 /* flags */, targets, changes, mMockT);
// Change contains last parent info.
assertEquals(1, info.getChanges().size());
assertEquals(lastParent.mRemoteToken.toWindowContainerToken(),
info.getChanges().get(0).getLastParent());
}
@Test
public void testIncludeEmbeddedActivityReparent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);