Merge "Generalize back animation and transition sequence..."
This commit is contained in:
committed by
Android (Google) Code Review
commit
827093037e
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user