diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 87cfbb2a57055..9b91cf2e9db62 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -89,6 +89,8 @@ public final class BackNavigationInfo implements Parcelable {
@Nullable
private final IOnBackInvokedCallback mOnBackInvokedCallback;
private final boolean mPrepareRemoteAnimation;
+ @Nullable
+ private WindowContainerToken mDepartingWindowContainerToken;
/**
* Create a new {@link BackNavigationInfo} instance.
@@ -97,6 +99,7 @@ public final class BackNavigationInfo implements Parcelable {
* @param onBackNavigationDone The callback to be called once the client is done with the
* back preview.
* @param onBackInvokedCallback The back callback registered by the current top level window.
+ * @param departingWindowContainerToken The {@link WindowContainerToken} of departing window.
* @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture
* animation, if true, the caller of startBackNavigation should
* be expected to receive an animation start callback.
@@ -104,11 +107,13 @@ public final class BackNavigationInfo implements Parcelable {
private BackNavigationInfo(@BackTargetType int type,
@Nullable RemoteCallback onBackNavigationDone,
@Nullable IOnBackInvokedCallback onBackInvokedCallback,
- boolean isPrepareRemoteAnimation) {
+ boolean isPrepareRemoteAnimation,
+ @Nullable WindowContainerToken departingWindowContainerToken) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
mPrepareRemoteAnimation = isPrepareRemoteAnimation;
+ mDepartingWindowContainerToken = departingWindowContainerToken;
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -116,6 +121,7 @@ public final class BackNavigationInfo implements Parcelable {
mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPrepareRemoteAnimation = in.readBoolean();
+ mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR);
}
@Override
@@ -124,6 +130,7 @@ public final class BackNavigationInfo implements Parcelable {
dest.writeTypedObject(mOnBackNavigationDone, flags);
dest.writeStrongInterface(mOnBackInvokedCallback);
dest.writeBoolean(mPrepareRemoteAnimation);
+ dest.writeTypedObject(mDepartingWindowContainerToken, flags);
}
/**
@@ -156,6 +163,18 @@ public final class BackNavigationInfo implements Parcelable {
return mPrepareRemoteAnimation;
}
+ /**
+ * Returns the {@link WindowContainerToken} of the highest container in the hierarchy being
+ * removed.
+ *
+ * For example, if an Activity is the last one of its Task, the Task's token will be given.
+ * Otherwise, it will be the Activity's token.
+ */
+ @Nullable
+ public WindowContainerToken getDepartingWindowContainerToken() {
+ return mDepartingWindowContainerToken;
+ }
+
/**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
@@ -193,6 +212,7 @@ public final class BackNavigationInfo implements Parcelable {
+ "mType=" + typeToString(mType) + " (" + mType + ")"
+ ", mOnBackNavigationDone=" + mOnBackNavigationDone
+ ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
+ + ", mWindowContainerToken=" + mDepartingWindowContainerToken
+ '}';
}
@@ -228,6 +248,9 @@ public final class BackNavigationInfo implements Parcelable {
@Nullable
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
private boolean mPrepareRemoteAnimation;
+ @Nullable
+ private WindowContainerToken mDepartingWindowContainerToken = null;
+
/**
* @see BackNavigationInfo#getType()
*/
@@ -261,12 +284,21 @@ public final class BackNavigationInfo implements Parcelable {
return this;
}
+ /**
+ * @see BackNavigationInfo#getDepartingWindowContainerToken()
+ */
+ public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) {
+ mDepartingWindowContainerToken = windowContainerToken;
+ }
+
/**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
return new BackNavigationInfo(mType, mOnBackNavigationDone,
- mOnBackInvokedCallback, mPrepareRemoteAnimation);
+ mOnBackInvokedCallback,
+ mPrepareRemoteAnimation,
+ mDepartingWindowContainerToken);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index cd77802fa10bb..6f9c8b18625f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -58,6 +58,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -75,6 +76,9 @@ public class BackAnimationController implements RemoteCallable mAnimationDefinition = new SparseArray<>();
+ private final Transitions mTransitions;
+ private BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
@@ -184,9 +190,11 @@ public class BackAnimationController implements RemoteCallable {
+ finishBackNavigation();
+ finishCallback.onTransitionFinished(null, null);
+ });
+ }
+
private void createAdapter() {
IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java
new file mode 100644
index 0000000000000..6d72d9c1f6375
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 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.back;
+
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.transition.Transitions;
+
+class BackTransitionHandler implements Transitions.TransitionHandler {
+ private BackAnimationController mBackAnimationController;
+ private WindowContainerToken mDepartingWindowContainerToken;
+
+ BackTransitionHandler(@NonNull BackAnimationController backAnimationController) {
+ mBackAnimationController = backAnimationController;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mDepartingWindowContainerToken != null) {
+ final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken);
+ if (change == null) {
+ return false;
+ }
+
+ startTransaction.hide(change.getLeash());
+ startTransaction.apply();
+ mDepartingWindowContainerToken = null;
+ mBackAnimationController.finishTransition(finishCallback);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ void setDepartingWindowContainerToken(
+ @Nullable WindowContainerToken departingWindowContainerToken) {
+ mDepartingWindowContainerToken = departingWindowContainerToken;
+ }
+}
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 80cdd1f79cb5d..2932bf1de4f61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -273,12 +273,13 @@ public abstract class WMShellBaseModule {
Context context,
ShellInit shellInit,
@ShellMainThread ShellExecutor shellExecutor,
- @ShellBackgroundThread Handler backgroundHandler
+ @ShellBackgroundThread Handler backgroundHandler,
+ Transitions transitions
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
new BackAnimationController(shellInit, shellExecutor, backgroundHandler,
- context));
+ context, transitions));
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 9f39598e70a52..ac5236f95040d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -61,6 +61,7 @@ import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Rule;
@@ -98,6 +99,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Mock
private IRemoteAnimationRunner mBackAnimationRunner;
+ @Mock
+ private Transitions mTransitions;
+
private BackAnimationController mController;
private int mEventTime = 0;
@@ -117,7 +121,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController = new BackAnimationController(mShellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver);
+ mContentResolver, mTransitions);
mShellInit.init();
mEventTime = 0;
mShellExecutor.flushAll();
@@ -209,7 +213,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController = new BackAnimationController(shellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver);
+ mContentResolver, mTransitions);
shellInit.init();
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
@@ -250,6 +254,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
verifyNoMoreInteractions(mIOnBackInvokedCallback);
mController.onBackAnimationFinished();
+ // Pretend the transition handler called finishAnimation.
+ mController.finishBackNavigation();
// Verify that more events from a rejected swipe cannot start animation.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
@@ -278,6 +284,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Simulate transition timeout.
mShellExecutor.flushAll();
mController.onBackAnimationFinished();
+ // Pretend the transition handler called finishAnimation.
+ mController.finishBackNavigation();
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d42ad58f6f217..e9774479233dd 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -43,6 +43,7 @@ import android.window.IBackAnimationFinishedCallback;
import android.window.OnBackInvokedCallbackInfo;
import android.window.ScreenCapture;
import android.window.TaskSnapshot;
+import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -61,6 +62,9 @@ class BackNavigationController {
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
+ // TODO (b/241808055) Find a appropriate time to remove during refactor
+ // Execute back animation with legacy transition system. Temporary flag for easier debugging.
+ static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions;
/**
* Returns true if the back predictability feature is enabled
*/
@@ -263,12 +267,14 @@ class BackNavigationController {
// Only prepare animation if no leash has been created (no animation is running).
// TODO(b/241808055): Cancel animation when preparing back animation.
if (prepareAnimation
- && removedWindowContainer.hasCommittedReparentToAnimationLeash()) {
+ && (removedWindowContainer.hasCommittedReparentToAnimationLeash()
+ || removedWindowContainer.mTransitionController.inTransition())) {
Slog.w(TAG, "Can't prepare back animation due to another animation is running.");
prepareAnimation = false;
}
if (prepareAnimation) {
+ infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask));
prepareAnimationIfNeeded(currentTask, prevTask, prevActivity,
removedWindowContainer, backType, adapter);
}
@@ -287,6 +293,13 @@ class BackNavigationController {
return infoBuilder.build();
}
+ private static WindowContainerToken toWindowContainerToken(WindowContainer> windowContainer) {
+ if (windowContainer == null || windowContainer.mRemoteToken == null) {
+ return null;
+ }
+ return windowContainer.mRemoteToken.toWindowContainerToken();
+ }
+
private void prepareAnimationIfNeeded(Task currentTask,
Task prevTask, ActivityRecord prevActivity, WindowContainer> removedWindowContainer,
int backType, BackAnimationAdapter adapter) {
@@ -352,8 +365,10 @@ class BackNavigationController {
leashes.add(screenshotSurface);
}
} else if (prevTask != null) {
- // Special handling for preventing next transition.
- currentTask.mBackGestureStarted = true;
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ // Special handling for preventing next transition.
+ currentTask.mBackGestureStarted = true;
+ }
prevActivity = prevTask.getTopNonFinishingActivity();
if (prevActivity != null) {
// Make previous task show from behind by marking its top activity as visible
@@ -396,16 +411,27 @@ class BackNavigationController {
}
synchronized (mWindowManagerService.mGlobalLock) {
- if (triggerBack) {
- final SurfaceControl surfaceControl =
- removedWindowContainer.getSurfaceControl();
- if (surfaceControl != null && surfaceControl.isValid()) {
- // When going back to home, hide the task surface before it is
- // re-parented to avoid flicker.
- finishedTransaction.hide(surfaceControl);
+ if (ENABLE_SHELL_TRANSITIONS) {
+ if (!triggerBack) {
+ if (!needsScreenshot(backType)) {
+ restoreLaunchBehind(finalPrevActivity);
+ }
+ }
+ } else {
+ if (triggerBack) {
+ final SurfaceControl surfaceControl =
+ removedWindowContainer.getSurfaceControl();
+ if (surfaceControl != null && surfaceControl.isValid()) {
+ // When going back to home, hide the task surface before it
+ // re-parented to avoid flicker.
+ finishedTransaction.hide(surfaceControl);
+ }
+ } else {
+ currentTask.mBackGestureStarted = false;
+ if (!needsScreenshot(backType)) {
+ restoreLaunchBehind(finalPrevActivity);
+ }
}
- } else if (!needsScreenshot(backType)) {
- restoreLaunchBehind(finalPrevActivity);
}
}
finishedTransaction.apply();