summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/WindowManager.java7
-rw-r--r--core/java/android/window/WindowContainerTransaction.java13
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java282
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java1
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java123
-rw-r--r--services/core/java/com/android/server/wm/SnapshotController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java13
9 files changed, 437 insertions, 25 deletions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 85d4ec00c7bc..a35f4d3c3156 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -482,6 +482,11 @@ public interface WindowManager extends ViewManager {
* @hide
*/
int TRANSIT_PREPARE_BACK_NAVIGATION = 13;
+ /**
+ * An Activity was going to be invisible from back navigation.
+ * @hide
+ */
+ int TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION = 14;
/**
* The first slot for custom transition types. Callers (like Shell) can make use of custom
@@ -512,6 +517,7 @@ public interface WindowManager extends ViewManager {
TRANSIT_WAKE,
TRANSIT_SLEEP,
TRANSIT_PREPARE_BACK_NAVIGATION,
+ TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -1926,6 +1932,7 @@ public interface WindowManager extends ViewManager {
case TRANSIT_WAKE: return "WAKE";
case TRANSIT_SLEEP: return "SLEEP";
case TRANSIT_PREPARE_BACK_NAVIGATION: return "PREDICTIVE_BACK";
+ case TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION: return "CLOSE_PREDICTIVE_BACK";
case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
default:
if (type > TRANSIT_FIRST_CUSTOM) {
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index e9d77f8aaf80..314bf8985cb4 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -700,6 +700,18 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Restore the back navigation target from visible to invisible for canceling gesture animation.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction restoreBackNavi() {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+ /**
* Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
*
* @param receiver The window container that the insets source is added to.
@@ -1436,6 +1448,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION = 17;
public static final int HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK = 18;
public static final int HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE = 19;
+ public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index dc022b4afd3b..9027bf34a58e 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -59,7 +60,8 @@ public class TransitionUtil {
public static boolean isOpeningType(@WindowManager.TransitionType int type) {
return type == TRANSIT_OPEN
|| type == TRANSIT_TO_FRONT
- || type == TRANSIT_KEYGUARD_GOING_AWAY;
+ || type == TRANSIT_KEYGUARD_GOING_AWAY
+ || type == TRANSIT_PREPARE_BACK_NAVIGATION;
}
/** @return true if the transition was triggered by closing something vs opening something */
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 f14f4198c3f2..7275c6494140 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,9 +16,14 @@
package com.android.wm.shell.back;
+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.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
+import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
import static com.android.window.flags.Flags.migratePredictiveBackTransition;
@@ -31,6 +36,8 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
+import android.app.TaskInfo;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
@@ -837,8 +844,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
// The next callback should be {@link #onBackAnimationFinished}.
+ final boolean migrateBackToTransition = migratePredictiveBackTransition();
if (mCurrentTracker.getTriggerBack()) {
- if (migratePredictiveBackTransition()) {
+ if (migrateBackToTransition) {
// notify core gesture is commit
if (shouldTriggerCloseTransition()) {
mBackTransitionHandler.mCloseTransitionRequested = true;
@@ -856,6 +864,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// start post animation
dispatchOnBackInvoked(mActiveCallback);
} else {
+ if (migrateBackToTransition
+ && mBackTransitionHandler.mPrepareOpenTransition != null) {
+ mBackTransitionHandler.createClosePrepareTransition();
+ }
tryDispatchOnBackCancelled(mActiveCallback);
}
}
@@ -960,6 +972,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
mReceivedNullNavigationInfo = false;
+ mBackTransitionHandler.mLastTrigger = triggerBack;
if (mBackNavigationInfo != null) {
mPreviousNavigationType = mBackNavigationInfo.getType();
mBackNavigationInfo.onBackNavigationFinished(triggerBack);
@@ -1128,12 +1141,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
Runnable mOnAnimationFinishCallback;
boolean mCloseTransitionRequested;
- boolean mOpeningRunning;
SurfaceControl.Transaction mFinishOpenTransaction;
Transitions.TransitionFinishCallback mFinishOpenTransitionCallback;
QueuedTransition mQueuedTransition = null;
+ boolean mLastTrigger;
+ // The Transition to make behindActivity become visible
+ IBinder mPrepareOpenTransition;
+ // The Transition to make behindActivity become invisible, if prepare open exist and
+ // animation is canceled, start a close prepare transition to finish the whole transition.
+ IBinder mClosePrepareTransition;
+ TransitionInfo mOpenTransitionInfo;
void onAnimationFinished() {
- if (!mCloseTransitionRequested) {
+ if (!mCloseTransitionRequested && mClosePrepareTransition == null) {
applyFinishOpenTransition();
}
if (mOnAnimationFinishCallback != null) {
@@ -1158,7 +1177,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mFinishOpenTransitionCallback.onTransitionFinished(null);
mFinishOpenTransitionCallback = null;
}
- mOpeningRunning = false;
+ mOpenTransitionInfo = null;
+ mPrepareOpenTransition = null;
}
private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
@@ -1178,21 +1198,42 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull Transitions.TransitionFinishCallback finishCallback) {
// Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
// need to post to ShellExecutor when called.
+ if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
+ // only consume it if this transition hasn't being processed.
+ if (mClosePrepareTransition != null) {
+ mClosePrepareTransition = null;
+ applyAndFinish(st, ft, finishCallback);
+ return true;
+ }
+ return false;
+ }
+
if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
&& !isGestureBackTransition(info)) {
return false;
}
+
+ if (shouldCancelAnimation(info)) {
+ return false;
+ }
+
if (mApps == null || mApps.length == 0) {
if (mBackNavigationInfo != null && mShellBackAnimationRegistry
.isWaitingAnimation(mBackNavigationInfo.getType())) {
// Waiting for animation? Queue update to wait for animation start.
consumeQueuedTransitionIfNeeded();
mQueuedTransition = new QueuedTransition(info, st, ft, finishCallback);
- } else {
+ return true;
+ } else if (mLastTrigger) {
// animation was done, consume directly
applyAndFinish(st, ft, finishCallback);
+ return true;
+ } else {
+ // animation was cancelled but transition haven't happen, we must handle it
+ if (mClosePrepareTransition == null && mCurrentTracker.isFinished()) {
+ createClosePrepareTransition();
+ }
}
- return true;
}
if (handlePrepareTransition(info, st, ft, finishCallback)) {
@@ -1201,12 +1242,131 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return handleCloseTransition(info, st, ft, finishCallback);
}
+ void createClosePrepareTransition() {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.restoreBackNavi();
+ mClosePrepareTransition = mTransitions.startTransition(
+ TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, wct, mBackTransitionHandler);
+ }
+ private void mergePendingTransitions(TransitionInfo info) {
+ if (mOpenTransitionInfo == null) {
+ return;
+ }
+ // Copy initial changes to final transition
+ final TransitionInfo init = mOpenTransitionInfo;
+ // find prepare open target
+ boolean openShowWallpaper = false;
+ ComponentName openComponent = null;
+ int tmpSize;
+ int openTaskId = INVALID_TASK_ID;
+ for (int j = init.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change change = init.getChanges().get(j);
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ openComponent = findComponentName(change);
+ openTaskId = findTaskId(change);
+ if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
+ openShowWallpaper = true;
+ }
+ break;
+ }
+ }
+ if (openComponent == null && openTaskId == INVALID_TASK_ID) {
+ // shouldn't happen.
+ return;
+ }
+ // find first non-prepare open target
+ boolean isOpen = false;
+ tmpSize = info.getChanges().size();
+ for (int j = 0; j < tmpSize; ++j) {
+ final TransitionInfo.Change change = info.getChanges().get(j);
+ final ComponentName firstNonOpen = findComponentName(change);
+ final int firstTaskId = findTaskId(change);
+ if ((firstNonOpen != null && firstNonOpen != openComponent)
+ || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) {
+ // this is original close target, potential be close, but cannot determine from
+ // it
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ isOpen = !TransitionUtil.isClosingMode(change.getMode());
+ } else {
+ isOpen = TransitionUtil.isOpeningMode(change.getMode());
+ break;
+ }
+ }
+ }
+
+ if (!isOpen) {
+ // Close transition, the transition info should be:
+ // init info(open A & wallpaper)
+ // current info(close B target)
+ // remove init info(open/change A target & wallpaper)
+ boolean moveToTop = false;
+ for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change change = info.getChanges().get(j);
+ if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
+ info.getChanges().remove(j);
+ } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
+ || !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ info.getChanges().remove(j);
+ }
+ }
+ tmpSize = info.getChanges().size();
+ for (int i = 0; i < tmpSize; ++i) {
+ final TransitionInfo.Change change = init.getChanges().get(i);
+ if (moveToTop) {
+ if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
+ }
+ }
+ info.getChanges().add(i, change);
+ }
+ } else {
+ // Open transition, the transition info should be:
+ // init info(open A & wallpaper)
+ // current info(open C target + close B target + close A & wallpaper)
+
+ // If close target isn't back navigated, filter out close A & wallpaper because the
+ // (open C + close B) pair didn't participant prepare close
+ boolean nonBackOpen = false;
+ boolean nonBackClose = false;
+ tmpSize = info.getChanges().size();
+ for (int j = 0; j < tmpSize; ++j) {
+ final TransitionInfo.Change change = info.getChanges().get(j);
+ if (!change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+ && canBeTransitionTarget(change)) {
+ final int mode = change.getMode();
+ nonBackOpen |= TransitionUtil.isOpeningMode(mode);
+ nonBackClose |= TransitionUtil.isClosingMode(mode);
+ }
+ }
+ if (nonBackClose && nonBackOpen) {
+ for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change change = info.getChanges().get(j);
+ if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ info.getChanges().remove(j);
+ } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
+ info.getChanges().remove(j);
+ }
+ }
+ }
+ }
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending "
+ + "transitions result=%s", info);
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (!isGestureBackTransition(info)) {
- if (mOpeningRunning) {
+ if (mClosePrepareTransition == transition) {
+ mClosePrepareTransition = null;
+ }
+ // try to handle unexpected transition
+ mergePendingTransitions(info);
+
+ if (!isGestureBackTransition(info) || shouldCancelAnimation(info)
+ || !mCloseTransitionRequested) {
+ if (mPrepareOpenTransition != null) {
applyFinishOpenTransition();
}
if (mQueuedTransition != null) {
@@ -1222,7 +1382,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// animation was done
applyFinishOpenTransition();
mCloseTransitionRequested = false;
- } // else, let queued transition to play
+ } // let queued transition finish.
} else {
// we are animating, wait until animation finish
mOnAnimationFinishCallback = () -> {
@@ -1233,6 +1393,56 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
+ // Cancel close animation if something happen unexpected, let another handler to handle
+ private boolean shouldCancelAnimation(@NonNull TransitionInfo info) {
+ final boolean noCloseAllowed =
+ info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
+ boolean unableToHandle = false;
+ boolean filterTargets = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ final boolean backGestureAnimated = c.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
+ if (!backGestureAnimated && !c.hasFlags(FLAG_IS_WALLPAPER)) {
+ // something we cannot handle?
+ unableToHandle = true;
+ filterTargets = true;
+ } else if (noCloseAllowed && backGestureAnimated
+ && TransitionUtil.isClosingMode(c.getMode())) {
+ // Prepare back navigation shouldn't contain close change, unless top app
+ // request close.
+ unableToHandle = true;
+ }
+ }
+ if (!unableToHandle) {
+ return false;
+ }
+ if (!filterTargets) {
+ return true;
+ }
+ if (TransitionUtil.isOpeningType(info.getType())
+ || TransitionUtil.isClosingType(info.getType())) {
+ boolean removeWallpaper = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ // filter out opening target, keep original closing target in this transition
+ if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+ && TransitionUtil.isOpeningMode(c.getMode())) {
+ info.getChanges().remove(i);
+ removeWallpaper |= c.hasFlags(FLAG_SHOW_WALLPAPER);
+ }
+ }
+ if (removeWallpaper) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+ info.getChanges().remove(i);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
/**
* Check whether this transition is prepare for predictive back animation, which could
* happen when core make an activity become visible.
@@ -1247,9 +1457,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
SurfaceControl openingLeash = null;
- for (int i = mApps.length - 1; i >= 0; --i) {
- if (mApps[i].mode == MODE_OPENING) {
- openingLeash = mApps[i].leash;
+ if (mApps != null) {
+ for (int i = mApps.length - 1; i >= 0; --i) {
+ if (mApps[i].mode == MODE_OPENING) {
+ openingLeash = mApps[i].leash;
+ }
}
}
if (openingLeash != null) {
@@ -1259,13 +1471,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
st.reparent(c.getLeash(), openingLeash);
+ st.setAlpha(c.getLeash(), 1.0f);
}
}
}
st.apply();
mFinishOpenTransaction = ft;
mFinishOpenTransitionCallback = finishCallback;
- mOpeningRunning = true;
+ mOpenTransitionInfo = info;
return true;
}
@@ -1288,6 +1501,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
+ || !mCloseTransitionRequested) {
+ return false;
+ }
SurfaceControl openingLeash = null;
SurfaceControl closingLeash = null;
for (int i = mApps.length - 1; i >= 0; --i) {
@@ -1325,7 +1542,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
public WindowContainerTransaction handleRequest(
@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- if (request.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+ final int type = request.getType();
+ if (type == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+ mPrepareOpenTransition = transition;
+ return new WindowContainerTransaction();
+ }
+ if (type == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
return new WindowContainerTransaction();
}
if (TransitionUtil.isClosingType(request.getType()) && mCloseTransitionRequested) {
@@ -1369,4 +1591,36 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
}
+
+ private static ComponentName findComponentName(TransitionInfo.Change change) {
+ final ComponentName componentName = change.getActivityComponent();
+ if (componentName != null) {
+ return componentName;
+ }
+ final TaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
+ return taskInfo.topActivity;
+ }
+ return null;
+ }
+
+ private static int findTaskId(TransitionInfo.Change change) {
+ final TaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
+ return taskInfo.taskId;
+ }
+ return INVALID_TASK_ID;
+ }
+
+ private static boolean isSameChangeTarget(ComponentName topActivity, int taskId,
+ TransitionInfo.Change change) {
+ final ComponentName openChange = findComponentName(change);
+ final int firstTaskId = findTaskId(change);
+ return (openChange != null && openChange == topActivity)
+ || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId);
+ }
+
+ private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
+ return findComponentName(change) != null || findTaskId(change) != INVALID_TASK_ID;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 75e7ddf53f9f..a27c14bda15a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -19,7 +19,9 @@ package com.android.wm.shell.transition;
import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_NONE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -221,6 +223,15 @@ public class TransitionAnimationHelper {
*/
public static int getTransitionTypeFromInfo(@NonNull TransitionInfo info) {
final int type = info.getType();
+ // This back navigation is canceled, check whether the transition should be open or close
+ if (type == TRANSIT_PREPARE_BACK_NAVIGATION
+ || type == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
+ if (!info.getChanges().isEmpty()) {
+ final TransitionInfo.Change change = info.getChanges().get(0);
+ return TransitionUtil.isOpeningMode(change.getMode())
+ ? TRANSIT_OPEN : TRANSIT_CLOSE;
+ }
+ }
// If the info transition type is opening transition, iterate its changes to see if it
// has any opening change, if none, returns TRANSIT_CLOSE type for closing animation.
if (type == TRANSIT_OPEN) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c8aa815db749..301b7b8ee611 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5463,6 +5463,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ mAtmService.mBackNavigationController.onAppVisibilityChanged(this, visible);
onChildVisibilityRequested(visible);
final DisplayContent displayContent = getDisplayContent();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 924f765ae79d..21270dd28f00 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -768,6 +768,48 @@ class BackNavigationController {
}
}
+ void onAppVisibilityChanged(@NonNull ActivityRecord ar, boolean visible) {
+ if (!mAnimationHandler.mComposed) {
+ return;
+ }
+
+ final boolean openingTransition = mAnimationHandler.mOpenAnimAdaptor
+ .mPreparedOpenTransition != null;
+ // Detect if another transition is collecting during predictive back animation.
+ if (openingTransition && !visible && mAnimationHandler.isTarget(ar, false /* open */)
+ && ar.mTransitionController.isCollecting(ar)) {
+ final TransitionController controller = ar.mTransitionController;
+ boolean collectTask = false;
+ ActivityRecord changedActivity = null;
+ for (int i = mAnimationHandler.mOpenActivities.length - 1; i >= 0; --i) {
+ final ActivityRecord next = mAnimationHandler.mOpenActivities[i];
+ if (next.mLaunchTaskBehind) {
+ // collect previous activity, so shell side can handle the transition.
+ controller.collect(next);
+ collectTask = true;
+ restoreLaunchBehind(next, true /* cancel */, false /* finishTransition */);
+ changedActivity = next;
+ }
+ }
+ if (collectTask && mAnimationHandler.mOpenAnimAdaptor.mAdaptors[0].mSwitchType
+ == AnimationHandler.TASK_SWITCH) {
+ final Task topTask = mAnimationHandler.mOpenAnimAdaptor.mAdaptors[0].getTopTask();
+ if (topTask != null) {
+ WindowContainer parent = mAnimationHandler.mOpenActivities[0].getParent();
+ while (parent != topTask && parent.isDescendantOf(topTask)) {
+ controller.collect(parent);
+ parent = parent.getParent();
+ }
+ controller.collect(topTask);
+ }
+ }
+ if (changedActivity != null) {
+ changedActivity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
+ true /* notifyClients */);
+ }
+ }
+ }
+
// For shell transition
/**
* Check whether the transition targets was animated by back gesture animation.
@@ -784,7 +826,13 @@ class BackNavigationController {
mAnimationHandler.markStartingSurfaceMatch(startTransaction);
return;
}
- if (!isMonitoringFinishTransition() || targets.isEmpty()) {
+ if (targets.isEmpty()) {
+ return;
+ }
+ final boolean migratePredictToTransition = Flags.migratePredictiveBackTransition();
+ if (migratePredictToTransition && !mAnimationHandler.mComposed) {
+ return;
+ } else if (!isMonitoringFinishTransition()) {
return;
}
if (mAnimationHandler.hasTargetDetached()) {
@@ -808,20 +856,27 @@ class BackNavigationController {
mTmpCloseApps.add(wc);
}
}
- final boolean matchAnimationTargets = isWaitBackTransition()
+ final boolean matchAnimationTargets;
+ if (migratePredictToTransition) {
+ matchAnimationTargets =
+ mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
+ } else {
+ matchAnimationTargets = isWaitBackTransition()
&& (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK)
&& mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
+ }
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"onTransactionReady, opening: %s, closing: %s, animating: %s, match: %b",
mTmpOpenApps, mTmpCloseApps, mAnimationHandler, matchAnimationTargets);
- if (!matchAnimationTargets) {
+ // Don't cancel transition, let transition handler to handle it
+ if (!matchAnimationTargets && !migratePredictToTransition) {
mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
} else {
if (mAnimationHandler.mPrepareCloseTransition != null) {
Slog.e(TAG, "Gesture animation is applied on another transition?");
}
mAnimationHandler.mPrepareCloseTransition = transition;
- if (!Flags.migratePredictiveBackTransition()) {
+ if (!migratePredictToTransition) {
// Because the target will reparent to transition root, so it cannot be controlled
// by animation leash. Hide the close target when transition starts.
startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
@@ -839,7 +894,19 @@ class BackNavigationController {
}
boolean isMonitorTransitionTarget(WindowContainer wc) {
- if ((isWaitBackTransition() && mAnimationHandler.mPrepareCloseTransition != null)
+ if (Flags.migratePredictiveBackTransition()) {
+ if (!mAnimationHandler.mComposed) {
+ return false;
+ }
+ if (mAnimationHandler.mSwitchType == AnimationHandler.TASK_SWITCH
+ && wc.asActivityRecord() != null
+ || (mAnimationHandler.mSwitchType == AnimationHandler.ACTIVITY_SWITCH
+ && wc.asTask() != null)) {
+ return false;
+ }
+ return (mAnimationHandler.isTarget(wc, true /* open */)
+ || mAnimationHandler.isTarget(wc, false /* open */));
+ } else if ((isWaitBackTransition() && mAnimationHandler.mPrepareCloseTransition != null)
|| (mAnimationHandler.mOpenAnimAdaptor != null
&& mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition != null)) {
return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
@@ -1841,6 +1908,43 @@ class BackNavigationController {
return openActivities;
}
+ boolean restoreBackNavigation() {
+ if (!mAnimationHandler.mComposed) {
+ return false;
+ }
+ ActivityRecord[] penActivities = mAnimationHandler.mOpenActivities;
+ boolean changed = false;
+ if (penActivities != null) {
+ for (int i = penActivities.length - 1; i >= 0; --i) {
+ ActivityRecord resetActivity = penActivities[i];
+ if (resetActivity.mLaunchTaskBehind) {
+ resetActivity.mTransitionController.collect(resetActivity);
+ restoreLaunchBehind(resetActivity, true, false);
+ changed = true;
+ }
+ }
+ }
+ return changed;
+ }
+
+ boolean restoreBackNavigationSetTransitionReady(Transition transition) {
+ if (!mAnimationHandler.mComposed) {
+ return false;
+ }
+ ActivityRecord[] penActivities = mAnimationHandler.mOpenActivities;
+ if (penActivities != null) {
+ for (int i = penActivities.length - 1; i >= 0; --i) {
+ ActivityRecord resetActivity = penActivities[i];
+ if (transition.isInTransition(resetActivity)) {
+ resetActivity.mTransitionController.setReady(
+ resetActivity.getDisplayContent(), true);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private static Transition setLaunchBehind(@NonNull ActivityRecord[] activities) {
final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
final ArrayList<ActivityRecord> affects = new ArrayList<>();
@@ -1920,9 +2024,12 @@ class BackNavigationController {
activity);
if (cancel) {
final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
- if (migrateBackTransition && finishTransition) {
- activity.commitVisibility(false /* visible */, false /* performLayout */,
- true /* fromTransition */);
+ // could be visible if transition is canceled due to top activity is finishing.
+ if (migrateBackTransition) {
+ if (finishTransition && !activity.shouldBeVisible()) {
+ activity.commitVisibility(false /* visible */, false /* performLayout */,
+ true /* fromTransition */);
+ }
} else {
// Restore the launch-behind state
// TODO b/347168362 Change status directly during collecting for a transition.
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 99e1e8b1a5c6..0f9c001dffa8 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -19,8 +19,10 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -202,10 +204,12 @@ class SnapshotController {
}
private static boolean isTransitionOpen(int type) {
- return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT
+ || type == TRANSIT_PREPARE_BACK_NAVIGATION;
}
private static boolean isTransitionClose(int type) {
- return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
+ return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK
+ || type == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0093e9d0788b..58c48ad3e9ac 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -59,6 +60,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
@@ -436,6 +438,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// the same transition instead of relying on this possible racing condition.
return;
}
+ if (transition.mType == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION
+ && mService.mBackNavigationController.restoreBackNavigationSetTransitionReady(
+ transition)) {
+ return;
+ }
transition.setAllReady();
}
@@ -1386,6 +1393,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.setTrimmableFromRecents(hop.isTrimmableFromRecents());
break;
}
+ case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: {
+ if (mService.mBackNavigationController.restoreBackNavigation()) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ break;
+ }
}
return effects;
}