ActivityOptions.java revision fd10cd1989966d01011a0cf75f3282f3e12ca5a6
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
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
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package android.app;
18
19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
21
22import android.annotation.Nullable;
23import android.annotation.TestApi;
24import android.content.Context;
25import android.content.Intent;
26import android.graphics.Bitmap;
27import android.graphics.Rect;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.IRemoteCallback;
31import android.os.Parcelable;
32import android.os.RemoteException;
33import android.os.ResultReceiver;
34import android.transition.Transition;
35import android.transition.TransitionManager;
36import android.util.Pair;
37import android.util.Slog;
38import android.view.AppTransitionAnimationSpec;
39import android.view.View;
40import android.view.ViewGroup;
41import android.view.Window;
42
43import java.util.ArrayList;
44
45/**
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.
49 */
50public class ActivityOptions {
51    private static final String TAG = "ActivityOptions";
52
53    /**
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.
56     */
57    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
58
59    /**
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).
63     */
64    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
65
66    /**
67     * The package name that created the options.
68     * @hide
69     */
70    public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
71
72    /**
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.
78     * @hide
79     */
80    public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
81
82    /**
83     * Type of animation that arguments specify.
84     * @hide
85     */
86    public static final String KEY_ANIM_TYPE = "android:activity.animType";
87
88    /**
89     * Custom enter animation resource ID.
90     * @hide
91     */
92    public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
93
94    /**
95     * Custom exit animation resource ID.
96     * @hide
97     */
98    public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
99
100    /**
101     * Custom in-place animation resource ID.
102     * @hide
103     */
104    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
105
106    /**
107     * Bitmap for thumbnail animation.
108     * @hide
109     */
110    public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
111
112    /**
113     * Start X position of thumbnail animation.
114     * @hide
115     */
116    public static final String KEY_ANIM_START_X = "android:activity.animStartX";
117
118    /**
119     * Start Y position of thumbnail animation.
120     * @hide
121     */
122    public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
123
124    /**
125     * Initial width of the animation.
126     * @hide
127     */
128    public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
129
130    /**
131     * Initial height of the animation.
132     * @hide
133     */
134    public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
135
136    /**
137     * Callback for when animation is started.
138     * @hide
139     */
140    public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
141
142    /**
143     * Callback for when the last frame of the animation is played.
144     * @hide
145     */
146    private static final String KEY_ANIMATION_FINISHED_LISTENER =
147            "android:activity.animationFinishedListener";
148
149    /**
150     * Descriptions of app transition animations to be played during the activity launch.
151     */
152    private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
153
154    /**
155     * The stack id the activity should be launched into.
156     * @hide
157     */
158    private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
159
160    /**
161     * The task id the activity should be launched into.
162     * @hide
163     */
164    private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
165
166    /**
167     * See {@link #setTaskOverlay}.
168     * @hide
169     */
170    private static final String KEY_TASK_OVERLAY = "android.activity.taskOverlay";
171
172    /**
173     * Where the docked stack should be positioned.
174     * @hide
175     */
176    private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
177
178    /**
179     * For Activity transitions, the calling Activity's TransitionListener used to
180     * notify the called Activity when the shared element and the exit transitions
181     * complete.
182     */
183    private static final String KEY_TRANSITION_COMPLETE_LISTENER
184            = "android:activity.transitionCompleteListener";
185
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";
194
195    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
196    private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint";
197
198    /** @hide */
199    public static final int ANIM_NONE = 0;
200    /** @hide */
201    public static final int ANIM_CUSTOM = 1;
202    /** @hide */
203    public static final int ANIM_SCALE_UP = 2;
204    /** @hide */
205    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
206    /** @hide */
207    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
208    /** @hide */
209    public static final int ANIM_SCENE_TRANSITION = 5;
210    /** @hide */
211    public static final int ANIM_DEFAULT = 6;
212    /** @hide */
213    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
214    /** @hide */
215    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
216    /** @hide */
217    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
218    /** @hide */
219    public static final int ANIM_CUSTOM_IN_PLACE = 10;
220    /** @hide */
221    public static final int ANIM_CLIP_REVEAL = 11;
222
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;
230    private int mStartX;
231    private int mStartY;
232    private int mWidth;
233    private int mHeight;
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;
249
250    /**
251     * Create an ActivityOptions specifying a custom animation to run when
252     * the activity is displayed.
253     *
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.
262     */
263    public static ActivityOptions makeCustomAnimation(Context context,
264            int enterResId, int exitResId) {
265        return makeCustomAnimation(context, enterResId, exitResId, null, null);
266    }
267
268    /**
269     * Create an ActivityOptions specifying a custom animation to run when
270     * the activity is displayed.
271     *
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.
285     * @hide
286     */
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);
295        return opts;
296    }
297
298    /**
299     * Creates an ActivityOptions specifying a custom animation to run in place on an existing
300     * activity.
301     *
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.
308     * @hide
309     */
310    public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
311        if (animId == 0) {
312            throw new RuntimeException("You must specify a valid animation.");
313        }
314
315        ActivityOptions opts = new ActivityOptions();
316        opts.mPackageName = context.getPackageName();
317        opts.mAnimationType = ANIM_CUSTOM_IN_PLACE;
318        opts.mCustomInPlaceResId = animId;
319        return opts;
320    }
321
322    private void setOnAnimationStartedListener(final Handler handler,
323            final OnAnimationStartedListener listener) {
324        if (listener != null) {
325            mAnimationStartedListener = new IRemoteCallback.Stub() {
326                @Override
327                public void sendResult(Bundle data) throws RemoteException {
328                    handler.post(new Runnable() {
329                        @Override public void run() {
330                            listener.onAnimationStarted();
331                        }
332                    });
333                }
334            };
335        }
336    }
337
338    /**
339     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
340     * to find out when the given animation has started running.
341     * @hide
342     */
343    public interface OnAnimationStartedListener {
344        void onAnimationStarted();
345    }
346
347    private void setOnAnimationFinishedListener(final Handler handler,
348            final OnAnimationFinishedListener listener) {
349        if (listener != null) {
350            mAnimationFinishedListener = new IRemoteCallback.Stub() {
351                @Override
352                public void sendResult(Bundle data) throws RemoteException {
353                    handler.post(new Runnable() {
354                        @Override
355                        public void run() {
356                            listener.onAnimationFinished();
357                        }
358                    });
359                }
360            };
361        }
362    }
363
364    /**
365     * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation}
366     * to find out when the given animation has drawn its last frame.
367     * @hide
368     */
369    public interface OnAnimationFinishedListener {
370        void onAnimationFinished();
371    }
372
373    /**
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.
377     *
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.
382     *
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.
391     */
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;
401        opts.mWidth = width;
402        opts.mHeight = height;
403        return opts;
404    }
405
406    /**
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.
410     *
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.
419     */
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;
428        opts.mWidth = width;
429        opts.mHeight = height;
430        return opts;
431    }
432
433    /**
434     * Create an ActivityOptions specifying an animation where a thumbnail
435     * is scaled from a given position to the new activity window that is
436     * being started.
437     *
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.
442     *
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
446     * of the animation.
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.
451     */
452    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
453            Bitmap thumbnail, int startX, int startY) {
454        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
455    }
456
457    /**
458     * Create an ActivityOptions specifying an animation where a thumbnail
459     * is scaled from a given position to the new activity window that is
460     * being started.
461     *
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
465     * of the animation.
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.
473     * @hide
474     */
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);
478    }
479
480    /**
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.
483     *
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
487     * of the animation.
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.
495     * @hide
496     */
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);
500    }
501
502    private static ActivityOptions makeThumbnailAnimation(View source,
503            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
504            boolean scaleUp) {
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);
514        return opts;
515    }
516
517    /**
518     * Create an ActivityOptions specifying an animation where the new activity
519     * window and a thumbnail is aspect-scaled to a new location.
520     *
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
524     * of the animation.
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.
534     * @hide
535     */
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);
541    }
542
543    /**
544     * Create an ActivityOptions specifying an animation where the new activity
545     * window and a thumbnail is aspect-scaled to a new location.
546     *
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
550     * of the animation.
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.
560     * @hide
561     */
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);
567    }
568
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);
584        return opts;
585    }
586
587    /** @hide */
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);
598        return opts;
599    }
600
601    /**
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.
607     *
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
614     *                          must not be null.
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)
619     */
620    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
621            View sharedElement, String sharedElementName) {
622        return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
623    }
624
625    /**
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.
631     *
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)
643     */
644    @SafeVarargs
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);
650        return opts;
651    }
652
653    /**
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.
658     * <p>
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.
661     *
662     * @hide
663     */
664    @SafeVarargs
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) {
670            return opts;
671        }
672        final ExitTransitionCoordinator exit =
673                makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
674        if (exit != null) {
675            HideWindowListener listener = new HideWindowListener(window, exit);
676            exit.setHideSharedElementsCallback(listener);
677            exit.startExit();
678        }
679        return opts;
680    }
681
682    /**
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.
686     *
687     * @hide
688     */
689    public static void stopSharedElementAnimation(Window window) {
690        final View decorView = window.getDecorView();
691        if (decorView == null) {
692            return;
693        }
694        final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
695                decorView.getTag(com.android.internal.R.id.cross_task_transition);
696        if (exit != null) {
697            exit.cancelPendingTransitions();
698            decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
699            TransitionManager.endTransitions((ViewGroup) decorView);
700            exit.resetViews();
701            exit.clearState();
702            decorView.setVisibility(View.VISIBLE);
703        }
704    }
705
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;
711            return null;
712        }
713        opts.mAnimationType = ANIM_SCENE_TRANSITION;
714
715        ArrayList<String> names = new ArrayList<String>();
716        ArrayList<View> views = new ArrayList<View>();
717
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");
724                }
725                names.add(sharedElementName);
726                View view = sharedElement.first;
727                if (view == null) {
728                    throw new IllegalArgumentException("Shared element must not be null");
729                }
730                views.add(sharedElement.first);
731            }
732        }
733
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;
741        } else {
742            opts.mExitCoordinatorIndex =
743                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
744        }
745        return exit;
746    }
747
748    /** @hide */
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);
761        return opts;
762    }
763
764    /**
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.
769     *
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>.
773     */
774    public static ActivityOptions makeTaskLaunchBehind() {
775        final ActivityOptions opts = new ActivityOptions();
776        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
777        return opts;
778    }
779
780    /**
781     * Create a basic ActivityOptions that has no special animation associated with it.
782     * Other options can still be set.
783     */
784    public static ActivityOptions makeBasic() {
785        final ActivityOptions opts = new ActivityOptions();
786        return opts;
787    }
788
789    /** @hide */
790    public boolean getLaunchTaskBehind() {
791        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
792    }
793
794    private ActivityOptions() {
795    }
796
797    /** @hide */
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);
802
803        mPackageName = opts.getString(KEY_PACKAGE_NAME);
804        try {
805            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
806        } catch (RuntimeException e) {
807            Slog.w(TAG, e);
808        }
809        mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
810        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
811        switch (mAnimationType) {
812            case ANIM_CUSTOM:
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));
817                break;
818
819            case ANIM_CUSTOM_IN_PLACE:
820                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
821                break;
822
823            case ANIM_SCALE_UP:
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);
829                break;
830
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));
842                break;
843
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);
851                break;
852        }
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];
862            }
863        }
864        if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
865            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
866                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
867        }
868        mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
869    }
870
871    /**
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.
875     * <p>
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.
880     */
881    public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
882        mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
883        return this;
884    }
885
886    /** @hide */
887    public String getPackageName() {
888        return mPackageName;
889    }
890
891    /**
892     * Returns the bounds that should be used to launch the activity.
893     * @see #setLaunchBounds(Rect)
894     * @return Bounds used to launch the activity.
895     */
896    @Nullable
897    public Rect getLaunchBounds() {
898        return mLaunchBounds;
899    }
900
901    /** @hide */
902    public int getAnimationType() {
903        return mAnimationType;
904    }
905
906    /** @hide */
907    public int getCustomEnterResId() {
908        return mCustomEnterResId;
909    }
910
911    /** @hide */
912    public int getCustomExitResId() {
913        return mCustomExitResId;
914    }
915
916    /** @hide */
917    public int getCustomInPlaceResId() {
918        return mCustomInPlaceResId;
919    }
920
921    /** @hide */
922    public Bitmap getThumbnail() {
923        return mThumbnail;
924    }
925
926    /** @hide */
927    public int getStartX() {
928        return mStartX;
929    }
930
931    /** @hide */
932    public int getStartY() {
933        return mStartY;
934    }
935
936    /** @hide */
937    public int getWidth() {
938        return mWidth;
939    }
940
941    /** @hide */
942    public int getHeight() {
943        return mHeight;
944    }
945
946    /** @hide */
947    public IRemoteCallback getOnAnimationStartListener() {
948        return mAnimationStartedListener;
949    }
950
951    /** @hide */
952    public IRemoteCallback getAnimationFinishedListener() {
953        return mAnimationFinishedListener;
954    }
955
956    /** @hide */
957    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
958
959    /** @hide */
960    public void abort() {
961        if (mAnimationStartedListener != null) {
962            try {
963                mAnimationStartedListener.sendResult(null);
964            } catch (RemoteException e) {
965            }
966        }
967    }
968
969    /** @hide */
970    public boolean isReturning() {
971        return mIsReturning;
972    }
973
974    /**
975     * Returns whether or not the ActivityOptions was created with
976     * {@link #startSharedElementAnimation(Window, Pair[])}.
977     *
978     * @hide
979     */
980    boolean isCrossTask() {
981        return mExitCoordinatorIndex < 0;
982    }
983
984    /** @hide */
985    public ArrayList<String> getSharedElementNames() {
986        return mSharedElementNames;
987    }
988
989    /** @hide */
990    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
991
992    /** @hide */
993    public int getResultCode() { return mResultCode; }
994
995    /** @hide */
996    public Intent getResultData() { return mResultData; }
997
998    /** @hide */
999    public PendingIntent getUsageTimeReport() {
1000        return mUsageTimeReport;
1001    }
1002
1003    /** @hide */
1004    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
1005
1006    /** @hide */
1007    public static ActivityOptions fromBundle(Bundle bOptions) {
1008        return bOptions != null ? new ActivityOptions(bOptions) : null;
1009    }
1010
1011    /** @hide */
1012    public static void abort(ActivityOptions options) {
1013        if (options != null) {
1014            options.abort();
1015        }
1016    }
1017
1018    /** @hide */
1019    public int getLaunchStackId() {
1020        return mLaunchStackId;
1021    }
1022
1023    /** @hide */
1024    @TestApi
1025    public void setLaunchStackId(int launchStackId) {
1026        mLaunchStackId = launchStackId;
1027    }
1028
1029    /**
1030     * Sets the task the activity will be launched in.
1031     * @hide
1032     */
1033    public void setLaunchTaskId(int taskId) {
1034        mLaunchTaskId = taskId;
1035    }
1036
1037    /**
1038     * @hide
1039     */
1040    public int getLaunchTaskId() {
1041        return mLaunchTaskId;
1042    }
1043
1044    /**
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.
1048     * @hide
1049     */
1050    public void setTaskOverlay(boolean taskOverlay) {
1051        mTaskOverlay = taskOverlay;
1052    }
1053
1054    /**
1055     * @hide
1056     */
1057    public boolean getTaskOverlay() {
1058        return mTaskOverlay;
1059    }
1060
1061    /** @hide */
1062    public int getDockCreateMode() {
1063        return mDockCreateMode;
1064    }
1065
1066    /** @hide */
1067    public void setDockCreateMode(int dockCreateMode) {
1068        mDockCreateMode = dockCreateMode;
1069    }
1070
1071    /**
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.
1075     */
1076    public void update(ActivityOptions otherOptions) {
1077        if (otherOptions.mPackageName != null) {
1078            mPackageName = otherOptions.mPackageName;
1079        }
1080        mUsageTimeReport = otherOptions.mUsageTimeReport;
1081        mTransitionReceiver = null;
1082        mSharedElementNames = null;
1083        mIsReturning = false;
1084        mResultData = null;
1085        mResultCode = 0;
1086        mExitCoordinatorIndex = 0;
1087        mAnimationType = otherOptions.mAnimationType;
1088        switch (otherOptions.mAnimationType) {
1089            case ANIM_CUSTOM:
1090                mCustomEnterResId = otherOptions.mCustomEnterResId;
1091                mCustomExitResId = otherOptions.mCustomExitResId;
1092                mThumbnail = null;
1093                if (mAnimationStartedListener != null) {
1094                    try {
1095                        mAnimationStartedListener.sendResult(null);
1096                    } catch (RemoteException e) {
1097                    }
1098                }
1099                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1100                break;
1101            case ANIM_CUSTOM_IN_PLACE:
1102                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
1103                break;
1104            case ANIM_SCALE_UP:
1105                mStartX = otherOptions.mStartX;
1106                mStartY = otherOptions.mStartY;
1107                mWidth = otherOptions.mWidth;
1108                mHeight = otherOptions.mHeight;
1109                if (mAnimationStartedListener != null) {
1110                    try {
1111                        mAnimationStartedListener.sendResult(null);
1112                    } catch (RemoteException e) {
1113                    }
1114                }
1115                mAnimationStartedListener = null;
1116                break;
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) {
1127                    try {
1128                        mAnimationStartedListener.sendResult(null);
1129                    } catch (RemoteException e) {
1130                    }
1131                }
1132                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1133                break;
1134            case ANIM_SCENE_TRANSITION:
1135                mTransitionReceiver = otherOptions.mTransitionReceiver;
1136                mSharedElementNames = otherOptions.mSharedElementNames;
1137                mIsReturning = otherOptions.mIsReturning;
1138                mThumbnail = null;
1139                mAnimationStartedListener = null;
1140                mResultData = otherOptions.mResultData;
1141                mResultCode = otherOptions.mResultCode;
1142                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1143                break;
1144        }
1145        mAnimSpecs = otherOptions.mAnimSpecs;
1146        mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1147    }
1148
1149    /**
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.
1156     */
1157    public Bundle toBundle() {
1158        if (mAnimationType == ANIM_DEFAULT) {
1159            return null;
1160        }
1161        Bundle b = new Bundle();
1162        if (mPackageName != null) {
1163            b.putString(KEY_PACKAGE_NAME, mPackageName);
1164        }
1165        if (mLaunchBounds != null) {
1166            b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1167        }
1168        b.putInt(KEY_ANIM_TYPE, mAnimationType);
1169        if (mUsageTimeReport != null) {
1170            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1171        }
1172        switch (mAnimationType) {
1173            case ANIM_CUSTOM:
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);
1178                break;
1179            case ANIM_CUSTOM_IN_PLACE:
1180                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1181                break;
1182            case ANIM_SCALE_UP:
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);
1188                break;
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);
1200                break;
1201            case ANIM_SCENE_TRANSITION:
1202                if (mTransitionReceiver != null) {
1203                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1204                }
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);
1210                break;
1211        }
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);
1218        }
1219        if (mAnimationFinishedListener != null) {
1220            b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1221        }
1222        b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
1223
1224        return b;
1225    }
1226
1227    /**
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}
1231     * filled in.
1232     *
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>
1241     *
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>
1248     *
1249     * @param receiver A broadcast receiver that willl receive the report.
1250     */
1251    public void requestUsageTimeReport(PendingIntent receiver) {
1252        mUsageTimeReport = receiver;
1253    }
1254
1255    /**
1256     * Return the filtered options only meant to be seen by the target activity itself
1257     * @hide
1258     */
1259    public ActivityOptions forTargetActivity() {
1260        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1261            final ActivityOptions result = new ActivityOptions();
1262            result.update(this);
1263            return result;
1264        }
1265
1266        return null;
1267    }
1268
1269    /**
1270     * Returns the rotation animation set by {@link setRotationAnimationHint} or -1
1271     * if unspecified.
1272     * @hide
1273     */
1274    public int getRotationAnimationHint() {
1275        return mRotationAnimationHint;
1276    }
1277
1278
1279    /**
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
1283     * animation values.
1284     * @hide
1285     */
1286    public void setRotationAnimationHint(int hint) {
1287        mRotationAnimationHint = hint;
1288    }
1289
1290    /** @hide */
1291    @Override
1292    public String toString() {
1293        return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1294                + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1295                + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1296    }
1297
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;
1306
1307        public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
1308            mWindow = window;
1309            mExit = exit;
1310            mSharedElements = new ArrayList<>(exit.mSharedElements);
1311            Transition transition = mWindow.getExitTransition();
1312            if (transition != null) {
1313                transition.addListener(this);
1314                mWaitingForTransition = true;
1315            } else {
1316                mWaitingForTransition = false;
1317            }
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");
1323                }
1324                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
1325            }
1326        }
1327
1328        @Override
1329        public void onTransitionEnd(Transition transition) {
1330            mTransitionEnded = true;
1331            hideWhenDone();
1332            transition.removeListener(this);
1333        }
1334
1335        @Override
1336        public void hideSharedElements() {
1337            mSharedElementHidden = true;
1338            hideWhenDone();
1339        }
1340
1341        private void hideWhenDone() {
1342            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
1343                mExit.resetViews();
1344                int numSharedElements = mSharedElements.size();
1345                for (int i = 0; i < numSharedElements; i++) {
1346                    View view = mSharedElements.get(i);
1347                    view.requestLayout();
1348                }
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);
1354                }
1355            }
1356        }
1357    }
1358}
1359