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