Merge "Snapshot all tasks in pending recents animation when rotating devices" into sc-v2-dev
This commit is contained in:
@@ -25,7 +25,6 @@ import static org.hamcrest.core.Is.is;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -41,6 +40,7 @@ import android.util.Pair;
|
||||
import android.view.IRecentsAnimationController;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.lifecycle.Stage;
|
||||
@@ -210,7 +210,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
|
||||
public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots)
|
||||
throws RemoteException {
|
||||
Assume.assumeNoException(
|
||||
new AssertionError("onAnimationCanceled should not be called"));
|
||||
}
|
||||
|
||||
@@ -35,15 +35,17 @@ oneway interface IRecentsAnimationRunner {
|
||||
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
|
||||
* amount of time.
|
||||
*
|
||||
* @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).
|
||||
* @param taskIds Indicates tasks with cancelling snapshot.
|
||||
* @param taskSnapshots If the snapshots is null, the animation will be cancelled and the leash
|
||||
* will be inactive immediately. Otherwise, the contents of the tasks will
|
||||
* be replaced with {@param taskSnapshots}, 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}
|
||||
*/
|
||||
void onAnimationCanceled(in @nullable TaskSnapshot taskSnapshot) = 1;
|
||||
void onAnimationCanceled(in @nullable int[] taskIds,
|
||||
in @nullable TaskSnapshot[] taskSnapshots) = 1;
|
||||
|
||||
/**
|
||||
* Called when the system is ready for the handler to start animating all the visible tasks.
|
||||
|
||||
@@ -22,7 +22,6 @@ import static android.graphics.Bitmap.Config.ARGB_8888;
|
||||
|
||||
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.window.TaskSnapshot;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
@@ -30,6 +29,9 @@ import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.util.Log;
|
||||
import android.view.WindowInsetsController.Appearance;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Data for a single thumbnail.
|
||||
@@ -80,6 +82,18 @@ public class ThumbnailData {
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) {
|
||||
HashMap<Integer, ThumbnailData> temp = new HashMap<>();
|
||||
if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) {
|
||||
return temp;
|
||||
}
|
||||
|
||||
for (int i = snapshots.length - 1; i >= 0; i--) {
|
||||
temp.put(taskIds[i], new ThumbnailData(snapshots[i]));
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
public ThumbnailData(TaskSnapshot snapshot) {
|
||||
thumbnail = makeThumbnail(snapshot);
|
||||
insets = new Rect(snapshot.getContentInsets());
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.app.ActivityClient;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RecentTaskInfo;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.AppGlobals;
|
||||
@@ -50,6 +49,7 @@ import android.util.Log;
|
||||
import android.view.IRecentsAnimationController;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.app.IVoiceInteractionManagerService;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
@@ -189,9 +189,9 @@ public class ActivityManagerWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCanceled(TaskSnapshot taskSnapshot) {
|
||||
public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
|
||||
animationHandler.onAnimationCanceled(
|
||||
taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null);
|
||||
ThumbnailData.wrap(taskIds, taskSnapshots));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,12 +39,14 @@ public class RecentsAnimationControllerCompat {
|
||||
|
||||
public ThumbnailData screenshotTask(int taskId) {
|
||||
try {
|
||||
TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
|
||||
return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData();
|
||||
final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
|
||||
if (snapshot != null) {
|
||||
return new ThumbnailData(snapshot);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to screenshot task", e);
|
||||
return new ThumbnailData();
|
||||
}
|
||||
return new ThumbnailData();
|
||||
}
|
||||
|
||||
public void setInputConsumerEnabled(boolean enabled) {
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.graphics.Rect;
|
||||
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public interface RecentsAnimationListener {
|
||||
/**
|
||||
* Called when the animation into Recents can start. This call is made on the binder thread.
|
||||
@@ -31,7 +33,7 @@ public interface RecentsAnimationListener {
|
||||
/**
|
||||
* Called when the animation into Recents was canceled. This call is made on the binder thread.
|
||||
*/
|
||||
void onAnimationCanceled(ThumbnailData thumbnailData);
|
||||
void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas);
|
||||
|
||||
/**
|
||||
* Called when the task of an activity that has been started while the recents animation
|
||||
|
||||
@@ -473,7 +473,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
|
||||
*/
|
||||
static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
|
||||
try {
|
||||
recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */);
|
||||
recentsAnimationRunner.onAnimationCanceled(null /* taskIds */,
|
||||
null /* taskSnapshots */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation before start", e);
|
||||
}
|
||||
|
||||
@@ -853,37 +853,37 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
mCanceled = true;
|
||||
|
||||
if (screenshot && !mPendingAnimations.isEmpty()) {
|
||||
final TaskAnimationAdapter adapter = mPendingAnimations.get(0);
|
||||
final Task task = adapter.mTask;
|
||||
// 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 TaskSnapshot taskSnapshot = screenshotRecentTask(task);
|
||||
final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
|
||||
mPendingCancelWithScreenshotReorderMode = reorderMode;
|
||||
try {
|
||||
mRunner.onAnimationCanceled(taskSnapshot);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation", e);
|
||||
}
|
||||
if (taskSnapshot != null) {
|
||||
// Defer until the runner calls back to cleanupScreenshot()
|
||||
adapter.setSnapshotOverlay(taskSnapshot);
|
||||
|
||||
if (!snapshotMap.isEmpty()) {
|
||||
try {
|
||||
int[] taskIds = new int[snapshotMap.size()];
|
||||
TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
|
||||
for (int i = snapshotMap.size() - 1; i >= 0; i--) {
|
||||
taskIds[i] = snapshotMap.keyAt(i).mTaskId;
|
||||
snapshots[i] = snapshotMap.valueAt(i);
|
||||
}
|
||||
mRunner.onAnimationCanceled(taskIds, snapshots);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation", e);
|
||||
}
|
||||
// Schedule a new failsafe for if the runner doesn't clean up the screenshot
|
||||
scheduleFailsafe();
|
||||
} else {
|
||||
// Do a normal cancel since we couldn't screenshot
|
||||
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
|
||||
return;
|
||||
}
|
||||
} 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(null /* taskSnapshot */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation", e);
|
||||
}
|
||||
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
|
||||
// Fallback to a normal cancel since we couldn't screenshot
|
||||
}
|
||||
|
||||
// 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(null /* taskIds */, null /* taskSnapshots */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to cancel recents animation", e);
|
||||
}
|
||||
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,13 +957,23 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
|
||||
}
|
||||
|
||||
TaskSnapshot screenshotRecentTask(Task task) {
|
||||
private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
|
||||
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
|
||||
final ArraySet<Task> tasks = Sets.newArraySet(task);
|
||||
snapshotController.snapshotTasks(tasks);
|
||||
snapshotController.addSkipClosingAppSnapshotTasks(tasks);
|
||||
return snapshotController.getSnapshot(task.mTaskId, task.mUserId,
|
||||
false /* restoreFromDisk */, false /* isLowResolution */);
|
||||
final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
|
||||
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
|
||||
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
|
||||
final Task task = adapter.mTask;
|
||||
snapshotController.recordTaskSnapshot(task, false /* allowSnapshotHome */);
|
||||
final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
|
||||
false /* restoreFromDisk */, false /* isLowResolution */);
|
||||
if (snapshot != null) {
|
||||
snapshotMap.put(task, snapshot);
|
||||
// Defer until the runner calls back to cleanupScreenshot()
|
||||
adapter.setSnapshotOverlay(snapshot);
|
||||
}
|
||||
}
|
||||
snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet());
|
||||
return snapshotMap;
|
||||
}
|
||||
|
||||
void cleanupAnimation(@ReorderMode int reorderMode) {
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.android.server.wm.utils.InsetUtils;
|
||||
import com.google.android.collect.Sets;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
|
||||
@@ -167,7 +168,7 @@ class TaskSnapshotController {
|
||||
* calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
|
||||
void addSkipClosingAppSnapshotTasks(Set<Task> tasks) {
|
||||
if (shouldDisableSnapshots()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
// Verify that the finish callback to reparent the leash is called
|
||||
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter));
|
||||
// Verify the animation canceled callback to the app was made
|
||||
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
|
||||
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
wallpaperWindowToken.cancelAnimation();
|
||||
assertTrue(mController.isAnimatingTask(activity.getTask()));
|
||||
assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
|
||||
verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */);
|
||||
verify(mMockRunner, never()).onAnimationCanceled(null /* taskIds */,
|
||||
null /* taskSnapshots */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -254,7 +255,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
|
||||
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
|
||||
mController.cancelAnimationWithScreenshot(false /* screenshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
|
||||
|
||||
// Simulate the app transition finishing
|
||||
mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
|
||||
@@ -281,7 +282,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
|
||||
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
|
||||
mController.cancelAnimationWithScreenshot(true /* screenshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(any(int[].class) /* taskIds */,
|
||||
any(TaskSnapshot[].class) /* taskSnapshots */);
|
||||
|
||||
// Continue the animation (simulating a call to cleanupScreenshot())
|
||||
mController.continueDeferredCancelAnimation();
|
||||
@@ -322,7 +324,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
|
||||
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
|
||||
mController.cancelAnimationWithScreenshot(true /* screenshot */);
|
||||
verify(mMockRunner).onAnimationCanceled(any());
|
||||
verify(mMockRunner).onAnimationCanceled(any(), any());
|
||||
|
||||
// Simulate process crashing and ensure the animation is still canceled
|
||||
mController.binderDied();
|
||||
@@ -700,7 +702,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
mController.setWillFinishToHome(true);
|
||||
mController.cancelAnimationForDisplayChange();
|
||||
|
||||
verify(mMockRunner).onAnimationCanceled(any());
|
||||
verify(mMockRunner).onAnimationCanceled(any(), any());
|
||||
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
|
||||
}
|
||||
|
||||
@@ -715,7 +717,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
mController.setWillFinishToHome(false);
|
||||
mController.cancelAnimationForDisplayChange();
|
||||
|
||||
verify(mMockRunner).onAnimationCanceled(any());
|
||||
verify(mMockRunner).onAnimationCanceled(any(), any());
|
||||
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
|
||||
}
|
||||
|
||||
@@ -735,7 +737,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
|
||||
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
|
||||
mController.cancelAnimationForHomeStart();
|
||||
verify(mMockRunner).onAnimationCanceled(any());
|
||||
verify(mMockRunner).onAnimationCanceled(any(), any());
|
||||
|
||||
// Continue the animation (simulating a call to cleanupScreenshot())
|
||||
mController.continueDeferredCancelAnimation();
|
||||
|
||||
Reference in New Issue
Block a user