ActivityOptions.java revision 04073dc5be4c8faad41f549617ed1c3ef999d6b2
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
197    /** @hide */
198    public static final int ANIM_NONE = 0;
199    /** @hide */
200    public static final int ANIM_CUSTOM = 1;
201    /** @hide */
202    public static final int ANIM_SCALE_UP = 2;
203    /** @hide */
204    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
205    /** @hide */
206    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
207    /** @hide */
208    public static final int ANIM_SCENE_TRANSITION = 5;
209    /** @hide */
210    public static final int ANIM_DEFAULT = 6;
211    /** @hide */
212    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
213    /** @hide */
214    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
215    /** @hide */
216    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
217    /** @hide */
218    public static final int ANIM_CUSTOM_IN_PLACE = 10;
219    /** @hide */
220    public static final int ANIM_CLIP_REVEAL = 11;
221
222    private String mPackageName;
223    private Rect mLaunchBounds;
224    private int mAnimationType = ANIM_NONE;
225    private int mCustomEnterResId;
226    private int mCustomExitResId;
227    private int mCustomInPlaceResId;
228    private Bitmap mThumbnail;
229    private int mStartX;
230    private int mStartY;
231    private int mWidth;
232    private int mHeight;
233    private IRemoteCallback mAnimationStartedListener;
234    private IRemoteCallback mAnimationFinishedListener;
235    private ResultReceiver mTransitionReceiver;
236    private boolean mIsReturning;
237    private ArrayList<String> mSharedElementNames;
238    private Intent mResultData;
239    private int mResultCode;
240    private int mExitCoordinatorIndex;
241    private PendingIntent mUsageTimeReport;
242    private int mLaunchStackId = INVALID_STACK_ID;
243    private int mLaunchTaskId = -1;
244    private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
245    private boolean mTaskOverlay;
246    private AppTransitionAnimationSpec mAnimSpecs[];
247
248    /**
249     * Create an ActivityOptions specifying a custom animation to run when
250     * the activity is displayed.
251     *
252     * @param context Who is defining this.  This is the application that the
253     * animation resources will be loaded from.
254     * @param enterResId A resource ID of the animation resource to use for
255     * the incoming activity.  Use 0 for no animation.
256     * @param exitResId A resource ID of the animation resource to use for
257     * the outgoing activity.  Use 0 for no animation.
258     * @return Returns a new ActivityOptions object that you can use to
259     * supply these options as the options Bundle when starting an activity.
260     */
261    public static ActivityOptions makeCustomAnimation(Context context,
262            int enterResId, int exitResId) {
263        return makeCustomAnimation(context, enterResId, exitResId, null, null);
264    }
265
266    /**
267     * Create an ActivityOptions specifying a custom animation to run when
268     * the activity is displayed.
269     *
270     * @param context Who is defining this.  This is the application that the
271     * animation resources will be loaded from.
272     * @param enterResId A resource ID of the animation resource to use for
273     * the incoming activity.  Use 0 for no animation.
274     * @param exitResId A resource ID of the animation resource to use for
275     * the outgoing activity.  Use 0 for no animation.
276     * @param handler If <var>listener</var> is non-null this must be a valid
277     * Handler on which to dispatch the callback; otherwise it should be null.
278     * @param listener Optional OnAnimationStartedListener to find out when the
279     * requested animation has started running.  If for some reason the animation
280     * is not executed, the callback will happen immediately.
281     * @return Returns a new ActivityOptions object that you can use to
282     * supply these options as the options Bundle when starting an activity.
283     * @hide
284     */
285    public static ActivityOptions makeCustomAnimation(Context context,
286            int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
287        ActivityOptions opts = new ActivityOptions();
288        opts.mPackageName = context.getPackageName();
289        opts.mAnimationType = ANIM_CUSTOM;
290        opts.mCustomEnterResId = enterResId;
291        opts.mCustomExitResId = exitResId;
292        opts.setOnAnimationStartedListener(handler, listener);
293        return opts;
294    }
295
296    /**
297     * Creates an ActivityOptions specifying a custom animation to run in place on an existing
298     * activity.
299     *
300     * @param context Who is defining this.  This is the application that the
301     * animation resources will be loaded from.
302     * @param animId A resource ID of the animation resource to use for
303     * the incoming activity.
304     * @return Returns a new ActivityOptions object that you can use to
305     * supply these options as the options Bundle when running an in-place animation.
306     * @hide
307     */
308    public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
309        if (animId == 0) {
310            throw new RuntimeException("You must specify a valid animation.");
311        }
312
313        ActivityOptions opts = new ActivityOptions();
314        opts.mPackageName = context.getPackageName();
315        opts.mAnimationType = ANIM_CUSTOM_IN_PLACE;
316        opts.mCustomInPlaceResId = animId;
317        return opts;
318    }
319
320    private void setOnAnimationStartedListener(final Handler handler,
321            final OnAnimationStartedListener listener) {
322        if (listener != null) {
323            mAnimationStartedListener = new IRemoteCallback.Stub() {
324                @Override
325                public void sendResult(Bundle data) throws RemoteException {
326                    handler.post(new Runnable() {
327                        @Override public void run() {
328                            listener.onAnimationStarted();
329                        }
330                    });
331                }
332            };
333        }
334    }
335
336    /**
337     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
338     * to find out when the given animation has started running.
339     * @hide
340     */
341    public interface OnAnimationStartedListener {
342        void onAnimationStarted();
343    }
344
345    private void setOnAnimationFinishedListener(final Handler handler,
346            final OnAnimationFinishedListener listener) {
347        if (listener != null) {
348            mAnimationFinishedListener = new IRemoteCallback.Stub() {
349                @Override
350                public void sendResult(Bundle data) throws RemoteException {
351                    handler.post(new Runnable() {
352                        @Override
353                        public void run() {
354                            listener.onAnimationFinished();
355                        }
356                    });
357                }
358            };
359        }
360    }
361
362    /**
363     * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation}
364     * to find out when the given animation has drawn its last frame.
365     * @hide
366     */
367    public interface OnAnimationFinishedListener {
368        void onAnimationFinished();
369    }
370
371    /**
372     * Create an ActivityOptions specifying an animation where the new
373     * activity is scaled from a small originating area of the screen to
374     * its final full representation.
375     *
376     * <p>If the Intent this is being used with has not set its
377     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
378     * those bounds will be filled in for you based on the initial
379     * bounds passed in here.
380     *
381     * @param source The View that the new activity is animating from.  This
382     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
383     * @param startX The x starting location of the new activity, relative to <var>source</var>.
384     * @param startY The y starting location of the activity, relative to <var>source</var>.
385     * @param width The initial width of the new activity.
386     * @param height The initial height of the new activity.
387     * @return Returns a new ActivityOptions object that you can use to
388     * supply these options as the options Bundle when starting an activity.
389     */
390    public static ActivityOptions makeScaleUpAnimation(View source,
391            int startX, int startY, int width, int height) {
392        ActivityOptions opts = new ActivityOptions();
393        opts.mPackageName = source.getContext().getPackageName();
394        opts.mAnimationType = ANIM_SCALE_UP;
395        int[] pts = new int[2];
396        source.getLocationOnScreen(pts);
397        opts.mStartX = pts[0] + startX;
398        opts.mStartY = pts[1] + startY;
399        opts.mWidth = width;
400        opts.mHeight = height;
401        return opts;
402    }
403
404    /**
405     * Create an ActivityOptions specifying an animation where the new
406     * activity is revealed from a small originating area of the screen to
407     * its final full representation.
408     *
409     * @param source The View that the new activity is animating from.  This
410     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
411     * @param startX The x starting location of the new activity, relative to <var>source</var>.
412     * @param startY The y starting location of the activity, relative to <var>source</var>.
413     * @param width The initial width of the new activity.
414     * @param height The initial height of the new activity.
415     * @return Returns a new ActivityOptions object that you can use to
416     * supply these options as the options Bundle when starting an activity.
417     */
418    public static ActivityOptions makeClipRevealAnimation(View source,
419            int startX, int startY, int width, int height) {
420        ActivityOptions opts = new ActivityOptions();
421        opts.mAnimationType = ANIM_CLIP_REVEAL;
422        int[] pts = new int[2];
423        source.getLocationOnScreen(pts);
424        opts.mStartX = pts[0] + startX;
425        opts.mStartY = pts[1] + startY;
426        opts.mWidth = width;
427        opts.mHeight = height;
428        return opts;
429    }
430
431    /**
432     * Create an ActivityOptions specifying an animation where a thumbnail
433     * is scaled from a given position to the new activity window that is
434     * being started.
435     *
436     * <p>If the Intent this is being used with has not set its
437     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
438     * those bounds will be filled in for you based on the initial
439     * thumbnail location and size provided here.
440     *
441     * @param source The View that this thumbnail is animating from.  This
442     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
443     * @param thumbnail The bitmap that will be shown as the initial thumbnail
444     * of the animation.
445     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
446     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
447     * @return Returns a new ActivityOptions object that you can use to
448     * supply these options as the options Bundle when starting an activity.
449     */
450    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
451            Bitmap thumbnail, int startX, int startY) {
452        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
453    }
454
455    /**
456     * Create an ActivityOptions specifying an animation where a thumbnail
457     * is scaled from a given position to the new activity window that is
458     * being started.
459     *
460     * @param source The View that this thumbnail is animating from.  This
461     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
462     * @param thumbnail The bitmap that will be shown as the initial thumbnail
463     * of the animation.
464     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
465     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
466     * @param listener Optional OnAnimationStartedListener to find out when the
467     * requested animation has started running.  If for some reason the animation
468     * is not executed, the callback will happen immediately.
469     * @return Returns a new ActivityOptions object that you can use to
470     * supply these options as the options Bundle when starting an activity.
471     * @hide
472     */
473    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
474            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
475        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
476    }
477
478    /**
479     * Create an ActivityOptions specifying an animation where an activity window
480     * is scaled from a given position to a thumbnail at a specified location.
481     *
482     * @param source The View that this thumbnail is animating to.  This
483     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
484     * @param thumbnail The bitmap that will be shown as the final thumbnail
485     * of the animation.
486     * @param startX The x end location of the bitmap, relative to <var>source</var>.
487     * @param startY The y end location of the bitmap, relative to <var>source</var>.
488     * @param listener Optional OnAnimationStartedListener to find out when the
489     * requested animation has started running.  If for some reason the animation
490     * is not executed, the callback will happen immediately.
491     * @return Returns a new ActivityOptions object that you can use to
492     * supply these options as the options Bundle when starting an activity.
493     * @hide
494     */
495    public static ActivityOptions makeThumbnailScaleDownAnimation(View source,
496            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
497        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false);
498    }
499
500    private static ActivityOptions makeThumbnailAnimation(View source,
501            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
502            boolean scaleUp) {
503        ActivityOptions opts = new ActivityOptions();
504        opts.mPackageName = source.getContext().getPackageName();
505        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
506        opts.mThumbnail = thumbnail;
507        int[] pts = new int[2];
508        source.getLocationOnScreen(pts);
509        opts.mStartX = pts[0] + startX;
510        opts.mStartY = pts[1] + startY;
511        opts.setOnAnimationStartedListener(source.getHandler(), listener);
512        return opts;
513    }
514
515    /**
516     * Create an ActivityOptions specifying an animation where the new activity
517     * window and a thumbnail is aspect-scaled to a new location.
518     *
519     * @param source The View that this thumbnail is animating from.  This
520     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
521     * @param thumbnail The bitmap that will be shown as the initial thumbnail
522     * of the animation.
523     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
524     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
525     * @param handler If <var>listener</var> is non-null this must be a valid
526     * Handler on which to dispatch the callback; otherwise it should be null.
527     * @param listener Optional OnAnimationStartedListener to find out when the
528     * requested animation has started running.  If for some reason the animation
529     * is not executed, the callback will happen immediately.
530     * @return Returns a new ActivityOptions object that you can use to
531     * supply these options as the options Bundle when starting an activity.
532     * @hide
533     */
534    public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source,
535            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
536            Handler handler, OnAnimationStartedListener listener) {
537        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
538                targetWidth, targetHeight, handler, listener, true);
539    }
540
541    /**
542     * Create an ActivityOptions specifying an animation where the new activity
543     * window and a thumbnail is aspect-scaled to a new location.
544     *
545     * @param source The View that this thumbnail is animating to.  This
546     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
547     * @param thumbnail The bitmap that will be shown as the final thumbnail
548     * of the animation.
549     * @param startX The x end location of the bitmap, relative to <var>source</var>.
550     * @param startY The y end location of the bitmap, relative to <var>source</var>.
551     * @param handler If <var>listener</var> is non-null this must be a valid
552     * Handler on which to dispatch the callback; otherwise it should be null.
553     * @param listener Optional OnAnimationStartedListener to find out when the
554     * requested animation has started running.  If for some reason the animation
555     * is not executed, the callback will happen immediately.
556     * @return Returns a new ActivityOptions object that you can use to
557     * supply these options as the options Bundle when starting an activity.
558     * @hide
559     */
560    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
561            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
562            Handler handler, OnAnimationStartedListener listener) {
563        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
564                targetWidth, targetHeight, handler, listener, false);
565    }
566
567    private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
568            int startX, int startY, int targetWidth, int targetHeight,
569            Handler handler, OnAnimationStartedListener listener, boolean scaleUp) {
570        ActivityOptions opts = new ActivityOptions();
571        opts.mPackageName = source.getContext().getPackageName();
572        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
573                ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
574        opts.mThumbnail = thumbnail;
575        int[] pts = new int[2];
576        source.getLocationOnScreen(pts);
577        opts.mStartX = pts[0] + startX;
578        opts.mStartY = pts[1] + startY;
579        opts.mWidth = targetWidth;
580        opts.mHeight = targetHeight;
581        opts.setOnAnimationStartedListener(handler, listener);
582        return opts;
583    }
584
585    /** @hide */
586    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
587            AppTransitionAnimationSpec[] specs, Handler handler,
588            OnAnimationStartedListener onAnimationStartedListener,
589            OnAnimationFinishedListener onAnimationFinishedListener) {
590        ActivityOptions opts = new ActivityOptions();
591        opts.mPackageName = source.getContext().getPackageName();
592        opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
593        opts.mAnimSpecs = specs;
594        opts.setOnAnimationStartedListener(handler, onAnimationStartedListener);
595        opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener);
596        return opts;
597    }
598
599    /**
600     * Create an ActivityOptions to transition between Activities using cross-Activity scene
601     * animations. This method carries the position of one shared element to the started Activity.
602     * The position of <code>sharedElement</code> will be used as the epicenter for the
603     * exit Transition. The position of the shared element in the launched Activity will be the
604     * epicenter of its entering Transition.
605     *
606     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
607     * enabled on the calling Activity to cause an exit transition. The same must be in
608     * the called Activity to get an entering transition.</p>
609     * @param activity The Activity whose window contains the shared elements.
610     * @param sharedElement The View to transition to the started Activity.
611     * @param sharedElementName The shared element name as used in the target Activity. This
612     *                          must not be null.
613     * @return Returns a new ActivityOptions object that you can use to
614     *         supply these options as the options Bundle when starting an activity.
615     * @see android.transition.Transition#setEpicenterCallback(
616     *          android.transition.Transition.EpicenterCallback)
617     */
618    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
619            View sharedElement, String sharedElementName) {
620        return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
621    }
622
623    /**
624     * Create an ActivityOptions to transition between Activities using cross-Activity scene
625     * animations. This method carries the position of multiple shared elements to the started
626     * Activity. The position of the first element in sharedElements
627     * will be used as the epicenter for the exit Transition. The position of the associated
628     * shared element in the launched Activity will be the epicenter of its entering Transition.
629     *
630     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
631     * enabled on the calling Activity to cause an exit transition. The same must be in
632     * the called Activity to get an entering transition.</p>
633     * @param activity The Activity whose window contains the shared elements.
634     * @param sharedElements The names of the shared elements to transfer to the called
635     *                       Activity and their associated Views. The Views must each have
636     *                       a unique shared element name.
637     * @return Returns a new ActivityOptions object that you can use to
638     *         supply these options as the options Bundle when starting an activity.
639     * @see android.transition.Transition#setEpicenterCallback(
640     *          android.transition.Transition.EpicenterCallback)
641     */
642    @SafeVarargs
643    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
644            Pair<View, String>... sharedElements) {
645        ActivityOptions opts = new ActivityOptions();
646        makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
647                activity.mExitTransitionListener, sharedElements);
648        return opts;
649    }
650
651    /**
652     * Call this immediately prior to startActivity to begin a shared element transition
653     * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS.
654     * The exit transition will start immediately and the shared element transition will
655     * start once the launched Activity's shared element is ready.
656     * <p>
657     * When all transitions have completed and the shared element has been transfered,
658     * the window's decor View will have its visibility set to View.GONE.
659     *
660     * @hide
661     */
662    @SafeVarargs
663    public static ActivityOptions startSharedElementAnimation(Window window,
664            Pair<View, String>... sharedElements) {
665        ActivityOptions opts = new ActivityOptions();
666        final View decorView = window.getDecorView();
667        if (decorView == null) {
668            return opts;
669        }
670        final ExitTransitionCoordinator exit =
671                makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
672        if (exit != null) {
673            HideWindowListener listener = new HideWindowListener(window, exit);
674            exit.setHideSharedElementsCallback(listener);
675            exit.startExit();
676        }
677        return opts;
678    }
679
680    /**
681     * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
682     * animation must be stopped and the Views reset. This can happen if there was an error
683     * from startActivity or a springboard activity and the animation should stop and reset.
684     *
685     * @hide
686     */
687    public static void stopSharedElementAnimation(Window window) {
688        final View decorView = window.getDecorView();
689        if (decorView == null) {
690            return;
691        }
692        final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
693                decorView.getTag(com.android.internal.R.id.cross_task_transition);
694        if (exit != null) {
695            exit.cancelPendingTransitions();
696            decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
697            TransitionManager.endTransitions((ViewGroup) decorView);
698            exit.resetViews();
699            exit.clearState();
700            decorView.setVisibility(View.VISIBLE);
701        }
702    }
703
704    static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
705            ActivityOptions opts, SharedElementCallback callback,
706            Pair<View, String>[] sharedElements) {
707        if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
708            opts.mAnimationType = ANIM_DEFAULT;
709            return null;
710        }
711        opts.mAnimationType = ANIM_SCENE_TRANSITION;
712
713        ArrayList<String> names = new ArrayList<String>();
714        ArrayList<View> views = new ArrayList<View>();
715
716        if (sharedElements != null) {
717            for (int i = 0; i < sharedElements.length; i++) {
718                Pair<View, String> sharedElement = sharedElements[i];
719                String sharedElementName = sharedElement.second;
720                if (sharedElementName == null) {
721                    throw new IllegalArgumentException("Shared element name must not be null");
722                }
723                names.add(sharedElementName);
724                View view = sharedElement.first;
725                if (view == null) {
726                    throw new IllegalArgumentException("Shared element must not be null");
727                }
728                views.add(sharedElement.first);
729            }
730        }
731
732        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
733                callback, names, names, views, false);
734        opts.mTransitionReceiver = exit;
735        opts.mSharedElementNames = names;
736        opts.mIsReturning = (activity == null);
737        if (activity == null) {
738            opts.mExitCoordinatorIndex = -1;
739        } else {
740            opts.mExitCoordinatorIndex =
741                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
742        }
743        return exit;
744    }
745
746    /** @hide */
747    static ActivityOptions makeSceneTransitionAnimation(Activity activity,
748            ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
749            int resultCode, Intent resultData) {
750        ActivityOptions opts = new ActivityOptions();
751        opts.mAnimationType = ANIM_SCENE_TRANSITION;
752        opts.mSharedElementNames = sharedElementNames;
753        opts.mTransitionReceiver = exitCoordinator;
754        opts.mIsReturning = true;
755        opts.mResultCode = resultCode;
756        opts.mResultData = resultData;
757        opts.mExitCoordinatorIndex =
758                activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
759        return opts;
760    }
761
762    /**
763     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
764     * presented to the user but will instead be only available through the recents task list.
765     * In addition, the new task wil be affiliated with the launching activity's task.
766     * Affiliated tasks are grouped together in the recents task list.
767     *
768     * <p>This behavior is not supported for activities with {@link
769     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
770     * <code>singleInstance</code> or <code>singleTask</code>.
771     */
772    public static ActivityOptions makeTaskLaunchBehind() {
773        final ActivityOptions opts = new ActivityOptions();
774        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
775        return opts;
776    }
777
778    /**
779     * Create a basic ActivityOptions that has no special animation associated with it.
780     * Other options can still be set.
781     */
782    public static ActivityOptions makeBasic() {
783        final ActivityOptions opts = new ActivityOptions();
784        return opts;
785    }
786
787    /** @hide */
788    public boolean getLaunchTaskBehind() {
789        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
790    }
791
792    private ActivityOptions() {
793    }
794
795    /** @hide */
796    public ActivityOptions(Bundle opts) {
797        // If the remote side sent us bad parcelables, they won't get the
798        // results they want, which is their loss.
799        opts.setDefusable(true);
800
801        mPackageName = opts.getString(KEY_PACKAGE_NAME);
802        try {
803            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
804        } catch (RuntimeException e) {
805            Slog.w(TAG, e);
806        }
807        mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
808        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
809        switch (mAnimationType) {
810            case ANIM_CUSTOM:
811                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
812                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
813                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
814                        opts.getBinder(KEY_ANIM_START_LISTENER));
815                break;
816
817            case ANIM_CUSTOM_IN_PLACE:
818                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
819                break;
820
821            case ANIM_SCALE_UP:
822            case ANIM_CLIP_REVEAL:
823                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
824                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
825                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
826                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
827                break;
828
829            case ANIM_THUMBNAIL_SCALE_UP:
830            case ANIM_THUMBNAIL_SCALE_DOWN:
831            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
832            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
833                mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
834                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
835                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
836                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
837                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
838                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
839                        opts.getBinder(KEY_ANIM_START_LISTENER));
840                break;
841
842            case ANIM_SCENE_TRANSITION:
843                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
844                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
845                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
846                mResultData = opts.getParcelable(KEY_RESULT_DATA);
847                mResultCode = opts.getInt(KEY_RESULT_CODE);
848                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
849                break;
850        }
851        mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
852        mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
853        mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
854        mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
855        if (opts.containsKey(KEY_ANIM_SPECS)) {
856            Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
857            mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
858            for (int i = specs.length - 1; i >= 0; i--) {
859                mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
860            }
861        }
862        if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
863            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
864                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
865        }
866    }
867
868    /**
869     * Sets the bounds (window size) that the activity should be launched in.
870     * Rect position should be provided in pixels and in screen coordinates.
871     * Set to null explicitly for fullscreen.
872     * <p>
873     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
874     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
875     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
876     * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
877     */
878    public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
879        mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
880        return this;
881    }
882
883    /** @hide */
884    public String getPackageName() {
885        return mPackageName;
886    }
887
888    /**
889     * Returns the bounds that should be used to launch the activity.
890     * @see #setLaunchBounds(Rect)
891     * @return Bounds used to launch the activity.
892     */
893    @Nullable
894    public Rect getLaunchBounds() {
895        return mLaunchBounds;
896    }
897
898    /** @hide */
899    public int getAnimationType() {
900        return mAnimationType;
901    }
902
903    /** @hide */
904    public int getCustomEnterResId() {
905        return mCustomEnterResId;
906    }
907
908    /** @hide */
909    public int getCustomExitResId() {
910        return mCustomExitResId;
911    }
912
913    /** @hide */
914    public int getCustomInPlaceResId() {
915        return mCustomInPlaceResId;
916    }
917
918    /** @hide */
919    public Bitmap getThumbnail() {
920        return mThumbnail;
921    }
922
923    /** @hide */
924    public int getStartX() {
925        return mStartX;
926    }
927
928    /** @hide */
929    public int getStartY() {
930        return mStartY;
931    }
932
933    /** @hide */
934    public int getWidth() {
935        return mWidth;
936    }
937
938    /** @hide */
939    public int getHeight() {
940        return mHeight;
941    }
942
943    /** @hide */
944    public IRemoteCallback getOnAnimationStartListener() {
945        return mAnimationStartedListener;
946    }
947
948    /** @hide */
949    public IRemoteCallback getAnimationFinishedListener() {
950        return mAnimationFinishedListener;
951    }
952
953    /** @hide */
954    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
955
956    /** @hide */
957    public void abort() {
958        if (mAnimationStartedListener != null) {
959            try {
960                mAnimationStartedListener.sendResult(null);
961            } catch (RemoteException e) {
962            }
963        }
964    }
965
966    /** @hide */
967    public boolean isReturning() {
968        return mIsReturning;
969    }
970
971    /**
972     * Returns whether or not the ActivityOptions was created with
973     * {@link #startSharedElementAnimation(Window, Pair[])}.
974     *
975     * @hide
976     */
977    boolean isCrossTask() {
978        return mExitCoordinatorIndex < 0;
979    }
980
981    /** @hide */
982    public ArrayList<String> getSharedElementNames() {
983        return mSharedElementNames;
984    }
985
986    /** @hide */
987    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
988
989    /** @hide */
990    public int getResultCode() { return mResultCode; }
991
992    /** @hide */
993    public Intent getResultData() { return mResultData; }
994
995    /** @hide */
996    public PendingIntent getUsageTimeReport() {
997        return mUsageTimeReport;
998    }
999
1000    /** @hide */
1001    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
1002
1003    /** @hide */
1004    public static ActivityOptions fromBundle(Bundle bOptions) {
1005        return bOptions != null ? new ActivityOptions(bOptions) : null;
1006    }
1007
1008    /** @hide */
1009    public static void abort(ActivityOptions options) {
1010        if (options != null) {
1011            options.abort();
1012        }
1013    }
1014
1015    /** @hide */
1016    public int getLaunchStackId() {
1017        return mLaunchStackId;
1018    }
1019
1020    /** @hide */
1021    @TestApi
1022    public void setLaunchStackId(int launchStackId) {
1023        mLaunchStackId = launchStackId;
1024    }
1025
1026    /**
1027     * Sets the task the activity will be launched in.
1028     * @hide
1029     */
1030    public void setLaunchTaskId(int taskId) {
1031        mLaunchTaskId = taskId;
1032    }
1033
1034    /**
1035     * @hide
1036     */
1037    public int getLaunchTaskId() {
1038        return mLaunchTaskId;
1039    }
1040
1041    /**
1042     * Set's whether the activity launched with this option should be a task overlay. That is the
1043     * activity will always be the top activity of the task and doesn't cause the task to be moved
1044     * to the front when it is added.
1045     * @hide
1046     */
1047    public void setTaskOverlay(boolean taskOverlay) {
1048        mTaskOverlay = taskOverlay;
1049    }
1050
1051    /**
1052     * @hide
1053     */
1054    public boolean getTaskOverlay() {
1055        return mTaskOverlay;
1056    }
1057
1058    /** @hide */
1059    public int getDockCreateMode() {
1060        return mDockCreateMode;
1061    }
1062
1063    /** @hide */
1064    public void setDockCreateMode(int dockCreateMode) {
1065        mDockCreateMode = dockCreateMode;
1066    }
1067
1068    /**
1069     * Update the current values in this ActivityOptions from those supplied
1070     * in <var>otherOptions</var>.  Any values
1071     * defined in <var>otherOptions</var> replace those in the base options.
1072     */
1073    public void update(ActivityOptions otherOptions) {
1074        if (otherOptions.mPackageName != null) {
1075            mPackageName = otherOptions.mPackageName;
1076        }
1077        mUsageTimeReport = otherOptions.mUsageTimeReport;
1078        mTransitionReceiver = null;
1079        mSharedElementNames = null;
1080        mIsReturning = false;
1081        mResultData = null;
1082        mResultCode = 0;
1083        mExitCoordinatorIndex = 0;
1084        mAnimationType = otherOptions.mAnimationType;
1085        switch (otherOptions.mAnimationType) {
1086            case ANIM_CUSTOM:
1087                mCustomEnterResId = otherOptions.mCustomEnterResId;
1088                mCustomExitResId = otherOptions.mCustomExitResId;
1089                mThumbnail = null;
1090                if (mAnimationStartedListener != null) {
1091                    try {
1092                        mAnimationStartedListener.sendResult(null);
1093                    } catch (RemoteException e) {
1094                    }
1095                }
1096                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1097                break;
1098            case ANIM_CUSTOM_IN_PLACE:
1099                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
1100                break;
1101            case ANIM_SCALE_UP:
1102                mStartX = otherOptions.mStartX;
1103                mStartY = otherOptions.mStartY;
1104                mWidth = otherOptions.mWidth;
1105                mHeight = otherOptions.mHeight;
1106                if (mAnimationStartedListener != null) {
1107                    try {
1108                        mAnimationStartedListener.sendResult(null);
1109                    } catch (RemoteException e) {
1110                    }
1111                }
1112                mAnimationStartedListener = null;
1113                break;
1114            case ANIM_THUMBNAIL_SCALE_UP:
1115            case ANIM_THUMBNAIL_SCALE_DOWN:
1116            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1117            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1118                mThumbnail = otherOptions.mThumbnail;
1119                mStartX = otherOptions.mStartX;
1120                mStartY = otherOptions.mStartY;
1121                mWidth = otherOptions.mWidth;
1122                mHeight = otherOptions.mHeight;
1123                if (mAnimationStartedListener != null) {
1124                    try {
1125                        mAnimationStartedListener.sendResult(null);
1126                    } catch (RemoteException e) {
1127                    }
1128                }
1129                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1130                break;
1131            case ANIM_SCENE_TRANSITION:
1132                mTransitionReceiver = otherOptions.mTransitionReceiver;
1133                mSharedElementNames = otherOptions.mSharedElementNames;
1134                mIsReturning = otherOptions.mIsReturning;
1135                mThumbnail = null;
1136                mAnimationStartedListener = null;
1137                mResultData = otherOptions.mResultData;
1138                mResultCode = otherOptions.mResultCode;
1139                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1140                break;
1141        }
1142        mAnimSpecs = otherOptions.mAnimSpecs;
1143        mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1144    }
1145
1146    /**
1147     * Returns the created options as a Bundle, which can be passed to
1148     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
1149     * Context.startActivity(Intent, Bundle)} and related methods.
1150     * Note that the returned Bundle is still owned by the ActivityOptions
1151     * object; you must not modify it, but can supply it to the startActivity
1152     * methods that take an options Bundle.
1153     */
1154    public Bundle toBundle() {
1155        if (mAnimationType == ANIM_DEFAULT) {
1156            return null;
1157        }
1158        Bundle b = new Bundle();
1159        if (mPackageName != null) {
1160            b.putString(KEY_PACKAGE_NAME, mPackageName);
1161        }
1162        if (mLaunchBounds != null) {
1163            b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1164        }
1165        b.putInt(KEY_ANIM_TYPE, mAnimationType);
1166        if (mUsageTimeReport != null) {
1167            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1168        }
1169        switch (mAnimationType) {
1170            case ANIM_CUSTOM:
1171                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
1172                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
1173                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1174                        != null ? mAnimationStartedListener.asBinder() : null);
1175                break;
1176            case ANIM_CUSTOM_IN_PLACE:
1177                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1178                break;
1179            case ANIM_SCALE_UP:
1180            case ANIM_CLIP_REVEAL:
1181                b.putInt(KEY_ANIM_START_X, mStartX);
1182                b.putInt(KEY_ANIM_START_Y, mStartY);
1183                b.putInt(KEY_ANIM_WIDTH, mWidth);
1184                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1185                break;
1186            case ANIM_THUMBNAIL_SCALE_UP:
1187            case ANIM_THUMBNAIL_SCALE_DOWN:
1188            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1189            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1190                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
1191                b.putInt(KEY_ANIM_START_X, mStartX);
1192                b.putInt(KEY_ANIM_START_Y, mStartY);
1193                b.putInt(KEY_ANIM_WIDTH, mWidth);
1194                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1195                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1196                        != null ? mAnimationStartedListener.asBinder() : null);
1197                break;
1198            case ANIM_SCENE_TRANSITION:
1199                if (mTransitionReceiver != null) {
1200                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1201                }
1202                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
1203                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
1204                b.putParcelable(KEY_RESULT_DATA, mResultData);
1205                b.putInt(KEY_RESULT_CODE, mResultCode);
1206                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
1207                break;
1208        }
1209        b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
1210        b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
1211        b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
1212        b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
1213        if (mAnimSpecs != null) {
1214            b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1215        }
1216        if (mAnimationFinishedListener != null) {
1217            b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1218        }
1219
1220        return b;
1221    }
1222
1223    /**
1224     * Ask the the system track that time the user spends in the app being launched, and
1225     * report it back once done.  The report will be sent to the given receiver, with
1226     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1227     * filled in.
1228     *
1229     * <p>The time interval tracked is from launching this activity until the user leaves
1230     * that activity's flow.  They are considered to stay in the flow as long as
1231     * new activities are being launched or returned to from the original flow,
1232     * even if this crosses package or task boundaries.  For example, if the originator
1233     * starts an activity to view an image, and while there the user selects to share,
1234     * which launches their email app in a new task, and they complete the share, the
1235     * time during that entire operation will be included until they finally hit back from
1236     * the original image viewer activity.</p>
1237     *
1238     * <p>The user is considered to complete a flow once they switch to another
1239     * activity that is not part of the tracked flow.  This may happen, for example, by
1240     * using the notification shade, launcher, or recents to launch or switch to another
1241     * app.  Simply going in to these navigation elements does not break the flow (although
1242     * the launcher and recents stops time tracking of the session); it is the act of
1243     * going somewhere else that completes the tracking.</p>
1244     *
1245     * @param receiver A broadcast receiver that willl receive the report.
1246     */
1247    public void requestUsageTimeReport(PendingIntent receiver) {
1248        mUsageTimeReport = receiver;
1249    }
1250
1251    /**
1252     * Return the filtered options only meant to be seen by the target activity itself
1253     * @hide
1254     */
1255    public ActivityOptions forTargetActivity() {
1256        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1257            final ActivityOptions result = new ActivityOptions();
1258            result.update(this);
1259            return result;
1260        }
1261
1262        return null;
1263    }
1264
1265    /** @hide */
1266    @Override
1267    public String toString() {
1268        return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1269                + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1270                + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1271    }
1272
1273    private static class HideWindowListener extends Transition.TransitionListenerAdapter
1274        implements ExitTransitionCoordinator.HideSharedElementsCallback {
1275        private final Window mWindow;
1276        private final ExitTransitionCoordinator mExit;
1277        private final boolean mWaitingForTransition;
1278        private boolean mTransitionEnded;
1279        private boolean mSharedElementHidden;
1280        private ArrayList<View> mSharedElements;
1281
1282        public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
1283            mWindow = window;
1284            mExit = exit;
1285            mSharedElements = new ArrayList<>(exit.mSharedElements);
1286            Transition transition = mWindow.getExitTransition();
1287            if (transition != null) {
1288                transition.addListener(this);
1289                mWaitingForTransition = true;
1290            } else {
1291                mWaitingForTransition = false;
1292            }
1293            View decorView = mWindow.getDecorView();
1294            if (decorView != null) {
1295                if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
1296                    throw new IllegalStateException(
1297                            "Cannot start a transition while one is running");
1298                }
1299                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
1300            }
1301        }
1302
1303        @Override
1304        public void onTransitionEnd(Transition transition) {
1305            mTransitionEnded = true;
1306            hideWhenDone();
1307            transition.removeListener(this);
1308        }
1309
1310        @Override
1311        public void hideSharedElements() {
1312            mSharedElementHidden = true;
1313            hideWhenDone();
1314        }
1315
1316        private void hideWhenDone() {
1317            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
1318                mExit.resetViews();
1319                int numSharedElements = mSharedElements.size();
1320                for (int i = 0; i < numSharedElements; i++) {
1321                    View view = mSharedElements.get(i);
1322                    view.requestLayout();
1323                }
1324                View decorView = mWindow.getDecorView();
1325                if (decorView != null) {
1326                    decorView.setTagInternal(
1327                            com.android.internal.R.id.cross_task_transition, null);
1328                    decorView.setVisibility(View.GONE);
1329                }
1330            }
1331        }
1332    }
1333}
1334