ActivityOptions.java revision d64ef3ef33c50a03b4be3b2baaa93aab7319fca8
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;
20
21import android.content.Context;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.graphics.Rect;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.IRemoteCallback;
28import android.os.Parcelable;
29import android.os.RemoteException;
30import android.os.ResultReceiver;
31import android.util.Pair;
32import android.util.Slog;
33import android.view.AppTransitionAnimationSpec;
34import android.view.View;
35import android.view.Window;
36
37import java.util.ArrayList;
38
39/**
40 * Helper class for building an options Bundle that can be used with
41 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
42 * Context.startActivity(Intent, Bundle)} and related methods.
43 */
44public class ActivityOptions {
45    private static final String TAG = "ActivityOptions";
46
47    /**
48     * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
49     * the total time (in ms) the user spent in the app flow.
50     */
51    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
52
53    /**
54     * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
55     * detailed information about the time spent in each package associated with the app;
56     * each key is a package name, whose value is a long containing the time (in ms).
57     */
58    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
59
60    /**
61     * The package name that created the options.
62     * @hide
63     */
64    public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
65
66    /**
67     * The bounds that the activity should be started in. Set to null explicitly
68     * for full screen. If the key is not found, previous bounds will be preserved.
69     * @hide
70     */
71    public static final String KEY_BOUNDS = "android:activity.bounds";
72
73    /**
74     * Type of animation that arguments specify.
75     * @hide
76     */
77    public static final String KEY_ANIM_TYPE = "android:activity.animType";
78
79    /**
80     * Custom enter animation resource ID.
81     * @hide
82     */
83    public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
84
85    /**
86     * Custom exit animation resource ID.
87     * @hide
88     */
89    public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
90
91    /**
92     * Custom in-place animation resource ID.
93     * @hide
94     */
95    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
96
97    /**
98     * Bitmap for thumbnail animation.
99     * @hide
100     */
101    public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
102
103    /**
104     * Start X position of thumbnail animation.
105     * @hide
106     */
107    public static final String KEY_ANIM_START_X = "android:activity.animStartX";
108
109    /**
110     * Start Y position of thumbnail animation.
111     * @hide
112     */
113    public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
114
115    /**
116     * Initial width of the animation.
117     * @hide
118     */
119    public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
120
121    /**
122     * Initial height of the animation.
123     * @hide
124     */
125    public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
126
127    /**
128     * Callback for when animation is started.
129     * @hide
130     */
131    public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
132
133    /**
134     * Descriptions of app transition animations to be played during the activity launch.
135     */
136    private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
137
138    /**
139     * Where the docked stack should be positioned.
140     * @hide
141     */
142    private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
143
144    /**
145     * For Activity transitions, the calling Activity's TransitionListener used to
146     * notify the called Activity when the shared element and the exit transitions
147     * complete.
148     */
149    private static final String KEY_TRANSITION_COMPLETE_LISTENER
150            = "android:activity.transitionCompleteListener";
151
152    private static final String KEY_TRANSITION_IS_RETURNING
153            = "android:activity.transitionIsReturning";
154    private static final String KEY_TRANSITION_SHARED_ELEMENTS
155            = "android:activity.sharedElementNames";
156    private static final String KEY_RESULT_DATA = "android:activity.resultData";
157    private static final String KEY_RESULT_CODE = "android:activity.resultCode";
158    private static final String KEY_EXIT_COORDINATOR_INDEX
159            = "android:activity.exitCoordinatorIndex";
160
161    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
162
163    /** @hide */
164    public static final int ANIM_NONE = 0;
165    /** @hide */
166    public static final int ANIM_CUSTOM = 1;
167    /** @hide */
168    public static final int ANIM_SCALE_UP = 2;
169    /** @hide */
170    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
171    /** @hide */
172    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
173    /** @hide */
174    public static final int ANIM_SCENE_TRANSITION = 5;
175    /** @hide */
176    public static final int ANIM_DEFAULT = 6;
177    /** @hide */
178    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
179    /** @hide */
180    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
181    /** @hide */
182    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
183    /** @hide */
184    public static final int ANIM_CUSTOM_IN_PLACE = 10;
185    /** @hide */
186    public static final int ANIM_CLIP_REVEAL = 11;
187
188    private String mPackageName;
189    private boolean mHasBounds;
190    private Rect mBounds;
191    private int mAnimationType = ANIM_NONE;
192    private int mCustomEnterResId;
193    private int mCustomExitResId;
194    private int mCustomInPlaceResId;
195    private Bitmap mThumbnail;
196    private int mStartX;
197    private int mStartY;
198    private int mWidth;
199    private int mHeight;
200    private IRemoteCallback mAnimationStartedListener;
201    private ResultReceiver mTransitionReceiver;
202    private boolean mIsReturning;
203    private ArrayList<String> mSharedElementNames;
204    private Intent mResultData;
205    private int mResultCode;
206    private int mExitCoordinatorIndex;
207    private PendingIntent mUsageTimeReport;
208    private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
209    private AppTransitionAnimationSpec mAnimSpecs[];
210
211    /**
212     * Create an ActivityOptions specifying a custom animation to run when
213     * the activity is displayed.
214     *
215     * @param context Who is defining this.  This is the application that the
216     * animation resources will be loaded from.
217     * @param enterResId A resource ID of the animation resource to use for
218     * the incoming activity.  Use 0 for no animation.
219     * @param exitResId A resource ID of the animation resource to use for
220     * the outgoing activity.  Use 0 for no animation.
221     * @return Returns a new ActivityOptions object that you can use to
222     * supply these options as the options Bundle when starting an activity.
223     */
224    public static ActivityOptions makeCustomAnimation(Context context,
225            int enterResId, int exitResId) {
226        return makeCustomAnimation(context, enterResId, exitResId, null, null);
227    }
228
229    /**
230     * Create an ActivityOptions specifying a custom animation to run when
231     * the activity is displayed.
232     *
233     * @param context Who is defining this.  This is the application that the
234     * animation resources will be loaded from.
235     * @param enterResId A resource ID of the animation resource to use for
236     * the incoming activity.  Use 0 for no animation.
237     * @param exitResId A resource ID of the animation resource to use for
238     * the outgoing activity.  Use 0 for no animation.
239     * @param handler If <var>listener</var> is non-null this must be a valid
240     * Handler on which to dispatch the callback; otherwise it should be null.
241     * @param listener Optional OnAnimationStartedListener to find out when the
242     * requested animation has started running.  If for some reason the animation
243     * is not executed, the callback will happen immediately.
244     * @return Returns a new ActivityOptions object that you can use to
245     * supply these options as the options Bundle when starting an activity.
246     * @hide
247     */
248    public static ActivityOptions makeCustomAnimation(Context context,
249            int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
250        ActivityOptions opts = new ActivityOptions();
251        opts.mPackageName = context.getPackageName();
252        opts.mAnimationType = ANIM_CUSTOM;
253        opts.mCustomEnterResId = enterResId;
254        opts.mCustomExitResId = exitResId;
255        opts.setOnAnimationStartedListener(handler, listener);
256        return opts;
257    }
258
259    /**
260     * Creates an ActivityOptions specifying a custom animation to run in place on an existing
261     * activity.
262     *
263     * @param context Who is defining this.  This is the application that the
264     * animation resources will be loaded from.
265     * @param animId A resource ID of the animation resource to use for
266     * the incoming activity.
267     * @return Returns a new ActivityOptions object that you can use to
268     * supply these options as the options Bundle when running an in-place animation.
269     * @hide
270     */
271    public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
272        if (animId == 0) {
273            throw new RuntimeException("You must specify a valid animation.");
274        }
275
276        ActivityOptions opts = new ActivityOptions();
277        opts.mPackageName = context.getPackageName();
278        opts.mAnimationType = ANIM_CUSTOM_IN_PLACE;
279        opts.mCustomInPlaceResId = animId;
280        return opts;
281    }
282
283    private void setOnAnimationStartedListener(Handler handler,
284            OnAnimationStartedListener listener) {
285        if (listener != null) {
286            final Handler h = handler;
287            final OnAnimationStartedListener finalListener = listener;
288            mAnimationStartedListener = new IRemoteCallback.Stub() {
289                @Override public void sendResult(Bundle data) throws RemoteException {
290                    h.post(new Runnable() {
291                        @Override public void run() {
292                            finalListener.onAnimationStarted();
293                        }
294                    });
295                }
296            };
297        }
298    }
299
300    /**
301     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
302     * to find out when the given animation has started running.
303     * @hide
304     */
305    public interface OnAnimationStartedListener {
306        void onAnimationStarted();
307    }
308
309    /**
310     * Create an ActivityOptions specifying an animation where the new
311     * activity is scaled from a small originating area of the screen to
312     * its final full representation.
313     *
314     * <p>If the Intent this is being used with has not set its
315     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
316     * those bounds will be filled in for you based on the initial
317     * bounds passed in here.
318     *
319     * @param source The View that the new activity is animating from.  This
320     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
321     * @param startX The x starting location of the new activity, relative to <var>source</var>.
322     * @param startY The y starting location of the activity, relative to <var>source</var>.
323     * @param width The initial width of the new activity.
324     * @param height The initial height of the new activity.
325     * @return Returns a new ActivityOptions object that you can use to
326     * supply these options as the options Bundle when starting an activity.
327     */
328    public static ActivityOptions makeScaleUpAnimation(View source,
329            int startX, int startY, int width, int height) {
330        ActivityOptions opts = new ActivityOptions();
331        opts.mPackageName = source.getContext().getPackageName();
332        opts.mAnimationType = ANIM_SCALE_UP;
333        int[] pts = new int[2];
334        source.getLocationOnScreen(pts);
335        opts.mStartX = pts[0] + startX;
336        opts.mStartY = pts[1] + startY;
337        opts.mWidth = width;
338        opts.mHeight = height;
339        return opts;
340    }
341
342    /**
343     * Create an ActivityOptions specifying an animation where the new
344     * activity is revealed from a small originating area of the screen to
345     * its final full representation.
346     *
347     * @param source The View that the new activity is animating from.  This
348     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
349     * @param startX The x starting location of the new activity, relative to <var>source</var>.
350     * @param startY The y starting location of the activity, relative to <var>source</var>.
351     * @param width The initial width of the new activity.
352     * @param height The initial height of the new activity.
353     * @return Returns a new ActivityOptions object that you can use to
354     * supply these options as the options Bundle when starting an activity.
355     */
356    public static ActivityOptions makeClipRevealAnimation(View source,
357            int startX, int startY, int width, int height) {
358        ActivityOptions opts = new ActivityOptions();
359        opts.mAnimationType = ANIM_CLIP_REVEAL;
360        int[] pts = new int[2];
361        source.getLocationOnScreen(pts);
362        opts.mStartX = pts[0] + startX;
363        opts.mStartY = pts[1] + startY;
364        opts.mWidth = width;
365        opts.mHeight = height;
366        return opts;
367    }
368
369    /**
370     * Create an ActivityOptions specifying an animation where a thumbnail
371     * is scaled from a given position to the new activity window that is
372     * being started.
373     *
374     * <p>If the Intent this is being used with has not set its
375     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
376     * those bounds will be filled in for you based on the initial
377     * thumbnail location and size provided here.
378     *
379     * @param source The View that this thumbnail is animating from.  This
380     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
381     * @param thumbnail The bitmap that will be shown as the initial thumbnail
382     * of the animation.
383     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
384     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
385     * @return Returns a new ActivityOptions object that you can use to
386     * supply these options as the options Bundle when starting an activity.
387     */
388    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
389            Bitmap thumbnail, int startX, int startY) {
390        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
391    }
392
393    /**
394     * Create an ActivityOptions specifying an animation where a thumbnail
395     * is scaled from a given position to the new activity window that is
396     * being started.
397     *
398     * @param source The View that this thumbnail is animating from.  This
399     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
400     * @param thumbnail The bitmap that will be shown as the initial thumbnail
401     * of the animation.
402     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
403     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
404     * @param listener Optional OnAnimationStartedListener to find out when the
405     * requested animation has started running.  If for some reason the animation
406     * is not executed, the callback will happen immediately.
407     * @return Returns a new ActivityOptions object that you can use to
408     * supply these options as the options Bundle when starting an activity.
409     * @hide
410     */
411    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
412            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
413        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
414    }
415
416    /**
417     * Create an ActivityOptions specifying an animation where an activity window
418     * is scaled from a given position to a thumbnail at a specified location.
419     *
420     * @param source The View that this thumbnail is animating to.  This
421     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
422     * @param thumbnail The bitmap that will be shown as the final thumbnail
423     * of the animation.
424     * @param startX The x end location of the bitmap, relative to <var>source</var>.
425     * @param startY The y end location of the bitmap, relative to <var>source</var>.
426     * @param listener Optional OnAnimationStartedListener to find out when the
427     * requested animation has started running.  If for some reason the animation
428     * is not executed, the callback will happen immediately.
429     * @return Returns a new ActivityOptions object that you can use to
430     * supply these options as the options Bundle when starting an activity.
431     * @hide
432     */
433    public static ActivityOptions makeThumbnailScaleDownAnimation(View source,
434            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
435        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false);
436    }
437
438    private static ActivityOptions makeThumbnailAnimation(View source,
439            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
440            boolean scaleUp) {
441        ActivityOptions opts = new ActivityOptions();
442        opts.mPackageName = source.getContext().getPackageName();
443        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
444        opts.mThumbnail = thumbnail;
445        int[] pts = new int[2];
446        source.getLocationOnScreen(pts);
447        opts.mStartX = pts[0] + startX;
448        opts.mStartY = pts[1] + startY;
449        opts.setOnAnimationStartedListener(source.getHandler(), listener);
450        return opts;
451    }
452
453    /**
454     * Create an ActivityOptions specifying an animation where the new activity
455     * window and a thumbnail is aspect-scaled to a new location.
456     *
457     * @param source The View that this thumbnail is animating from.  This
458     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
459     * @param thumbnail The bitmap that will be shown as the initial thumbnail
460     * of the animation.
461     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
462     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
463     * @param handler If <var>listener</var> is non-null this must be a valid
464     * Handler on which to dispatch the callback; otherwise it should be null.
465     * @param listener Optional OnAnimationStartedListener to find out when the
466     * requested animation has started running.  If for some reason the animation
467     * is not executed, the callback will happen immediately.
468     * @return Returns a new ActivityOptions object that you can use to
469     * supply these options as the options Bundle when starting an activity.
470     * @hide
471     */
472    public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source,
473            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
474            Handler handler, OnAnimationStartedListener listener) {
475        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
476                targetWidth, targetHeight, handler, listener, true);
477    }
478
479    /**
480     * Create an ActivityOptions specifying an animation where the new activity
481     * window and a thumbnail is aspect-scaled to a new location.
482     *
483     * @param source The View that this thumbnail is animating to.  This
484     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
485     * @param thumbnail The bitmap that will be shown as the final thumbnail
486     * of the animation.
487     * @param startX The x end location of the bitmap, relative to <var>source</var>.
488     * @param startY The y end location of the bitmap, relative to <var>source</var>.
489     * @param handler If <var>listener</var> is non-null this must be a valid
490     * Handler on which to dispatch the callback; otherwise it should be null.
491     * @param listener Optional OnAnimationStartedListener to find out when the
492     * requested animation has started running.  If for some reason the animation
493     * is not executed, the callback will happen immediately.
494     * @return Returns a new ActivityOptions object that you can use to
495     * supply these options as the options Bundle when starting an activity.
496     * @hide
497     */
498    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
499            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
500            Handler handler, OnAnimationStartedListener listener) {
501        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
502                targetWidth, targetHeight, handler, listener, false);
503    }
504
505    private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
506            int startX, int startY, int targetWidth, int targetHeight,
507            Handler handler, OnAnimationStartedListener listener, boolean scaleUp) {
508        ActivityOptions opts = new ActivityOptions();
509        opts.mPackageName = source.getContext().getPackageName();
510        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
511                ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
512        opts.mThumbnail = thumbnail;
513        int[] pts = new int[2];
514        source.getLocationOnScreen(pts);
515        opts.mStartX = pts[0] + startX;
516        opts.mStartY = pts[1] + startY;
517        opts.mWidth = targetWidth;
518        opts.mHeight = targetHeight;
519        opts.setOnAnimationStartedListener(handler, listener);
520        return opts;
521    }
522
523    /** @hide */
524    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
525            AppTransitionAnimationSpec[] specs, Handler handler,
526            OnAnimationStartedListener listener) {
527        ActivityOptions opts = new ActivityOptions();
528        opts.mPackageName = source.getContext().getPackageName();
529        opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
530        opts.mAnimSpecs = specs;
531        opts.setOnAnimationStartedListener(handler, listener);
532        return opts;
533    }
534
535    /**
536     * Create an ActivityOptions to transition between Activities using cross-Activity scene
537     * animations. This method carries the position of one shared element to the started Activity.
538     * The position of <code>sharedElement</code> will be used as the epicenter for the
539     * exit Transition. The position of the shared element in the launched Activity will be the
540     * epicenter of its entering Transition.
541     *
542     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
543     * enabled on the calling Activity to cause an exit transition. The same must be in
544     * the called Activity to get an entering transition.</p>
545     * @param activity The Activity whose window contains the shared elements.
546     * @param sharedElement The View to transition to the started Activity.
547     * @param sharedElementName The shared element name as used in the target Activity. This
548     *                          must not be null.
549     * @return Returns a new ActivityOptions object that you can use to
550     *         supply these options as the options Bundle when starting an activity.
551     * @see android.transition.Transition#setEpicenterCallback(
552     *          android.transition.Transition.EpicenterCallback)
553     */
554    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
555            View sharedElement, String sharedElementName) {
556        return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
557    }
558
559    /**
560     * Create an ActivityOptions to transition between Activities using cross-Activity scene
561     * animations. This method carries the position of multiple shared elements to the started
562     * Activity. The position of the first element in sharedElements
563     * will be used as the epicenter for the exit Transition. The position of the associated
564     * shared element in the launched Activity will be the epicenter of its entering Transition.
565     *
566     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
567     * enabled on the calling Activity to cause an exit transition. The same must be in
568     * the called Activity to get an entering transition.</p>
569     * @param activity The Activity whose window contains the shared elements.
570     * @param sharedElements The names of the shared elements to transfer to the called
571     *                       Activity and their associated Views. The Views must each have
572     *                       a unique shared element name.
573     * @return Returns a new ActivityOptions object that you can use to
574     *         supply these options as the options Bundle when starting an activity.
575     * @see android.transition.Transition#setEpicenterCallback(
576     *          android.transition.Transition.EpicenterCallback)
577     */
578    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
579            Pair<View, String>... sharedElements) {
580        ActivityOptions opts = new ActivityOptions();
581        if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
582            opts.mAnimationType = ANIM_DEFAULT;
583            return opts;
584        }
585        opts.mAnimationType = ANIM_SCENE_TRANSITION;
586
587        ArrayList<String> names = new ArrayList<String>();
588        ArrayList<View> views = new ArrayList<View>();
589
590        if (sharedElements != null) {
591            for (int i = 0; i < sharedElements.length; i++) {
592                Pair<View, String> sharedElement = sharedElements[i];
593                String sharedElementName = sharedElement.second;
594                if (sharedElementName == null) {
595                    throw new IllegalArgumentException("Shared element name must not be null");
596                }
597                names.add(sharedElementName);
598                View view = sharedElement.first;
599                if (view == null) {
600                    throw new IllegalArgumentException("Shared element must not be null");
601                }
602                views.add(sharedElement.first);
603            }
604        }
605
606        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
607                views, false);
608        opts.mTransitionReceiver = exit;
609        opts.mSharedElementNames = names;
610        opts.mIsReturning = false;
611        opts.mExitCoordinatorIndex =
612                activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
613        return opts;
614    }
615
616    /** @hide */
617    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
618            ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
619            int resultCode, Intent resultData) {
620        ActivityOptions opts = new ActivityOptions();
621        opts.mAnimationType = ANIM_SCENE_TRANSITION;
622        opts.mSharedElementNames = sharedElementNames;
623        opts.mTransitionReceiver = exitCoordinator;
624        opts.mIsReturning = true;
625        opts.mResultCode = resultCode;
626        opts.mResultData = resultData;
627        opts.mExitCoordinatorIndex =
628                activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
629        return opts;
630    }
631
632    /**
633     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
634     * presented to the user but will instead be only available through the recents task list.
635     * In addition, the new task wil be affiliated with the launching activity's task.
636     * Affiliated tasks are grouped together in the recents task list.
637     *
638     * <p>This behavior is not supported for activities with {@link
639     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
640     * <code>singleInstance</code> or <code>singleTask</code>.
641     */
642    public static ActivityOptions makeTaskLaunchBehind() {
643        final ActivityOptions opts = new ActivityOptions();
644        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
645        return opts;
646    }
647
648    /**
649     * Create a basic ActivityOptions that has no special animation associated with it.
650     * Other options can still be set.
651     */
652    public static ActivityOptions makeBasic() {
653        final ActivityOptions opts = new ActivityOptions();
654        return opts;
655    }
656
657    /** @hide */
658    public boolean getLaunchTaskBehind() {
659        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
660    }
661
662    private ActivityOptions() {
663    }
664
665    /** @hide */
666    public ActivityOptions(Bundle opts) {
667        mPackageName = opts.getString(KEY_PACKAGE_NAME);
668        try {
669            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
670        } catch (RuntimeException e) {
671            Slog.w(TAG, e);
672        }
673        mHasBounds = opts.containsKey(KEY_BOUNDS);
674        if (mHasBounds) {
675            mBounds = opts.getParcelable(KEY_BOUNDS);
676        }
677        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
678        switch (mAnimationType) {
679            case ANIM_CUSTOM:
680                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
681                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
682                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
683                        opts.getBinder(KEY_ANIM_START_LISTENER));
684                break;
685
686            case ANIM_CUSTOM_IN_PLACE:
687                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
688                break;
689
690            case ANIM_SCALE_UP:
691            case ANIM_CLIP_REVEAL:
692                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
693                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
694                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
695                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
696                break;
697
698            case ANIM_THUMBNAIL_SCALE_UP:
699            case ANIM_THUMBNAIL_SCALE_DOWN:
700            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
701            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
702                mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
703                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
704                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
705                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
706                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
707                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
708                        opts.getBinder(KEY_ANIM_START_LISTENER));
709                break;
710
711            case ANIM_SCENE_TRANSITION:
712                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
713                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
714                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
715                mResultData = opts.getParcelable(KEY_RESULT_DATA);
716                mResultCode = opts.getInt(KEY_RESULT_CODE);
717                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
718                break;
719        }
720        mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
721        if (opts.containsKey(KEY_ANIM_SPECS)) {
722            Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
723            mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
724            for (int i = specs.length - 1; i >= 0; i--) {
725                mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
726            }
727        }
728    }
729
730    /** @hide */
731    public ActivityOptions setBounds(Rect bounds) {
732        mHasBounds = true;
733        mBounds = bounds;
734        return this;
735    }
736
737    /** @hide */
738    public String getPackageName() {
739        return mPackageName;
740    }
741
742    /** @hide */
743    public boolean hasBounds() {
744        return mHasBounds;
745    }
746
747    /** @hide */
748    public Rect getBounds(){
749        return mBounds;
750    }
751
752    /** @hide */
753    public int getAnimationType() {
754        return mAnimationType;
755    }
756
757    /** @hide */
758    public int getCustomEnterResId() {
759        return mCustomEnterResId;
760    }
761
762    /** @hide */
763    public int getCustomExitResId() {
764        return mCustomExitResId;
765    }
766
767    /** @hide */
768    public int getCustomInPlaceResId() {
769        return mCustomInPlaceResId;
770    }
771
772    /** @hide */
773    public Bitmap getThumbnail() {
774        return mThumbnail;
775    }
776
777    /** @hide */
778    public int getStartX() {
779        return mStartX;
780    }
781
782    /** @hide */
783    public int getStartY() {
784        return mStartY;
785    }
786
787    /** @hide */
788    public int getWidth() {
789        return mWidth;
790    }
791
792    /** @hide */
793    public int getHeight() {
794        return mHeight;
795    }
796
797    /** @hide */
798    public IRemoteCallback getOnAnimationStartListener() {
799        return mAnimationStartedListener;
800    }
801
802    /** @hide */
803    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
804
805    /** @hide */
806    public void abort() {
807        if (mAnimationStartedListener != null) {
808            try {
809                mAnimationStartedListener.sendResult(null);
810            } catch (RemoteException e) {
811            }
812        }
813    }
814
815    /** @hide */
816    public boolean isReturning() {
817        return mIsReturning;
818    }
819
820    /** @hide */
821    public ArrayList<String> getSharedElementNames() {
822        return mSharedElementNames;
823    }
824
825    /** @hide */
826    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
827
828    /** @hide */
829    public int getResultCode() { return mResultCode; }
830
831    /** @hide */
832    public Intent getResultData() { return mResultData; }
833
834    /** @hide */
835    public PendingIntent getUsageTimeReport() {
836        return mUsageTimeReport;
837    }
838
839    /** @hide */
840    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
841
842    /** @hide */
843    public static void abort(Bundle options) {
844        if (options != null) {
845            (new ActivityOptions(options)).abort();
846        }
847    }
848
849    /** @hide */
850    public int getDockCreateMode() { return mDockCreateMode; }
851
852    /** @hide */
853    public void setDockCreateMode(int dockCreateMode) {
854        mDockCreateMode = dockCreateMode;
855    }
856
857    /**
858     * Update the current values in this ActivityOptions from those supplied
859     * in <var>otherOptions</var>.  Any values
860     * defined in <var>otherOptions</var> replace those in the base options.
861     */
862    public void update(ActivityOptions otherOptions) {
863        if (otherOptions.mPackageName != null) {
864            mPackageName = otherOptions.mPackageName;
865        }
866        mUsageTimeReport = otherOptions.mUsageTimeReport;
867        mTransitionReceiver = null;
868        mSharedElementNames = null;
869        mIsReturning = false;
870        mResultData = null;
871        mResultCode = 0;
872        mExitCoordinatorIndex = 0;
873        mAnimationType = otherOptions.mAnimationType;
874        switch (otherOptions.mAnimationType) {
875            case ANIM_CUSTOM:
876                mCustomEnterResId = otherOptions.mCustomEnterResId;
877                mCustomExitResId = otherOptions.mCustomExitResId;
878                mThumbnail = null;
879                if (mAnimationStartedListener != null) {
880                    try {
881                        mAnimationStartedListener.sendResult(null);
882                    } catch (RemoteException e) {
883                    }
884                }
885                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
886                break;
887            case ANIM_CUSTOM_IN_PLACE:
888                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
889                break;
890            case ANIM_SCALE_UP:
891                mStartX = otherOptions.mStartX;
892                mStartY = otherOptions.mStartY;
893                mWidth = otherOptions.mWidth;
894                mHeight = otherOptions.mHeight;
895                if (mAnimationStartedListener != null) {
896                    try {
897                        mAnimationStartedListener.sendResult(null);
898                    } catch (RemoteException e) {
899                    }
900                }
901                mAnimationStartedListener = null;
902                break;
903            case ANIM_THUMBNAIL_SCALE_UP:
904            case ANIM_THUMBNAIL_SCALE_DOWN:
905            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
906            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
907                mThumbnail = otherOptions.mThumbnail;
908                mStartX = otherOptions.mStartX;
909                mStartY = otherOptions.mStartY;
910                mWidth = otherOptions.mWidth;
911                mHeight = otherOptions.mHeight;
912                if (mAnimationStartedListener != null) {
913                    try {
914                        mAnimationStartedListener.sendResult(null);
915                    } catch (RemoteException e) {
916                    }
917                }
918                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
919                break;
920            case ANIM_SCENE_TRANSITION:
921                mTransitionReceiver = otherOptions.mTransitionReceiver;
922                mSharedElementNames = otherOptions.mSharedElementNames;
923                mIsReturning = otherOptions.mIsReturning;
924                mThumbnail = null;
925                mAnimationStartedListener = null;
926                mResultData = otherOptions.mResultData;
927                mResultCode = otherOptions.mResultCode;
928                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
929                break;
930        }
931        mAnimSpecs = otherOptions.mAnimSpecs;
932    }
933
934    /**
935     * Returns the created options as a Bundle, which can be passed to
936     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
937     * Context.startActivity(Intent, Bundle)} and related methods.
938     * Note that the returned Bundle is still owned by the ActivityOptions
939     * object; you must not modify it, but can supply it to the startActivity
940     * methods that take an options Bundle.
941     */
942    public Bundle toBundle() {
943        if (mAnimationType == ANIM_DEFAULT) {
944            return null;
945        }
946        Bundle b = new Bundle();
947        if (mPackageName != null) {
948            b.putString(KEY_PACKAGE_NAME, mPackageName);
949        }
950        if (mHasBounds) {
951            b.putParcelable(KEY_BOUNDS, mBounds);
952        }
953        b.putInt(KEY_ANIM_TYPE, mAnimationType);
954        if (mUsageTimeReport != null) {
955            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
956        }
957        switch (mAnimationType) {
958            case ANIM_CUSTOM:
959                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
960                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
961                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
962                        != null ? mAnimationStartedListener.asBinder() : null);
963                break;
964            case ANIM_CUSTOM_IN_PLACE:
965                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
966                break;
967            case ANIM_SCALE_UP:
968            case ANIM_CLIP_REVEAL:
969                b.putInt(KEY_ANIM_START_X, mStartX);
970                b.putInt(KEY_ANIM_START_Y, mStartY);
971                b.putInt(KEY_ANIM_WIDTH, mWidth);
972                b.putInt(KEY_ANIM_HEIGHT, mHeight);
973                break;
974            case ANIM_THUMBNAIL_SCALE_UP:
975            case ANIM_THUMBNAIL_SCALE_DOWN:
976            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
977            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
978                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
979                b.putInt(KEY_ANIM_START_X, mStartX);
980                b.putInt(KEY_ANIM_START_Y, mStartY);
981                b.putInt(KEY_ANIM_WIDTH, mWidth);
982                b.putInt(KEY_ANIM_HEIGHT, mHeight);
983                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
984                        != null ? mAnimationStartedListener.asBinder() : null);
985                break;
986            case ANIM_SCENE_TRANSITION:
987                if (mTransitionReceiver != null) {
988                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
989                }
990                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
991                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
992                b.putParcelable(KEY_RESULT_DATA, mResultData);
993                b.putInt(KEY_RESULT_CODE, mResultCode);
994                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
995                break;
996        }
997        b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
998        if (mAnimSpecs != null) {
999            b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1000        }
1001
1002        return b;
1003    }
1004
1005    /**
1006     * Ask the the system track that time the user spends in the app being launched, and
1007     * report it back once done.  The report will be sent to the given receiver, with
1008     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1009     * filled in.
1010     *
1011     * <p>The time interval tracked is from launching this activity until the user leaves
1012     * that activity's flow.  They are considered to stay in the flow as long as
1013     * new activities are being launched or returned to from the original flow,
1014     * even if this crosses package or task boundaries.  For example, if the originator
1015     * starts an activity to view an image, and while there the user selects to share,
1016     * which launches their email app in a new task, and they complete the share, the
1017     * time during that entire operation will be included until they finally hit back from
1018     * the original image viewer activity.</p>
1019     *
1020     * <p>The user is considered to complete a flow once they switch to another
1021     * activity that is not part of the tracked flow.  This may happen, for example, by
1022     * using the notification shade, launcher, or recents to launch or switch to another
1023     * app.  Simply going in to these navigation elements does not break the flow (although
1024     * the launcher and recents stops time tracking of the session); it is the act of
1025     * going somewhere else that completes the tracking.</p>
1026     *
1027     * @param receiver A broadcast receiver that willl receive the report.
1028     */
1029    public void requestUsageTimeReport(PendingIntent receiver) {
1030        mUsageTimeReport = receiver;
1031    }
1032
1033    /**
1034     * Return the filtered options only meant to be seen by the target activity itself
1035     * @hide
1036     */
1037    public ActivityOptions forTargetActivity() {
1038        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1039            final ActivityOptions result = new ActivityOptions();
1040            result.update(this);
1041            return result;
1042        }
1043
1044        return null;
1045    }
1046
1047}
1048