Merge "Generalize back animation and transition sequence..."

This commit is contained in:
Wei Sheng Shih
2022-11-11 06:42:49 +00:00
committed by Android (Google) Code Review
12 changed files with 324 additions and 243 deletions

View File

@@ -89,8 +89,6 @@ public final class BackNavigationInfo implements Parcelable {
@Nullable
private final IOnBackInvokedCallback mOnBackInvokedCallback;
private final boolean mPrepareRemoteAnimation;
@Nullable
private WindowContainerToken mDepartingWindowContainerToken;
/**
* Create a new {@link BackNavigationInfo} instance.
@@ -100,20 +98,15 @@ public final class BackNavigationInfo implements Parcelable {
* back preview.
* @param onBackInvokedCallback The back callback registered by the current top level window.
* @param departingWindowContainerToken The {@link WindowContainerToken} of departing window.
* @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture
* animation, if true, the caller of startBackNavigation should
* be expected to receive an animation start callback.
*/
private BackNavigationInfo(@BackTargetType int type,
@Nullable RemoteCallback onBackNavigationDone,
@Nullable IOnBackInvokedCallback onBackInvokedCallback,
boolean isPrepareRemoteAnimation,
@Nullable WindowContainerToken departingWindowContainerToken) {
boolean isPrepareRemoteAnimation) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
mPrepareRemoteAnimation = isPrepareRemoteAnimation;
mDepartingWindowContainerToken = departingWindowContainerToken;
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -121,7 +114,6 @@ public final class BackNavigationInfo implements Parcelable {
mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPrepareRemoteAnimation = in.readBoolean();
mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR);
}
@Override
@@ -130,7 +122,6 @@ public final class BackNavigationInfo implements Parcelable {
dest.writeTypedObject(mOnBackNavigationDone, flags);
dest.writeStrongInterface(mOnBackInvokedCallback);
dest.writeBoolean(mPrepareRemoteAnimation);
dest.writeTypedObject(mDepartingWindowContainerToken, flags);
}
/**
@@ -163,18 +154,6 @@ public final class BackNavigationInfo implements Parcelable {
return mPrepareRemoteAnimation;
}
/**
* Returns the {@link WindowContainerToken} of the highest container in the hierarchy being
* removed.
* <p>
* For example, if an Activity is the last one of its Task, the Task's token will be given.
* Otherwise, it will be the Activity's token.
*/
@Nullable
public WindowContainerToken getDepartingWindowContainerToken() {
return mDepartingWindowContainerToken;
}
/**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
@@ -212,7 +191,6 @@ public final class BackNavigationInfo implements Parcelable {
+ "mType=" + typeToString(mType) + " (" + mType + ")"
+ ", mOnBackNavigationDone=" + mOnBackNavigationDone
+ ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
+ ", mWindowContainerToken=" + mDepartingWindowContainerToken
+ '}';
}
@@ -248,8 +226,6 @@ public final class BackNavigationInfo implements Parcelable {
@Nullable
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
private boolean mPrepareRemoteAnimation;
@Nullable
private WindowContainerToken mDepartingWindowContainerToken = null;
/**
* @see BackNavigationInfo#getType()
@@ -284,21 +260,13 @@ public final class BackNavigationInfo implements Parcelable {
return this;
}
/**
* @see BackNavigationInfo#getDepartingWindowContainerToken()
*/
public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) {
mDepartingWindowContainerToken = windowContainerToken;
}
/**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
return new BackNavigationInfo(mType, mOnBackNavigationDone,
mOnBackInvokedCallback,
mPrepareRemoteAnimation,
mDepartingWindowContainerToken);
mPrepareRemoteAnimation);
}
}
}

View File

@@ -138,8 +138,11 @@ public final class TransitionInfo implements Parcelable {
/** The container is a system window, excluding wallpaper and input-method. */
public static final int FLAG_IS_SYSTEM_WINDOW = 1 << 16;
/** The window was animated by back gesture. */
public static final int FLAG_BACK_GESTURE_ANIMATED = 1 << 17;
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
public static final int FLAG_FIRST_CUSTOM = 1 << 17;
public static final int FLAG_FIRST_CUSTOM = 1 << 18;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -165,6 +168,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_IS_BEHIND_STARTING_WINDOW,
FLAG_IS_OCCLUDED,
FLAG_IS_SYSTEM_WINDOW,
FLAG_BACK_GESTURE_ANIMATED,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -380,6 +384,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_IS_SYSTEM_WINDOW) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_SYSTEM_WINDOW");
}
if ((flags & FLAG_BACK_GESTURE_ANIMATED) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FLAG_BACK_GESTURE_ANIMATED");
}
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}

