diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 02be051d973a1..52732d3eba78c 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -72,6 +72,7 @@ import android.view.IRemoteAnimationRunner; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationAdapter; +import android.window.BackAnimationAdaptor; import android.window.IWindowOrganizerController; import android.window.BackNavigationInfo; import android.window.SplashScreenView; @@ -356,5 +357,5 @@ interface IActivityTaskManager { * @param focusObserver a remote callback to nofify shell when the focused window lost focus. */ android.window.BackNavigationInfo startBackNavigation(in boolean requestAnimation, - in IWindowFocusObserver focusObserver); + in IWindowFocusObserver focusObserver, in BackAnimationAdaptor adaptor); } diff --git a/core/java/android/window/BackAnimationAdaptor.aidl b/core/java/android/window/BackAnimationAdaptor.aidl new file mode 100644 index 0000000000000..1082d0ace1ae2 --- /dev/null +++ b/core/java/android/window/BackAnimationAdaptor.aidl @@ -0,0 +1,22 @@ +/* + * 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 android.window; + +/** + * @hide + */ +parcelable BackAnimationAdaptor; \ No newline at end of file diff --git a/core/java/android/window/BackAnimationAdaptor.java b/core/java/android/window/BackAnimationAdaptor.java new file mode 100644 index 0000000000000..cf82046e7e555 --- /dev/null +++ b/core/java/android/window/BackAnimationAdaptor.java @@ -0,0 +1,72 @@ +/* + * 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 android.window; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Object that describes how to run a remote back animation. + * + * @hide + */ +public class BackAnimationAdaptor implements Parcelable { + + private final IBackAnimationRunner mRunner; + @BackNavigationInfo.BackTargetType + private final int mSupportType; + + public BackAnimationAdaptor(IBackAnimationRunner runner, int supportType) { + mRunner = runner; + mSupportType = supportType; + } + + public BackAnimationAdaptor(Parcel in) { + mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder()); + mSupportType = in.readInt(); + } + + public IBackAnimationRunner getRunner() { + return mRunner; + } + + @BackNavigationInfo.BackTargetType public int getSupportType() { + return mSupportType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongInterface(mRunner); + dest.writeInt(mSupportType); + } + + public static final @android.annotation.NonNull Creator CREATOR = + new Creator() { + public BackAnimationAdaptor createFromParcel(Parcel in) { + return new BackAnimationAdaptor(in); + } + + public BackAnimationAdaptor[] newArray(int size) { + return new BackAnimationAdaptor[size]; + } + }; +} diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index dd49014176711..941511ec33bec 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -101,6 +101,8 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private final IOnBackInvokedCallback mOnBackInvokedCallback; + private final boolean mIsPrepareRemoteAnimation; + /** * Create a new {@link BackNavigationInfo} instance. * @@ -117,6 +119,9 @@ 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 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. */ private BackNavigationInfo(@BackTargetType int type, @Nullable RemoteAnimationTarget departingAnimationTarget, @@ -124,7 +129,8 @@ public final class BackNavigationInfo implements Parcelable { @Nullable HardwareBuffer screenshotBuffer, @Nullable WindowConfiguration taskWindowConfiguration, @Nullable RemoteCallback onBackNavigationDone, - @Nullable IOnBackInvokedCallback onBackInvokedCallback) { + @Nullable IOnBackInvokedCallback onBackInvokedCallback, + boolean isPrepareRemoteAnimation) { mType = type; mDepartingAnimationTarget = departingAnimationTarget; mScreenshotSurface = screenshotSurface; @@ -132,6 +138,7 @@ public final class BackNavigationInfo implements Parcelable { mTaskWindowConfiguration = taskWindowConfiguration; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; + mIsPrepareRemoteAnimation = isPrepareRemoteAnimation; } private BackNavigationInfo(@NonNull Parcel in) { @@ -142,6 +149,7 @@ public final class BackNavigationInfo implements Parcelable { mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR); mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); + mIsPrepareRemoteAnimation = in.readBoolean(); } @Override @@ -153,6 +161,7 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mTaskWindowConfiguration, flags); dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); + dest.writeBoolean(mIsPrepareRemoteAnimation); } /** @@ -221,6 +230,10 @@ public final class BackNavigationInfo implements Parcelable { return mOnBackInvokedCallback; } + public boolean isPrepareRemoteAnimation() { + return mIsPrepareRemoteAnimation; + } + /** * 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. @@ -306,6 +319,8 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private IOnBackInvokedCallback mOnBackInvokedCallback = null; + private boolean mPrepareAnimation; + /** * @see BackNavigationInfo#getType() */ @@ -365,13 +380,21 @@ public final class BackNavigationInfo implements Parcelable { return this; } + /** + * @param prepareAnimation Whether core prepare animation for shell. + */ + public Builder setPrepareAnimation(boolean prepareAnimation) { + mPrepareAnimation = prepareAnimation; + return this; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { return new BackNavigationInfo(mType, mDepartingAnimationTarget, mScreenshotSurface, mScreenshotBuffer, mTaskWindowConfiguration, mOnBackNavigationDone, - mOnBackInvokedCallback); + mOnBackInvokedCallback, mPrepareAnimation); } } } diff --git a/core/java/android/window/IBackAnimationRunner.aidl b/core/java/android/window/IBackAnimationRunner.aidl new file mode 100644 index 0000000000000..ca04b9d358b89 --- /dev/null +++ b/core/java/android/window/IBackAnimationRunner.aidl @@ -0,0 +1,45 @@ +/* + * 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 android.window; + +import android.view.RemoteAnimationTarget; +import android.window.IBackNaviAnimationController; + +/** + * Interface that is used to callback from window manager to the process that runs a back gesture + * animation to start or cancel it. + * + * {@hide} + */ +oneway interface IBackAnimationRunner { + + /** + * Called when the system needs to cancel the current animation. This can be due to the + * wallpaper not drawing in time, or the handler not finishing the animation within a predefined + * amount of time. + * + */ + void onAnimationCancelled() = 1; + + /** + * Called when the system is ready for the handler to start animating all the visible tasks. + * + */ + void onAnimationStart(in IBackNaviAnimationController controller, in int type, + in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers, + in RemoteAnimationTarget[] nonApps) = 2; +} diff --git a/core/java/android/window/IBackNaviAnimationController.aidl b/core/java/android/window/IBackNaviAnimationController.aidl new file mode 100644 index 0000000000000..bba223ea339b4 --- /dev/null +++ b/core/java/android/window/IBackNaviAnimationController.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 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 android.window; + +/** + * Interface to be invoked by the controlling process when a back animation has finished. + * + * @param trigger Whether the back gesture has passed the triggering threshold. + * {@hide} + */ +interface IBackNaviAnimationController { + void finish(in boolean triggerBack); +} diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java index bbbc4230903ab..77d61d589015a 100644 --- a/core/tests/coretests/src/android/window/BackNavigationTest.java +++ b/core/tests/coretests/src/android/window/BackNavigationTest.java @@ -92,7 +92,7 @@ public class BackNavigationTest { try { mInstrumentation.getUiAutomation().waitForIdle(500, 1000); BackNavigationInfo info = ActivityTaskManager.getService() - .startBackNavigation(true, null); + .startBackNavigation(true, null, null); assertNotNull("BackNavigationInfo is null", info); assertNotNull("OnBackInvokedCallback is null", info.getOnBackInvokedCallback()); info.getOnBackInvokedCallback().onBackInvoked(); 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 d3e46f82efe5a..33ecdd88fad31 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 @@ -16,6 +16,9 @@ package com.android.wm.shell.back; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; + import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; @@ -27,8 +30,6 @@ import android.app.WindowConfiguration; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; -import android.graphics.Point; -import android.graphics.PointF; import android.hardware.HardwareBuffer; import android.hardware.input.InputManager; import android.net.Uri; @@ -47,8 +48,11 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.window.BackAnimationAdaptor; import android.window.BackEvent; import android.window.BackNavigationInfo; +import android.window.IBackAnimationRunner; +import android.window.IBackNaviAnimationController; import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; @@ -76,22 +80,15 @@ public class BackAnimationController implements RemoteCallable client. + finishAnimation(); + if (callback != null) { + if (triggerBack) { dispatchOnBackInvoked(callback); } else { dispatchOnBackCancelled(callback); } } - finishAnimation(); } /** @@ -300,6 +398,8 @@ public class BackAnimationController implements RemoteCallable= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; float progress = Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1); - int backType = mBackNavigationInfo.getType(); - RemoteAnimationTarget animationTarget = mBackNavigationInfo.getDepartingAnimationTarget(); + if (USE_TRANSITION) { + if (mBackAnimationController != null && mAnimationTarget != null) { + final BackEvent backEvent = new BackEvent( + touchX, touchY, progress, swipeEdge, mAnimationTarget); + dispatchOnBackProgressed(mBackToLauncherCallback, backEvent); + } + } else { + int backType = mBackNavigationInfo.getType(); + RemoteAnimationTarget animationTarget = + mBackNavigationInfo.getDepartingAnimationTarget(); - BackEvent backEvent = new BackEvent( - touchX, touchY, progress, swipeEdge, animationTarget); - IOnBackInvokedCallback targetCallback = null; - if (shouldDispatchToLauncher(backType)) { - targetCallback = mBackToLauncherCallback; - } else if (backType == BackNavigationInfo.TYPE_CROSS_TASK - || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { - // TODO(208427216) Run the actual animation - } else if (backType == BackNavigationInfo.TYPE_CALLBACK) { - targetCallback = mBackNavigationInfo.getOnBackInvokedCallback(); + BackEvent backEvent = new BackEvent( + touchX, touchY, progress, swipeEdge, animationTarget); + IOnBackInvokedCallback targetCallback = null; + if (shouldDispatchToLauncher(backType)) { + targetCallback = mBackToLauncherCallback; + } else if (backType == BackNavigationInfo.TYPE_CROSS_TASK + || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { + // TODO(208427216) Run the actual animation + } else if (backType == BackNavigationInfo.TYPE_CALLBACK) { + targetCallback = mBackNavigationInfo.getOnBackInvokedCallback(); + } + dispatchOnBackProgressed(targetCallback, backEvent); } - dispatchOnBackProgressed(targetCallback, backEvent); } private void injectBackKey() { @@ -474,6 +589,9 @@ public class BackAnimationController implements RemoteCallable { + mBackAnimationController = controller; + for (int i = 0; i < apps.length; i++) { + final RemoteAnimationTarget target = apps[i]; + if (MODE_CLOSING == target.mode) { + mAnimationTarget = target; + } else if (MODE_OPENING == target.mode) { + // TODO Home activity should handle the visibility for itself + // once it finish relayout for orientation change + SurfaceControl.Transaction tx = + new SurfaceControl.Transaction(); + tx.setAlpha(target.leash, 1); + tx.apply(); + } + } + // TODO animation target should be passed at onBackStarted + dispatchOnBackStarted(mBackToLauncherCallback); + // TODO This is Workaround for LauncherBackAnimationController, there will need + // to dispatch onBackProgressed twice(startBack & updateBackProgress) to + // initialize the animation data, for now that would happen when onMove + // called, but there will no expected animation if the down -> up gesture + // happen very fast which ACTION_MOVE only happen once. + final BackEvent backInit = new BackEvent( + mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 0, + mTouchTracker.mSwipeEdge, mAnimationTarget); + dispatchOnBackProgressed(mBackToLauncherCallback, backInit); + if (!mCachingBackDispatcher.consume()) { + dispatchOnBackProgressed(mBackToLauncherCallback, backInit); + } + }); + } + }; + mBackAnimationAdaptor = new BackAnimationAdaptor(mIBackAnimationRunner, + BackNavigationInfo.TYPE_RETURN_TO_HOME); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index da95c77d2b89d..fe8b305093d7f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -48,9 +48,10 @@ public class TestShellExecutor implements ShellExecutor { } public void flushAll() { - for (Runnable r : mRunnables) { + final ArrayList tmpRunnable = new ArrayList<>(mRunnables); + mRunnables.clear(); + for (Runnable r : tmpRunnable) { r.run(); } - mRunnables.clear(); } } 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 5b3b8fd7ad712..90a377309edd4 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 @@ -54,6 +54,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.BackEvent; import android.window.BackNavigationInfo; +import android.window.IBackNaviAnimationController; import android.window.IOnBackInvokedCallback; import androidx.test.filters.SmallTest; @@ -98,6 +99,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Mock private IOnBackInvokedCallback mIOnBackInvokedCallback; + @Mock + private IBackNaviAnimationController mIBackNaviAnimationController; + private BackAnimationController mController; private int mEventTime = 0; @@ -127,7 +131,7 @@ public class BackAnimationControllerTest extends ShellTestCase { SurfaceControl screenshotSurface, HardwareBuffer hardwareBuffer, int backType, - IOnBackInvokedCallback onBackInvokedCallback) { + IOnBackInvokedCallback onBackInvokedCallback, boolean prepareAnimation) { BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setType(backType) .setDepartingAnimationTarget(topAnimationTarget) @@ -135,7 +139,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setScreenshotBuffer(hardwareBuffer) .setTaskWindowConfiguration(new WindowConfiguration()) .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) - .setOnBackInvokedCallback(onBackInvokedCallback); + .setOnBackInvokedCallback(onBackInvokedCallback) + .setPrepareAnimation(prepareAnimation); createNavigationInfo(builder); } @@ -143,7 +148,7 @@ public class BackAnimationControllerTest extends ShellTestCase { private void createNavigationInfo(BackNavigationInfo.Builder builder) { try { doReturn(builder.build()).when(mActivityTaskManager) - .startBackNavigation(anyBoolean(), any()); + .startBackNavigation(anyBoolean(), any(), any()); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); } @@ -175,7 +180,7 @@ public class BackAnimationControllerTest extends ShellTestCase { SurfaceControl screenshotSurface = new SurfaceControl(); HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); createNavigationInfo(createAnimationTarget(), screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); + BackNavigationInfo.TYPE_CROSS_ACTIVITY, null, true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer); verify(mTransaction).setVisibility(screenshotSurface, true); @@ -188,7 +193,7 @@ public class BackAnimationControllerTest extends ShellTestCase { HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); + BackNavigationInfo.TYPE_CROSS_ACTIVITY, null, true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); // b/207481538, we check that the surface is not moved for now, we can re-enable this once @@ -222,15 +227,16 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); verify(mIOnBackInvokedCallback).onBackStarted(); ArgumentCaptor backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); - verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); + verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture()); assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget()); // Check that back invocation is dispatched. @@ -255,7 +261,7 @@ public class BackAnimationControllerTest extends ShellTestCase { IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class); ArgumentCaptor backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback); + BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback, false); triggerBackGesture(); @@ -273,9 +279,10 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true); triggerBackGesture(); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); // Check that back invocation is dispatched. verify(mIOnBackInvokedCallback).onBackInvoked(); @@ -294,6 +301,7 @@ public class BackAnimationControllerTest extends ShellTestCase { // Verify that we start accepting gestures again once transition finishes. doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); verify(mIOnBackInvokedCallback).onBackStarted(); } @@ -302,15 +310,17 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true); triggerBackGesture(); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); reset(mIOnBackInvokedCallback); // Simulate transition timeout. mShellExecutor.flushAll(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); verify(mIOnBackInvokedCallback).onBackStarted(); } @@ -321,11 +331,12 @@ public class BackAnimationControllerTest extends ShellTestCase { RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget); verify(mIOnBackInvokedCallback).onBackStarted(); // Check that back invocation is dispatched. @@ -349,4 +360,14 @@ public class BackAnimationControllerTest extends ShellTestCase { BackEvent.EDGE_LEFT); mEventTime += 10; } + + private void simulateRemoteAnimationStart(int type, RemoteAnimationTarget animationTarget) + throws RemoteException { + if (mController.mIBackAnimationRunner != null) { + final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget}; + mController.mIBackAnimationRunner.onAnimationStart(mIBackNaviAnimationController, type, + targets, null, null); + mShellExecutor.flushAll(); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d4bbc86c4850c..31f87416db67b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -226,6 +226,7 @@ import android.view.IWindowFocusObserver; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; +import android.window.BackAnimationAdaptor; import android.window.BackNavigationInfo; import android.window.IWindowOrganizerController; import android.window.SplashScreenView.SplashScreenViewParcelable; @@ -457,7 +458,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private final ClientLifecycleManager mLifecycleManager; @Nullable - private final BackNavigationController mBackNavigationController; + final BackNavigationController mBackNavigationController; private TaskChangeNotificationController mTaskChangeNotificationController; /** The controller for all operations related to locktask. */ @@ -1836,13 +1837,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public BackNavigationInfo startBackNavigation(boolean requestAnimation, - IWindowFocusObserver observer) { + IWindowFocusObserver observer, BackAnimationAdaptor backAnimationAdaptor) { mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "startBackNavigation()"); if (mBackNavigationController == null) { return null; } - return mBackNavigationController.startBackNavigation(requestAnimation, observer); + return mBackNavigationController.startBackNavigation( + requestAnimation, observer, backAnimationAdaptor); } /** diff --git a/services/core/java/com/android/server/wm/BackNaviAnimationController.java b/services/core/java/com/android/server/wm/BackNaviAnimationController.java new file mode 100644 index 0000000000000..ecc7534ad386a --- /dev/null +++ b/services/core/java/com/android/server/wm/BackNaviAnimationController.java @@ -0,0 +1,418 @@ +/* + * 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.server.wm; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; + +import android.annotation.NonNull; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.WindowInsets; +import android.window.BackNavigationInfo; +import android.window.IBackAnimationRunner; +import android.window.IBackNaviAnimationController; + +import com.android.server.wm.utils.InsetUtils; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Controls the back navigation animation. + * This is throw-away code and should only be used for Android T, most code is duplicated from + * RecentsAnimationController which should be stable to handle animation leash resources/flicker/ + * fixed rotation, etc. Remove this class at U and migrate to shell transition. + */ +public class BackNaviAnimationController implements IBinder.DeathRecipient { + private static final String TAG = BackNavigationController.TAG; + // Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state + private static final int MODE_UNKNOWN = -1; + + // The activity which host this animation + private ActivityRecord mTargetActivityRecord; + // The original top activity + private ActivityRecord mTopActivity; + + private final DisplayContent mDisplayContent; + private final WindowManagerService mWindowManagerService; + private final BackNavigationController mBackNavigationController; + + // We start the BackAnimationController in a pending-start state since we need to wait for + // the wallpaper/activity to draw before we can give control to the handler to start animating + // the visible task surfaces + private boolean mPendingStart; + private IBackAnimationRunner mRunner; + final IBackNaviAnimationController mRemoteController; + private boolean mLinkedToDeathOfRunner; + + private final ArrayList mPendingAnimations = new ArrayList<>(); + + BackNaviAnimationController(IBackAnimationRunner runner, + BackNavigationController backNavigationController, int displayId) { + mRunner = runner; + mBackNavigationController = backNavigationController; + mWindowManagerService = mBackNavigationController.mWindowManagerService; + mDisplayContent = mWindowManagerService.mRoot.getDisplayContent(displayId); + + mRemoteController = new IBackNaviAnimationController.Stub() { + @Override + public void finish(boolean triggerBack) { + synchronized (mWindowManagerService.getWindowManagerLock()) { + final long token = Binder.clearCallingIdentity(); + try { + mWindowManagerService.inSurfaceTransaction(() -> { + mWindowManagerService.mAtmService.deferWindowLayout(); + try { + if (triggerBack) { + mDisplayContent.mFixedRotationTransitionListener + .notifyRecentsWillBeTop(); + if (mTopActivity != null) { + mWindowManagerService.mTaskSnapshotController + .recordTaskSnapshot(mTopActivity.getTask(), false); + // TODO consume moveTaskToBack? + mTopActivity.commitVisibility(false, false, true); + } + } else { + mTargetActivityRecord.mTaskSupervisor + .scheduleLaunchTaskBehindComplete( + mTargetActivityRecord.token); + } + cleanupAnimation(); + } finally { + mWindowManagerService.mAtmService.continueWindowLayout(); + } + }); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + }; + } + + /** + * @param targetActivity The home or opening activity which should host the wallpaper + * @param topActivity The current top activity before animation start. + */ + void initialize(ActivityRecord targetActivity, ActivityRecord topActivity) { + mTargetActivityRecord = targetActivity; + mTopActivity = topActivity; + final Task topTask = mTopActivity.getTask(); + + createAnimationAdapter(topTask, (type, anim) -> topTask.forAllWindows( + win -> { + win.onAnimationFinished(type, anim); + }, true)); + final Task homeTask = mTargetActivityRecord.getRootTask(); + createAnimationAdapter(homeTask, (type, anim) -> homeTask.forAllWindows( + win -> { + win.onAnimationFinished(type, anim); + }, true)); + try { + linkToDeathOfRunner(); + } catch (RemoteException e) { + cancelAnimation(); + return; + } + + if (targetActivity.windowsCanBeWallpaperTarget()) { + mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + mDisplayContent.setLayoutNeeded(); + } + + mWindowManagerService.mWindowPlacerLocked.performSurfacePlacement(); + + mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity); + mPendingStart = true; + } + + void cleanupAnimation() { + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); + + removeAnimationAdapter(taskAdapter); + taskAdapter.onCleanup(); + } + mTargetActivityRecord.mLaunchTaskBehind = false; + // Clear references to the runner + unlinkToDeathOfRunner(); + mRunner = null; + + // Update the input windows after the animation is complete + final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); + inputMonitor.updateInputWindowsLw(true /*force*/); + + mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); + mBackNavigationController.finishAnimation(); + } + + void removeAnimationAdapter(TaskAnimationAdapter taskAdapter) { + taskAdapter.onRemove(); + mPendingAnimations.remove(taskAdapter); + } + + void checkAnimationReady(WallpaperController wallpaperController) { + if (mPendingStart) { + final boolean wallpaperReady = !isTargetOverWallpaper() + || (wallpaperController.getWallpaperTarget() != null + && wallpaperController.wallpaperTransitionReady()); + if (wallpaperReady) { + startAnimation(); + } + } + } + + boolean isWallpaperVisible(WindowState w) { + return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION + && ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord) + || isAnimatingTask(w.getTask())) + && isTargetOverWallpaper() && w.isOnScreen(); + } + + boolean isAnimatingTask(Task task) { + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + if (task == mPendingAnimations.get(i).mTask) { + return true; + } + } + return false; + } + + void linkFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) { + if (mTargetActivityRecord == null) { + return; + } + wallpaper.linkFixedRotationTransform(mTargetActivityRecord); + } + + private void linkToDeathOfRunner() throws RemoteException { + if (!mLinkedToDeathOfRunner) { + mRunner.asBinder().linkToDeath(this, 0); + mLinkedToDeathOfRunner = true; + } + } + + private void unlinkToDeathOfRunner() { + if (mLinkedToDeathOfRunner) { + mRunner.asBinder().unlinkToDeath(this, 0); + mLinkedToDeathOfRunner = false; + } + } + + void startAnimation() { + if (!mPendingStart) { + // Skip starting if we've already started or canceled the animation + return; + } + // Create the app targets + final RemoteAnimationTarget[] appTargets = createAppAnimations(); + + // Skip the animation if there is nothing to animate + if (appTargets.length == 0) { + cancelAnimation(); + return; + } + + mPendingStart = false; + + try { + mRunner.onAnimationStart(mRemoteController, BackNavigationInfo.TYPE_RETURN_TO_HOME, + appTargets, null /* wallpapers */, null /*nonApps*/); + } catch (RemoteException e) { + cancelAnimation(); + } + } + + @Override + public void binderDied() { + cancelAnimation(); + } + + TaskAnimationAdapter createAnimationAdapter(Task task, + SurfaceAnimator.OnAnimationFinishedCallback finishedCallback) { + final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, + mTargetActivityRecord, this::cancelAnimation); + // borrow from recents since we cannot start back animation if recents is playing + task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */, + ANIMATION_TYPE_RECENTS, finishedCallback); + task.commitPendingTransaction(); + mPendingAnimations.add(taskAdapter); + return taskAdapter; + } + + private RemoteAnimationTarget[] createAppAnimations() { + final ArrayList targets = new ArrayList<>(); + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); + final RemoteAnimationTarget target = + taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID, MODE_UNKNOWN); + if (target != null) { + targets.add(target); + } else { + removeAnimationAdapter(taskAdapter); + } + } + return targets.toArray(new RemoteAnimationTarget[targets.size()]); + } + + private void cancelAnimation() { + synchronized (mWindowManagerService.getWindowManagerLock()) { + // Notify the runner and clean up the animation immediately + // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls + // to the runner if we this actually triggers cancel twice on the caller + try { + mRunner.onAnimationCancelled(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to cancel recents animation", e); + } + cleanupAnimation(); + } + } + + private boolean isTargetOverWallpaper() { + if (mTargetActivityRecord == null) { + return false; + } + return mTargetActivityRecord.windowsCanBeWallpaperTarget(); + } + + private static class TaskAnimationAdapter implements AnimationAdapter { + private final Task mTask; + private SurfaceControl mCapturedLeash; + private SurfaceAnimator.OnAnimationFinishedCallback mCapturedFinishCallback; + @SurfaceAnimator.AnimationType private int mLastAnimationType; + private RemoteAnimationTarget mTarget; + private final ActivityRecord mTargetActivityRecord; + private final Runnable mCancelCallback; + + private final Rect mBounds = new Rect(); + // The bounds of the target relative to its parent. + private final Rect mLocalBounds = new Rect(); + + TaskAnimationAdapter(Task task, ActivityRecord target, Runnable cancelCallback) { + mTask = task; + mBounds.set(mTask.getBounds()); + + mLocalBounds.set(mBounds); + Point tmpPos = new Point(); + mTask.getRelativePosition(tmpPos); + mLocalBounds.offsetTo(tmpPos.x, tmpPos.y); + mTargetActivityRecord = target; + mCancelCallback = cancelCallback; + } + + // Keep overrideTaskId and overrideMode now, if we need to add other type of back animation + // on legacy transition system then they can be useful. + RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId, int overrideMode) { + ActivityRecord topApp = mTask.getTopRealVisibleActivity(); + if (topApp == null) { + topApp = mTask.getTopVisibleActivity(); + } + final WindowState mainWindow = topApp != null + ? topApp.findMainWindow() + : null; + if (mainWindow == null) { + return null; + } + final Rect insets = + mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( + mBounds, WindowInsets.Type.systemBars(), + false /* ignoreVisibility */).toRect(); + InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); + final int mode = overrideMode != MODE_UNKNOWN + ? overrideMode + : topApp.getActivityType() == mTargetActivityRecord.getActivityType() + ? MODE_OPENING + : MODE_CLOSING; + if (overrideTaskId < 0) { + overrideTaskId = mTask.mTaskId; + } + mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash, + !topApp.fillsParent(), new Rect(), + insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), + mLocalBounds, mBounds, mTask.getWindowConfiguration(), + true /* isNotInRecents */, null, null, mTask.getTaskInfo(), + topApp.checkEnterPictureInPictureAppOpsState()); + return mTarget; + } + @Override + public boolean getShowWallpaper() { + return false; + } + @Override + public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, + @SurfaceAnimator.AnimationType int type, + @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top); + final Rect tmpRect = new Rect(); + tmpRect.set(mLocalBounds); + tmpRect.offsetTo(0, 0); + t.setWindowCrop(animationLeash, tmpRect); + mCapturedLeash = animationLeash; + mCapturedFinishCallback = finishCallback; + mLastAnimationType = type; + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + mCancelCallback.run(); + } + + void onRemove() { + mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this); + } + + void onCleanup() { + final SurfaceControl.Transaction pendingTransaction = mTask.getPendingTransaction(); + if (!mTask.isAttached()) { + // Apply the task's pending transaction in case it is detached and its transaction + // is not reachable. + pendingTransaction.apply(); + } + } + + @Override + public long getDurationHint() { + return 0; + } + + @Override + public long getStatusBarTransitionsStartTime() { + return SystemClock.uptimeMillis(); + } + + @Override + public void dump(PrintWriter pw, String prefix) { } + + @Override + public void dumpDebug(ProtoOutputStream proto) { } + } +} diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d9ab971c9a784..35a39c048e57f 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -34,6 +34,7 @@ import android.util.Slog; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.window.BackAnimationAdaptor; import android.window.BackNavigationInfo; import android.window.OnBackInvokedCallbackInfo; import android.window.TaskSnapshot; @@ -46,9 +47,15 @@ import com.android.server.LocalServices; * Controller to handle actions related to the back gesture on the server side. */ class BackNavigationController { - private static final String TAG = "BackNavigationController"; - private WindowManagerService mWindowManagerService; + static final String TAG = "BackNavigationController"; + WindowManagerService mWindowManagerService; private IWindowFocusObserver mFocusObserver; + // 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 USE_TRANSITION = + SystemProperties.getInt("persist.wm.debug.predictive_back_ani_trans", 1) != 0; + + BackNaviAnimationController mBackNaviAnimationController; /** * Returns true if the back predictability feature is enabled @@ -72,7 +79,7 @@ class BackNavigationController { @VisibleForTesting @Nullable BackNavigationInfo startBackNavigation(boolean requestAnimation, - IWindowFocusObserver observer) { + IWindowFocusObserver observer, BackAnimationAdaptor backAnimationAdaptor) { final WindowManagerService wmService = mWindowManagerService; final SurfaceControl.Transaction tx = wmService.mTransactionFactory.get(); mFocusObserver = observer; @@ -259,6 +266,8 @@ class BackNavigationController { && requestAnimation // Only create a new leash if no leash has been created. // Otherwise return null for animation target to avoid conflict. + // TODO isAnimating, recents can cancel app transition animation, can't back + // cancel like recents? && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); if (prepareAnimation) { @@ -266,19 +275,21 @@ class BackNavigationController { currentTask.getTaskInfo().configuration.windowConfiguration; infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration); - // Prepare a leash to animate the current top window - // TODO(b/220934562): Use surface animator to better manage animation conflicts. - SurfaceControl animLeash = removedWindowContainer.makeAnimationLeash() - .setName("BackPreview Leash for " + removedWindowContainer) - .setHidden(false) - .setBLASTLayer() - .build(); - removedWindowContainer.reparentSurfaceControl(tx, animLeash); - animationLeashParent = removedWindowContainer.getAnimationLeashParent(); - topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, - currentActivity, - currentTask, animLeash); - infoBuilder.setDepartingAnimationTarget(topAppTarget); + if (!USE_TRANSITION) { + // Prepare a leash to animate the current top window + // TODO(b/220934562): Use surface animator to better manage animation conflicts. + SurfaceControl animLeash = removedWindowContainer.makeAnimationLeash() + .setName("BackPreview Leash for " + removedWindowContainer) + .setHidden(false) + .setBLASTLayer() + .build(); + removedWindowContainer.reparentSurfaceControl(tx, animLeash); + animationLeashParent = removedWindowContainer.getAnimationLeashParent(); + topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, + currentActivity, + currentTask, animLeash); + infoBuilder.setDepartingAnimationTarget(topAppTarget); + } } //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is @@ -293,21 +304,32 @@ class BackNavigationController { // Special handling for back to home animation if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation && prevTask != null) { - currentTask.mBackGestureStarted = true; - // Make launcher show from behind by marking its top activity as visible and - // launch-behind to bump its visibility for the duration of the back gesture. - prevActivity = prevTask.getTopNonFinishingActivity(); - if (prevActivity != null) { - if (!prevActivity.mVisibleRequested) { - prevActivity.setVisibility(true); + if (USE_TRANSITION && mBackNaviAnimationController == null) { + if (backAnimationAdaptor != null + && backAnimationAdaptor.getSupportType() == backType) { + mBackNaviAnimationController = new BackNaviAnimationController( + backAnimationAdaptor.getRunner(), this, + currentActivity.getDisplayId()); + prepareBackToHomeTransition(currentTask, prevTask); + infoBuilder.setPrepareAnimation(true); + } + } else { + currentTask.mBackGestureStarted = true; + // Make launcher show from behind by marking its top activity as visible and + // launch-behind to bump its visibility for the duration of the back gesture. + prevActivity = prevTask.getTopNonFinishingActivity(); + if (prevActivity != null) { + if (!prevActivity.mVisibleRequested) { + prevActivity.setVisibility(true); + } + prevActivity.mLaunchTaskBehind = true; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", + prevActivity); + prevActivity.mRootWindowContainer.ensureActivitiesVisible( + null /* starting */, 0 /* configChanges */, + false /* preserveWindows */); } - prevActivity.mLaunchTaskBehind = true; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - prevActivity); - prevActivity.mRootWindowContainer.ensureActivitiesVisible( - null /* starting */, 0 /* configChanges */, - false /* preserveWindows */); } } } // Release wm Lock @@ -388,29 +410,30 @@ class BackNavigationController { BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " + "task=%s, prevActivity=%s", backType, task, prevActivity); - - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) { - if (triggerBack) { - if (surfaceControl != null && surfaceControl.isValid()) { - // When going back to home, hide the task surface before it is re-parented to - // avoid flicker. - SurfaceControl.Transaction t = windowContainer.getSyncTransaction(); - t.hide(surfaceControl); - t.apply(); + if (!USE_TRANSITION) { + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) { + if (triggerBack) { + if (surfaceControl != null && surfaceControl.isValid()) { + // When going back to home, hide the task surface before it is re-parented + // to avoid flicker. + SurfaceControl.Transaction t = windowContainer.getSyncTransaction(); + t.hide(surfaceControl); + t.apply(); + } } + if (prevActivity != null && !triggerBack) { + // Restore the launch-behind state. + task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); + prevActivity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + prevActivity); + } + } else { + task.mBackGestureStarted = false; } - if (prevActivity != null && !triggerBack) { - // Restore the launch-behind state. - task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); - prevActivity.mLaunchTaskBehind = false; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - prevActivity); - } - } else if (task != null) { - task.mBackGestureStarted = false; + resetSurfaces(windowContainer); } - resetSurfaces(windowContainer); if (mFocusObserver != null) { focusedWindow.unregisterFocusObserver(mFocusObserver); @@ -465,4 +488,21 @@ class BackNavigationController { void setWindowManager(WindowManagerService wm) { mWindowManagerService = wm; } + + private void prepareBackToHomeTransition(Task currentTask, Task homeTask) { + final DisplayContent dc = currentTask.getDisplayContent(); + final ActivityRecord homeActivity = homeTask.getTopNonFinishingActivity(); + if (!homeActivity.mVisibleRequested) { + homeActivity.setVisibility(true); + } + homeActivity.mLaunchTaskBehind = true; + dc.ensureActivitiesVisible( + null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, true); + mBackNaviAnimationController.initialize(homeActivity, currentTask.getTopMostActivity()); + } + + void finishAnimation() { + mBackNaviAnimationController = null; + } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b79c6f44bad5c..856430dae6c40 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -847,6 +847,10 @@ class RootWindowContainer extends WindowContainer if (recentsAnimationController != null) { recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController); } + final BackNaviAnimationController bnac = mWmService.getBackNaviAnimationController(); + if (bnac != null) { + bnac.checkAnimationReady(defaultDisplay.mWallpaperController); + } for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) { final DisplayContent displayContent = mChildren.get(displayNdx); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 6245005606d75..e7c0a8aba2856 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -186,7 +186,7 @@ class WallpaperController { && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper; - if (isRecentsTransitionTarget(w)) { + if (isRecentsTransitionTarget(w) || isBackAnimationTarget(w)) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); mFindResults.setWallpaperTarget(w); return true; @@ -226,6 +226,13 @@ class WallpaperController { return controller != null && controller.isWallpaperVisible(w); } + private boolean isBackAnimationTarget(WindowState w) { + // The window is either the back activity or is in the task animating by the back gesture. + final BackNaviAnimationController bthController = mService.getBackNaviAnimationController(); + return bthController != null && bthController.isWallpaperVisible(w); + } + + /** * @see #computeLastWallpaperZoomOut() */ diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 6ee30bb956f09..8fdaec613ad52 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -128,12 +128,15 @@ class WallpaperWindowToken extends WindowToken { if (visible && wallpaperTarget != null) { final RecentsAnimationController recentsAnimationController = mWmService.getRecentsAnimationController(); + final BackNaviAnimationController bac = mWmService.getBackNaviAnimationController(); if (recentsAnimationController != null && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) { // If the Recents animation is running, and the wallpaper target is the animating // task we want the wallpaper to be rotated in the same orientation as the // RecentsAnimation's target (e.g the launcher) recentsAnimationController.linkFixedRotationTransformIfNeeded(this); + } else if (bac != null && bac.isAnimatingTask(wallpaperTarget.getTask())) { + bac.linkFixedRotationTransformIfNeeded(this); } else if ((wallpaperTarget.mActivityRecord == null // Ignore invisible activity because it may be moving to background. || wallpaperTarget.mActivityRecord.mVisibleRequested) diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f50bdee69eff7..285e0ac1c67a4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -9280,4 +9280,9 @@ public class WindowManagerService extends IWindowManager.Stub "Unexpected letterbox background type: " + letterboxBackgroundType); } } + + BackNaviAnimationController getBackNaviAnimationController() { + return mAtmService.mBackNavigationController != null + ? mAtmService.mBackNavigationController.mBackNaviAnimationController : null; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index c2ca0a227f26c..1cd0b198ff5ac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -80,10 +80,12 @@ public class BackNavigationControllerTests extends WindowTestsBase { IOnBackInvokedCallback callback = withSystemCallback(task); BackNavigationInfo backNavigationInfo = - mBackNavigationController.startBackNavigation(true, null); + mBackNavigationController.startBackNavigation(true, null, null); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); - assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); - assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); + if (!BackNavigationController.USE_TRANSITION) { + assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); + assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); + } assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); @@ -233,7 +235,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Nullable private BackNavigationInfo startBackNavigation() { - return mBackNavigationController.startBackNavigation(true, null); + return mBackNavigationController.startBackNavigation(true, null, null); } @NonNull