ActivityOptions.java revision 5122df094bcb07cff81dac02c397fb6a7cb2e009
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
21
22import android.annotation.Nullable;
23import android.content.Context;
24import android.content.Intent;
25import android.graphics.Bitmap;
26import android.graphics.Rect;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.IRemoteCallback;
30import android.os.Parcelable;
31import android.os.RemoteException;
32import android.os.ResultReceiver;
33import android.util.Pair;
34import android.util.Slog;
35import android.view.AppTransitionAnimationSpec;
36import android.view.View;
37import android.view.Window;
38
39import java.util.ArrayList;
40
41/**
42 * Helper class for building an options Bundle that can be used with
43 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
44 * Context.startActivity(Intent, Bundle)} and related methods.
45 */
46public class ActivityOptions {
47    private static final String TAG = "ActivityOptions";
48
49    /**
50     * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
51     * the total time (in ms) the user spent in the app flow.
52     */
53    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
54
55    /**
56     * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
57     * detailed information about the time spent in each package associated with the app;
58     * each key is a package name, whose value is a long containing the time (in ms).
59     */
60    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
61
62    /**
63     * The package name that created the options.
64     * @hide
65     */
66    public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
67
68    /**
69     * The bounds (window size) that the activity should be launched in. Set to null explicitly for
70     * full screen. If the key is not found, previous bounds will be preserved.
71     * NOTE: This value is ignored on devices that don't have
72     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
73     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
74     * @hide
75     */
76    public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
77
78    /**
79     * Type of animation that arguments specify.
80     * @hide
81     */
82    public static final String KEY_ANIM_TYPE = "android:activity.animType";
83
84    /**
85     * Custom enter animation resource ID.
86     * @hide
87     */
88    public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
89
90    /**
91     * Custom exit animation resource ID.
92     * @hide
93     */
94    public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
95
96    /**
97     * Custom in-place animation resource ID.
98     * @hide
99     */
100    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
101
102    /**
103     * Bitmap for thumbnail animation.
104     * @hide
105     */
106    public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
107
108    /**
109     * Start X position of thumbnail animation.
110     * @hide
111     */
112    public static final String KEY_ANIM_START_X = "android:activity.animStartX";
113
114    /**
115     * Start Y position of thumbnail animation.
116     * @hide
117     */
118    public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
119
120    /**
121     * Initial width of the animation.
122     * @hide
123     */
124    public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
125
126    /**
127     * Initial height of the animation.
128     * @hide
129     */
130    public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
131
132    /**
133     * Callback for when animation is started.
134     * @hide
135     */
136    public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
137
138    /**
139     * Callback for when the last frame of the animation is played.
140     * @hide
141     */
142    private static final String KEY_ANIMATION_FINISHED_LISTENER =
143            "android:activity.animationFinishedListener";
144
145    /**
146     * Descriptions of app transition animations to be played during the activity launch.
147     */
148    private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
149
150    /**
151     * The stack id the activity should be launched into.
152     * @hide
153     */
154    private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
155
156    /**
157     * Where the docked stack should be positioned.
158     * @hide
159     */
160    private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
161
162    /**
163     * For Activity transitions, the calling Activity's TransitionListener used to
164     * notify the called Activity when the shared element and the exit transitions
165     * complete.
166     */
167    private static final String KEY_TRANSITION_COMPLETE_LISTENER
168            = "android:activity.transitionCompleteListener";
169
170    private static final String KEY_TRANSITION_IS_RETURNING
171            = "android:activity.transitionIsReturning";
172    private static final String KEY_TRANSITION_SHARED_ELEMENTS
173            = "android:activity.sharedElementNames";
174    private static final String KEY_RESULT_DATA = "android:activity.resultData";
175    private static final String KEY_RESULT_CODE = "android:activity.resultCode";
176    private static final String KEY_EXIT_COORDINATOR_INDEX
177            = "android:activity.exitCoordinatorIndex";
178
179    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
180
181    /** @hide */
182    public static final int ANIM_NONE = 0;
183    /** @hide */
184    public static final int ANIM_CUSTOM = 1;
185    /** @hide */
186    public static final int ANIM_SCALE_UP = 2;
187    /** @hide */
188    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
189    /** @hide */
190    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
191    /** @hide */
192    public static final int ANIM_SCENE_TRANSITION = 5;
193    /** @hide */
194    public static final int ANIM_DEFAULT = 6;
195    /** @hide */
196    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
197    /** @hide */
198    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
199    /** @hide */
200    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
201    /** @hide */
202    public static final int ANIM_CUSTOM_IN_PLACE = 10;
203    /** @hide */
204    public static final int ANIM_CLIP_REVEAL = 11;
205
206    private String mPackageName;
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        mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
720        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
721        switch (mAnimationType) {
722            case ANIM_CUSTOM:
723                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
724                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
725                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
726                        opts.getBinder(KEY_ANIM_START_LISTENER));
727                break;
728
729            case ANIM_CUSTOM_IN_PLACE:
730                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
731                break;
732
733            case ANIM_SCALE_UP:
734            case ANIM_CLIP_REVEAL:
735                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
736                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
737                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
738                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
739                break;
740
741            case ANIM_THUMBNAIL_SCALE_UP:
742            case ANIM_THUMBNAIL_SCALE_DOWN:
743            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
744            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
745                mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
746                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
747                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
748                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
749                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
750                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
751                        opts.getBinder(KEY_ANIM_START_LISTENER));
752                break;
753
754            case ANIM_SCENE_TRANSITION:
755                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
756                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
757                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
758                mResultData = opts.getParcelable(KEY_RESULT_DATA);
759                mResultCode = opts.getInt(KEY_RESULT_CODE);
760                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
761                break;
762        }
763        mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
764        mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
765        if (opts.containsKey(KEY_ANIM_SPECS)) {
766            Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
767            mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
768            for (int i = specs.length - 1; i >= 0; i--) {
769                mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
770            }
771        }
772        if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
773            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
774                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
775        }
776    }
777
778    /**
779     * Sets the bounds (window size) that the activity should be launched in.
780     * Set to null explicitly for fullscreen.
781     * <p>
782     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
783     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
784     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
785     * @param launchBounds Launch bounds to use for the activity or null for fullscreen.
786     */
787    public ActivityOptions setLaunchBounds(@Nullable Rect launchBounds) {
788        mLaunchBounds = launchBounds != null ? new Rect(launchBounds) : null;
789        return this;
790    }
791
792    /** @hide */
793    public String getPackageName() {
794        return mPackageName;
795    }
796
797    /**
798     * Returns the bounds that should be used to launch the activity.
799     * @see #setLaunchBounds(Rect)
800     * @return Bounds used to launch the activity.
801     */
802    @Nullable
803    public Rect getLaunchBounds() {
804        return mLaunchBounds;
805    }
806
807    /** @hide */
808    public int getAnimationType() {
809        return mAnimationType;
810    }
811
812    /** @hide */
813    public int getCustomEnterResId() {
814        return mCustomEnterResId;
815    }
816
817    /** @hide */
818    public int getCustomExitResId() {
819        return mCustomExitResId;
820    }
821
822    /** @hide */
823    public int getCustomInPlaceResId() {
824        return mCustomInPlaceResId;
825    }
826
827    /** @hide */
828    public Bitmap getThumbnail() {
829        return mThumbnail;
830    }
831
832    /** @hide */
833    public int getStartX() {
834        return mStartX;
835    }
836
837    /** @hide */
838    public int getStartY() {
839        return mStartY;
840    }
841
842    /** @hide */
843    public int getWidth() {
844        return mWidth;
845    }
846
847    /** @hide */
848    public int getHeight() {
849        return mHeight;
850    }
851
852    /** @hide */
853    public IRemoteCallback getOnAnimationStartListener() {
854        return mAnimationStartedListener;
855    }
856
857    /** @hide */
858    public IRemoteCallback getAnimationFinishedListener() {
859        return mAnimationFinishedListener;
860    }
861
862    /** @hide */
863    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
864
865    /** @hide */
866    public void abort() {
867        if (mAnimationStartedListener != null) {
868            try {
869                mAnimationStartedListener.sendResult(null);
870            } catch (RemoteException e) {
871            }
872        }
873    }
874
875    /** @hide */
876    public boolean isReturning() {
877        return mIsReturning;
878    }
879
880    /** @hide */
881    public ArrayList<String> getSharedElementNames() {
882        return mSharedElementNames;
883    }
884
885    /** @hide */
886    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
887
888    /** @hide */
889    public int getResultCode() { return mResultCode; }
890
891    /** @hide */
892    public Intent getResultData() { return mResultData; }
893
894    /** @hide */
895    public PendingIntent getUsageTimeReport() {
896        return mUsageTimeReport;
897    }
898
899    /** @hide */
900    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
901
902    /** @hide */
903    public static ActivityOptions fromBundle(Bundle bOptions) {
904        return bOptions != null ? new ActivityOptions(bOptions) : null;
905    }
906
907    /** @hide */
908    public static void abort(ActivityOptions options) {
909        if (options != null) {
910            options.abort();
911        }
912    }
913
914    /** @hide */
915    public int getLaunchStackId() {
916        return mLaunchStackId;
917    }
918
919    /** @hide */
920    public void setLaunchStackId(int launchStackId) {
921        mLaunchStackId = launchStackId;
922    }
923
924    /** @hide */
925    public int getDockCreateMode() {
926        return mDockCreateMode;
927    }
928
929    /** @hide */
930    public void setDockCreateMode(int dockCreateMode) {
931        mDockCreateMode = dockCreateMode;
932    }
933
934    /**
935     * Update the current values in this ActivityOptions from those supplied
936     * in <var>otherOptions</var>.  Any values
937     * defined in <var>otherOptions</var> replace those in the base options.
938     */
939    public void update(ActivityOptions otherOptions) {
940        if (otherOptions.mPackageName != null) {
941            mPackageName = otherOptions.mPackageName;
942        }
943        mUsageTimeReport = otherOptions.mUsageTimeReport;
944        mTransitionReceiver = null;
945        mSharedElementNames = null;
946        mIsReturning = false;
947        mResultData = null;
948        mResultCode = 0;
949        mExitCoordinatorIndex = 0;
950        mAnimationType = otherOptions.mAnimationType;
951        switch (otherOptions.mAnimationType) {
952            case ANIM_CUSTOM:
953                mCustomEnterResId = otherOptions.mCustomEnterResId;
954                mCustomExitResId = otherOptions.mCustomExitResId;
955                mThumbnail = null;
956                if (mAnimationStartedListener != null) {
957                    try {
958                        mAnimationStartedListener.sendResult(null);
959                    } catch (RemoteException e) {
960                    }
961                }
962                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
963                break;
964            case ANIM_CUSTOM_IN_PLACE:
965                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
966                break;
967            case ANIM_SCALE_UP:
968                mStartX = otherOptions.mStartX;
969                mStartY = otherOptions.mStartY;
970                mWidth = otherOptions.mWidth;
971                mHeight = otherOptions.mHeight;
972                if (mAnimationStartedListener != null) {
973                    try {
974                        mAnimationStartedListener.sendResult(null);
975                    } catch (RemoteException e) {
976                    }
977                }
978                mAnimationStartedListener = null;
979                break;
980            case ANIM_THUMBNAIL_SCALE_UP:
981            case ANIM_THUMBNAIL_SCALE_DOWN:
982            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
983            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
984                mThumbnail = otherOptions.mThumbnail;
985                mStartX = otherOptions.mStartX;
986                mStartY = otherOptions.mStartY;
987                mWidth = otherOptions.mWidth;
988                mHeight = otherOptions.mHeight;
989                if (mAnimationStartedListener != null) {
990                    try {
991                        mAnimationStartedListener.sendResult(null);
992                    } catch (RemoteException e) {
993                    }
994                }
995                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
996                break;
997            case ANIM_SCENE_TRANSITION:
998                mTransitionReceiver = otherOptions.mTransitionReceiver;
999                mSharedElementNames = otherOptions.mSharedElementNames;
1000                mIsReturning = otherOptions.mIsReturning;
1001                mThumbnail = null;
1002                mAnimationStartedListener = null;
1003                mResultData = otherOptions.mResultData;
1004                mResultCode = otherOptions.mResultCode;
1005                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1006                break;
1007        }
1008        mAnimSpecs = otherOptions.mAnimSpecs;
1009        mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1010    }
1011
1012    /**
1013     * Returns the created options as a Bundle, which can be passed to
1014     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
1015     * Context.startActivity(Intent, Bundle)} and related methods.
1016     * Note that the returned Bundle is still owned by the ActivityOptions
1017     * object; you must not modify it, but can supply it to the startActivity
1018     * methods that take an options Bundle.
1019     */
1020    public Bundle toBundle() {
1021        if (mAnimationType == ANIM_DEFAULT) {
1022            return null;
1023        }
1024        Bundle b = new Bundle();
1025        if (mPackageName != null) {
1026            b.putString(KEY_PACKAGE_NAME, mPackageName);
1027        }
1028        if (mLaunchBounds != null) {
1029            b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1030        }
1031        b.putInt(KEY_ANIM_TYPE, mAnimationType);
1032        if (mUsageTimeReport != null) {
1033            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1034        }
1035        switch (mAnimationType) {
1036            case ANIM_CUSTOM:
1037                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
1038                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
1039                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1040                        != null ? mAnimationStartedListener.asBinder() : null);
1041                break;
1042            case ANIM_CUSTOM_IN_PLACE:
1043                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1044                break;
1045            case ANIM_SCALE_UP:
1046            case ANIM_CLIP_REVEAL:
1047                b.putInt(KEY_ANIM_START_X, mStartX);
1048                b.putInt(KEY_ANIM_START_Y, mStartY);
1049                b.putInt(KEY_ANIM_WIDTH, mWidth);
1050                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1051                break;
1052            case ANIM_THUMBNAIL_SCALE_UP:
1053            case ANIM_THUMBNAIL_SCALE_DOWN:
1054            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1055            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1056                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
1057                b.putInt(KEY_ANIM_START_X, mStartX);
1058                b.putInt(KEY_ANIM_START_Y, mStartY);
1059                b.putInt(KEY_ANIM_WIDTH, mWidth);
1060                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1061                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1062                        != null ? mAnimationStartedListener.asBinder() : null);
1063                break;
1064            case ANIM_SCENE_TRANSITION:
1065                if (mTransitionReceiver != null) {
1066                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1067                }
1068                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
1069                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
1070                b.putParcelable(KEY_RESULT_DATA, mResultData);
1071                b.putInt(KEY_RESULT_CODE, mResultCode);
1072                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
1073                break;
1074        }
1075        b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
1076        b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
1077        if (mAnimSpecs != null) {
1078            b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1079        }
1080        if (mAnimationFinishedListener != null) {
1081            b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1082        }
1083
1084        return b;
1085    }
1086
1087    /**
1088     * Ask the the system track that time the user spends in the app being launched, and
1089     * report it back once done.  The report will be sent to the given receiver, with
1090     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1091     * filled in.
1092     *
1093     * <p>The time interval tracked is from launching this activity until the user leaves
1094     * that activity's flow.  They are considered to stay in the flow as long as
1095     * new activities are being launched or returned to from the original flow,
1096     * even if this crosses package or task boundaries.  For example, if the originator
1097     * starts an activity to view an image, and while there the user selects to share,
1098     * which launches their email app in a new task, and they complete the share, the
1099     * time during that entire operation will be included until they finally hit back from
1100     * the original image viewer activity.</p>
1101     *
1102     * <p>The user is considered to complete a flow once they switch to another
1103     * activity that is not part of the tracked flow.  This may happen, for example, by
1104     * using the notification shade, launcher, or recents to launch or switch to another
1105     * app.  Simply going in to these navigation elements does not break the flow (although
1106     * the launcher and recents stops time tracking of the session); it is the act of
1107     * going somewhere else that completes the tracking.</p>
1108     *
1109     * @param receiver A broadcast receiver that willl receive the report.
1110     */
1111    public void requestUsageTimeReport(PendingIntent receiver) {
1112        mUsageTimeReport = receiver;
1113    }
1114
1115    /**
1116     * Return the filtered options only meant to be seen by the target activity itself
1117     * @hide
1118     */
1119    public ActivityOptions forTargetActivity() {
1120        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1121            final ActivityOptions result = new ActivityOptions();
1122            result.update(this);
1123            return result;
1124        }
1125
1126        return null;
1127    }
1128
1129    /** @hide */
1130    @Override
1131    public String toString() {
1132        return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1133                + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1134                + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1135    }
1136}
1137