View File

@@ -2179,12 +2179,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
"-23020844": {
"message": "Back: Reset surfaces",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-21399771": {
"message": "activity %s already destroying, skipping request with reason:%s",
"level": "VERBOSE",
@@ -3823,12 +3817,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"1544805551": {
"message": "Skipping app transition animation. task=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/Task.java"
},
"1557732761": {
"message": "For Intent %s bringing to top: %s",
"level": "DEBUG",

View File

@@ -62,7 +62,6 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -82,8 +81,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
SETTING_VALUE_OFF) == SETTING_VALUE_ON;
/** Predictive back animation developer option */
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
// TODO (b/241808055) Find a appropriate time to remove during refactor
private static final boolean ENABLE_SHELL_TRANSITIONS = Transitions.ENABLE_SHELL_TRANSITIONS;
/**
* Max duration to wait for a transition to finish before accepting another gesture start
* request.
@@ -121,8 +118,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final TouchTracker mTouchTracker = new TouchTracker();
private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
private final Transitions mTransitions;
private BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
@@ -148,11 +143,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context,
Transitions transitions) {
Context context) {
this(shellInit, shellController, shellExecutor, backgroundHandler,
ActivityTaskManager.getService(), context, context.getContentResolver(),
transitions);
ActivityTaskManager.getService(), context, context.getContentResolver());
}
@VisibleForTesting
@@ -162,8 +155,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull IActivityTaskManager activityTaskManager,
Context context, ContentResolver contentResolver,
Transitions transitions) {
Context context, ContentResolver contentResolver) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -171,7 +163,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mContentResolver = contentResolver;
mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
mTransitions = transitions;
}
@VisibleForTesting
@@ -182,10 +173,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void onInit() {
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
createAdapter();
if (ENABLE_SHELL_TRANSITIONS) {
mBackTransitionHandler = new BackTransitionHandler(this);
mTransitions.addHandler(mBackTransitionHandler);
}
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
this::createExternalInterface, this);
@@ -335,17 +322,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
// In legacy transition, it would use `Task.mBackGestureStarted` in core to handle the
// following transition when back callback is invoked.
// If the back callback is not invoked, we should reset the token and finish the whole back
// navigation without waiting the transition.
if (!ENABLE_SHELL_TRANSITIONS) {
finishBackNavigation();
} else if (!mTriggerBack) {
// reset the token to prevent it consume next transition.
mBackTransitionHandler.setDepartingWindowContainerToken(null);
finishBackNavigation();
}
finishBackNavigation();
}
/**
@@ -614,10 +591,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
mTransitionInProgress = true;
if (ENABLE_SHELL_TRANSITIONS) {
mBackTransitionHandler.setDepartingWindowContainerToken(
mBackNavigationInfo.getDepartingWindowContainerToken());
}
mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
}
@@ -626,19 +599,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTransitionInProgress = false;
}
/**
* This should be called from {@link BackTransitionHandler#startAnimation} when the following
* transition is triggered by the real back callback in {@link #onBackAnimationFinished}.
* Will consume the default transition and finish current back navigation.
*/
void finishTransition(Transitions.TransitionFinishCallback finishCallback) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishTransition()");
mShellExecutor.execute(() -> {
finishBackNavigation();
finishCallback.onTransitionFinished(null, null);
});
}
private void createAdapter() {
IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
@Override

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.back;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.transition.Transitions;
class BackTransitionHandler implements Transitions.TransitionHandler {
private BackAnimationController mBackAnimationController;
private WindowContainerToken mDepartingWindowContainerToken;
BackTransitionHandler(@NonNull BackAnimationController backAnimationController) {
mBackAnimationController = backAnimationController;
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mDepartingWindowContainerToken != null) {
final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken);
if (change == null) {
return false;
}
startTransaction.hide(change.getLeash());
startTransaction.apply();
mDepartingWindowContainerToken = null;
mBackAnimationController.finishTransition(finishCallback);
return true;
}
return false;
}
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
return null;
}
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
}
void setDepartingWindowContainerToken(
@Nullable WindowContainerToken departingWindowContainerToken) {
mDepartingWindowContainerToken = departingWindowContainerToken;
}
}

