Merge "Pass the captured snapshot data to Launcher when recents animation is cancelled."

This commit is contained in:
Tracy Zhou
2019-08-11 21:57:27 +00:00
committed by Android (Google) Code Review
7 changed files with 58 additions and 45 deletions

View File

@@ -26,6 +26,7 @@ import static org.hamcrest.core.AnyOf.anyOf;
import static org.hamcrest.core.Is.is;
import android.app.Activity;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
@@ -216,7 +217,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
}
@Override
public void onAnimationCanceled(boolean deferredWithScreenshot) throws RemoteException {
public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
Assume.assumeNoException(
new AssertionError("onAnimationCanceled should not be called"));
}

View File

@@ -16,6 +16,7 @@
package android.view;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
@@ -33,16 +34,16 @@ oneway interface IRecentsAnimationRunner {
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*
* @param deferredWithScreenshot If set to {@code true}, the contents of the task will be
* replaced with a screenshot, such that the runner's leash is
* still active. As soon as the runner doesn't need the leash
* anymore, it must call
* {@link IRecentsAnimationController#cleanupScreenshot).
* @param taskSnapshot If the snapshot is null, the animation will be cancelled and the leash
* will be inactive immediately. Otherwise, the contents of the task will be
* replaced with {@param taskSnapshot}, such that the runner's leash is
* still active. As soon as the runner doesn't need the leash anymore, it
* must call {@link IRecentsAnimationController#cleanupScreenshot).
*
* @see {@link RecentsAnimationController#cleanupScreenshot}
*/
@UnsupportedAppUsage
void onAnimationCanceled(boolean deferredWithScreenshot) = 1;
void onAnimationCanceled(in @nullable ActivityManager.TaskSnapshot taskSnapshot) = 1;
/**
* Called when the system is ready for the handler to start animating all the visible tasks.

View File

@@ -31,6 +31,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
@@ -235,9 +236,9 @@ public class ActivityManagerWrapper {
}
@Override
public void onAnimationCanceled(boolean deferredWithScreenshot) {
public void onAnimationCanceled(TaskSnapshot taskSnapshot) {
animationHandler.onAnimationCanceled(
deferredWithScreenshot ? new ThumbnailData() : null);
taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null);
}
};
}

View File

@@ -460,7 +460,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
*/
static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
try {
recentsAnimationRunner.onAnimationCanceled(false /* deferredWithScreenshot */);
recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation before start", e);
}

View File

@@ -23,8 +23,8 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
@@ -525,18 +525,23 @@ public class RecentsAnimationController implements DeathRecipient {
// Screen shot previous task when next task starts transition and notify the runner.
// We will actually finish the animation once the runner calls cleanUpScreenshot().
final Task task = mPendingAnimations.get(0).mTask;
screenshotRecentTask(task, reorderMode, runSynchronously);
final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode,
runSynchronously);
try {
mRunner.onAnimationCanceled(true /* deferredWithScreenshot */);
mRunner.onAnimationCanceled(taskSnapshot);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
if (taskSnapshot == null) {
mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
false /* sendUserLeaveHint */);
}
} else {
// Otherwise, notify the runner and clean up the animation immediately
// Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
// to the runner if we this actually triggers cancel twice on the caller
try {
mRunner.onAnimationCanceled(false /* deferredWithScreenshot */);
mRunner.onAnimationCanceled(null /* taskSnapshot */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
@@ -587,20 +592,32 @@ public class RecentsAnimationController implements DeathRecipient {
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
}
void screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously) {
final TaskScreenshotAnimatable animatable = TaskScreenshotAnimatable.create(task);
if (animatable != null) {
mRecentScreenshotAnimator = new SurfaceAnimator(
animatable,
() -> {
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "mRecentScreenshotAnimator finish");
}
mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode,
boolean runSynchronously) {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
snapshotController.addSkipClosingAppSnapshotTasks(tasks);
final TaskSnapshot taskSnapshot = snapshotController.getSnapshot(task.mTaskId,
task.mUserId, false /* restoreFromDisk */, false /* reducedResolution */);
if (taskSnapshot == null) {
return null;
}
final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(task,
new SurfaceControl.ScreenshotGraphicBuffer(taskSnapshot.getSnapshot(),
taskSnapshot.getColorSpace(), false /* containsSecureLayers */));
mRecentScreenshotAnimator = new SurfaceAnimator(
animatable,
() -> {
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "mRecentScreenshotAnimator finish");
}
mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
return taskSnapshot;
}
void cleanupAnimation(@ReorderMode int reorderMode) {

View File

@@ -18,7 +18,6 @@ package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -37,20 +36,7 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
private int mWidth;
private int mHeight;
public static TaskScreenshotAnimatable create(Task task) {
return new TaskScreenshotAnimatable(task, getBufferFromTask(task));
}
private static SurfaceControl.ScreenshotGraphicBuffer getBufferFromTask(Task task) {
if (task == null) {
return null;
}
final Rect tmpRect = task.getBounds();
tmpRect.offset(0, 0);
return SurfaceControl.captureLayers(task.getSurfaceControl(), tmpRect, 1f);
}
private TaskScreenshotAnimatable(Task task,
TaskScreenshotAnimatable(Task task,
SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
GraphicBuffer buffer = screenshotBuffer == null
? null : screenshotBuffer.getGraphicBuffer();

View File

@@ -44,6 +44,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import android.app.ActivityManager.TaskSnapshot;
import android.os.Binder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
@@ -73,6 +74,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Mock OnAnimationFinishedCallback mFinishedCallback;
@Mock IRecentsAnimationRunner mMockRunner;
@Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks;
@Mock TaskSnapshot mMockTaskSnapshot;
private RecentsAnimationController mController;
@Before
@@ -104,7 +106,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
// Verify that the finish callback to reparent the leash is called
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
// Verify the animation canceled callback to the app was made
verify(mMockRunner).onAnimationCanceled(false);
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -162,7 +164,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */);
verify(mMockRunner).onAnimationCanceled(false /* deferredWithScreenshot */);
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
assertNull(mController.mRecentScreenshotAnimator);
// Simulate the app transition finishing
@@ -183,9 +185,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
assertTrue(mController.isAnimatingTask(appWindow.getTask()));
spyOn(mWm.mTaskSnapshotController);
doNothing().when(mWm.mTaskSnapshotController).notifyAppVisibilityChanged(any(),
anyBoolean());
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* reducedResolution */);
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
mController.cancelAnimationWithScreenshot(true /* screenshot */);
verify(mMockRunner).onAnimationCanceled(true /* deferredWithScreenshot */);
verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */);
assertNotNull(mController.mRecentScreenshotAnimator);
assertTrue(mController.mRecentScreenshotAnimator.isAnimating());