From 642b0d8cdd745656b7fd77158db1a9b085fe32ef Mon Sep 17 00:00:00 2001 From: Shan Huang Date: Tue, 1 Feb 2022 06:10:23 +0000 Subject: [PATCH] Invoke callbacks based on back navigation type. Test: m -j. Test: Open and swipe back on pre-T and T apps. Test: atest .../BackNavigationControllerTests.java Test: atest .../BackAnimationControllerTest.java Bug: b/195946584 Change-Id: I00cf7ab5b57760d57d30ac74dc5b3443a4203f38 --- core/java/android/view/IWindowSession.aidl | 4 +- .../android/view/WindowlessWindowManager.java | 4 +- .../android/window/BackNavigationInfo.java | 58 +++--- .../window/WindowOnBackInvokedDispatcher.java | 4 +- .../WindowOnBackInvokedDispatcherTest.java | 22 ++- data/etc/services.core.protolog.json | 12 +- .../android/wm/shell/back/BackAnimation.java | 7 + .../shell/back/BackAnimationController.java | 168 +++++++++++++++--- .../back/BackAnimationControllerTest.java | 70 ++++++-- packages/SystemUI/res/values/dimens.xml | 2 + .../gestural/NavigationBarEdgePanel.java | 14 +- .../server/wm/BackNavigationController.java | 66 +++++-- .../java/com/android/server/wm/Session.java | 9 +- .../com/android/server/wm/WindowState.java | 27 ++- .../wm/BackNavigationControllerTests.java | 2 +- 15 files changed, 364 insertions(+), 105 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 1ed35f7b6dcf4..a266a28dcacae 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -337,8 +337,10 @@ interface IWindowSession { * * @param window The token for the window to set the callback to. * @param callback The {@link IOnBackInvokedCallback} to set. + * @param priority The priority of the callback. */ - oneway void setOnBackInvokedCallback(IWindow window, IOnBackInvokedCallback callback); + oneway void setOnBackInvokedCallback( + IWindow window, IOnBackInvokedCallback callback, int priority); /** * Clears a touchable region set by {@link #setInsets}. diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 21221521d21f0..c81b8ccf94d66 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -27,8 +27,6 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Log; import android.util.MergedConfiguration; -import android.view.InsetsState; -import android.view.IWindow; import android.window.ClientWindowFrames; import android.window.IOnBackInvokedCallback; @@ -511,7 +509,7 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void setOnBackInvokedCallback(IWindow iWindow, - IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException { } + IOnBackInvokedCallback iOnBackInvokedCallback, int priority) throws RemoteException { } @Override public boolean dropForAccessibility(IWindow window, int x, int y) { diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index 18c20e2b1fa54..1e922d91e7b51 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -21,9 +21,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.hardware.HardwareBuffer; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteCallback; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; /** @@ -64,6 +66,12 @@ public final class BackNavigationInfo implements Parcelable { */ public static final int TYPE_CALLBACK = 4; + /** + * Key to access the boolean value passed in {#mOnBackNavigationDone} result bundle + * that represents if back navigation has been triggered. + */ + public static final String KEY_TRIGGER_BACK = "TriggerBack"; + /** * Defines the type of back destinations a back even can lead to. This is used to define the * type of animation that need to be run on SystemUI. @@ -79,13 +87,13 @@ public final class BackNavigationInfo implements Parcelable { private final int mType; @Nullable - private final SurfaceControl mDepartingWindowContainer; + private final RemoteAnimationTarget mDepartingAnimationTarget; @Nullable private final SurfaceControl mScreenshotSurface; @Nullable private final HardwareBuffer mScreenshotBuffer; @Nullable - private final RemoteCallback mRemoteCallback; + private final RemoteCallback mOnBackNavigationDone; @Nullable private final WindowConfiguration mTaskWindowConfiguration; @Nullable @@ -96,8 +104,9 @@ public final class BackNavigationInfo implements Parcelable { * * @param type The {@link BackTargetType} of the destination (what will be * displayed after the back action). - * @param topWindowLeash The leash to animate away the current topWindow. The consumer - * of the leash is responsible for removing it. + * @param departingAnimationTarget The remote animation target, containing a leash to animate + * away the departing window. The consumer of the leash is + * responsible for removing it. * @param screenshotSurface The screenshot of the previous activity to be displayed. * @param screenshotBuffer A buffer containing a screenshot used to display the activity. * See {@link #getScreenshotHardwareBuffer()} for information @@ -108,39 +117,39 @@ public final class BackNavigationInfo implements Parcelable { * @param onBackInvokedCallback The back callback registered by the current top level window. */ public BackNavigationInfo(@BackTargetType int type, - @Nullable SurfaceControl topWindowLeash, + @Nullable RemoteAnimationTarget departingAnimationTarget, @Nullable SurfaceControl screenshotSurface, @Nullable HardwareBuffer screenshotBuffer, @Nullable WindowConfiguration taskWindowConfiguration, - @Nullable RemoteCallback onBackNavigationDone, - @Nullable IOnBackInvokedCallback onBackInvokedCallback) { + @NonNull RemoteCallback onBackNavigationDone, + @NonNull IOnBackInvokedCallback onBackInvokedCallback) { mType = type; - mDepartingWindowContainer = topWindowLeash; + mDepartingAnimationTarget = departingAnimationTarget; mScreenshotSurface = screenshotSurface; mScreenshotBuffer = screenshotBuffer; mTaskWindowConfiguration = taskWindowConfiguration; - mRemoteCallback = onBackNavigationDone; + mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; } private BackNavigationInfo(@NonNull Parcel in) { mType = in.readInt(); - mDepartingWindowContainer = in.readTypedObject(SurfaceControl.CREATOR); + mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR); mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR); mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR); - mRemoteCallback = in.readTypedObject(RemoteCallback.CREATOR); + mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); - dest.writeTypedObject(mDepartingWindowContainer, flags); + dest.writeTypedObject(mDepartingAnimationTarget, flags); dest.writeTypedObject(mScreenshotSurface, flags); dest.writeTypedObject(mScreenshotBuffer, flags); dest.writeTypedObject(mTaskWindowConfiguration, flags); - dest.writeTypedObject(mRemoteCallback, flags); + dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); } @@ -154,12 +163,13 @@ public final class BackNavigationInfo implements Parcelable { } /** - * Returns a leash to the top window container that needs to be animated. This can be null if - * the back animation is controlled by the application. + * Returns a {@link RemoteAnimationTarget}, containing a leash to the top window container + * that needs to be animated. This can be null if the back animation is controlled by + * the application. */ @Nullable - public SurfaceControl getDepartingWindowContainer() { - return mDepartingWindowContainer; + public RemoteAnimationTarget getDepartingAnimationTarget() { + return mDepartingAnimationTarget; } /** @@ -212,10 +222,14 @@ public final class BackNavigationInfo implements Parcelable { /** * 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. + * + * @param triggerBack Boolean indicating if back navigation has been triggered. */ - public void onBackNavigationFinished() { - if (mRemoteCallback != null) { - mRemoteCallback.sendResult(null); + public void onBackNavigationFinished(boolean triggerBack) { + if (mOnBackNavigationDone != null) { + Bundle result = new Bundle(); + result.putBoolean(KEY_TRIGGER_BACK, triggerBack); + mOnBackNavigationDone.sendResult(result); } } @@ -240,11 +254,11 @@ public final class BackNavigationInfo implements Parcelable { public String toString() { return "BackNavigationInfo{" + "mType=" + typeToString(mType) + " (" + mType + ")" - + ", mDepartingWindowContainer=" + mDepartingWindowContainer + + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget + ", mScreenshotSurface=" + mScreenshotSurface + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration + ", mScreenshotBuffer=" + mScreenshotBuffer - + ", mRemoteCallback=" + mRemoteCallback + + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + '}'; } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 03de4796ed747..62292f97ad4df 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -160,11 +160,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } try { if (callback == null) { - mWindowSession.setOnBackInvokedCallback(mWindow, null); + mWindowSession.setOnBackInvokedCallback(mWindow, null, PRIORITY_DEFAULT); } else { int priority = mAllCallbacks.get(callback); mWindowSession.setOnBackInvokedCallback( - mWindow, new OnBackInvokedCallbackWrapper(callback, priority)); + mWindow, new OnBackInvokedCallbackWrapper(callback, priority), priority); } } catch (RemoteException e) { Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index a1a1e20d6982c..1f2bcfbff3cc9 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -83,8 +83,10 @@ public class WindowOnBackInvokedDispatcherTest { mDispatcher.registerOnBackInvokedCallback( mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); - verify(mWindowSession, times(2)) - .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture()); + verify(mWindowSession, times(2)).setOnBackInvokedCallback( + Mockito.eq(mWindow), + captor.capture(), + Mockito.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT)); captor.getAllValues().get(0).onBackStarted(); waitForIdle(); verify(mCallback1).onBackStarted(); @@ -106,8 +108,9 @@ public class WindowOnBackInvokedDispatcherTest { mDispatcher.registerOnBackInvokedCallback( mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); - verify(mWindowSession) - .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture()); + verify(mWindowSession).setOnBackInvokedCallback( + Mockito.eq(mWindow), captor.capture(), + Mockito.eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY)); verifyNoMoreInteractions(mWindowSession); captor.getValue().onBackStarted(); waitForIdle(); @@ -126,7 +129,10 @@ public class WindowOnBackInvokedDispatcherTest { verifyZeroInteractions(mWindowSession); mDispatcher.unregisterOnBackInvokedCallback(mCallback2); - verify(mWindowSession).setOnBackInvokedCallback(Mockito.eq(mWindow), isNull()); + verify(mWindowSession).setOnBackInvokedCallback( + Mockito.eq(mWindow), + isNull(), + Mockito.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT)); } @@ -145,8 +151,10 @@ public class WindowOnBackInvokedDispatcherTest { reset(mWindowSession); mDispatcher.registerOnBackInvokedCallback( mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY); - verify(mWindowSession) - .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture()); + verify(mWindowSession).setOnBackInvokedCallback( + Mockito.eq(mWindow), + captor.capture(), + Mockito.eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY)); captor.getValue().onBackStarted(); waitForIdle(); verify(mCallback2).onBackStarted(); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index f2a875c76f1cb..4aa0f07f3cb98 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1567,6 +1567,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-432881038": { + "message": "startBackNavigation task=%s, topRunningActivity=%s, applicationBackCallback=%s, systemBackCallback=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-415865166": { "message": "findFocusedWindow: Found new focus @ %s", "level": "VERBOSE", @@ -3691,12 +3697,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "1898905572": { - "message": "startBackNavigation task=%s, topRunningActivity=%s, topWindow=%s backCallback=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "1903353011": { "message": "notifyAppStopped: %s", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index 4c505f6583fc7..7cf359729ee88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -43,4 +43,11 @@ public interface BackAnimation { default IBackAnimation createExternalInterface() { return null; } + + /** + * Sets the threshold values that defining edge swipe behavior. + * @param triggerThreshold the min threshold to trigger back. + * @param progressThreshold the max threshold to keep progressing back animation. + */ + void setSwipeThresholds(float triggerThreshold, float progressThreshold); } 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 32ac43da951c9..8d5fdfbc8cb08 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 @@ -35,6 +35,7 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.util.Log; import android.view.MotionEvent; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.BackEvent; import android.window.BackNavigationInfo; @@ -54,6 +55,10 @@ public class BackAnimationController implements RemoteCallable 0; + private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP = + "persist.debug.back_predictability_progress_threshold"; + private static final int PROGRESS_THRESHOLD = SystemProperties + .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1); private static final String TAG = "BackAnimationController"; /** @@ -80,6 +85,8 @@ public class BackAnimationController implements RemoteCallable BackAnimationController.this.setTriggerBack(triggerBack)); } + + @Override + public void setSwipeThresholds(float triggerThreshold, float progressThreshold) { + mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds( + triggerThreshold, progressThreshold)); + } } private static class IBackAnimationImpl extends IBackAnimation.Stub { @@ -168,7 +181,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(); + + BackEvent backEvent = new BackEvent(0, 0, progress, swipeEdge, animationTarget); + IOnBackInvokedCallback targetCallback = null; + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME) { + targetCallback = mBackToLauncherCallback; + } else if (backType == BackNavigationInfo.TYPE_CROSS_TASK + || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { + if (animationTarget != null) { + mTransaction.setPosition(animationTarget.leash, deltaX, deltaY); + mTouchEventDelta.set(deltaX, deltaY); + mTransaction.apply(); + } + } else if (backType == BackNavigationInfo.TYPE_CALLBACK) { + targetCallback = mBackNavigationInfo.getOnBackInvokedCallback(); + } + dispatchOnBackProgressed(targetCallback, backEvent); } private void onGestureFinished() { ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack); - if (mBackGestureStarted) { + if (!mBackGestureStarted || mBackNavigationInfo == null) { + return; + } + int backType = mBackNavigationInfo.getType(); + boolean shouldDispatchToLauncher = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + && mBackToLauncherCallback != null; + IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher + ? mBackToLauncherCallback + : mBackNavigationInfo.getOnBackInvokedCallback(); + if (mTriggerBack) { + dispatchOnBackInvoked(targetCallback); + } else { + dispatchOnBackCancelled(targetCallback); + } + if (backType == BackNavigationInfo.TYPE_CALLBACK) { + finishAnimation(); + } else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + && !shouldDispatchToLauncher) { + // Launcher callback missing. Simply finish animation. + finishAnimation(); + } else if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY + || backType == BackNavigationInfo.TYPE_CROSS_TASK) { if (mTriggerBack) { prepareTransition(); } else { resetPositionAnimated(); } } - mBackGestureStarted = false; - mTriggerBack = false; + } + + private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) { + if (callback == null) { + return; + } + try { + callback.onBackStarted(); + } catch (RemoteException e) { + Log.e(TAG, "dispatchOnBackStarted error: ", e); + } + } + + private static void dispatchOnBackInvoked(IOnBackInvokedCallback callback) { + if (callback == null) { + return; + } + try { + callback.onBackInvoked(); + } catch (RemoteException e) { + Log.e(TAG, "dispatchOnBackInvoked error: ", e); + } + } + + private static void dispatchOnBackCancelled(IOnBackInvokedCallback callback) { + if (callback == null) { + return; + } + try { + callback.onBackCancelled(); + } catch (RemoteException e) { + Log.e(TAG, "dispatchOnBackCancelled error: ", e); + } + } + + private static void dispatchOnBackProgressed( + IOnBackInvokedCallback callback, BackEvent backEvent) { + if (callback == null) { + return; + } + try { + callback.onBackProgressed(backEvent); + } catch (RemoteException e) { + Log.e(TAG, "dispatchOnBackProgressed error: ", e); + } } /** @@ -309,9 +419,12 @@ public class BackAnimationController implements RemoteCallable backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); + verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); + assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget()); + + // Check that back invocation is dispatched. + mController.setTriggerBack(true); // Fake trigger back + mController.onMotionEvent( + MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0), + BackEvent.EDGE_LEFT); + verify(mIOnBackInvokedCallback).onBackInvoked(); + } } diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d1f4f1906f337..47ffb18e15b3b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -43,6 +43,8 @@ 96dp 16dp + + 400dp 64dp diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 4da574d571b2f..a6bad15e08654 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -162,7 +162,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl // The amount the arrow is shifted to avoid the finger. private int mFingerOffset; - private final float mSwipeThreshold; + private final float mSwipeTriggerThreshold; + private final float mSwipeProgressThreshold; private final Path mArrowPath = new Path(); private final Point mDisplaySize = new Point(); @@ -352,10 +353,15 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl loadColors(context); updateArrowDirection(); - mSwipeThreshold = context.getResources() + mSwipeTriggerThreshold = context.getResources() .getDimension(R.dimen.navigation_edge_action_drag_threshold); - setVisibility(GONE); + mSwipeProgressThreshold = context.getResources() + .getDimension(R.dimen.navigation_edge_action_progress_threshold); + if (mBackAnimation != null) { + mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold); + } + setVisibility(GONE); Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR); boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY; mRegionSamplingHelper = new RegionSamplingHelper(this, @@ -730,7 +736,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl mPreviousTouchTranslation = touchTranslation; // Apply a haptic on drag slop passed - if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) { + if (!mDragSlopPassed && touchTranslation > mSwipeTriggerThreshold) { mDragSlopPassed = true; mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); mVibrationTime = SystemClock.uptimeMillis(); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 45a6cb9d3920a..e26748c928d19 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -22,11 +22,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.ComponentName; +import android.graphics.Point; +import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemProperties; import android.util.Slog; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.BackNavigationInfo; import android.window.IOnBackInvokedCallback; @@ -88,34 +91,39 @@ class BackNavigationController { ActivityRecord activityRecord; SurfaceControl animationLeashParent; WindowConfiguration taskWindowConfiguration; - SurfaceControl animLeash; HardwareBuffer screenshotBuffer = null; int prevTaskId; int prevUserId; - IOnBackInvokedCallback callback; + IOnBackInvokedCallback applicationCallback = null; + IOnBackInvokedCallback systemCallback = null; + RemoteAnimationTarget topAppTarget; + SurfaceControl animLeash; synchronized (task.mWmService.mGlobalLock) { activityRecord = task.topRunningActivity(); removedWindowContainer = activityRecord; taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; - - WindowState topChild = activityRecord.getTopChild(); - callback = topChild.getOnBackInvokedCallback(); + WindowState window = task.getWindow(WindowState::isFocused); + if (window != null) { + applicationCallback = window.getApplicationOnBackInvokedCallback(); + systemCallback = window.getSystemOnBackInvokedCallback(); + } ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " - + "topRunningActivity=%s, topWindow=%s backCallback=%s", - task, activityRecord, topChild, - callback != null ? callback.getClass().getSimpleName() : null); + + "topRunningActivity=%s, applicationBackCallback=%s, " + + "systemBackCallback=%s", + task, activityRecord, applicationCallback, systemCallback); // For IME and Home, either a callback is registered, or we do nothing. In both cases, // we don't need to pass the leashes below. if (task.getDisplayContent().getImeContainer().isVisible() || activityRecord.isActivityTypeHome()) { - if (callback != null) { + if (applicationCallback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, - null /* onBackNavigationDone */, callback /* onBackInvokedCallback */); + null /* onBackNavigationDone */, + applicationCallback /* onBackInvokedCallback */); } else { return null; } @@ -124,8 +132,12 @@ class BackNavigationController { prev = task.getActivity( (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity()); - if (callback != null) { - backType = BackNavigationInfo.TYPE_CALLBACK; + if (applicationCallback != null) { + return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, + null /* topWindowLeash */, null /* screenshotSurface */, + null /* screenshotBuffer */, null /* taskWindowConfiguration */, + null /* onBackNavigationDone */, + applicationCallback /* onBackInvokedCallback */); } else if (prev != null) { backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else if (task.returnsToHomeRootTask()) { @@ -159,6 +171,11 @@ class BackNavigationController { screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent); } + // Only create a new leash if no leash has been created. + // Otherwise return null for animation target to avoid conflict. + if (removedWindowContainer.hasCommittedReparentToAnimationLeash()) { + return null; + } // Prepare a leash to animate the current top window animLeash = removedWindowContainer.makeAnimationLeash() .setName("BackPreview Leash for " + removedWindowContainer) @@ -166,8 +183,25 @@ class BackNavigationController { .setBLASTLayer() .build(); removedWindowContainer.reparentSurfaceControl(tx, animLeash); - animationLeashParent = removedWindowContainer.getAnimationLeashParent(); + topAppTarget = new RemoteAnimationTarget( + task.mTaskId, + RemoteAnimationTarget.MODE_CLOSING, + animLeash, + false /* isTransluscent */, + new Rect() /* clipRect */, + new Rect() /* contentInsets */, + activityRecord.getPrefixOrderIndex(), + new Point(0, 0) /* position */, + new Rect() /* localBounds */, + new Rect() /* screenSpaceBounds */, + removedWindowContainer.getWindowConfiguration(), + true /* isNotInRecent */, + null, + null, + task.getTaskInfo(), + false, + activityRecord.windowType); } SurfaceControl.Builder builder = new SurfaceControl.Builder() @@ -187,7 +221,7 @@ class BackNavigationController { // The Animation leash needs to be above the screenshot surface, but the animation leash // needs to be added before to be in the synchronized block. - tx.setLayer(animLeash, 1); + tx.setLayer(topAppTarget.leash, 1); tx.apply(); WindowContainer finalRemovedWindowContainer = removedWindowContainer; @@ -200,11 +234,13 @@ class BackNavigationController { return null; } + final IOnBackInvokedCallback callback = + applicationCallback != null ? applicationCallback : systemCallback; RemoteCallback onBackNavigationDone = new RemoteCallback( result -> resetSurfaces(finalRemovedWindowContainer )); return new BackNavigationInfo(backType, - animLeash, + topAppTarget, screenshotSurface, screenshotBuffer, taskWindowConfiguration, diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index de8ea8c60522e..cd8ddf4e92114 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -69,6 +69,7 @@ import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsVisibilities; +import android.view.OnBackInvokedDispatcher; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; @@ -905,15 +906,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public void setOnBackInvokedCallback(IWindow window, - IOnBackInvokedCallback onBackInvokedCallback) throws RemoteException { + public void setOnBackInvokedCallback( + IWindow window, + IOnBackInvokedCallback onBackInvokedCallback, + @OnBackInvokedDispatcher.Priority int priority) throws RemoteException { synchronized (mService.mGlobalLock) { WindowState windowState = mService.windowForClientLocked(this, window, false); if (windowState == null) { Slog.e(TAG_WM, "setOnBackInvokedCallback(): Can't find window state for window:" + window); } else { - windowState.setOnBackInvokedCallback(onBackInvokedCallback); + windowState.setOnBackInvokedCallback(onBackInvokedCallback, priority); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 498eaabb93862..6378a1e1dc17a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -850,7 +850,8 @@ class WindowState extends WindowContainer implements WindowManagerP /** * @see #setOnBackInvokedCallback(IOnBackInvokedCallback) */ - private IOnBackInvokedCallback mOnBackInvokedCallback; + private IOnBackInvokedCallback mApplicationOnBackInvokedCallback; + private IOnBackInvokedCallback mSystemOnBackInvokedCallback; @Override WindowState asWindowState() { @@ -1073,15 +1074,25 @@ class WindowState extends WindowContainer implements WindowManagerP * called when a back navigation action is initiated. * @see BackNavigationController */ - void setOnBackInvokedCallback(@Nullable IOnBackInvokedCallback onBackInvokedCallback) { + void setOnBackInvokedCallback( + @Nullable IOnBackInvokedCallback onBackInvokedCallback, int priority) { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s", this, onBackInvokedCallback); - mOnBackInvokedCallback = onBackInvokedCallback; + if (priority >= 0) { + mApplicationOnBackInvokedCallback = onBackInvokedCallback; + } else { + mSystemOnBackInvokedCallback = onBackInvokedCallback; + } } @Nullable - IOnBackInvokedCallback getOnBackInvokedCallback() { - return mOnBackInvokedCallback; + IOnBackInvokedCallback getApplicationOnBackInvokedCallback() { + return mApplicationOnBackInvokedCallback; + } + + @Nullable + IOnBackInvokedCallback getSystemOnBackInvokedCallback() { + return mSystemOnBackInvokedCallback; } interface PowerManagerWrapper { @@ -2385,7 +2396,8 @@ class WindowState extends WindowContainer implements WindowManagerP dc.getDisplayPolicy().removeWindowLw(this); disposeInputChannel(); - mOnBackInvokedCallback = null; + mSystemOnBackInvokedCallback = null; + mApplicationOnBackInvokedCallback = null; mSession.windowRemovedLocked(); try { @@ -2439,7 +2451,8 @@ class WindowState extends WindowContainer implements WindowManagerP try { disposeInputChannel(); - mOnBackInvokedCallback = null; + mSystemOnBackInvokedCallback = null; + mApplicationOnBackInvokedCallback = null; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b " 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 fb3a6264169a0..3d89805297ff1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -97,7 +97,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); - assertThat(backNavigationInfo.getDepartingWindowContainer()).isNotNull(); + assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); assertThat(backNavigationInfo.getScreenshotSurface()).isNotNull(); assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNotNull(); assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();