View File

@@ -260,13 +260,12 @@ public abstract class WMShellBaseModule {
ShellInit shellInit,
ShellController shellController,
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler,
Transitions transitions
@ShellBackgroundThread Handler backgroundHandler
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
new BackAnimationController(shellInit, shellController, shellExecutor,
backgroundHandler, context, transitions));
backgroundHandler, context));
}
return Optional.empty();
}

View File

@@ -41,6 +41,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -395,6 +396,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
// The back gesture has animated this change before transition happen, so here we don't
// play the animation again.
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
continue;
}
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;

View File

@@ -64,7 +64,6 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Rule;
@@ -102,9 +101,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Mock
private IRemoteAnimationRunner mBackAnimationRunner;
@Mock
private Transitions mTransitions;
@Mock
private ShellController mShellController;
@@ -127,7 +123,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController = new BackAnimationController(mShellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
mContentResolver, mTransitions);
mContentResolver);
mController.setEnableUAnimation(true);
mShellInit.init();
mEventTime = 0;
@@ -225,7 +221,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController = new BackAnimationController(shellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
mContentResolver, mTransitions);
mContentResolver);
shellInit.init();
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);

View File

@@ -215,9 +215,20 @@ public class AppTransitionController {
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) {
tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController
.removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations(null);
}
}
@TransitionOldType final int transit = getTransitCompatType(
mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
mDisplayContent.mAppTransition, tmpOpenApps,
tmpCloseApps, mDisplayContent.mChangingContainers,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
mDisplayContent.mSkipAppTransitionAnimation = false;
@@ -225,22 +236,21 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"handleAppTransitionReady: displayId=%d appTransition={%s}"
+ " openingApps=[%s] closingApps=[%s] transit=%s",
mDisplayContent.mDisplayId, appTransition.toString(), mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, AppTransition.appTransitionOldToString(transit));
mDisplayContent.mDisplayId, appTransition.toString(), tmpOpenApps,
tmpCloseApps, AppTransition.appTransitionOldToString(transit));
// Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme. If all closing windows are obscured, then there is
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final ArraySet<Integer> activityTypes = collectActivityTypes(tmpOpenApps,
tmpCloseApps, mDisplayContent.mChangingContainers);
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
mDisplayContent.mChangingContainers);
tmpOpenApps, tmpCloseApps, mDisplayContent.mChangingContainers);
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
getTopApp(tmpOpenApps, false /* ignoreHidden */);
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
getTopApp(tmpCloseApps, false /* ignoreHidden */);
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
@@ -258,8 +268,7 @@ public class AppTransitionController {
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
animLp, voiceInteraction);
applyAnimations(tmpOpenApps, tmpCloseApps, transit, animLp, voiceInteraction);
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);

View File

