2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
22 import android.annotation.Nullable;
23 import android.annotation.TestApi;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.graphics.Bitmap;
27 import android.graphics.Rect;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IRemoteCallback;
31 import android.os.Parcelable;
32 import android.os.RemoteException;
33 import android.os.ResultReceiver;
34 import android.transition.Transition;
35 import android.transition.TransitionManager;
36 import android.util.Pair;
37 import android.util.Slog;
38 import android.view.AppTransitionAnimationSpec;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.view.Window;
43 import java.util.ArrayList;
46 * Helper class for building an options Bundle that can be used with
47 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
48 * Context.startActivity(Intent, Bundle)} and related methods.
50 public class ActivityOptions {
51 private static final String TAG = "ActivityOptions";
54 * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
55 * the total time (in ms) the user spent in the app flow.
57 public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
60 * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
61 * detailed information about the time spent in each package associated with the app;
62 * each key is a package name, whose value is a long containing the time (in ms).
64 public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
67 * The package name that created the options.
70 public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
73 * The bounds (window size) that the activity should be launched in. Set to null explicitly for
74 * full screen. If the key is not found, previous bounds will be preserved.
75 * NOTE: This value is ignored on devices that don't have
76 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
77 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
80 public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
83 * Type of animation that arguments specify.
86 public static final String KEY_ANIM_TYPE = "android:activity.animType";
89 * Custom enter animation resource ID.
92 public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
95 * Custom exit animation resource ID.
98 public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
101 * Custom in-place animation resource ID.
104 public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
107 * Bitmap for thumbnail animation.
110 public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
113 * Start X position of thumbnail animation.
116 public static final String KEY_ANIM_START_X = "android:activity.animStartX";
119 * Start Y position of thumbnail animation.
122 public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
125 * Initial width of the animation.
128 public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
131 * Initial height of the animation.
134 public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
137 * Callback for when animation is started.
140 public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
143 * Callback for when the last frame of the animation is played.
146 private static final String KEY_ANIMATION_FINISHED_LISTENER =
147 "android:activity.animationFinishedListener";
150 * Descriptions of app transition animations to be played during the activity launch.
152 private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
155 * The stack id the activity should be launched into.
158 private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
161 * The task id the activity should be launched into.
164 private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
167 * See {@link #setTaskOverlay}.
170 private static final String KEY_TASK_OVERLAY = "android.activity.taskOverlay";
173 * Where the docked stack should be positioned.
176 private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
179 * For Activity transitions, the calling Activity's TransitionListener used to
180 * notify the called Activity when the shared element and the exit transitions
183 private static final String KEY_TRANSITION_COMPLETE_LISTENER
184 = "android:activity.transitionCompleteListener";
186 private static final String KEY_TRANSITION_IS_RETURNING
187 = "android:activity.transitionIsReturning";
188 private static final String KEY_TRANSITION_SHARED_ELEMENTS
189 = "android:activity.sharedElementNames";
190 private static final String KEY_RESULT_DATA = "android:activity.resultData";
191 private static final String KEY_RESULT_CODE = "android:activity.resultCode";
192 private static final String KEY_EXIT_COORDINATOR_INDEX
193 = "android:activity.exitCoordinatorIndex";
195 private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
196 private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint";
199 public static final int ANIM_NONE = 0;
201 public static final int ANIM_CUSTOM = 1;
203 public static final int ANIM_SCALE_UP = 2;
205 public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
207 public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
209 public static final int ANIM_SCENE_TRANSITION = 5;
211 public static final int ANIM_DEFAULT = 6;
213 public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
215 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
217 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
219 public static final int ANIM_CUSTOM_IN_PLACE = 10;
221 public static final int ANIM_CLIP_REVEAL = 11;
223 private String mPackageName;
224 private Rect mLaunchBounds;
225 private int mAnimationType = ANIM_NONE;
226 private int mCustomEnterResId;
227 private int mCustomExitResId;
228 private int mCustomInPlaceResId;
229 private Bitmap mThumbnail;
234 private IRemoteCallback mAnimationStartedListener;
235 private IRemoteCallback mAnimationFinishedListener;
236 private ResultReceiver mTransitionReceiver;
237 private boolean mIsReturning;
238 private ArrayList<String> mSharedElementNames;
239 private Intent mResultData;
240 private int mResultCode;
241 private int mExitCoordinatorIndex;
242 private PendingIntent mUsageTimeReport;
243 private int mLaunchStackId = INVALID_STACK_ID;
244 private int mLaunchTaskId = -1;
245 private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
246 private boolean mTaskOverlay;
247 private AppTransitionAnimationSpec mAnimSpecs[];
248 private int mRotationAnimationHint = -1;
251 * Create an ActivityOptions specifying a custom animation to run when
252 * the activity is displayed.
254 * @param context Who is defining this. This is the application that the
255 * animation resources will be loaded from.
256 * @param enterResId A resource ID of the animation resource to use for
257 * the incoming activity. Use 0 for no animation.
258 * @param exitResId A resource ID of the animation resource to use for
259 * the outgoing activity. Use 0 for no animation.
260 * @return Returns a new ActivityOptions object that you can use to
261 * supply these options as the options Bundle when starting an activity.
263 public static ActivityOptions makeCustomAnimation(Context context,
264 int enterResId, int exitResId) {
265 return makeCustomAnimation(context, enterResId, exitResId, null, null);
269 * Create an ActivityOptions specifying a custom animation to run when
270 * the activity is displayed.
272 * @param context Who is defining this. This is the application that the
273 * animation resources will be loaded from.
274 * @param enterResId A resource ID of the animation resource to use for
275 * the incoming activity. Use 0 for no animation.
276 * @param exitResId A resource ID of the animation resource to use for
277 * the outgoing activity. Use 0 for no animation.
278 * @param handler If <var>listener</var> is non-null this must be a valid
279 * Handler on which to dispatch the callback; otherwise it should be null.
280 * @param listener Optional OnAnimationStartedListener to find out when the
281 * requested animation has started running. If for some reason the animation
282 * is not executed, the callback will happen immediately.
283 * @return Returns a new ActivityOptions object that you can use to
284 * supply these options as the options Bundle when starting an activity.
287 public static ActivityOptions makeCustomAnimation(Context context,
288 int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
289 ActivityOptions opts = new ActivityOptions();
290 opts.mPackageName = context.getPackageName();
291 opts.mAnimationType = ANIM_CUSTOM;
292 opts.mCustomEnterResId = enterResId;
293 opts.mCustomExitResId = exitResId;
294 opts.setOnAnimationStartedListener(handler, listener);
299 * Creates an ActivityOptions specifying a custom animation to run in place on an existing
302 * @param context Who is defining this. This is the application that the
303 * animation resources will be loaded from.
304 * @param animId A resource ID of the animation resource to use for
305 * the incoming activity.
306 * @return Returns a new ActivityOptions object that you can use to
307 * supply these options as the options Bundle when running an in-place animation.
310 public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
312 throw new RuntimeException("You must specify a valid animation.");
315 ActivityOptions opts = new ActivityOptions();
316 opts.mPackageName = context.getPackageName();
317 opts.mAnimationType = ANIM_CUSTOM_IN_PLACE;
318 opts.mCustomInPlaceResId = animId;
322 private void setOnAnimationStartedListener(final Handler handler,
323 final OnAnimationStartedListener listener) {
324 if (listener != null) {
325 mAnimationStartedListener = new IRemoteCallback.Stub() {
327 public void sendResult(Bundle data) throws RemoteException {
328 handler.post(new Runnable() {
329 @Override public void run() {
330 listener.onAnimationStarted();
339 * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
340 * to find out when the given animation has started running.
343 public interface OnAnimationStartedListener {
344 void onAnimationStarted();
347 private void setOnAnimationFinishedListener(final Handler handler,
348 final OnAnimationFinishedListener listener) {
349 if (listener != null) {
350 mAnimationFinishedListener = new IRemoteCallback.Stub() {
352 public void sendResult(Bundle data) throws RemoteException {
353 handler.post(new Runnable() {
356 listener.onAnimationFinished();
365 * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation}
366 * to find out when the given animation has drawn its last frame.
369 public interface OnAnimationFinishedListener {
370 void onAnimationFinished();
374 * Create an ActivityOptions specifying an animation where the new
375 * activity is scaled from a small originating area of the screen to
376 * its final full representation.
378 * <p>If the Intent this is being used with has not set its
379 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
380 * those bounds will be filled in for you based on the initial
381 * bounds passed in here.
383 * @param source The View that the new activity is animating from. This
384 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
385 * @param startX The x starting location of the new activity, relative to <var>source</var>.
386 * @param startY The y starting location of the activity, relative to <var>source</var>.
387 * @param width The initial width of the new activity.
388 * @param height The initial height of the new activity.
389 * @return Returns a new ActivityOptions object that you can use to
390 * supply these options as the options Bundle when starting an activity.
392 public static ActivityOptions makeScaleUpAnimation(View source,
393 int startX, int startY, int width, int height) {
394 ActivityOptions opts = new ActivityOptions();
395 opts.mPackageName = source.getContext().getPackageName();
396 opts.mAnimationType = ANIM_SCALE_UP;
397 int[] pts = new int[2];
398 source.getLocationOnScreen(pts);
399 opts.mStartX = pts[0] + startX;
400 opts.mStartY = pts[1] + startY;
402 opts.mHeight = height;
407 * Create an ActivityOptions specifying an animation where the new
408 * activity is revealed from a small originating area of the screen to
409 * its final full representation.
411 * @param source The View that the new activity is animating from. This
412 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
413 * @param startX The x starting location of the new activity, relative to <var>source</var>.
414 * @param startY The y starting location of the activity, relative to <var>source</var>.
415 * @param width The initial width of the new activity.
416 * @param height The initial height of the new activity.
417 * @return Returns a new ActivityOptions object that you can use to
418 * supply these options as the options Bundle when starting an activity.
420 public static ActivityOptions makeClipRevealAnimation(View source,
421 int startX, int startY, int width, int height) {
422 ActivityOptions opts = new ActivityOptions();
423 opts.mAnimationType = ANIM_CLIP_REVEAL;
424 int[] pts = new int[2];
425 source.getLocationOnScreen(pts);
426 opts.mStartX = pts[0] + startX;
427 opts.mStartY = pts[1] + startY;
429 opts.mHeight = height;
434 * Create an ActivityOptions specifying an animation where a thumbnail
435 * is scaled from a given position to the new activity window that is
438 * <p>If the Intent this is being used with has not set its
439 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
440 * those bounds will be filled in for you based on the initial
441 * thumbnail location and size provided here.
443 * @param source The View that this thumbnail is animating from. This
444 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
445 * @param thumbnail The bitmap that will be shown as the initial thumbnail
447 * @param startX The x starting location of the bitmap, relative to <var>source</var>.
448 * @param startY The y starting location of the bitmap, relative to <var>source</var>.
449 * @return Returns a new ActivityOptions object that you can use to
450 * supply these options as the options Bundle when starting an activity.
452 public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
453 Bitmap thumbnail, int startX, int startY) {
454 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
458 * Create an ActivityOptions specifying an animation where a thumbnail
459 * is scaled from a given position to the new activity window that is
462 * @param source The View that this thumbnail is animating from. This
463 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
464 * @param thumbnail The bitmap that will be shown as the initial thumbnail
466 * @param startX The x starting location of the bitmap, relative to <var>source</var>.
467 * @param startY The y starting location of the bitmap, relative to <var>source</var>.
468 * @param listener Optional OnAnimationStartedListener to find out when the
469 * requested animation has started running. If for some reason the animation
470 * is not executed, the callback will happen immediately.
471 * @return Returns a new ActivityOptions object that you can use to
472 * supply these options as the options Bundle when starting an activity.
475 public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
476 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
477 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
481 * Create an ActivityOptions specifying an animation where an activity window
482 * is scaled from a given position to a thumbnail at a specified location.
484 * @param source The View that this thumbnail is animating to. This
485 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
486 * @param thumbnail The bitmap that will be shown as the final thumbnail
488 * @param startX The x end location of the bitmap, relative to <var>source</var>.
489 * @param startY The y end location of the bitmap, relative to <var>source</var>.
490 * @param listener Optional OnAnimationStartedListener to find out when the
491 * requested animation has started running. If for some reason the animation
492 * is not executed, the callback will happen immediately.
493 * @return Returns a new ActivityOptions object that you can use to
494 * supply these options as the options Bundle when starting an activity.
497 public static ActivityOptions makeThumbnailScaleDownAnimation(View source,
498 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
499 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false);
502 private static ActivityOptions makeThumbnailAnimation(View source,
503 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
505 ActivityOptions opts = new ActivityOptions();
506 opts.mPackageName = source.getContext().getPackageName();
507 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
508 opts.mThumbnail = thumbnail;
509 int[] pts = new int[2];
510 source.getLocationOnScreen(pts);
511 opts.mStartX = pts[0] + startX;
512 opts.mStartY = pts[1] + startY;
513 opts.setOnAnimationStartedListener(source.getHandler(), listener);
518 * Create an ActivityOptions specifying an animation where the new activity
519 * window and a thumbnail is aspect-scaled to a new location.
521 * @param source The View that this thumbnail is animating from. This
522 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
523 * @param thumbnail The bitmap that will be shown as the initial thumbnail
525 * @param startX The x starting location of the bitmap, relative to <var>source</var>.
526 * @param startY The y starting location of the bitmap, relative to <var>source</var>.
527 * @param handler If <var>listener</var> is non-null this must be a valid
528 * Handler on which to dispatch the callback; otherwise it should be null.
529 * @param listener Optional OnAnimationStartedListener to find out when the
530 * requested animation has started running. If for some reason the animation
531 * is not executed, the callback will happen immediately.
532 * @return Returns a new ActivityOptions object that you can use to
533 * supply these options as the options Bundle when starting an activity.
536 public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source,
537 Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
538 Handler handler, OnAnimationStartedListener listener) {
539 return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
540 targetWidth, targetHeight, handler, listener, true);
544 * Create an ActivityOptions specifying an animation where the new activity
545 * window and a thumbnail is aspect-scaled to a new location.
547 * @param source The View that this thumbnail is animating to. This
548 * defines the coordinate space for <var>startX</var> and <var>startY</var>.
549 * @param thumbnail The bitmap that will be shown as the final thumbnail
551 * @param startX The x end location of the bitmap, relative to <var>source</var>.
552 * @param startY The y end location of the bitmap, relative to <var>source</var>.
553 * @param handler If <var>listener</var> is non-null this must be a valid
554 * Handler on which to dispatch the callback; otherwise it should be null.
555 * @param listener Optional OnAnimationStartedListener to find out when the
556 * requested animation has started running. If for some reason the animation
557 * is not executed, the callback will happen immediately.
558 * @return Returns a new ActivityOptions object that you can use to
559 * supply these options as the options Bundle when starting an activity.
562 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
563 Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
564 Handler handler, OnAnimationStartedListener listener) {
565 return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
566 targetWidth, targetHeight, handler, listener, false);
569 private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
570 int startX, int startY, int targetWidth, int targetHeight,
571 Handler handler, OnAnimationStartedListener listener, boolean scaleUp) {
572 ActivityOptions opts = new ActivityOptions();
573 opts.mPackageName = source.getContext().getPackageName();
574 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
575 ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
576 opts.mThumbnail = thumbnail;
577 int[] pts = new int[2];
578 source.getLocationOnScreen(pts);
579 opts.mStartX = pts[0] + startX;
580 opts.mStartY = pts[1] + startY;
581 opts.mWidth = targetWidth;
582 opts.mHeight = targetHeight;
583 opts.setOnAnimationStartedListener(handler, listener);
588 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
589 AppTransitionAnimationSpec[] specs, Handler handler,
590 OnAnimationStartedListener onAnimationStartedListener,
591 OnAnimationFinishedListener onAnimationFinishedListener) {
592 ActivityOptions opts = new ActivityOptions();
593 opts.mPackageName = source.getContext().getPackageName();
594 opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
595 opts.mAnimSpecs = specs;
596 opts.setOnAnimationStartedListener(handler, onAnimationStartedListener);
597 opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener);
602 * Create an ActivityOptions to transition between Activities using cross-Activity scene
603 * animations. This method carries the position of one shared element to the started Activity.
604 * The position of <code>sharedElement</code> will be used as the epicenter for the
605 * exit Transition. The position of the shared element in the launched Activity will be the
606 * epicenter of its entering Transition.
608 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
609 * enabled on the calling Activity to cause an exit transition. The same must be in
610 * the called Activity to get an entering transition.</p>
611 * @param activity The Activity whose window contains the shared elements.
612 * @param sharedElement The View to transition to the started Activity.
613 * @param sharedElementName The shared element name as used in the target Activity. This
615 * @return Returns a new ActivityOptions object that you can use to
616 * supply these options as the options Bundle when starting an activity.
617 * @see android.transition.Transition#setEpicenterCallback(
618 * android.transition.Transition.EpicenterCallback)
620 public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
621 View sharedElement, String sharedElementName) {
622 return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
626 * Create an ActivityOptions to transition between Activities using cross-Activity scene
627 * animations. This method carries the position of multiple shared elements to the started
628 * Activity. The position of the first element in sharedElements
629 * will be used as the epicenter for the exit Transition. The position of the associated
630 * shared element in the launched Activity will be the epicenter of its entering Transition.
632 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
633 * enabled on the calling Activity to cause an exit transition. The same must be in
634 * the called Activity to get an entering transition.</p>
635 * @param activity The Activity whose window contains the shared elements.
636 * @param sharedElements The names of the shared elements to transfer to the called
637 * Activity and their associated Views. The Views must each have
638 * a unique shared element name.
639 * @return Returns a new ActivityOptions object that you can use to
640 * supply these options as the options Bundle when starting an activity.
641 * @see android.transition.Transition#setEpicenterCallback(
642 * android.transition.Transition.EpicenterCallback)
645 public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
646 Pair<View, String>... sharedElements) {
647 ActivityOptions opts = new ActivityOptions();
648 makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
649 activity.mExitTransitionListener, sharedElements);
654 * Call this immediately prior to startActivity to begin a shared element transition
655 * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS.
656 * The exit transition will start immediately and the shared element transition will
657 * start once the launched Activity's shared element is ready.
659 * When all transitions have completed and the shared element has been transfered,
660 * the window's decor View will have its visibility set to View.GONE.
665 public static ActivityOptions startSharedElementAnimation(Window window,
666 Pair<View, String>... sharedElements) {
667 ActivityOptions opts = new ActivityOptions();
668 final View decorView = window.getDecorView();
669 if (decorView == null) {
672 final ExitTransitionCoordinator exit =
673 makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
675 HideWindowListener listener = new HideWindowListener(window, exit);
676 exit.setHideSharedElementsCallback(listener);
683 * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
684 * animation must be stopped and the Views reset. This can happen if there was an error
685 * from startActivity or a springboard activity and the animation should stop and reset.
689 public static void stopSharedElementAnimation(Window window) {
690 final View decorView = window.getDecorView();
691 if (decorView == null) {
694 final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
695 decorView.getTag(com.android.internal.R.id.cross_task_transition);
697 exit.cancelPendingTransitions();
698 decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
699 TransitionManager.endTransitions((ViewGroup) decorView);
702 decorView.setVisibility(View.VISIBLE);
706 static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
707 ActivityOptions opts, SharedElementCallback callback,
708 Pair<View, String>[] sharedElements) {
709 if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
710 opts.mAnimationType = ANIM_DEFAULT;
713 opts.mAnimationType = ANIM_SCENE_TRANSITION;
715 ArrayList<String> names = new ArrayList<String>();
716 ArrayList<View> views = new ArrayList<View>();
718 if (sharedElements != null) {
719 for (int i = 0; i < sharedElements.length; i++) {
720 Pair<View, String> sharedElement = sharedElements[i];
721 String sharedElementName = sharedElement.second;
722 if (sharedElementName == null) {
723 throw new IllegalArgumentException("Shared element name must not be null");
725 names.add(sharedElementName);
726 View view = sharedElement.first;
728 throw new IllegalArgumentException("Shared element must not be null");
730 views.add(sharedElement.first);
734 ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
735 callback, names, names, views, false);
736 opts.mTransitionReceiver = exit;
737 opts.mSharedElementNames = names;
738 opts.mIsReturning = (activity == null);
739 if (activity == null) {
740 opts.mExitCoordinatorIndex = -1;
742 opts.mExitCoordinatorIndex =
743 activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
749 static ActivityOptions makeSceneTransitionAnimation(Activity activity,
750 ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
751 int resultCode, Intent resultData) {
752 ActivityOptions opts = new ActivityOptions();
753 opts.mAnimationType = ANIM_SCENE_TRANSITION;
754 opts.mSharedElementNames = sharedElementNames;
755 opts.mTransitionReceiver = exitCoordinator;
756 opts.mIsReturning = true;
757 opts.mResultCode = resultCode;
758 opts.mResultData = resultData;
759 opts.mExitCoordinatorIndex =
760 activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
765 * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
766 * presented to the user but will instead be only available through the recents task list.
767 * In addition, the new task wil be affiliated with the launching activity's task.
768 * Affiliated tasks are grouped together in the recents task list.
770 * <p>This behavior is not supported for activities with {@link
771 * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
772 * <code>singleInstance</code> or <code>singleTask</code>.
774 public static ActivityOptions makeTaskLaunchBehind() {
775 final ActivityOptions opts = new ActivityOptions();
776 opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
781 * Create a basic ActivityOptions that has no special animation associated with it.
782 * Other options can still be set.
784 public static ActivityOptions makeBasic() {
785 final ActivityOptions opts = new ActivityOptions();
790 public boolean getLaunchTaskBehind() {
791 return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
794 private ActivityOptions() {
798 public ActivityOptions(Bundle opts) {
799 // If the remote side sent us bad parcelables, they won't get the
800 // results they want, which is their loss.
801 opts.setDefusable(true);
803 mPackageName = opts.getString(KEY_PACKAGE_NAME);
805 mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
806 } catch (RuntimeException e) {
809 mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
810 mAnimationType = opts.getInt(KEY_ANIM_TYPE);
811 switch (mAnimationType) {
813 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
814 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
815 mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
816 opts.getBinder(KEY_ANIM_START_LISTENER));
819 case ANIM_CUSTOM_IN_PLACE:
820 mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
824 case ANIM_CLIP_REVEAL:
825 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
826 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
827 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
828 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
831 case ANIM_THUMBNAIL_SCALE_UP:
832 case ANIM_THUMBNAIL_SCALE_DOWN:
833 case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
834 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
835 mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
836 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
837 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
838 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
839 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
840 mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
841 opts.getBinder(KEY_ANIM_START_LISTENER));
844 case ANIM_SCENE_TRANSITION:
845 mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
846 mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
847 mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
848 mResultData = opts.getParcelable(KEY_RESULT_DATA);
849 mResultCode = opts.getInt(KEY_RESULT_CODE);
850 mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
853 mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
854 mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
855 mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
856 mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
857 if (opts.containsKey(KEY_ANIM_SPECS)) {
858 Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
859 mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
860 for (int i = specs.length - 1; i >= 0; i--) {
861 mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
864 if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
865 mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
866 opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
868 mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
872 * Sets the bounds (window size) that the activity should be launched in.
873 * Rect position should be provided in pixels and in screen coordinates.
874 * Set to null explicitly for fullscreen.
876 * <strong>NOTE:<strong/> This value is ignored on devices that don't have
877 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
878 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
879 * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
881 public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
882 mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
887 public String getPackageName() {
892 * Returns the bounds that should be used to launch the activity.
893 * @see #setLaunchBounds(Rect)
894 * @return Bounds used to launch the activity.
897 public Rect getLaunchBounds() {
898 return mLaunchBounds;
902 public int getAnimationType() {
903 return mAnimationType;
907 public int getCustomEnterResId() {
908 return mCustomEnterResId;
912 public int getCustomExitResId() {
913 return mCustomExitResId;
917 public int getCustomInPlaceResId() {
918 return mCustomInPlaceResId;
922 public Bitmap getThumbnail() {
927 public int getStartX() {
932 public int getStartY() {
937 public int getWidth() {
942 public int getHeight() {
947 public IRemoteCallback getOnAnimationStartListener() {
948 return mAnimationStartedListener;
952 public IRemoteCallback getAnimationFinishedListener() {
953 return mAnimationFinishedListener;
957 public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
960 public void abort() {
961 if (mAnimationStartedListener != null) {
963 mAnimationStartedListener.sendResult(null);
964 } catch (RemoteException e) {
970 public boolean isReturning() {
975 * Returns whether or not the ActivityOptions was created with
976 * {@link #startSharedElementAnimation(Window, Pair[])}.
980 boolean isCrossTask() {
981 return mExitCoordinatorIndex < 0;
985 public ArrayList<String> getSharedElementNames() {
986 return mSharedElementNames;
990 public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
993 public int getResultCode() { return mResultCode; }
996 public Intent getResultData() { return mResultData; }
999 public PendingIntent getUsageTimeReport() {
1000 return mUsageTimeReport;
1004 public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
1007 public static ActivityOptions fromBundle(Bundle bOptions) {
1008 return bOptions != null ? new ActivityOptions(bOptions) : null;
1012 public static void abort(ActivityOptions options) {
1013 if (options != null) {
1019 public int getLaunchStackId() {
1020 return mLaunchStackId;
1025 public void setLaunchStackId(int launchStackId) {
1026 mLaunchStackId = launchStackId;
1030 * Sets the task the activity will be launched in.
1033 public void setLaunchTaskId(int taskId) {
1034 mLaunchTaskId = taskId;
1040 public int getLaunchTaskId() {
1041 return mLaunchTaskId;
1045 * Set's whether the activity launched with this option should be a task overlay. That is the
1046 * activity will always be the top activity of the task and doesn't cause the task to be moved
1047 * to the front when it is added.
1050 public void setTaskOverlay(boolean taskOverlay) {
1051 mTaskOverlay = taskOverlay;
1057 public boolean getTaskOverlay() {
1058 return mTaskOverlay;
1062 public int getDockCreateMode() {
1063 return mDockCreateMode;
1067 public void setDockCreateMode(int dockCreateMode) {
1068 mDockCreateMode = dockCreateMode;
1072 * Update the current values in this ActivityOptions from those supplied
1073 * in <var>otherOptions</var>. Any values
1074 * defined in <var>otherOptions</var> replace those in the base options.
1076 public void update(ActivityOptions otherOptions) {
1077 if (otherOptions.mPackageName != null) {
1078 mPackageName = otherOptions.mPackageName;
1080 mUsageTimeReport = otherOptions.mUsageTimeReport;
1081 mTransitionReceiver = null;
1082 mSharedElementNames = null;
1083 mIsReturning = false;
1086 mExitCoordinatorIndex = 0;
1087 mAnimationType = otherOptions.mAnimationType;
1088 switch (otherOptions.mAnimationType) {
1090 mCustomEnterResId = otherOptions.mCustomEnterResId;
1091 mCustomExitResId = otherOptions.mCustomExitResId;
1093 if (mAnimationStartedListener != null) {
1095 mAnimationStartedListener.sendResult(null);
1096 } catch (RemoteException e) {
1099 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1101 case ANIM_CUSTOM_IN_PLACE:
1102 mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
1105 mStartX = otherOptions.mStartX;
1106 mStartY = otherOptions.mStartY;
1107 mWidth = otherOptions.mWidth;
1108 mHeight = otherOptions.mHeight;
1109 if (mAnimationStartedListener != null) {
1111 mAnimationStartedListener.sendResult(null);
1112 } catch (RemoteException e) {
1115 mAnimationStartedListener = null;
1117 case ANIM_THUMBNAIL_SCALE_UP:
1118 case ANIM_THUMBNAIL_SCALE_DOWN:
1119 case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1120 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1121 mThumbnail = otherOptions.mThumbnail;
1122 mStartX = otherOptions.mStartX;
1123 mStartY = otherOptions.mStartY;
1124 mWidth = otherOptions.mWidth;
1125 mHeight = otherOptions.mHeight;
1126 if (mAnimationStartedListener != null) {
1128 mAnimationStartedListener.sendResult(null);
1129 } catch (RemoteException e) {
1132 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1134 case ANIM_SCENE_TRANSITION:
1135 mTransitionReceiver = otherOptions.mTransitionReceiver;
1136 mSharedElementNames = otherOptions.mSharedElementNames;
1137 mIsReturning = otherOptions.mIsReturning;
1139 mAnimationStartedListener = null;
1140 mResultData = otherOptions.mResultData;
1141 mResultCode = otherOptions.mResultCode;
1142 mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1145 mAnimSpecs = otherOptions.mAnimSpecs;
1146 mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1150 * Returns the created options as a Bundle, which can be passed to
1151 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
1152 * Context.startActivity(Intent, Bundle)} and related methods.
1153 * Note that the returned Bundle is still owned by the ActivityOptions
1154 * object; you must not modify it, but can supply it to the startActivity
1155 * methods that take an options Bundle.
1157 public Bundle toBundle() {
1158 if (mAnimationType == ANIM_DEFAULT) {
1161 Bundle b = new Bundle();
1162 if (mPackageName != null) {
1163 b.putString(KEY_PACKAGE_NAME, mPackageName);
1165 if (mLaunchBounds != null) {
1166 b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1168 b.putInt(KEY_ANIM_TYPE, mAnimationType);
1169 if (mUsageTimeReport != null) {
1170 b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1172 switch (mAnimationType) {
1174 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
1175 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
1176 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1177 != null ? mAnimationStartedListener.asBinder() : null);
1179 case ANIM_CUSTOM_IN_PLACE:
1180 b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1183 case ANIM_CLIP_REVEAL:
1184 b.putInt(KEY_ANIM_START_X, mStartX);
1185 b.putInt(KEY_ANIM_START_Y, mStartY);
1186 b.putInt(KEY_ANIM_WIDTH, mWidth);
1187 b.putInt(KEY_ANIM_HEIGHT, mHeight);
1189 case ANIM_THUMBNAIL_SCALE_UP:
1190 case ANIM_THUMBNAIL_SCALE_DOWN:
1191 case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1192 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1193 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
1194 b.putInt(KEY_ANIM_START_X, mStartX);
1195 b.putInt(KEY_ANIM_START_Y, mStartY);
1196 b.putInt(KEY_ANIM_WIDTH, mWidth);
1197 b.putInt(KEY_ANIM_HEIGHT, mHeight);
1198 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1199 != null ? mAnimationStartedListener.asBinder() : null);
1201 case ANIM_SCENE_TRANSITION:
1202 if (mTransitionReceiver != null) {
1203 b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1205 b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
1206 b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
1207 b.putParcelable(KEY_RESULT_DATA, mResultData);
1208 b.putInt(KEY_RESULT_CODE, mResultCode);
1209 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
1212 b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
1213 b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
1214 b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
1215 b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
1216 if (mAnimSpecs != null) {
1217 b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1219 if (mAnimationFinishedListener != null) {
1220 b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1222 b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
1228 * Ask the the system track that time the user spends in the app being launched, and
1229 * report it back once done. The report will be sent to the given receiver, with
1230 * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1233 * <p>The time interval tracked is from launching this activity until the user leaves
1234 * that activity's flow. They are considered to stay in the flow as long as
1235 * new activities are being launched or returned to from the original flow,
1236 * even if this crosses package or task boundaries. For example, if the originator
1237 * starts an activity to view an image, and while there the user selects to share,
1238 * which launches their email app in a new task, and they complete the share, the
1239 * time during that entire operation will be included until they finally hit back from
1240 * the original image viewer activity.</p>
1242 * <p>The user is considered to complete a flow once they switch to another
1243 * activity that is not part of the tracked flow. This may happen, for example, by
1244 * using the notification shade, launcher, or recents to launch or switch to another
1245 * app. Simply going in to these navigation elements does not break the flow (although
1246 * the launcher and recents stops time tracking of the session); it is the act of
1247 * going somewhere else that completes the tracking.</p>
1249 * @param receiver A broadcast receiver that willl receive the report.
1251 public void requestUsageTimeReport(PendingIntent receiver) {
1252 mUsageTimeReport = receiver;
1256 * Return the filtered options only meant to be seen by the target activity itself
1259 public ActivityOptions forTargetActivity() {
1260 if (mAnimationType == ANIM_SCENE_TRANSITION) {
1261 final ActivityOptions result = new ActivityOptions();
1262 result.update(this);
1270 * Returns the rotation animation set by {@link setRotationAnimationHint} or -1
1274 public int getRotationAnimationHint() {
1275 return mRotationAnimationHint;
1280 * Set a rotation animation to be used if launching the activity
1281 * triggers an orientation change, or -1 to clear. See
1282 * {@link android.view.WindowManager.LayoutParams} for rotation
1286 public void setRotationAnimationHint(int hint) {
1287 mRotationAnimationHint = hint;
1292 public String toString() {
1293 return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1294 + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1295 + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1298 private static class HideWindowListener extends Transition.TransitionListenerAdapter
1299 implements ExitTransitionCoordinator.HideSharedElementsCallback {
1300 private final Window mWindow;
1301 private final ExitTransitionCoordinator mExit;
1302 private final boolean mWaitingForTransition;
1303 private boolean mTransitionEnded;
1304 private boolean mSharedElementHidden;
1305 private ArrayList<View> mSharedElements;
1307 public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
1310 mSharedElements = new ArrayList<>(exit.mSharedElements);
1311 Transition transition = mWindow.getExitTransition();
1312 if (transition != null) {
1313 transition.addListener(this);
1314 mWaitingForTransition = true;
1316 mWaitingForTransition = false;
1318 View decorView = mWindow.getDecorView();
1319 if (decorView != null) {
1320 if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
1321 throw new IllegalStateException(
1322 "Cannot start a transition while one is running");
1324 decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
1329 public void onTransitionEnd(Transition transition) {
1330 mTransitionEnded = true;
1332 transition.removeListener(this);
1336 public void hideSharedElements() {
1337 mSharedElementHidden = true;
1341 private void hideWhenDone() {
1342 if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
1344 int numSharedElements = mSharedElements.size();
1345 for (int i = 0; i < numSharedElements; i++) {
1346 View view = mSharedElements.get(i);
1347 view.requestLayout();
1349 View decorView = mWindow.getDecorView();
1350 if (decorView != null) {
1351 decorView.setTagInternal(
1352 com.android.internal.R.id.cross_task_transition, null);
1353 decorView.setVisibility(View.GONE);