ActivityOptions.java revision 7f1bdd9f9180574c8b6b98cefe3c542b92477295
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    @SafeVarargs
625    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
626            Pair<View, String>... sharedElements) {
627        ActivityOptions opts = new ActivityOptions();
628        if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
629            opts.mAnimationType = ANIM_DEFAULT;
630            return opts;
631        }
632        opts.mAnimationType = ANIM_SCENE_TRANSITION;
633
634        ArrayList<String> names = new ArrayList<String>();
635        ArrayList<View> views = new ArrayList<View>();
636
637        if (sharedElements != null) {
638            for (int i = 0; i < sharedElements.length; i++) {
639                Pair<View, String> sharedElement = sharedElements[i];
640                String sharedElementName = sharedElement.second;
641                if (sharedElementName == null) {
642                    throw new IllegalArgumentException("Shared element name must not be null");
643                }
644                names.add(sharedElementName);
645                View view = sharedElement.first;
646                if (view == null) {
647                    throw new IllegalArgumentException("Shared element must not be null");
648                }
649                views.add(sharedElement.first);
650            }
651        }
652
653        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
654                views, false);
655        opts.mTransitionReceiver = exit;
656        opts.mSharedElementNames = names;
657        opts.mIsReturning = false;
658        opts.mExitCoordinatorIndex =
659                activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
660        return opts;
661    }
662
663    /** @hide */
664    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
665            ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
666            int resultCode, Intent resultData) {
667        ActivityOptions opts = new ActivityOptions();
668        opts.mAnimationType = ANIM_SCENE_TRANSITION;
669        opts.mSharedElementNames = sharedElementNames;
670        opts.mTransitionReceiver = exitCoordinator;
671        opts.mIsReturning = true;
672        opts.mResultCode = resultCode;
673        opts.mResultData = resultData;
674        opts.mExitCoordinatorIndex =
675                activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
676        return opts;
677    }
678
679    /**
680     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
681     * presented to the user but will instead be only available through the recents task list.
682     * In addition, the new task wil be affiliated with the launching activity's task.
683     * Affiliated tasks are grouped together in the recents task list.
684     *
685     * <p>This behavior is not supported for activities with {@link
686     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
687     * <code>singleInstance</code> or <code>singleTask</code>.
688     */
689    public static ActivityOptions makeTaskLaunchBehind() {
690        final ActivityOptions opts = new ActivityOptions();
691        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
692        return opts;
693    }
694
695    /**
696     * Create a basic ActivityOptions that has no special animation associated with it.
697     * Other options can still be set.
698     */
699    public static ActivityOptions makeBasic() {
700        final ActivityOptions opts = new ActivityOptions();
701        return opts;
702    }
703
704    /** @hide */
705    public boolean getLaunchTaskBehind() {
706        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
707    }
708
709    private ActivityOptions() {
710    }
711
712    /** @hide */
713    public ActivityOptions(Bundle opts) {
714        // If the remote side sent us bad parcelables, they won't get the
715        // results they want, which is their loss.
716        opts.setDefusable(true);
717
718        mPackageName = opts.getString(KEY_PACKAGE_NAME);
719        try {
720            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
721        } catch (RuntimeException e) {
722            Slog.w(TAG, e);
723        }
724        mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
725        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
726        switch (mAnimationType) {
727            case ANIM_CUSTOM:
728                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
729                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
730                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
731                        opts.getBinder(KEY_ANIM_START_LISTENER));
732                break;
733
734            case ANIM_CUSTOM_IN_PLACE:
735                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
736                break;
737
738            case ANIM_SCALE_UP:
739            case ANIM_CLIP_REVEAL:
740                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
741                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
742                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
743                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
744                break;
745
746            case ANIM_THUMBNAIL_SCALE_UP:
747            case ANIM_THUMBNAIL_SCALE_DOWN:
748            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
749            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
750                mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
751                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
752                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
753                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
754                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
755                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
756                        opts.getBinder(KEY_ANIM_START_LISTENER));
757                break;
758
759            case ANIM_SCENE_TRANSITION:
760                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
761                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
762                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
763                mResultData = opts.getParcelable(KEY_RESULT_DATA);
764                mResultCode = opts.getInt(KEY_RESULT_CODE);
765                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
766                break;
767        }
768        mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
769        mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
770        if (opts.containsKey(KEY_ANIM_SPECS)) {
771            Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
772            mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
773            for (int i = specs.length - 1; i >= 0; i--) {
774                mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
775            }
776        }
777        if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
778            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
779                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
780        }
781    }
782
783    /**
784     * Sets the bounds (window size) that the activity should be launched in.
785     * Rect position should be provided in pixels and in screen coordinates.
786     * Set to null explicitly for fullscreen.
787     * <p>
788     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
789     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
790     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
791     * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
792     */
793    public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
794        mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
795        return this;
796    }
797
798    /** @hide */
799    public String getPackageName() {
800        return mPackageName;
801    }
802
803    /**
804     * Returns the bounds that should be used to launch the activity.
805     * @see #setLaunchBounds(Rect)
806     * @return Bounds used to launch the activity.
807     */
808    @Nullable
809    public Rect getLaunchBounds() {
810        return mLaunchBounds;
811    }
812
813    /** @hide */
814    public int getAnimationType() {
815        return mAnimationType;
816    }
817
818    /** @hide */
819    public int getCustomEnterResId() {
820        return mCustomEnterResId;
821    }
822
823    /** @hide */
824    public int getCustomExitResId() {
825        return mCustomExitResId;
826    }
827
828    /** @hide */
829    public int getCustomInPlaceResId() {
830        return mCustomInPlaceResId;
831    }
832
833    /** @hide */
834    public Bitmap getThumbnail() {
835        return mThumbnail;
836    }
837
838    /** @hide */
839    public int getStartX() {
840        return mStartX;
841    }
842
843    /** @hide */
844    public int getStartY() {
845        return mStartY;
846    }
847
848    /** @hide */
849    public int getWidth() {
850        return mWidth;
851    }
852
853    /** @hide */
854    public int getHeight() {
855        return mHeight;
856    }
857
858    /** @hide */
859    public IRemoteCallback getOnAnimationStartListener() {
860        return mAnimationStartedListener;
861    }
862
863    /** @hide */
864    public IRemoteCallback getAnimationFinishedListener() {
865        return mAnimationFinishedListener;
866    }
867
868    /** @hide */
869    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
870
871    /** @hide */
872    public void abort() {
873        if (mAnimationStartedListener != null) {
874            try {
875                mAnimationStartedListener.sendResult(null);
876            } catch (RemoteException e) {
877            }
878        }
879    }
880
881    /** @hide */
882    public boolean isReturning() {
883        return mIsReturning;
884    }
885
886    /** @hide */
887    public ArrayList<String> getSharedElementNames() {
888        return mSharedElementNames;
889    }
890
891    /** @hide */
892    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
893
894    /** @hide */
895    public int getResultCode() { return mResultCode; }
896
897    /** @hide */
898    public Intent getResultData() { return mResultData; }
899
900    /** @hide */
901    public PendingIntent getUsageTimeReport() {
902        return mUsageTimeReport;
903    }
904
905    /** @hide */
906    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
907
908    /** @hide */
909    public static ActivityOptions fromBundle(Bundle bOptions) {
910        return bOptions != null ? new ActivityOptions(bOptions) : null;
911    }
912
913    /** @hide */
914    public static void abort(ActivityOptions options) {
915        if (options != null) {
916            options.abort();
917        }
918    }
919
920    /** @hide */
921    public int getLaunchStackId() {
922        return mLaunchStackId;
923    }
924
925    /** @hide */
926    public void setLaunchStackId(int launchStackId) {
927        mLaunchStackId = launchStackId;
928    }
929
930    /** @hide */
931    public int getDockCreateMode() {
932        return mDockCreateMode;
933    }
934
935    /** @hide */
936    public void setDockCreateMode(int dockCreateMode) {
937        mDockCreateMode = dockCreateMode;
938    }
939
940    /**
941     * Update the current values in this ActivityOptions from those supplied
942     * in <var>otherOptions</var>.  Any values
943     * defined in <var>otherOptions</var> replace those in the base options.
944     */
945    public void update(ActivityOptions otherOptions) {
946        if (otherOptions.mPackageName != null) {
947            mPackageName = otherOptions.mPackageName;
948        }
949        mUsageTimeReport = otherOptions.mUsageTimeReport;
950        mTransitionReceiver = null;
951        mSharedElementNames = null;
952        mIsReturning = false;
953        mResultData = null;
954        mResultCode = 0;
955        mExitCoordinatorIndex = 0;
956        mAnimationType = otherOptions.mAnimationType;
957        switch (otherOptions.mAnimationType) {
958            case ANIM_CUSTOM:
959                mCustomEnterResId = otherOptions.mCustomEnterResId;
960                mCustomExitResId = otherOptions.mCustomExitResId;
961                mThumbnail = null;
962                if (mAnimationStartedListener != null) {
963                    try {
964                        mAnimationStartedListener.sendResult(null);
965                    } catch (RemoteException e) {
966                    }
967                }
968                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
969                break;
970            case ANIM_CUSTOM_IN_PLACE:
971                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
972                break;
973            case ANIM_SCALE_UP:
974                mStartX = otherOptions.mStartX;
975                mStartY = otherOptions.mStartY;
976                mWidth = otherOptions.mWidth;
977                mHeight = otherOptions.mHeight;
978                if (mAnimationStartedListener != null) {
979                    try {
980                        mAnimationStartedListener.sendResult(null);
981                    } catch (RemoteException e) {
982                    }
983                }
984                mAnimationStartedListener = null;
985                break;
986            case ANIM_THUMBNAIL_SCALE_UP:
987            case ANIM_THUMBNAIL_SCALE_DOWN:
988            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
989            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
990                mThumbnail = otherOptions.mThumbnail;
991                mStartX = otherOptions.mStartX;
992                mStartY = otherOptions.mStartY;
993                mWidth = otherOptions.mWidth;
994                mHeight = otherOptions.mHeight;
995                if (mAnimationStartedListener != null) {
996                    try {
997                        mAnimationStartedListener.sendResult(null);
998                    } catch (RemoteException e) {
999                    }
1000                }
1001                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1002                break;
1003            case ANIM_SCENE_TRANSITION:
1004                mTransitionReceiver = otherOptions.mTransitionReceiver;
1005                mSharedElementNames = otherOptions.mSharedElementNames;
1006                mIsReturning = otherOptions.mIsReturning;
1007                mThumbnail = null;
1008                mAnimationStartedListener = null;
1009                mResultData = otherOptions.mResultData;
1010                mResultCode = otherOptions.mResultCode;
1011                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1012                break;
1013        }
1014        mAnimSpecs = otherOptions.mAnimSpecs;
1015        mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1016    }
1017
1018    /**
1019     * Returns the created options as a Bundle, which can be passed to
1020     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
1021     * Context.startActivity(Intent, Bundle)} and related methods.
1022     * Note that the returned Bundle is still owned by the ActivityOptions
1023     * object; you must not modify it, but can supply it to the startActivity
1024     * methods that take an options Bundle.
1025     */
1026    public Bundle toBundle() {
1027        if (mAnimationType == ANIM_DEFAULT) {
1028            return null;
1029        }
1030        Bundle b = new Bundle();
1031        if (mPackageName != null) {
1032            b.putString(KEY_PACKAGE_NAME, mPackageName);
1033        }
1034        if (mLaunchBounds != null) {
1035            b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1036        }
1037        b.putInt(KEY_ANIM_TYPE, mAnimationType);
1038        if (mUsageTimeReport != null) {
1039            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1040        }
1041        switch (mAnimationType) {
1042            case ANIM_CUSTOM:
1043                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
1044                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
1045                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1046                        != null ? mAnimationStartedListener.asBinder() : null);
1047                break;
1048            case ANIM_CUSTOM_IN_PLACE:
1049                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1050                break;
1051            case ANIM_SCALE_UP:
1052            case ANIM_CLIP_REVEAL:
1053                b.putInt(KEY_ANIM_START_X, mStartX);
1054                b.putInt(KEY_ANIM_START_Y, mStartY);
1055                b.putInt(KEY_ANIM_WIDTH, mWidth);
1056                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1057                break;
1058            case ANIM_THUMBNAIL_SCALE_UP:
1059            case ANIM_THUMBNAIL_SCALE_DOWN:
1060            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1061            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1062                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
1063                b.putInt(KEY_ANIM_START_X, mStartX);
1064                b.putInt(KEY_ANIM_START_Y, mStartY);
1065                b.putInt(KEY_ANIM_WIDTH, mWidth);
1066                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1067                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1068                        != null ? mAnimationStartedListener.asBinder() : null);
1069                break;
1070            case ANIM_SCENE_TRANSITION:
1071                if (mTransitionReceiver != null) {
1072                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1073                }
1074                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
1075                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
1076                b.putParcelable(KEY_RESULT_DATA, mResultData);
1077                b.putInt(KEY_RESULT_CODE, mResultCode);
1078                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
1079                break;
1080        }
1081        b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
1082        b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
1083        if (mAnimSpecs != null) {
1084            b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1085        }
1086        if (mAnimationFinishedListener != null) {
1087            b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1088        }
1089
1090        return b;
1091    }
1092
1093    /**
1094     * Ask the the system track that time the user spends in the app being launched, and
1095     * report it back once done.  The report will be sent to the given receiver, with
1096     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1097     * filled in.
1098     *
1099     * <p>The time interval tracked is from launching this activity until the user leaves
1100     * that activity's flow.  They are considered to stay in the flow as long as
1101     * new activities are being launched or returned to from the original flow,
1102     * even if this crosses package or task boundaries.  For example, if the originator
1103     * starts an activity to view an image, and while there the user selects to share,
1104     * which launches their email app in a new task, and they complete the share, the
1105     * time during that entire operation will be included until they finally hit back from
1106     * the original image viewer activity.</p>
1107     *
1108     * <p>The user is considered to complete a flow once they switch to another
1109     * activity that is not part of the tracked flow.  This may happen, for example, by
1110     * using the notification shade, launcher, or recents to launch or switch to another
1111     * app.  Simply going in to these navigation elements does not break the flow (although
1112     * the launcher and recents stops time tracking of the session); it is the act of
1113     * going somewhere else that completes the tracking.</p>
1114     *
1115     * @param receiver A broadcast receiver that willl receive the report.
1116     */
1117    public void requestUsageTimeReport(PendingIntent receiver) {
1118        mUsageTimeReport = receiver;
1119    }
1120
1121    /**
1122     * Return the filtered options only meant to be seen by the target activity itself
1123     * @hide
1124     */
1125    public ActivityOptions forTargetActivity() {
1126        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1127            final ActivityOptions result = new ActivityOptions();
1128            result.update(this);
1129            return result;
1130        }
1131
1132        return null;
1133    }
1134
1135    /** @hide */
1136    @Override
1137    public String toString() {
1138        return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1139                + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1140                + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1141    }
1142}
1143