Merge "Only play ActivityEmbedding animation if there is AE split" into tm-qpr-dev
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 9c0025a..33ea2e4 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -113,8 +113,8 @@
/** 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 @@
/** 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 @@
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 @@
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");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 1c403f0..521a65c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -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 @@
@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.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
index 84befdd..3792e83 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -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 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 @@
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;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 2849b24..baecf6f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -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.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 @@
@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 @@
@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 @@
}
@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 @@
() -> 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);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f3c8062..488fe67 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -46,6 +46,7 @@
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 @@
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) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 5ba4411..28bcc03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,6 +19,7 @@
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_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;
@@ -1108,6 +1110,51 @@
}
@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);
final Task task = createTask(mDisplayContent);