Fix several issues with RecentsAnimation
- Prevent starting the recents animation while an app transition is already
set, if the animation was canceled before the pending start animation
could be processed, or if there are truly no visible tasks to animate
- Fix case where we were improperly cleaning up the recents animation if it
was canceled while initializing. In particular we need to move the
initialization out of the constructor for the cancel logic to work
(which checks for a non-null controller before cleaning up). Similarly
we were posting the timeout runnable after the initialization of the
recents animation, even if that resulted in the animation being canceled.
Instead, post it before initialization to ensure that canceling the
animation will also remove the timeout.
Bug: 72953248
Test: Introduce artificial delay when starting recents animation such that
it happens after starting an activity
Change-Id: Ie0ec37038822f28327e789e7e5a3c4c36f945ff9
Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
@@ -315,6 +315,17 @@ public class ActivityManagerWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the remote recents animation started from {@link #startRecentsActivity}.
|
||||
*/
|
||||
public void cancelRecentsAnimation() {
|
||||
try {
|
||||
ActivityManager.getService().cancelRecentsAnimation();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to cancel recents animation", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a task from Recents.
|
||||
*
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.OverviewProxyService;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
|
||||
@@ -269,11 +270,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
|
||||
if (doIt) {
|
||||
// If there was a pending remote recents animation, then we need to
|
||||
// cancel the animation now before we handle the button itself
|
||||
try {
|
||||
ActivityManager.getService().cancelRecentsAnimation();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not cancel recents animation", e);
|
||||
}
|
||||
ActivityManagerWrapper.getInstance().cancelRecentsAnimation();
|
||||
sendEvent(KeyEvent.ACTION_UP, 0);
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||
} else {
|
||||
|
||||
@@ -28,7 +28,9 @@ import android.app.ActivityOptions;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Trace;
|
||||
import android.util.Slog;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
|
||||
import com.android.server.wm.WindowManagerService;
|
||||
@@ -63,6 +65,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
mHandler = new Handler(mStackSupervisor.mLooper);
|
||||
mWindowManager = wm;
|
||||
mUserController = userController;
|
||||
|
||||
mCancelAnimationRunnable = () -> {
|
||||
// The caller has not finished the animation in a predefined amount of time, so
|
||||
// force-cancel the animation
|
||||
@@ -73,13 +76,33 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
|
||||
ComponentName recentsComponent, int recentsUid) {
|
||||
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
|
||||
|
||||
if (!mWindowManager.canStartRecentsAnimation()) {
|
||||
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the existing home activity is already on top, then cancel
|
||||
ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
|
||||
final boolean hasExistingHomeActivity = homeActivity != null;
|
||||
if (hasExistingHomeActivity) {
|
||||
final ActivityDisplay display = homeActivity.getDisplay();
|
||||
mRestoreHomeBehindStack = display.getStackAboveHome();
|
||||
if (mRestoreHomeBehindStack == null) {
|
||||
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mWindowManager.deferSurfaceLayout();
|
||||
try {
|
||||
// Cancel the previous recents animation if necessary
|
||||
mWindowManager.cancelRecentsAnimation();
|
||||
|
||||
final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
|
||||
if (!hasExistingHomeActivity) {
|
||||
final ActivityDisplay display;
|
||||
if (hasExistingHomeActivity) {
|
||||
// Move the home activity into place for the animation if it is not already top most
|
||||
display = homeActivity.getDisplay();
|
||||
display.moveHomeStackBehindBottomMostVisibleStack();
|
||||
} else {
|
||||
// No home activity
|
||||
final ActivityOptions opts = ActivityOptions.makeBasic();
|
||||
opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
|
||||
@@ -95,25 +118,20 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
.execute();
|
||||
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
|
||||
|
||||
homeActivity = mStackSupervisor.getHomeActivity();
|
||||
display = homeActivity.getDisplay();
|
||||
|
||||
// TODO: Maybe wait for app to draw in this particular case?
|
||||
}
|
||||
|
||||
final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
|
||||
final ActivityDisplay display = homeActivity.getDisplay();
|
||||
|
||||
// Save the initial position of the home activity stack to be restored to after the
|
||||
// animation completes
|
||||
mRestoreHomeBehindStack = hasExistingHomeActivity
|
||||
? display.getStackAboveHome()
|
||||
: null;
|
||||
|
||||
// Move the home activity into place for the animation
|
||||
display.moveHomeStackBehindBottomMostVisibleStack();
|
||||
|
||||
// Mark the home activity as launch-behind to bump its visibility for the
|
||||
// duration of the gesture that is driven by the recents component
|
||||
homeActivity.mLaunchTaskBehind = true;
|
||||
|
||||
// Post a timeout for the animation. This needs to happen before initializing the
|
||||
// recents animation on the WM side since we may decide to cancel the animation there
|
||||
mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
|
||||
|
||||
// Fetch all the surface controls and pass them to the client to get the animation
|
||||
// started
|
||||
mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
|
||||
@@ -122,9 +140,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
// If we updated the launch-behind state, update the visibility of the activities after
|
||||
// we fetch the visible tasks to be controlled by the animation
|
||||
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
|
||||
|
||||
// Post a timeout for the animation
|
||||
mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
|
||||
} finally {
|
||||
mWindowManager.continueSurfaceLayout();
|
||||
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
|
||||
@@ -178,4 +193,15 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only when the animation should be canceled prior to starting.
|
||||
*/
|
||||
private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
|
||||
try {
|
||||
recentsAnimationRunner.onAnimationCanceled();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation before start", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class RecentsAnimationController {
|
||||
private final IRecentsAnimationRunner mRunner;
|
||||
private final RecentsAnimationCallbacks mCallbacks;
|
||||
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
|
||||
private final int mDisplayId;
|
||||
|
||||
// The recents component app token that is shown behind the visibile tasks
|
||||
private AppWindowToken mHomeAppToken;
|
||||
@@ -159,8 +160,6 @@ public class RecentsAnimationController {
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new RecentsAnimationController.
|
||||
*
|
||||
* @param remoteAnimationRunner The remote runner which should be notified when the animation is
|
||||
* ready to start or has been canceled
|
||||
* @param callbacks Callbacks to be made when the animation finishes
|
||||
@@ -171,16 +170,19 @@ public class RecentsAnimationController {
|
||||
mService = service;
|
||||
mRunner = remoteAnimationRunner;
|
||||
mCallbacks = callbacks;
|
||||
mDisplayId = displayId;
|
||||
}
|
||||
|
||||
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
|
||||
final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
|
||||
if (visibleTasks.isEmpty()) {
|
||||
cancelAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the recents animation controller. This is a separate call from the constructor
|
||||
* because it may call cancelAnimation() which needs to properly clean up the controller
|
||||
* in the window manager.
|
||||
*/
|
||||
public void initialize() {
|
||||
// Make leashes for each of the visible tasks and add it to the recents animation to be
|
||||
// started
|
||||
final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
|
||||
final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
|
||||
final int taskCount = visibleTasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
final Task task = visibleTasks.get(i);
|
||||
@@ -193,6 +195,12 @@ public class RecentsAnimationController {
|
||||
addAnimation(task);
|
||||
}
|
||||
|
||||
// Skip the animation if there is nothing to animate
|
||||
if (mPendingAnimations.isEmpty()) {
|
||||
cancelAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the wallpaper visibility for the showing home activity
|
||||
final AppWindowToken recentsComponentAppToken =
|
||||
dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
|
||||
@@ -222,8 +230,10 @@ public class RecentsAnimationController {
|
||||
}
|
||||
|
||||
void startAnimation() {
|
||||
if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
|
||||
if (!mPendingStart) {
|
||||
if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
|
||||
+ " mCanceled=" + mCanceled);
|
||||
if (!mPendingStart || mCanceled) {
|
||||
// Skip starting if we've already started or canceled the animation
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -2680,6 +2680,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
cancelRecentsAnimation();
|
||||
mRecentsAnimationController = new RecentsAnimationController(this,
|
||||
recentsAnimationRunner, callbacks, displayId);
|
||||
mRecentsAnimationController.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2687,6 +2688,19 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return mRecentsAnimationController;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the next recents animation can continue to start. Called from
|
||||
* {@link RecentsAnimation#startRecentsActivity}.
|
||||
*/
|
||||
public boolean canStartRecentsAnimation() {
|
||||
synchronized (mWindowMap) {
|
||||
if (mAppTransition.isTransitionSet()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelRecentsAnimation() {
|
||||
synchronized (mWindowMap) {
|
||||
if (mRecentsAnimationController != null) {
|
||||
|
||||
Reference in New Issue
Block a user