Merge "Only play ActivityEmbedding animation if there is AE split" into tm-qpr-dev
This commit is contained in:
@@ -113,8 +113,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
/** The container is in a Task with embedded activity. */
|
||||
public static final int FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY = 1 << 9;
|
||||
|
||||
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
|
||||
public static final int FLAG_FIRST_CUSTOM = 1 << 10;
|
||||
/** The container fills its parent Task before and after the transition. */
|
||||
public static final int FLAG_FILLS_TASK = 1 << 10;
|
||||
|
||||
/** The container is going to show IME on its task after the transition. */
|
||||
public static final int FLAG_WILL_IME_SHOWN = 1 << 11;
|
||||
@@ -125,6 +125,9 @@ public final class TransitionInfo implements Parcelable {
|
||||
/** The container attaches work profile thumbnail for cross profile animation. */
|
||||
public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13;
|
||||
|
||||
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
|
||||
public static final int FLAG_FIRST_CUSTOM = 1 << 14;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "FLAG_" }, value = {
|
||||
FLAG_NONE,
|
||||
@@ -138,8 +141,11 @@ public final class TransitionInfo implements Parcelable {
|
||||
FLAG_DISPLAY_HAS_ALERT_WINDOWS,
|
||||
FLAG_IS_INPUT_METHOD,
|
||||
FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY,
|
||||
FLAG_FIRST_CUSTOM,
|
||||
FLAG_WILL_IME_SHOWN
|
||||
FLAG_FILLS_TASK,
|
||||
FLAG_WILL_IME_SHOWN,
|
||||
FLAG_CROSS_PROFILE_OWNER_THUMBNAIL,
|
||||
FLAG_CROSS_PROFILE_WORK_THUMBNAIL,
|
||||
FLAG_FIRST_CUSTOM
|
||||
})
|
||||
public @interface ChangeFlags {}
|
||||
|
||||
@@ -342,6 +348,9 @@ public final class TransitionInfo implements Parcelable {
|
||||
if ((flags & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0) {
|
||||
sb.append(sb.length() == 0 ? "" : "|").append("IN_TASK_WITH_EMBEDDED_ACTIVITY");
|
||||
}
|
||||
if ((flags & FLAG_FILLS_TASK) != 0) {
|
||||
sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK");
|
||||
}
|
||||
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
|
||||
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.wm.shell.activityembedding;
|
||||
|
||||
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
|
||||
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -84,12 +85,23 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
// TODO(b/207070762) Handle AE animation as a part of other transitions.
|
||||
boolean containsEmbeddingSplit = false;
|
||||
for (TransitionInfo.Change change : info.getChanges()) {
|
||||
if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
|
||||
// Only animate the transition if all changes are in a Task with ActivityEmbedding.
|
||||
return false;
|
||||
}
|
||||
if (!containsEmbeddingSplit && !change.hasFlags(FLAG_FILLS_TASK)) {
|
||||
// Whether the Task contains any ActivityEmbedding split before or after the
|
||||
// transition.
|
||||
containsEmbeddingSplit = true;
|
||||
}
|
||||
}
|
||||
if (!containsEmbeddingSplit) {
|
||||
// Let the system to play the default animation if there is no ActivityEmbedding split
|
||||
// window. This allows to play the app customized animation when there is no embedding,
|
||||
// such as the device is in a folded state.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start ActivityEmbedding animation.
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.wm.shell.activityembedding;
|
||||
|
||||
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
|
||||
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -24,6 +27,8 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.TransitionInfo;
|
||||
@@ -80,4 +85,23 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
|
||||
return new TransitionInfo.Change(mock(WindowContainerToken.class),
|
||||
mock(SurfaceControl.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock {@link TransitionInfo.Change} with
|
||||
* {@link TransitionInfo#FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY} flag.
|
||||
*/
|
||||
static TransitionInfo.Change createEmbeddedChange(@NonNull Rect startBounds,
|
||||
@NonNull Rect endBounds, @NonNull Rect taskBounds) {
|
||||
final TransitionInfo.Change change = createChange();
|
||||
change.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
|
||||
change.setStartAbsBounds(startBounds);
|
||||
change.setEndAbsBounds(endBounds);
|
||||
if (taskBounds.width() == startBounds.width()
|
||||
&& taskBounds.height() == startBounds.height()
|
||||
&& taskBounds.width() == endBounds.width()
|
||||
&& taskBounds.height() == endBounds.height()) {
|
||||
change.setFlags(FLAG_FILLS_TASK);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.wm.shell.activityembedding;
|
||||
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
||||
|
||||
@@ -29,6 +28,7 @@ import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.window.TransitionInfo;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -48,6 +48,10 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimationTestBase {
|
||||
|
||||
private static final Rect TASK_BOUNDS = new Rect(0, 0, 1000, 500);
|
||||
private static final Rect EMBEDDED_LEFT_BOUNDS = new Rect(0, 0, 500, 500);
|
||||
private static final Rect EMBEDDED_RIGHT_BOUNDS = new Rect(500, 0, 1000, 500);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
super.setUp();
|
||||
@@ -77,13 +81,13 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
|
||||
@Test
|
||||
public void testStartAnimation_containsNonActivityEmbeddingChange() {
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createChange();
|
||||
embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
|
||||
EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
|
||||
final TransitionInfo.Change nonEmbeddingChange = createChange();
|
||||
info.addChange(embeddingChange);
|
||||
info.addChange(nonEmbeddingChange);
|
||||
|
||||
// No-op
|
||||
// No-op because it contains non-embedded change.
|
||||
assertFalse(mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback));
|
||||
verify(mAnimRunner, never()).startAnimation(any(), any(), any(), any());
|
||||
@@ -93,13 +97,65 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAnimation_onlyActivityEmbeddingChange() {
|
||||
public void testStartAnimation_containsOnlyFillTaskActivityEmbeddingChange() {
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createChange();
|
||||
embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(TASK_BOUNDS, TASK_BOUNDS,
|
||||
TASK_BOUNDS);
|
||||
info.addChange(embeddingChange);
|
||||
|
||||
// No-op
|
||||
// No-op because it only contains embedded change that fills the Task. We will let the
|
||||
// default handler to animate such transition.
|
||||
assertFalse(mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback));
|
||||
verify(mAnimRunner, never()).startAnimation(any(), any(), any(), any());
|
||||
verifyNoMoreInteractions(mStartTransaction);
|
||||
verifyNoMoreInteractions(mFinishTransaction);
|
||||
verifyNoMoreInteractions(mFinishCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAnimation_containsActivityEmbeddingSplitChange() {
|
||||
// Change that occupies only part of the Task.
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
|
||||
EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
|
||||
info.addChange(embeddingChange);
|
||||
|
||||
// ActivityEmbeddingController will handle such transition.
|
||||
assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback));
|
||||
verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction);
|
||||
verify(mStartTransaction).apply();
|
||||
verifyNoMoreInteractions(mFinishTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAnimation_containsChangeEnterActivityEmbeddingSplit() {
|
||||
// Change that is entering ActivityEmbedding split.
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(TASK_BOUNDS,
|
||||
EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
|
||||
info.addChange(embeddingChange);
|
||||
|
||||
// ActivityEmbeddingController will handle such transition.
|
||||
assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback));
|
||||
verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction);
|
||||
verify(mStartTransaction).apply();
|
||||
verifyNoMoreInteractions(mFinishTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAnimation_containsChangeExitActivityEmbeddingSplit() {
|
||||
// Change that is exiting ActivityEmbedding split.
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS,
|
||||
TASK_BOUNDS, TASK_BOUNDS);
|
||||
info.addChange(embeddingChange);
|
||||
|
||||
// ActivityEmbeddingController will handle such transition.
|
||||
assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback));
|
||||
verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
|
||||
@@ -115,8 +171,8 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
|
||||
() -> mController.onAnimationFinished(mTransition));
|
||||
|
||||
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
|
||||
final TransitionInfo.Change embeddingChange = createChange();
|
||||
embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
|
||||
final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
|
||||
EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
|
||||
info.addChange(embeddingChange);
|
||||
mController.startAnimation(mTransition, info, mStartTransaction,
|
||||
mFinishTransaction, mFinishCallback);
|
||||
|
||||
@@ -46,6 +46,7 @@ import static android.view.WindowManager.TransitionFlags;
|
||||
import static android.view.WindowManager.TransitionType;
|
||||
import static android.view.WindowManager.transitTypeToString;
|
||||
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
|
||||
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
|
||||
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
||||
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
|
||||
@@ -1888,10 +1889,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
if (taskFragment != null && task == null) {
|
||||
parentTask = taskFragment.getTask();
|
||||
}
|
||||
if (parentTask != null
|
||||
&& parentTask.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
|
||||
// Whether this is in a Task with embedded activity.
|
||||
flags |= FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
if (parentTask != null) {
|
||||
if (parentTask.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
|
||||
// Whether this is in a Task with embedded activity.
|
||||
flags |= FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
}
|
||||
final Rect taskBounds = parentTask.getBounds();
|
||||
final Rect startBounds = mAbsoluteBounds;
|
||||
final Rect endBounds = wc.getBounds();
|
||||
if (taskBounds.width() == startBounds.width()
|
||||
&& taskBounds.height() == startBounds.height()
|
||||
&& taskBounds.width() == endBounds.width()
|
||||
&& taskBounds.height() == endBounds.height()) {
|
||||
// Whether the container fills the Task bounds before and after the transition.
|
||||
flags |= FLAG_FILLS_TASK;
|
||||
}
|
||||
}
|
||||
final DisplayContent dc = wc.asDisplayContent();
|
||||
if (dc != null) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.wm;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
|
||||
@@ -31,6 +32,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
|
||||
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
|
||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
|
||||
@@ -1107,6 +1109,51 @@ public class TransitionTests extends WindowTestsBase {
|
||||
assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagFillsTask() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
|
||||
final ArraySet<WindowContainer> participants = transition.mParticipants;
|
||||
|
||||
final Task task = createTask(mDisplayContent);
|
||||
// Set to multi-windowing mode in order to set bounds.
|
||||
task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
|
||||
final Rect taskBounds = new Rect(0, 0, 500, 1000);
|
||||
task.setBounds(taskBounds);
|
||||
final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
|
||||
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
|
||||
mAtm.mTaskFragmentOrganizerController.registerOrganizer(
|
||||
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
|
||||
final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.createActivityCount(1)
|
||||
.setOrganizer(organizer)
|
||||
.build();
|
||||
final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
|
||||
// Start states.
|
||||
changes.put(task, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
|
||||
changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */,
|
||||
false /* exChg */));
|
||||
changes.put(embeddedTf, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
|
||||
// End states.
|
||||
nonEmbeddedActivity.mVisibleRequested = false;
|
||||
embeddedActivity.mVisibleRequested = true;
|
||||
embeddedTf.setBounds(new Rect(0, 0, 500, 500));
|
||||
|
||||
participants.add(nonEmbeddedActivity);
|
||||
participants.add(embeddedTf);
|
||||
final ArrayList<WindowContainer> targets = Transition.calculateTargets(
|
||||
participants, changes);
|
||||
final TransitionInfo info = Transition.calculateTransitionInfo(
|
||||
transition.mType, 0 /* flags */, targets, changes, mMockT);
|
||||
|
||||
// The embedded with bounds overridden should not have the flag.
|
||||
assertEquals(2, info.getChanges().size());
|
||||
assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
|
||||
assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds());
|
||||
assertFalse(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeEmbeddedActivityReparent() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
|
||||
Reference in New Issue
Block a user