Include activity reparent in transition
When an activity is reparented from a Task to a TaskFragment of different bounds, freeze the surface and include it in app transition. Bug: 196173550 Test: test with demo app Test: AppTransitionTests#testActivityRecordReparentToTaskFragment Change-Id: I73edc2c4091f0e4156ecf963ce4d1fcbd00333c8
This commit is contained in:
@@ -1389,6 +1389,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return parent != null ? parent.asTaskFragment() : null;
|
||||
}
|
||||
|
||||
/** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
|
||||
private boolean shouldStartChangeTransition(
|
||||
@Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
|
||||
if (mWmService.mDisableTransitionAnimation
|
||||
|| mDisplayContent == null || newParent == null || oldParent == null
|
||||
|| getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transition change for the activity moving into a TaskFragment of different bounds.
|
||||
return newParent.isOrganizedTaskFragment()
|
||||
&& !newParent.getBounds().equals(oldParent.getBounds());
|
||||
}
|
||||
|
||||
@Override
|
||||
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
|
||||
final TaskFragment oldParent = (TaskFragment) rawOldParent;
|
||||
@@ -1397,6 +1411,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
final Task newTask = newParent != null ? newParent.getTask() : null;
|
||||
this.task = newTask;
|
||||
|
||||
if (shouldStartChangeTransition(newParent, oldParent)) {
|
||||
initializeChangeTransition(getBounds());
|
||||
}
|
||||
|
||||
super.onParentChanged(newParent, oldParent);
|
||||
|
||||
if (isPersistable()) {
|
||||
@@ -2607,6 +2625,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return parent != null ? parent.getOrganizedTaskFragment() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEmbedded() {
|
||||
final TaskFragment parent = getTaskFragment();
|
||||
return parent != null && parent.isEmbedded();
|
||||
|
||||
@@ -237,6 +237,8 @@ public class AppTransitionController {
|
||||
|
||||
// Check if there is any override
|
||||
if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
|
||||
// Unfreeze the windows that were previously frozen for TaskFragment animation.
|
||||
unfreezeEmbeddedChangingWindows();
|
||||
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
|
||||
}
|
||||
|
||||
@@ -341,6 +343,9 @@ public class AppTransitionController {
|
||||
switch (changingType) {
|
||||
case TYPE_TASK:
|
||||
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
|
||||
case TYPE_ACTIVITY:
|
||||
// ActivityRecord is put in a change transition only when it is reparented
|
||||
// to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition.
|
||||
case TYPE_TASK_FRAGMENT:
|
||||
return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
|
||||
default:
|
||||
@@ -511,6 +516,16 @@ public class AppTransitionController {
|
||||
: null;
|
||||
}
|
||||
|
||||
private void unfreezeEmbeddedChangingWindows() {
|
||||
final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers;
|
||||
for (int i = changingContainers.size() - 1; i >= 0; i--) {
|
||||
final WindowContainer wc = changingContainers.valueAt(i);
|
||||
if (wc.isEmbedded()) {
|
||||
wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the pending transition with the remote animation defined by the
|
||||
* {@link ITaskFragmentOrganizer} if all windows in the transition are children of
|
||||
|
||||
@@ -33,7 +33,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
|
||||
import static android.os.UserHandle.USER_NULL;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
|
||||
import static android.view.WindowManager.TRANSIT_NONE;
|
||||
@@ -465,7 +464,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns {@code true} if this is a container for embedded activities or tasks. */
|
||||
@Override
|
||||
boolean isEmbedded() {
|
||||
if (mIsEmbedded) {
|
||||
return true;
|
||||
@@ -2073,15 +2072,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return !startBounds.equals(getBounds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
|
||||
*/
|
||||
void initializeChangeTransition(Rect startBounds) {
|
||||
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
|
||||
mDisplayContent.mChangingContainers.add(this);
|
||||
mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setSurfaceControl(SurfaceControl sc) {
|
||||
super.setSurfaceControl(sc);
|
||||
@@ -2163,6 +2153,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return mTaskFragmentOrganizer != null;
|
||||
}
|
||||
|
||||
/** Whether this is an organized {@link TaskFragment} and not a {@link Task}. */
|
||||
final boolean isOrganizedTaskFragment() {
|
||||
return mTaskFragmentOrganizer != null;
|
||||
}
|
||||
|
||||
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
|
||||
void clearLastPausedActivity() {
|
||||
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
|
||||
|
||||
@@ -30,6 +30,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.os.UserHandle.USER_NULL;
|
||||
import static android.view.SurfaceControl.Transaction;
|
||||
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
|
||||
@@ -2578,13 +2579,34 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
mSurfaceFreezer.unfreeze(getPendingTransaction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
|
||||
*
|
||||
* For now, this will only be called for the following cases:
|
||||
* 1. {@link Task} is changing windowing mode between fullscreen and freeform.
|
||||
* 2. {@link TaskFragment} is organized and is changing window bounds.
|
||||
* 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
|
||||
*
|
||||
* This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
|
||||
*/
|
||||
void initializeChangeTransition(Rect startBounds) {
|
||||
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
|
||||
mDisplayContent.mChangingContainers.add(this);
|
||||
mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
|
||||
}
|
||||
|
||||
ArraySet<WindowContainer> getAnimationSources() {
|
||||
return mSurfaceAnimationSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceControl getFreezeSnapshotTarget() {
|
||||
return null;
|
||||
// Only allow freezing if this window is in a TRANSIT_CHANGE
|
||||
if (!mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)
|
||||
|| !mDisplayContent.mChangingContainers.contains(this)) {
|
||||
return null;
|
||||
}
|
||||
return getSurfaceControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3189,6 +3211,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return {@code true} if this is a container for embedded activities or tasks. */
|
||||
boolean isEmbedded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if this container's surface should be shown when it is created.
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,7 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -54,6 +55,8 @@ import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.WindowManager;
|
||||
import android.window.ITaskFragmentOrganizer;
|
||||
import android.window.TaskFragmentOrganizer;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -391,6 +394,35 @@ public class AppTransitionTests extends WindowTestsBase {
|
||||
mDc.mAppTransition.getAnimationStyleResId(attrs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityRecordReparentToTaskFragment() {
|
||||
final ActivityRecord activity = createActivityRecord(mDc);
|
||||
activity.setVisibility(true);
|
||||
final Task task = activity.getTask();
|
||||
|
||||
// Add a TaskFragment of half of the Task size.
|
||||
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
|
||||
final ITaskFragmentOrganizer iOrganizer =
|
||||
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
|
||||
mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
|
||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.setOrganizer(organizer)
|
||||
.build();
|
||||
final Rect taskBounds = new Rect();
|
||||
task.getBounds(taskBounds);
|
||||
taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
|
||||
|
||||
assertTrue(mDc.mChangingContainers.isEmpty());
|
||||
assertFalse(mDc.mAppTransition.isTransitionSet());
|
||||
|
||||
// Schedule app transition when reparent activity to a TaskFragment of different size.
|
||||
activity.reparent(taskFragment, POSITION_TOP);
|
||||
|
||||
assertTrue(mDc.mChangingContainers.contains(activity));
|
||||
assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE));
|
||||
}
|
||||
|
||||
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
|
||||
boolean mCancelled = false;
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user