@@ -19,6 +19,8 @@ package com.android.server.wm;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
@@ -32,6 +34,7 @@ import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Slog;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationTarget;
@@ -41,7 +44,6 @@ import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.OnBackInvokedCallbackInfo;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -60,9 +62,9 @@ class BackNavigationController {
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
// TODO (b/241808055) Find a appropriate time to remove during refactor
// Execute back animation with legacy transition system. Temporary flag for easier debugging.
static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions;
private final AnimationTargets mAnimationTargets = new AnimationTargets();
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
/**
* true if the back predictability feature is enabled
@@ -284,7 +286,6 @@ class BackNavigationController {
}
if (prepareAnimation) {
infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask));
prepareAnimationIfNeeded(currentTask, prevTask, prevActivity,
removedWindowContainer, backType, adapter);
}
@@ -303,11 +304,233 @@ class BackNavigationController {
return infoBuilder.build();
}
private static WindowContainerToken toWindowContainerToken(WindowContainer<?> windowContainer) {
if (windowContainer == null || windowContainer.mRemoteToken == null) {
return null;
boolean isWaitBackTransition() {
return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition;
}
// For legacy transition.
/**
* Once we find the transition targets match back animation targets, remove the target from
* list, so that transition won't count them in since the close animation was finished.
*
* @return {@code true} if the participants of this transition was animated by back gesture
* animations, and shouldn't join next transition.
*/
boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
ArraySet<ActivityRecord> closeApps) {
if (!isWaitBackTransition()) {
return false;
}
mTmpCloseApps.addAll(closeApps);
boolean result = false;
// Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
// mOpeningApps if there is no visibility change.
if (mAnimationTargets.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
// remove close target from close list, open target from open list;
// but the open target can be in close list.
for (int i = openApps.size() - 1; i >= 0; --i) {
final ActivityRecord ar = openApps.valueAt(i);
if (mAnimationTargets.isTarget(ar, true /* open */)) {
openApps.removeAt(i);
}
}
for (int i = closeApps.size() - 1; i >= 0; --i) {
final ActivityRecord ar = closeApps.valueAt(i);
if (mAnimationTargets.isTarget(ar, false /* open */)) {
closeApps.removeAt(i);
}
}
result = true;
}
mTmpCloseApps.clear();
return result;
}
// For shell transition
/**
* Check whether the transition targets was animated by back gesture animation.
* Because the opening target could request to do other stuff at onResume, so it could become
* close target for a transition. So the condition here is
* The closing target should only exist in close list, but the opening target can be either in
* open or close list.
* @return {@code true} if the participants of this transition was animated by back gesture
* animations, and shouldn't join next transition.
*/
boolean containsBackAnimationTargets(Transition transition) {
if (!mAnimationTargets.mComposed
|| (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) {
return false;
}
final ArraySet<WindowContainer> targets = transition.mParticipants;
for (int i = targets.size() - 1; i >= 0; --i) {
final WindowContainer wc = targets.valueAt(i);
if (wc.asActivityRecord() == null && wc.asTask() == null) {
continue;
}
// WC can be visible due to setLaunchBehind
if (wc.isVisibleRequested()) {
mTmpOpenApps.add(wc);
} else {
mTmpCloseApps.add(wc);
}
}
final boolean result = mAnimationTargets.containsBackAnimationTargets(
mTmpOpenApps, mTmpCloseApps);
mTmpOpenApps.clear();
mTmpCloseApps.clear();
return result;
}
boolean isMonitorTransitionTarget(WindowContainer wc) {
if (!mAnimationTargets.mComposed || !mAnimationTargets.mWaitTransition) {
return false;
}
return mAnimationTargets.isTarget(wc, wc.isVisibleRequested() /* open */);
}
/**
* Cleanup animation, this can either happen when transition ready or finish.
* @param cleanupTransaction The transaction which the caller want to apply the internal
* cleanup together.
*/
void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) {
mAnimationTargets.clearBackAnimateTarget(cleanupTransaction);
}
/**
* TODO: Animation composer
* prepareAnimationIfNeeded will become too complicated in order to support
* ActivityRecord/WindowState, using a factory class to create the RemoteAnimationTargets for
* different scenario.
*/
private static class AnimationTargets {
ActivityRecord mCloseTarget; // Must be activity
WindowContainer mOpenTarget; // Can be activity or task if activity was removed
private boolean mComposed;
private boolean mWaitTransition;
private int mSwitchType = UNKNOWN;
private SurfaceControl.Transaction mFinishedTransaction;
private static final int UNKNOWN = 0;
private static final int TASK_SWITCH = 1;
private static final int ACTIVITY_SWITCH = 2;
void reset(@NonNull WindowContainer close, @NonNull WindowContainer open) {
clearBackAnimateTarget(null);
if (close.asActivityRecord() != null && open.asActivityRecord() != null
&& (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) {
mSwitchType = ACTIVITY_SWITCH;
mCloseTarget = close.asActivityRecord();
} else if (close.asTask() != null && open.asTask() != null
&& close.asTask() != open.asTask()) {
mSwitchType = TASK_SWITCH;
mCloseTarget = close.asTask().getTopNonFinishingActivity();
} else {
mSwitchType = UNKNOWN;
return;
}
mOpenTarget = open;
mComposed = false;
mWaitTransition = false;
}
void composeNewAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) {
reset(close, open);
if (mSwitchType == UNKNOWN || mComposed || mCloseTarget == mOpenTarget
|| mCloseTarget == null || mOpenTarget == null) {
return;
}
mComposed = true;
mWaitTransition = false;
}
boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
for (int i = wcs.size() - 1; i >= 0; --i) {
if (isTarget(wcs.get(i), open)) {
return true;
}
}
return wcs.isEmpty();
}
boolean isTarget(WindowContainer wc, boolean open) {
if (open) {
return wc == mOpenTarget || mOpenTarget.hasChild(wc);
}
if (mSwitchType == TASK_SWITCH) {
return wc == mCloseTarget
|| (wc.asTask() != null && wc.hasChild(mCloseTarget));
} else if (mSwitchType == ACTIVITY_SWITCH) {
return wc == mCloseTarget;
}
return false;
}
boolean setFinishTransaction(SurfaceControl.Transaction finishTransaction) {
if (!mComposed) {
return false;
}
mFinishedTransaction = finishTransaction;
return true;
}
void finishPresentAnimations(SurfaceControl.Transaction t) {
if (!mComposed) {
return;
}
final SurfaceControl.Transaction pt = t != null ? t
: mOpenTarget.getPendingTransaction();
if (mFinishedTransaction != null) {
pt.merge(mFinishedTransaction);
mFinishedTransaction = null;
}
}
void clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) {
finishPresentAnimations(cleanupTransaction);
mCloseTarget = null;
mOpenTarget = null;
mComposed = false;
mWaitTransition = false;
mSwitchType = UNKNOWN;
if (mFinishedTransaction != null) {
Slog.w(TAG, "Clear back animation, found un-processed finished transaction");
if (cleanupTransaction != null) {
cleanupTransaction.merge(mFinishedTransaction);
} else {
mFinishedTransaction.apply();
}
mFinishedTransaction = null;
}
}
// The close target must in close list
// The open target can either in close or open list
boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps,
ArrayList<WindowContainer> closeApps) {
return containTarget(closeApps, false /* open */)
&& (containTarget(openApps, true /* open */)
|| containTarget(openApps, false /* open */));
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(128);
sb.append("AnimationTargets{");
sb.append(" mOpenTarget= ");
sb.append(mOpenTarget);
sb.append(" mCloseTarget= ");
sb.append(mCloseTarget);
sb.append(" mSwitchType= ");
sb.append(mSwitchType);
sb.append(" mComposed= ");
sb.append(mComposed);
sb.append(" mWaitTransition= ");
sb.append(mWaitTransition);
sb.append('}');
return sb.toString();
}
return windowContainer.mRemoteToken.toWindowContainerToken();
}
private void prepareAnimationIfNeeded(Task currentTask,
@@ -373,10 +596,6 @@ class BackNavigationController {
leashes.add(screenshotSurface);
}
} else if (prevTask != null) {
if (!ENABLE_SHELL_TRANSITIONS) {
// Special handling for preventing next transition.
currentTask.mBackGestureStarted = true;
}
prevActivity = prevTask.getTopNonFinishingActivity();
if (prevActivity != null) {
// Make previous task show from behind by marking its top activity as visible
@@ -417,35 +636,36 @@ class BackNavigationController {
for (SurfaceControl sc: leashes) {
finishedTransaction.remove(sc);
}
synchronized (mWindowManagerService.mGlobalLock) {
if (ENABLE_SHELL_TRANSITIONS) {
if (!triggerBack) {
if (!needsScreenshot(backType)) {
restoreLaunchBehind(finalPrevActivity);
}
if (triggerBack) {
final SurfaceControl surfaceControl =
removedWindowContainer.getSurfaceControl();
if (surfaceControl != null && surfaceControl.isValid()) {
// The animation is finish and start waiting for transition,
// hide the task surface before it re-parented to avoid flicker.
finishedTransaction.hide(surfaceControl);
}
} else if (!needsScreenshot(backType)) {
restoreLaunchBehind(finalPrevActivity);
}
if (!mAnimationTargets.setFinishTransaction(finishedTransaction)) {
finishedTransaction.apply();
}
if (!triggerBack) {
mAnimationTargets.clearBackAnimateTarget(null);
} else {
if (triggerBack) {
final SurfaceControl surfaceControl =
removedWindowContainer.getSurfaceControl();
if (surfaceControl != null && surfaceControl.isValid()) {
// When going back to home, hide the task surface before it
// re-parented to avoid flicker.
finishedTransaction.hide(surfaceControl);
}
} else {
currentTask.mBackGestureStarted = false;
if (!needsScreenshot(backType)) {
restoreLaunchBehind(finalPrevActivity);
}
}
mAnimationTargets.mWaitTransition = true;
}
}
finishedTransaction.apply();
// TODO Add timeout monitor if transition didn't happen
}
};
if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
mAnimationTargets.composeNewAnimations(removedWindowContainer, prevActivity);
} else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
|| backType == BackNavigationInfo.TYPE_CROSS_TASK) {
mAnimationTargets.composeNewAnimations(removedWindowContainer, prevTask);
}
scheduleAnimationLocked(backType, targets, adapter, callback);
}

