Loading core/java/android/app/ActivityOptions.java +30 −45 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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; } } /** /** Loading Loading @@ -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)); } } } /** /** Loading Loading @@ -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; Loading Loading @@ -1205,6 +1186,7 @@ public class ActivityOptions { } } mAnimSpecs = otherOptions.mAnimSpecs; mAnimSpecs = otherOptions.mAnimSpecs; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mSpecsFuture = otherOptions.mSpecsFuture; } } /** /** Loading Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -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 */); } } /** /** Loading Loading @@ -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() { Loading packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +41 −21 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -1116,10 +1119,12 @@ 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) { return; } if (taskKey.stackId == DOCKED_STACK_ID) { if (taskKey.stackId == DOCKED_STACK_ID) { // We show non-visible docked tasks in Recents, but we always want to launch // We show non-visible docked tasks in Recents, but we always want to launch // them in the fullscreen stack. // them in the fullscreen stack. Loading @@ -1133,14 +1138,25 @@ public class SystemServicesProxy { } } options.setLaunchStackId(stackId); 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( mIam.startActivityFromRecents( taskKey.id, options == null ? null : options.toBundle()); taskKey.id, finalOptions == null ? null : finalOptions.toBundle()); return true; if (resultListener != null) { mHandler.post(() -> resultListener.onStartActivityResult(true)); } } catch (Exception e) { } catch (Exception e) { Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); Log.e(TAG, context.getString( R.string.recents_launch_error_message, taskName), e); if (resultListener != null) { mHandler.post(() -> resultListener.onStartActivityResult(false)); } } } } return false; }); } } /** Starts an in-place animation on the front most application windows. */ /** Starts an in-place animation on the front most application windows. */ Loading Loading @@ -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; Loading packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +92 −73 Original line number Original line Diff line number Diff line Loading @@ -101,24 +101,18 @@ 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 // Fetch window rect here already in order not to be blocked on lock contention in WM public List<AppTransitionAnimationSpec> composeSpecs() { // when the future calls it. return composeAnimationSpecs(task, stackView, destinationStack); final Rect windowRect = Recents.getSystemServices().getWindowRect(); } transitionFuture = getAppTransitionFuture( }); () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect)); animStartedListener = new ActivityOptions.OnAnimationStartedListener() { animStartedListener = () -> { @Override public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // If we are launching into another task, cancel the previous task's // window transition // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); Loading @@ -130,28 +124,26 @@ public class RecentsTransitionHelper { mStartScreenPinningRunnable.taskId = task.key.id; mStartScreenPinningRunnable.taskId = task.key.id; mHandler.postDelayed(mStartScreenPinningRunnable, 350); 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 public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // If we are launching into another task, cancel the previous task's // window transition // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); stackView.cancelAllTaskViewAnimations(); 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); Loading @@ -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( Loading Loading @@ -199,10 +190,12 @@ 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, succeeded -> { if (succeeded) { // Keep track of the index of the task launch // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndexFromFront = 0; int taskIndex = stack.indexOfStackTask(task); int taskIndex = stack.indexOfStackTask(task); Loading @@ -219,10 +212,9 @@ public class RecentsTransitionHelper { // Keep track of failed launches // Keep track of failed launches EventBus.getDefault().send(new LaunchTaskFailedEvent()); EventBus.getDefault().send(new LaunchTaskFailedEvent()); } } }); if (transitionFuture != null) { if (transitionFuture != null) { ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture, mHandler.post(transitionFuture::precacheSpecs); wrapStartedListener(animStartedListener), true /* scaleUp */); } } } } Loading @@ -231,22 +223,19 @@ 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 public void run() { synchronized (RecentsTransitionHelper.this) { synchronized (RecentsTransitionHelper.this) { mAppTransitionAnimationSpecs = composer.composeSpecs(); mAppTransitionAnimationSpecs = composer.composeSpecs(); RecentsTransitionHelper.this.notifyAll(); RecentsTransitionHelper.this.notifyAll(); } } } }); }); synchronized (RecentsTransitionHelper.this) { synchronized (RecentsTransitionHelper.this) { while (mAppTransitionAnimationSpecs == SPECS_WAITING) { while (mAppTransitionAnimationSpecs == SPECS_WAITING) { Loading @@ -265,6 +254,7 @@ public class RecentsTransitionHelper { } } } } }; }; return new AppTransitionAnimationSpecsFuture(composer, future); } } /** /** Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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(); } } } } } packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +4 −8 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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 */); Loading Loading
core/java/android/app/ActivityOptions.java +30 −45 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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; } } /** /** Loading Loading @@ -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)); } } } /** /** Loading Loading @@ -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; Loading Loading @@ -1205,6 +1186,7 @@ public class ActivityOptions { } } mAnimSpecs = otherOptions.mAnimSpecs; mAnimSpecs = otherOptions.mAnimSpecs; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mSpecsFuture = otherOptions.mSpecsFuture; } } /** /** Loading Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -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 */); } } /** /** Loading Loading @@ -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() { Loading
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +41 −21 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -1116,10 +1119,12 @@ 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) { return; } if (taskKey.stackId == DOCKED_STACK_ID) { if (taskKey.stackId == DOCKED_STACK_ID) { // We show non-visible docked tasks in Recents, but we always want to launch // We show non-visible docked tasks in Recents, but we always want to launch // them in the fullscreen stack. // them in the fullscreen stack. Loading @@ -1133,14 +1138,25 @@ public class SystemServicesProxy { } } options.setLaunchStackId(stackId); 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( mIam.startActivityFromRecents( taskKey.id, options == null ? null : options.toBundle()); taskKey.id, finalOptions == null ? null : finalOptions.toBundle()); return true; if (resultListener != null) { mHandler.post(() -> resultListener.onStartActivityResult(true)); } } catch (Exception e) { } catch (Exception e) { Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); Log.e(TAG, context.getString( R.string.recents_launch_error_message, taskName), e); if (resultListener != null) { mHandler.post(() -> resultListener.onStartActivityResult(false)); } } } } return false; }); } } /** Starts an in-place animation on the front most application windows. */ /** Starts an in-place animation on the front most application windows. */ Loading Loading @@ -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; Loading
packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +92 −73 Original line number Original line Diff line number Diff line Loading @@ -101,24 +101,18 @@ 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 // Fetch window rect here already in order not to be blocked on lock contention in WM public List<AppTransitionAnimationSpec> composeSpecs() { // when the future calls it. return composeAnimationSpecs(task, stackView, destinationStack); final Rect windowRect = Recents.getSystemServices().getWindowRect(); } transitionFuture = getAppTransitionFuture( }); () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect)); animStartedListener = new ActivityOptions.OnAnimationStartedListener() { animStartedListener = () -> { @Override public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // If we are launching into another task, cancel the previous task's // window transition // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); Loading @@ -130,28 +124,26 @@ public class RecentsTransitionHelper { mStartScreenPinningRunnable.taskId = task.key.id; mStartScreenPinningRunnable.taskId = task.key.id; mHandler.postDelayed(mStartScreenPinningRunnable, 350); 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 public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // If we are launching into another task, cancel the previous task's // window transition // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); stackView.cancelAllTaskViewAnimations(); 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); Loading @@ -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( Loading Loading @@ -199,10 +190,12 @@ 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, succeeded -> { if (succeeded) { // Keep track of the index of the task launch // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndexFromFront = 0; int taskIndex = stack.indexOfStackTask(task); int taskIndex = stack.indexOfStackTask(task); Loading @@ -219,10 +212,9 @@ public class RecentsTransitionHelper { // Keep track of failed launches // Keep track of failed launches EventBus.getDefault().send(new LaunchTaskFailedEvent()); EventBus.getDefault().send(new LaunchTaskFailedEvent()); } } }); if (transitionFuture != null) { if (transitionFuture != null) { ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture, mHandler.post(transitionFuture::precacheSpecs); wrapStartedListener(animStartedListener), true /* scaleUp */); } } } } Loading @@ -231,22 +223,19 @@ 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 public void run() { synchronized (RecentsTransitionHelper.this) { synchronized (RecentsTransitionHelper.this) { mAppTransitionAnimationSpecs = composer.composeSpecs(); mAppTransitionAnimationSpecs = composer.composeSpecs(); RecentsTransitionHelper.this.notifyAll(); RecentsTransitionHelper.this.notifyAll(); } } } }); }); synchronized (RecentsTransitionHelper.this) { synchronized (RecentsTransitionHelper.this) { while (mAppTransitionAnimationSpecs == SPECS_WAITING) { while (mAppTransitionAnimationSpecs == SPECS_WAITING) { Loading @@ -265,6 +254,7 @@ public class RecentsTransitionHelper { } } } } }; }; return new AppTransitionAnimationSpecsFuture(composer, future); } } /** /** Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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(); } } } } }
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +4 −8 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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 */); Loading