Fix open ActivityEmbedding split with Shell transition

1. Collect the TaskFragment when reparenting an activity into it.
2. Check adjacent TaskFragment for transition flag FLAG_TRANSLUCENT.
3. Offset the relative position for default animation.

Bug: 207070762
Test: pass existing
Change-Id: I62b533dbb087007e3ef126170691a59929eb75e1
This commit is contained in:
Chris Li
2022-07-18 17:29:57 +08:00
parent bbb516199e
commit 0dfebbe4d7
6 changed files with 94 additions and 13 deletions

View File

@@ -3139,6 +3139,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
"956467125": {
"message": "Reparenting Activity to embedded TaskFragment, but the Activity is not collected",
"level": "WARN",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
"958338552": {
"message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
"level": "VERBOSE",

View File

@@ -483,11 +483,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
postStartTransactionCallbacks.add(t ->
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor,
null /* position */, cornerRadius, clipRect));
change.getEndRelOffset(), cornerRadius, clipRect));
} else {
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
cornerRadius, clipRect);
mTransactionPool, mMainExecutor, mAnimExecutor,
change.getEndRelOffset(), cornerRadius, clipRect);
}
if (info.getAnimationOptions() != null) {
@@ -934,7 +934,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
cornerRadius, change.getEndAbsBounds());
}
@@ -959,7 +959,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
mMainExecutor, mAnimExecutor, null /* position */,
mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
cornerRadius, change.getEndAbsBounds());
}

View File

@@ -2973,6 +2973,10 @@ class ActivityStarter {
newParent = candidateTf;
}
}
if (newParent.canHaveEmbeddingActivityTransition(mStartActivity)) {
// Make sure the embedded TaskFragment is included in the start activity transition.
newParent.collectEmbeddedTaskFragmentIfNeeded();
}
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);

View File

@@ -2316,6 +2316,26 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return !startBounds.equals(getBounds());
}
boolean canHaveEmbeddingActivityTransition(@NonNull ActivityRecord child) {
if (!isOrganizedTaskFragment() || !mTransitionController.isShellTransitionsEnabled()) {
return false;
}
// The activity should request open transition when it is becoming visible.
return child.isVisibleRequested();
}
void collectEmbeddedTaskFragmentIfNeeded() {
if (!isOrganizedTaskFragment() || mTransitionController.isCollecting(this)) {
return;
}
if (getChildCount() == 0) {
// The TaskFragment is new created, and just becoming non-empty.
mTransitionController.collectExistenceChange(this);
} else {
mTransitionController.collect(this);
}
}
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);

View File

@@ -1149,6 +1149,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return false;
}
private static boolean isTranslucent(@NonNull WindowContainer wc) {
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null) {
if (taskFragment.isTranslucent(null /* starting */)) {
return true;
}
final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
if (adjacentTaskFragment != null) {
// Treat the TaskFragment as translucent if its adjacent TF is, otherwise everything
// behind two adjacent TaskFragments are occluded.
return adjacentTaskFragment.isTranslucent(null /* starting */);
}
}
// TODO(b/172695805): hierarchical check. This is non-trivial because for containers
// it is effected by child visibility but needs to work even
// before visibility is committed. This means refactoring some
// checks to use requested visibility.
return !wc.fillsParent();
}
/**
* Under some conditions (eg. all visible targets within a parent container are transitioning
* the same way) the transition can be "promoted" to the parent container. This means an
@@ -1701,20 +1721,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mShowWallpaper || wc.showWallpaper()) {
flags |= FLAG_SHOW_WALLPAPER;
}
if (!wc.fillsParent()) {
// TODO(b/172695805): hierarchical check. This is non-trivial because for containers
// it is effected by child visibility but needs to work even
// before visibility is committed. This means refactoring some
// checks to use requested visibility.
if (isTranslucent(wc)) {
flags |= FLAG_TRANSLUCENT;
}
final Task task = wc.asTask();
if (task != null && task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
}
if (task != null && task.isTranslucent(null)) {
flags |= FLAG_TRANSLUCENT;
}
final ActivityRecord record = wc.asActivityRecord();
if (record != null) {
if (record.mUseTransferredAnimation) {

View File

@@ -21,6 +21,7 @@ import static android.app.ActivityManager.isStartResultSuccessful;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -837,6 +838,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(parent,
activity);
activity.reparent(parent, POSITION_TOP);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
@@ -1061,6 +1064,41 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects;
}
private void prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(
@NonNull TaskFragment taskFragment, @NonNull ActivityRecord activity) {
if (!taskFragment.canHaveEmbeddingActivityTransition(activity)) {
return;
}
// The reparent can happen in the following cases:
// 1. Reparent an existing activity to split when app launches new intent.
// - This happens after app calls to start activity, but before the activity is actually
// started, so we don't expect any collecting transition, but if it does, we can't
// queue the WCT because the start activity won't wait.
// 2. Reparent an existing activity to split to launch placeholder when Task size changed.
// - We expect to have a collecting transition for the Task resize, so just collect.
// 3. Reparent a new launching activity to an always-expand container.
// 4. Reparent a new launching activity to split to launch placeholder together.
// 5. Reparent a new launching activity to an existing split.
// - The new launching activity should have start an OPEN transition, so just collect.
// 6. Reparent PiP activity back to the original Task.
// - This should be part of the exiting PiP transition, so just collect.
if (!taskFragment.getBounds().equals(activity.getBounds()) && activity.isVisible()
&& !mTransitionController.isCollecting()) {
// 1. Reparent an existing activity to split when app launches new intent.
mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, activity);
}
// We expect the activity to be in the transition already, so just collect the TaskFragment.
if (mTransitionController.isCollecting(activity)) {
taskFragment.collectEmbeddedTaskFragmentIfNeeded();
} else {
ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Reparenting Activity"
+ " to embedded TaskFragment, but the Activity is not collected");
}
}
/** A helper method to send minimum dimension violation error to the client. */
private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
IBinder errorCallbackToken, String reason) {