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