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