View File

@@ -67,7 +67,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -607,12 +606,6 @@ class Task extends TaskFragment {
boolean mLastSurfaceShowing = true;
/**
* Tracks if a back gesture is in progress.
* Skips any system transition animations if this is set to {@code true}.
*/
boolean mBackGestureStarted = false;
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -3333,14 +3326,6 @@ class Task extends TaskFragment {
}
});
}
} else if (mBackGestureStarted) {
// Cancel playing transitions if a back navigation animation is in progress.
// This bit is set by {@link BackNavigationController} when a back gesture is started.
// It is used as a one-off transition overwrite that is cleared when the back gesture
// is committed and triggers a transition, or when the gesture is cancelled.
mBackGestureStarted = false;
mDisplayContent.mSkipAppTransitionAnimation = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this);
} else {
super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
}

View File

@@ -928,10 +928,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
// Check whether the participants were animated from back navigation.
final boolean markBackAnimated = mController.mAtm.mBackNavigationController
.containsBackAnimationTargets(this);
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges,
transaction);
if (markBackAnimated) {
mController.mAtm.mBackNavigationController.clearBackAnimations(mStartTransaction);
}
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
@@ -1935,9 +1941,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final Task task = wc.asTask();
if (task != null) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
if (topActivity != null && topActivity.mStartingData != null
&& topActivity.mStartingData.hasImeSurface()) {
flags |= FLAG_WILL_IME_SHOWN;
if (topActivity != null) {
if (topActivity.mStartingData != null
&& topActivity.mStartingData.hasImeSurface()) {
flags |= FLAG_WILL_IME_SHOWN;
}
if (topActivity.mAtmService.mBackNavigationController
.isMonitorTransitionTarget(topActivity)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
} else {
if (task.mAtmService.mBackNavigationController
.isMonitorTransitionTarget(task)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
}
if (task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
@@ -1951,6 +1968,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
flags |= FLAG_IS_VOICE_INTERACTION;
}
flags |= record.mTransitionChangeFlags;
if (record.mAtmService.mBackNavigationController
.isMonitorTransitionTarget(record)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
}
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null && task == null) {