Merge "Optimize hot launching recents" into oc-dev

This commit is contained in:
Jorim Jaggi
2017-05-16 19:34:45 +00:00
committed by Android (Google) Code Review
6 changed files with 181 additions and 152 deletions

View File

@@ -38,6 +38,7 @@ import android.transition.TransitionManager;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.view.AppTransitionAnimationSpec; import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
@@ -213,6 +214,7 @@ public class ActivityOptions {
private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
= "android:instantapps.installerbundle"; = "android:instantapps.installerbundle";
private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
/** @hide */ /** @hide */
public static final int ANIM_NONE = 0; public static final int ANIM_NONE = 0;
@@ -268,6 +270,7 @@ public class ActivityOptions {
private AppTransitionAnimationSpec mAnimSpecs[]; private AppTransitionAnimationSpec mAnimSpecs[];
private int mRotationAnimationHint = -1; private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle; private Bundle mAppVerificationBundle;
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
/** /**
* Create an ActivityOptions specifying a custom animation to run when * Create an ActivityOptions specifying a custom animation to run when
@@ -492,35 +495,12 @@ public class ActivityOptions {
* is not executed, the callback will happen immediately. * is not executed, the callback will happen immediately.
* @return Returns a new ActivityOptions object that you can use to * @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity. * supply these options as the options Bundle when starting an activity.
* @hide
*/ */
public static ActivityOptions makeThumbnailScaleUpAnimation(View source, private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true); return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
} }
/**
* Create an ActivityOptions specifying an animation where an activity window
* is scaled from a given position to a thumbnail at a specified location.
*
* @param source The View that this thumbnail is animating to. This
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the final thumbnail
* of the animation.
* @param startX The x end location of the bitmap, relative to <var>source</var>.
* @param startY The y end location of the bitmap, relative to <var>source</var>.
* @param listener Optional OnAnimationStartedListener to find out when the
* requested animation has started running. If for some reason the animation
* is not executed, the callback will happen immediately.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
* @hide
*/
public static ActivityOptions makeThumbnailScaleDownAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false);
}
private static ActivityOptions makeThumbnailAnimation(View source, private static ActivityOptions makeThumbnailAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
boolean scaleUp) { boolean scaleUp) {
@@ -537,29 +517,21 @@ public class ActivityOptions {
} }
/** /**
* Create an ActivityOptions specifying an animation where the new activity * Create an ActivityOptions specifying an animation where a list of activity windows and
* window and a thumbnail is aspect-scaled to a new location. * thumbnails are aspect scaled to/from a new location.
*
* @param source The View that this thumbnail is animating from. This
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the initial thumbnail
* of the animation.
* @param startX The x starting location of the bitmap, relative to <var>source</var>.
* @param startY The y starting location of the bitmap, relative to <var>source</var>.
* @param handler If <var>listener</var> is non-null this must be a valid
* Handler on which to dispatch the callback; otherwise it should be null.
* @param listener Optional OnAnimationStartedListener to find out when the
* requested animation has started running. If for some reason the animation
* is not executed, the callback will happen immediately.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
* @hide * @hide
*/ */
public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source, public static ActivityOptions makeMultiThumbFutureAspectScaleAnimation(Context context,
Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, Handler handler, IAppTransitionAnimationSpecsFuture specsFuture,
Handler handler, OnAnimationStartedListener listener) { OnAnimationStartedListener listener, boolean scaleUp) {
return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, ActivityOptions opts = new ActivityOptions();
targetWidth, targetHeight, handler, listener, true); opts.mPackageName = context.getPackageName();
opts.mAnimationType = scaleUp
? ANIM_THUMBNAIL_ASPECT_SCALE_UP
: ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
opts.mSpecsFuture = specsFuture;
opts.setOnAnimationStartedListener(handler, listener);
return opts;
} }
/** /**
@@ -891,6 +863,10 @@ public class ActivityOptions {
} }
mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT); mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE); mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE);
if (opts.containsKey(KEY_SPECS_FUTURE)) {
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
KEY_SPECS_FUTURE));
}
} }
/** /**
@@ -1028,6 +1004,11 @@ public class ActivityOptions {
/** @hide */ /** @hide */
public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; } public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
/** @hide */
public IAppTransitionAnimationSpecsFuture getSpecsFuture() {
return mSpecsFuture;
}
/** @hide */ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) { public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null; return bOptions != null ? new ActivityOptions(bOptions) : null;
@@ -1205,6 +1186,7 @@ public class ActivityOptions {
} }
mAnimSpecs = otherOptions.mAnimSpecs; mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
} }
/** /**
@@ -1279,6 +1261,9 @@ public class ActivityOptions {
if (mAnimationFinishedListener != null) { if (mAnimationFinishedListener != null) {
b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder()); b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
} }
if (mSpecsFuture != null) {
b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder());
}
b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint); b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
if (mAppVerificationBundle != null) { if (mAppVerificationBundle != null) {
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle); b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);

View File

@@ -477,7 +477,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Launch the task // Launch the task
ssp.startActivityFromRecents( ssp.startActivityFromRecents(
mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID); mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
null /* resultListener */);
} }
/** /**
@@ -550,7 +551,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Launch the task // Launch the task
ssp.startActivityFromRecents( ssp.startActivityFromRecents(
mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID); mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
null /* resultListener */);
} }
public void showNextAffiliatedTask() { public void showNextAffiliatedTask() {

View File

@@ -26,6 +26,7 @@ import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo; import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityManager.TaskSnapshot;
@@ -70,6 +71,7 @@ import android.util.ArraySet;
import android.util.IconDrawableFactory; import android.util.IconDrawableFactory;
import android.util.Log; import android.util.Log;
import android.util.MutableBoolean; import android.util.MutableBoolean;
import android.view.AppTransitionAnimationSpec;
import android.view.Display; import android.view.Display;
import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener; import android.view.IDockedStackListener;
@@ -89,6 +91,7 @@ import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl; import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.ThumbnailData; import com.android.systemui.recents.model.ThumbnailData;
import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -1116,31 +1119,44 @@ public class SystemServicesProxy {
} }
/** Starts an activity from recents. */ /** Starts an activity from recents. */
public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
ActivityOptions options, int stackId) { ActivityOptions options, int stackId,
if (mIam != null) { @Nullable final StartActivityFromRecentsResultListener resultListener) {
try { if (mIam == null) {
if (taskKey.stackId == DOCKED_STACK_ID) { return;
// We show non-visible docked tasks in Recents, but we always want to launch
// them in the fullscreen stack.
if (options == null) {
options = ActivityOptions.makeBasic();
}
options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
} else if (stackId != INVALID_STACK_ID){
if (options == null) {
options = ActivityOptions.makeBasic();
}
options.setLaunchStackId(stackId);
}
mIam.startActivityFromRecents(
taskKey.id, options == null ? null : options.toBundle());
return true;
} catch (Exception e) {
Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
}
} }
return false; if (taskKey.stackId == DOCKED_STACK_ID) {
// We show non-visible docked tasks in Recents, but we always want to launch
// them in the fullscreen stack.
if (options == null) {
options = ActivityOptions.makeBasic();
}
options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
} else if (stackId != INVALID_STACK_ID) {
if (options == null) {
options = ActivityOptions.makeBasic();
}
options.setLaunchStackId(stackId);
}
final ActivityOptions finalOptions = options;
// Execute this from another thread such that we can do other things (like caching the
// bitmap for the thumbnail) while AM is busy starting our activity.
mOnewayExecutor.submit(() -> {
try {
mIam.startActivityFromRecents(
taskKey.id, finalOptions == null ? null : finalOptions.toBundle());
if (resultListener != null) {
mHandler.post(() -> resultListener.onStartActivityResult(true));
}
} catch (Exception e) {
Log.e(TAG, context.getString(
R.string.recents_launch_error_message, taskName), e);
if (resultListener != null) {
mHandler.post(() -> resultListener.onStartActivityResult(false));
}
}
});
} }
/** Starts an in-place animation on the front most application windows. */ /** Starts an in-place animation on the front most application windows. */
@@ -1258,6 +1274,10 @@ public class SystemServicesProxy {
} }
} }
public interface StartActivityFromRecentsResultListener {
void onStartActivityResult(boolean succeeded);
}
private final class H extends Handler { private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_TASK_SNAPSHOT_CHANGED = 2;

View File

@@ -101,57 +101,49 @@ public class RecentsTransitionHelper {
*/ */
public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
final TaskStackView stackView, final TaskView taskView, final TaskStackView stackView, final TaskView taskView,
final boolean screenPinningRequested, final Rect bounds, final int destinationStack) { final boolean screenPinningRequested, final int destinationStack) {
final ActivityOptions opts = ActivityOptions.makeBasic();
if (bounds != null) {
opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions.OnAnimationStartedListener animStartedListener; final ActivityOptions.OnAnimationStartedListener animStartedListener;
final IAppTransitionAnimationSpecsFuture transitionFuture; final AppTransitionAnimationSpecsFuture transitionFuture;
if (taskView != null) { if (taskView != null) {
transitionFuture = getAppTransitionFuture(new AnimationSpecComposer() {
@Override
public List<AppTransitionAnimationSpec> composeSpecs() {
return composeAnimationSpecs(task, stackView, destinationStack);
}
});
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
// If we are launching into another task, cancel the previous task's
// window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
stackView.cancelAllTaskViewAnimations();
if (screenPinningRequested) { // Fetch window rect here already in order not to be blocked on lock contention in WM
// Request screen pinning after the animation runs // when the future calls it.
mStartScreenPinningRunnable.taskId = task.key.id; final Rect windowRect = Recents.getSystemServices().getWindowRect();
mHandler.postDelayed(mStartScreenPinningRunnable, 350); transitionFuture = getAppTransitionFuture(
} () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect));
animStartedListener = () -> {
// If we are launching into another task, cancel the previous task's
// window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
stackView.cancelAllTaskViewAnimations();
if (screenPinningRequested) {
// Request screen pinning after the animation runs
mStartScreenPinningRunnable.taskId = task.key.id;
mHandler.postDelayed(mStartScreenPinningRunnable, 350);
} }
}; };
} else { } else {
// This is only the case if the task is not on screen (scrolled offscreen for example) // This is only the case if the task is not on screen (scrolled offscreen for example)
transitionFuture = null; transitionFuture = null;
animStartedListener = new ActivityOptions.OnAnimationStartedListener() { animStartedListener = () -> {
@Override // If we are launching into another task, cancel the previous task's
public void onAnimationStarted() { // window transition
// If we are launching into another task, cancel the previous task's EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
// window transition EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); stackView.cancelAllTaskViewAnimations();
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
stackView.cancelAllTaskViewAnimations();
}
}; };
} }
final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
mHandler, transitionFuture != null ? transitionFuture.future : null,
animStartedListener, true /* scaleUp */);
if (taskView == null) { if (taskView == null) {
// If there is no task view, then we do not need to worry about animating out occluding // If there is no task view, then we do not need to worry about animating out occluding
// task views, and we can launch immediately // task views, and we can launch immediately
startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener, startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack);
destinationStack);
} else { } else {
LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView, LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
screenPinningRequested); screenPinningRequested);
@@ -160,14 +152,13 @@ public class RecentsTransitionHelper {
@Override @Override
public void run() { public void run() {
startTaskActivity(stack, task, taskView, opts, transitionFuture, startTaskActivity(stack, task, taskView, opts, transitionFuture,
animStartedListener, destinationStack); destinationStack);
} }
}); });
EventBus.getDefault().send(launchStartedEvent); EventBus.getDefault().send(launchStartedEvent);
} else { } else {
EventBus.getDefault().send(launchStartedEvent); EventBus.getDefault().send(launchStartedEvent);
startTaskActivity(stack, task, taskView, opts, transitionFuture, startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack);
animStartedListener, destinationStack);
} }
} }
Recents.getSystemServices().sendCloseSystemWindows( Recents.getSystemServices().sendCloseSystemWindows(
@@ -199,30 +190,31 @@ public class RecentsTransitionHelper {
* @param destinationStack id of the stack to put the task into. * @param destinationStack id of the stack to put the task into.
*/ */
private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView, private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture, ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
final OnAnimationStartedListener animStartedListener, int destinationStack) { int destinationStack) {
SystemServicesProxy ssp = Recents.getSystemServices(); SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts, destinationStack)) { ssp.startActivityFromRecents(mContext, task.key, task.title, opts, destinationStack,
// Keep track of the index of the task launch succeeded -> {
int taskIndexFromFront = 0; if (succeeded) {
int taskIndex = stack.indexOfStackTask(task); // Keep track of the index of the task launch
if (taskIndex > -1) { int taskIndexFromFront = 0;
taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; int taskIndex = stack.indexOfStackTask(task);
} if (taskIndex > -1) {
EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront)); taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
} else { }
// Dismiss the task if we fail to launch it EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
if (taskView != null) { } else {
taskView.dismissTask(); // Dismiss the task if we fail to launch it
} if (taskView != null) {
taskView.dismissTask();
// Keep track of failed launches }
EventBus.getDefault().send(new LaunchTaskFailedEvent());
}
// Keep track of failed launches
EventBus.getDefault().send(new LaunchTaskFailedEvent());
}
});
if (transitionFuture != null) { if (transitionFuture != null) {
ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture, mHandler.post(transitionFuture::precacheSpecs);
wrapStartedListener(animStartedListener), true /* scaleUp */);
} }
} }
@@ -231,21 +223,18 @@ public class RecentsTransitionHelper {
* *
* @param composer The implementation that composes the specs on the UI thread. * @param composer The implementation that composes the specs on the UI thread.
*/ */
public IAppTransitionAnimationSpecsFuture getAppTransitionFuture( public AppTransitionAnimationSpecsFuture getAppTransitionFuture(
final AnimationSpecComposer composer) { final AnimationSpecComposer composer) {
synchronized (this) { synchronized (this) {
mAppTransitionAnimationSpecs = SPECS_WAITING; mAppTransitionAnimationSpecs = SPECS_WAITING;
} }
return new IAppTransitionAnimationSpecsFuture.Stub() { IAppTransitionAnimationSpecsFuture future = new IAppTransitionAnimationSpecsFuture.Stub() {
@Override @Override
public AppTransitionAnimationSpec[] get() throws RemoteException { public AppTransitionAnimationSpec[] get() throws RemoteException {
mHandler.post(new Runnable() { mHandler.post(() -> {
@Override synchronized (RecentsTransitionHelper.this) {
public void run() { mAppTransitionAnimationSpecs = composer.composeSpecs();
synchronized (RecentsTransitionHelper.this) { RecentsTransitionHelper.this.notifyAll();
mAppTransitionAnimationSpecs = composer.composeSpecs();
RecentsTransitionHelper.this.notifyAll();
}
} }
}); });
synchronized (RecentsTransitionHelper.this) { synchronized (RecentsTransitionHelper.this) {
@@ -265,6 +254,7 @@ public class RecentsTransitionHelper {
} }
} }
}; };
return new AppTransitionAnimationSpecsFuture(composer, future);
} }
/** /**
@@ -283,7 +273,7 @@ public class RecentsTransitionHelper {
* Composes the animation specs for all the tasks in the target stack. * Composes the animation specs for all the tasks in the target stack.
*/ */
private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task, private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
final TaskStackView stackView, final int destinationStack) { final TaskStackView stackView, final int destinationStack, Rect windowRect) {
// Ensure we have a valid target stack id // Ensure we have a valid target stack id
final int targetStackId = destinationStack != INVALID_STACK_ID ? final int targetStackId = destinationStack != INVALID_STACK_ID ?
destinationStack : task.key.stackId; destinationStack : task.key.stackId;
@@ -309,8 +299,7 @@ public class RecentsTransitionHelper {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect)); specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else { } else {
mTmpTransform.fillIn(taskView); mTmpTransform.fillIn(taskView);
stackLayout.transformToScreenCoordinates(mTmpTransform, stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
null /* windowOverrideRect */);
AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView, AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView,
mTmpTransform, true /* addHeaderBitmap */); mTmpTransform, true /* addHeaderBitmap */);
if (spec != null) { if (spec != null) {
@@ -430,4 +419,34 @@ public class RecentsTransitionHelper {
public interface AnimationSpecComposer { public interface AnimationSpecComposer {
List<AppTransitionAnimationSpec> composeSpecs(); List<AppTransitionAnimationSpec> composeSpecs();
} }
/**
* Class to be returned from {@link #composeAnimationSpec} that gives access to both the future
* and the anonymous class used for composing.
*/
public class AppTransitionAnimationSpecsFuture {
private final AnimationSpecComposer composer;
private final IAppTransitionAnimationSpecsFuture future;
private AppTransitionAnimationSpecsFuture(AnimationSpecComposer composer,
IAppTransitionAnimationSpecsFuture future) {
this.composer = composer;
this.future = future;
}
public IAppTransitionAnimationSpecsFuture getFuture() {
return future;
}
/**
* Manually generates and caches the spec such that they are already available when the
* future needs.
*/
public void precacheSpecs() {
synchronized (RecentsTransitionHelper.this) {
mAppTransitionAnimationSpecs = composer.composeSpecs();
}
}
}
} }

View File

@@ -24,19 +24,16 @@ import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.AppTransitionAnimationSpec; import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewDebug; import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator; import android.view.ViewPropertyAnimator;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@@ -73,10 +70,10 @@ import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer; import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
import com.android.systemui.stackdivider.WindowManagerProxy; import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.FlingAnimationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -440,8 +437,7 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(LaunchTaskEvent event) { public final void onBusEvent(LaunchTaskEvent event) {
mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView, mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
event.taskView, event.screenPinningRequested, event.targetTaskBounds, event.taskView, event.screenPinningRequested, event.targetTaskStack);
event.targetTaskStack);
} }
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
@@ -523,7 +519,7 @@ public class RecentsView extends FrameLayout {
}; };
final Rect taskRect = getTaskRect(event.taskView); final Rect taskRect = getTaskRect(event.taskView);
IAppTransitionAnimationSpecsFuture future = AppTransitionAnimationSpecsFuture future =
mTransitionHelper.getAppTransitionFuture( mTransitionHelper.getAppTransitionFuture(
new AnimationSpecComposer() { new AnimationSpecComposer() {
@Override @Override
@@ -532,7 +528,7 @@ public class RecentsView extends FrameLayout {
event.taskView, taskRect); event.taskView, taskRect);
} }
}); });
ssp.overridePendingAppTransitionMultiThumbFuture(future, ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(),
mTransitionHelper.wrapStartedListener(startedListener), mTransitionHelper.wrapStartedListener(startedListener),
true /* scaleUp */); true /* scaleUp */);

View File

@@ -149,6 +149,7 @@ import android.util.MergedConfiguration;
import android.util.Slog; import android.util.Slog;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.view.AppTransitionAnimationSpec; import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken; import android.view.IApplicationToken;
import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams;
@@ -1437,7 +1438,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
case ANIM_THUMBNAIL_ASPECT_SCALE_UP: case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN final IAppTransitionAnimationSpecsFuture specsFuture =
pendingOptions.getSpecsFuture();
if (specsFuture != null) {
service.mWindowManager.overridePendingAppTransitionMultiThumbFuture(
specsFuture, pendingOptions.getOnAnimationStartListener(),
animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
} else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
&& specs != null) { && specs != null) {
service.mWindowManager.overridePendingAppTransitionMultiThumb( service.mWindowManager.overridePendingAppTransitionMultiThumb(
specs, pendingOptions.getOnAnimationStartListener(), specs, pendingOptions.getOnAnimationStartListener(),