Unrotate going-away apps during a launch-into-orientation transition
When going from a non-launcher task to another non-launcher task
that is in a different orientation, we just animate the new task
opening in the new orientation (no screen rotation animation). To
make this work, though, we need to unrotate all the going-away
windows because they are in the old orientation.
Added CounterRotator to a utility area in shell so it can be
shared with other components (like Sysui shared lib).
Bug: 183993924
Test: atest OpenAppColdTest
Change-Id: I0263dfb8d529b6fc3dfbe3775e3e6e0b77f6ca8a
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 9aaef3b..3ba1a34 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,6 +39,14 @@
}
filegroup {
+ name: "wm_shell_util-sources",
+ srcs: [
+ "src/com/android/wm/shell/util/**/*.java",
+ ],
+ path: "src",
+}
+
+filegroup {
name: "wm_shell-aidls",
srcs: [
"src/**/*.aidl",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index b673d48..663d647 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -41,6 +41,7 @@
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -70,6 +71,7 @@
import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -82,6 +84,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
@@ -269,9 +272,16 @@
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
+ final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
+ for (int i = 0; i < counterRotators.size(); ++i) {
+ counterRotators.valueAt(i).cleanUp(info.getRootLeash());
+ }
+ counterRotators.clear();
+
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -285,16 +295,44 @@
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
- && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
- final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
- mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash());
- mRotationAnimation.startAnimation(animations, onAnimFinish,
- mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
- continue;
+ if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ int rotateDelta = change.getEndRotation() - change.getStartRotation();
+ int displayW = change.getEndAbsBounds().width();
+ int displayH = change.getEndAbsBounds().height();
+ if (info.getType() == TRANSIT_CHANGE) {
+ boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ final int anim = getRotationAnimation(info);
+ if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash());
+ mRotationAnimation.startAnimation(animations, onAnimFinish,
+ mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ continue;
+ }
+ } else {
+ // opening/closing an app into a new orientation. Counter-rotate all
+ // "going-away" things since they are still in the old orientation.
+ for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change innerChange = info.getChanges().get(j);
+ if (!Transitions.isClosingType(innerChange.getMode())
+ || !isIndependent(innerChange, info)
+ || innerChange.getParent() == null) {
+ continue;
+ }
+ CounterRotator crot = counterRotators.get(innerChange.getParent());
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction,
+ info.getChange(innerChange.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (crot.getSurface() != null) {
+ int layer = info.getChanges().size() - j;
+ startTransaction.setLayer(crot.getSurface(), layer);
+ }
+ counterRotators.put(innerChange.getParent(), crot);
+ }
+ crot.addChild(startTransaction, innerChange.getLeash());
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
new file mode 100644
index 0000000..b9b6716
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util;
+
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class that takes care of counter-rotating surfaces during a transition animation.
+ */
+public class CounterRotator {
+ SurfaceControl mSurface = null;
+ ArrayList<SurfaceControl> mRotateChildren = null;
+
+ /** Gets the surface with the counter-rotation. */
+ public SurfaceControl getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * Sets up this rotator.
+ *
+ * @param rotateDelta is the forward rotation change (the rotation the display is making).
+ * @param displayW (and H) Is the size of the rotating display.
+ */
+ public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
+ float displayW, float displayH) {
+ if (rotateDelta == 0) return;
+ mRotateChildren = new ArrayList<>();
+ // We want to counter-rotate, so subtract from 4
+ rotateDelta = 4 - (rotateDelta + 4) % 4;
+ mSurface = new SurfaceControl.Builder()
+ .setName("Transition Unrotate")
+ .setContainerLayer()
+ .setParent(parent)
+ .build();
+ // column-major
+ if (rotateDelta == 1) {
+ t.setMatrix(mSurface, 0, 1, -1, 0);
+ t.setPosition(mSurface, displayW, 0);
+ } else if (rotateDelta == 2) {
+ t.setMatrix(mSurface, -1, 0, 0, -1);
+ t.setPosition(mSurface, displayW, displayH);
+ } else if (rotateDelta == 3) {
+ t.setMatrix(mSurface, 0, -1, 1, 0);
+ t.setPosition(mSurface, 0, displayH);
+ }
+ t.show(mSurface);
+ }
+
+ /**
+ * Add a surface that needs to be counter-rotate.
+ */
+ public void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
+ if (mSurface == null) return;
+ t.reparent(child, mSurface);
+ mRotateChildren.add(child);
+ }
+
+ /**
+ * Clean-up. This undoes any reparenting and effectively stops the counter-rotation.
+ */
+ public void cleanUp(SurfaceControl rootLeash) {
+ if (mSurface == null) return;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
+ t.reparent(mRotateChildren.get(i), rootLeash);
+ }
+ t.remove(mSurface);
+ t.apply();
+ }
+}