summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Luca Zuccarini <acul@google.com> 2024-11-20 14:16:26 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-20 14:16:26 +0000
commitbee9f829cbdd23b3de4bfe658e7937a94e348e68 (patch)
treea10acfc3c982283542dc669b95bf1aa2bb554bbf
parent341179468fba6b8f3ff9969c7c869b36761538f9 (diff)
parent3c2f615fb79094457abd5615800e1e3e95018c98 (diff)
Merge "Animation takeovers in Predictive Back." into main
-rw-r--r--core/java/android/window/IBackAnimationHandoffHandler.aidl40
-rw-r--r--core/java/android/window/IOnBackInvokedCallback.aidl6
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java5
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java53
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java5
7 files changed, 208 insertions, 5 deletions
diff --git a/core/java/android/window/IBackAnimationHandoffHandler.aidl b/core/java/android/window/IBackAnimationHandoffHandler.aidl
new file mode 100644
index 000000000000..577948dd0abc
--- /dev/null
+++ b/core/java/android/window/IBackAnimationHandoffHandler.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.Bundle;
+import android.view.RemoteAnimationTarget;
+import android.window.BackMotionEvent;
+import android.window.WindowAnimationState;
+
+
+/**
+ * Interface that allows an {@link OnBackInvokedCallback} object to hand off an animation to another
+ * handler.
+ *
+ * @hide
+ */
+oneway interface IBackAnimationHandoffHandler {
+ /**
+ * Triggers a handoff of the animation of the given targets and their associated states.
+ * Important: since this is a one-way method, the caller must first make sure that the animation
+ * can indeed be taken over.
+ */
+ oneway void handOffAnimation(in RemoteAnimationTarget[] targets,
+ in WindowAnimationState[] states);
+}
diff --git a/core/java/android/window/IOnBackInvokedCallback.aidl b/core/java/android/window/IOnBackInvokedCallback.aidl
index e07d4a9fc61b..81ad4b7bd19e 100644
--- a/core/java/android/window/IOnBackInvokedCallback.aidl
+++ b/core/java/android/window/IOnBackInvokedCallback.aidl
@@ -18,6 +18,7 @@
package android.window;
import android.window.BackMotionEvent;
+import android.window.IBackAnimationHandoffHandler;
/**
* Interface that wraps a {@link OnBackInvokedCallback} object, to be stored in window manager
@@ -61,4 +62,9 @@ oneway interface IOnBackInvokedCallback {
* Sets whether the back gesture is past the trigger threshold.
*/
void setTriggerBack(in boolean triggerBack);
+
+ /**
+ * Sets a {@link IBackAnimationHandoffHandler} that can be used to hand off the back animation.
+ */
+ void setHandoffHandler(in IBackAnimationHandoffHandler handoffHandler);
}
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index c67b9cac250b..d478108d928a 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -392,6 +392,11 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
// no-op
}
+ @Override
+ public void setHandoffHandler(IBackAnimationHandoffHandler handoffHandler) {
+ // no-op
+ }
+
private void maybeRunOnAnimationCallback(Consumer<OnBackAnimationCallback> block) {
if (mCallback instanceof OnBackAnimationCallback) {
mHandler.post(() -> block.accept((OnBackAnimationCallback) mCallback));
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 0ea4bb41d3a4..20e3f6b93bd0 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -546,6 +546,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
@Override
+ public void setHandoffHandler(IBackAnimationHandoffHandler handoffHandler) {
+ // no-op
+ }
+
+ @Override
public void onBackProgressed(BackMotionEvent backEvent) {
// This is only called in some special cases such as when activity embedding is active
// or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is
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 ce7a97703f44..e9cfd9bc2209 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
@@ -37,6 +37,7 @@ import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_B
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskInfo;
@@ -73,10 +74,12 @@ import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
import android.window.BackTouchTracker;
import android.window.IBackAnimationFinishedCallback;
+import android.window.IBackAnimationHandoffHandler;
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -84,6 +87,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.animation.TransitionAnimator;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -227,6 +232,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private Runnable mPilferPointerCallback;
private BackAnimation.TopUiRequest mRequestTopUiCallback;
+ private final IBackAnimationHandoffHandler mHandoffHandler =
+ new IBackAnimationHandoffHandler.Stub() {
+ @Override
+ public void handOffAnimation(
+ RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
+ mBackTransitionHandler.handOffAnimation(targets, states);
+ }
+ };
+
public BackAnimationController(
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -282,7 +296,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellCommandHandler = shellCommandHandler;
mWindowManager = context.getSystemService(WindowManager.class);
mTransitions = transitions;
- mBackTransitionHandler = new BackTransitionHandler();
+ mBackTransitionHandler = new BackTransitionHandler(mTransitions);
mTransitions.addHandler(mBackTransitionHandler);
mHandler = handler;
mTransitions.registerObserver(mBackTransitionObserver);
@@ -715,6 +729,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
try {
callback.onBackStarted(backEvent);
+ if (mBackTransitionHandler.canHandOffAnimation()) {
+ callback.setHandoffHandler(mHandoffHandler);
+ }
mOnBackStartDispatched = true;
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackStarted error: ", e);
@@ -1192,6 +1209,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
class BackTransitionHandler implements Transitions.TransitionHandler {
+ private final Transitions mTransitions;
Runnable mOnAnimationFinishCallback;
boolean mCloseTransitionRequested;
@@ -1203,6 +1221,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// animation is canceled, start a close prepare transition to finish the whole transition.
IBinder mClosePrepareTransition;
TransitionInfo mOpenTransitionInfo;
+ Transitions.TransitionHandler mTakeoverHandler;
+
+ BackTransitionHandler(Transitions transitions) {
+ mTransitions = transitions;
+ }
+
void onAnimationFinished() {
if (!mCloseTransitionRequested && mPrepareOpenTransition != null) {
createClosePrepareTransition();
@@ -1214,18 +1238,23 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private void applyFinishOpenTransition() {
- mOpenTransitionInfo = null;
- mPrepareOpenTransition = null;
if (mFinishOpenTransaction != null) {
final SurfaceControl.Transaction t = mFinishOpenTransaction;
- mFinishOpenTransaction = null;
t.apply();
}
if (mFinishOpenTransitionCallback != null) {
final Transitions.TransitionFinishCallback callback = mFinishOpenTransitionCallback;
- mFinishOpenTransitionCallback = null;
callback.onTransitionFinished(null);
}
+ cleanUpInternalState();
+ }
+
+ private void cleanUpInternalState() {
+ mOpenTransitionInfo = null;
+ mPrepareOpenTransition = null;
+ mFinishOpenTransaction = null;
+ mFinishOpenTransitionCallback = null;
+ mTakeoverHandler = null;
}
private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
@@ -1237,6 +1266,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
finishCallback.onTransitionFinished(null);
mCloseTransitionRequested = false;
}
+
@Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@@ -1246,6 +1276,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final boolean isPrepareTransition =
info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
if (isPrepareTransition) {
+ if (checkTakeoverFlags()) {
+ mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
+ }
kickStartAnimation();
}
// Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
@@ -1288,6 +1321,57 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return handleCloseTransition(info, st, ft, finishCallback);
}
+ private boolean canHandOffAnimation() {
+ if (!checkTakeoverFlags()) {
+ return false;
+ }
+
+ return mTakeoverHandler != null;
+ }
+
+ private void handOffAnimation(
+ RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
+ if (!checkTakeoverFlags()) {
+ ProtoLog.e(WM_SHELL_BACK_PREVIEW,
+ "Trying to hand off the animation, but the required flags are disabled.");
+ return;
+ } else if (mTakeoverHandler == null) {
+ ProtoLog.e(WM_SHELL_BACK_PREVIEW,
+ "Missing takeover handler when trying to hand off animation.");
+ return;
+ } else if (targets.length != states.length) {
+ ProtoLog.e(WM_SHELL_BACK_PREVIEW,
+ "Targets passed for takeover don't match the window states.");
+ return;
+ }
+
+ // The states passed to this method are paired with the targets, but they need to be
+ // paired with the changes inside the TransitionInfo. So for each change we find its
+ // matching target, and leave the state for any change missing a matching target blank.
+ WindowAnimationState[] updatedStates =
+ new WindowAnimationState[mOpenTransitionInfo.getChanges().size()];
+ for (int i = 0; i < mOpenTransitionInfo.getChanges().size(); i++) {
+ ActivityManager.RunningTaskInfo taskInfo =
+ mOpenTransitionInfo.getChanges().get(i).getTaskInfo();
+ if (taskInfo == null) {
+ continue;
+ }
+
+ for (int j = 0; j < targets.length; j++) {
+ if (taskInfo.taskId == targets[j].taskId) {
+ updatedStates[i] = states[j];
+ break;
+ }
+ }
+ }
+
+ mTakeoverHandler.takeOverAnimation(
+ mPrepareOpenTransition, mOpenTransitionInfo, new SurfaceControl.Transaction(),
+ mFinishOpenTransitionCallback, updatedStates);
+
+ cleanUpInternalState();
+ }
+
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishTransaction) {
@@ -1673,6 +1757,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
return null;
}
+
+ private static boolean checkTakeoverFlags() {
+ return TransitionAnimator.Companion.longLivedReturnAnimationsEnabled()
+ && Flags.unifyBackNavigationTransition();
+ }
}
private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) {
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 13a8518ae8ed..c3e396524da1 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
@@ -57,6 +57,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContentResolver;
@@ -91,6 +93,7 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -140,6 +143,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Mock
private Handler mHandler;
+ @Mock
+ private Transitions.TransitionHandler mTakeoverHandler;
private BackAnimationController mController;
private TestableContentResolver mContentResolver;
@@ -152,6 +157,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
+ @Rule
+ public SetFlagsRule mSetflagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -605,6 +613,51 @@ public class BackAnimationControllerTest extends ShellTestCase {
verify(mAnimatorCallback, never()).onBackInvoked();
}
+ @EnableFlags({com.android.systemui.shared.Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ com.android.systemui.shared.Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ com.android.window.flags.Flags.FLAG_UNIFY_BACK_NAVIGATION_TRANSITION})
+ @Test
+ public void appCallback_receivesTakeoverHandler_whenAvailable() throws RemoteException {
+ registerAnimation(BackNavigationInfo.TYPE_CROSS_TASK);
+ mBackTransitionHandler.mTakeoverHandler = mTakeoverHandler;
+
+ final int type = BackNavigationInfo.TYPE_CALLBACK;
+ final ResultListener result = new ResultListener();
+ createNavigationInfo(new BackNavigationInfo.Builder()
+ .setType(type)
+ .setOnBackInvokedCallback(mAppCallback)
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion)
+ .setAppProgressAllowed(true));
+
+ triggerBackGesture();
+ mShellExecutor.flushAll();
+ releaseBackGesture();
+ mShellExecutor.flushAll();
+
+ verify(mAppCallback).setHandoffHandler(any());
+ }
+
+ @Test
+ public void appCallback_doesNotReceiveTakeoverHandler_whenUnavailable() throws RemoteException {
+ registerAnimation(BackNavigationInfo.TYPE_CROSS_TASK);
+
+ final int type = BackNavigationInfo.TYPE_CALLBACK;
+ final ResultListener result = new ResultListener();
+ createNavigationInfo(new BackNavigationInfo.Builder()
+ .setType(type)
+ .setOnBackInvokedCallback(mAppCallback)
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion)
+ .setAppProgressAllowed(true));
+ triggerBackGesture();
+ mShellExecutor.flushAll();
+ releaseBackGesture();
+ mShellExecutor.flushAll();
+
+ verify(mAppCallback, never()).setHandoffHandler(any());
+ }
+
@Test
public void skipsCancelWithoutStart() throws RemoteException {
final int type = BackNavigationInfo.TYPE_CALLBACK;
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 d6be9159694b..40da9ea2d718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -66,6 +66,7 @@ import android.view.WindowManager;
import android.window.BackAnimationAdapter;
import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
+import android.window.IBackAnimationHandoffHandler;
import android.window.IOnBackInvokedCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedCallbackInfo;
@@ -780,6 +781,10 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Override
public void setTriggerBack(boolean triggerBack) {
}
+
+ @Override
+ public void setHandoffHandler(IBackAnimationHandoffHandler unused) {
+ }
};
}