1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
21import static android.view.Display.INVALID_DISPLAY;
22
23import android.annotation.Nullable;
24import android.annotation.TestApi;
25import android.content.Context;
26import android.content.Intent;
27import android.graphics.Bitmap;
28import android.graphics.Bitmap.Config;
29import android.graphics.GraphicBuffer;
30import android.graphics.Rect;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.IRemoteCallback;
34import android.os.Parcelable;
35import android.os.RemoteException;
36import android.os.ResultReceiver;
37import android.transition.Transition;
38import android.transition.TransitionListenerAdapter;
39import android.transition.TransitionManager;
40import android.util.Pair;
41import android.util.Slog;
42import android.view.AppTransitionAnimationSpec;
43import android.view.IAppTransitionAnimationSpecsFuture;
44import android.view.View;
45import android.view.ViewGroup;
46import android.view.Window;
47
48import java.util.ArrayList;
49
50/**
51 * Helper class for building an options Bundle that can be used with
52 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
53 * Context.startActivity(Intent, Bundle)} and related methods.
54 */
55public class ActivityOptions {
56    private static final String TAG = "ActivityOptions";
57
58    /**
59     * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
60     * the total time (in ms) the user spent in the app flow.
61     */
62    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
63
64    /**
65     * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
66     * detailed information about the time spent in each package associated with the app;
67     * each key is a package name, whose value is a long containing the time (in ms).
68     */
69    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
70
71    /**
72     * The package name that created the options.
73     * @hide
74     */
75    public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
76
77    /**
78     * The bounds (window size) that the activity should be launched in. Set to null explicitly for
79     * full screen. If the key is not found, previous bounds will be preserved.
80     * NOTE: This value is ignored on devices that don't have
81     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
82     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
83     * @hide
84     */
85    public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
86
87    /**
88     * Type of animation that arguments specify.
89     * @hide
90     */
91    public static final String KEY_ANIM_TYPE = "android:activity.animType";
92
93    /**
94     * Custom enter animation resource ID.
95     * @hide
96     */
97    public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
98
99    /**
100     * Custom exit animation resource ID.
101     * @hide
102     */
103    public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
104
105    /**
106     * Custom in-place animation resource ID.
107     * @hide
108     */
109    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
110
111    /**
112     * Bitmap for thumbnail animation.
113     * @hide
114     */
115    public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
116
117    /**
118     * Start X position of thumbnail animation.
119     * @hide
120     */
121    public static final String KEY_ANIM_START_X = "android:activity.animStartX";
122
123    /**
124     * Start Y position of thumbnail animation.
125     * @hide
126     */
127    public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
128
129    /**
130     * Initial width of the animation.
131     * @hide
132     */
133    public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
134
135    /**
136     * Initial height of the animation.
137     * @hide
138     */
139    public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
140
141    /**
142     * Callback for when animation is started.
143     * @hide
144     */
145    public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
146
147    /**
148     * Callback for when the last frame of the animation is played.
149     * @hide
150     */
151    private static final String KEY_ANIMATION_FINISHED_LISTENER =
152            "android:activity.animationFinishedListener";
153
154    /**
155     * Descriptions of app transition animations to be played during the activity launch.
156     */
157    private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
158
159    /**
160     * The display id the activity should be launched into.
161     * @see #setLaunchDisplayId(int)
162     * @hide
163     */
164    private static final String KEY_LAUNCH_DISPLAY_ID = "android.activity.launchDisplayId";
165
166    /**
167     * The stack id the activity should be launched into.
168     * @hide
169     */
170    private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
171
172    /**
173     * The task id the activity should be launched into.
174     * @hide
175     */
176    private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
177
178    /**
179     * See {@link #setTaskOverlay}.
180     * @hide
181     */
182    private static final String KEY_TASK_OVERLAY = "android.activity.taskOverlay";
183
184    /**
185     * See {@link #setTaskOverlay}.
186     * @hide
187     */
188    private static final String KEY_TASK_OVERLAY_CAN_RESUME =
189            "android.activity.taskOverlayCanResume";
190
191    /**
192     * Where the docked stack should be positioned.
193     * @hide
194     */
195    private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
196
197    /**
198     * For Activity transitions, the calling Activity's TransitionListener used to
199     * notify the called Activity when the shared element and the exit transitions
200     * complete.
201     */
202    private static final String KEY_TRANSITION_COMPLETE_LISTENER
203            = "android:activity.transitionCompleteListener";
204
205    private static final String KEY_TRANSITION_IS_RETURNING
206            = "android:activity.transitionIsReturning";
207    private static final String KEY_TRANSITION_SHARED_ELEMENTS
208            = "android:activity.sharedElementNames";
209    private static final String KEY_RESULT_DATA = "android:activity.resultData";
210    private static final String KEY_RESULT_CODE = "android:activity.resultCode";
211    private static final String KEY_EXIT_COORDINATOR_INDEX
212            = "android:activity.exitCoordinatorIndex";
213
214    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
215    private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint";
216
217    private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
218            = "android:instantapps.installerbundle";
219    private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
220
221    /** @hide */
222    public static final int ANIM_NONE = 0;
223    /** @hide */
224    public static final int ANIM_CUSTOM = 1;
225    /** @hide */
226    public static final int ANIM_SCALE_UP = 2;
227    /** @hide */
228    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
229    /** @hide */
230    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
231    /** @hide */
232    public static final int ANIM_SCENE_TRANSITION = 5;
233    /** @hide */
234    public static final int ANIM_DEFAULT = 6;
235    /** @hide */
236    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
237    /** @hide */
238    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
239    /** @hide */
240    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
241    /** @hide */
242    public static final int ANIM_CUSTOM_IN_PLACE = 10;
243    /** @hide */
244    public static final int ANIM_CLIP_REVEAL = 11;
245
246    private String mPackageName;
247    private Rect mLaunchBounds;
248    private int mAnimationType = ANIM_NONE;
249    private int mCustomEnterResId;
250    private int mCustomExitResId;
251    private int mCustomInPlaceResId;
252    private Bitmap mThumbnail;
253    private int mStartX;
254    private int mStartY;
255    private int mWidth;
256    private int mHeight;
257    private IRemoteCallback mAnimationStartedListener;
258    private IRemoteCallback mAnimationFinishedListener;
259    private ResultReceiver mTransitionReceiver;
260    private boolean mIsReturning;
261    private ArrayList<String> mSharedElementNames;
262    private Intent mResultData;
263    private int mResultCode;
264    private int mExitCoordinatorIndex;
265    private PendingIntent mUsageTimeReport;
266    private int mLaunchDisplayId = INVALID_DISPLAY;
267    private int mLaunchStackId = INVALID_STACK_ID;
268    private int mLaunchTaskId = -1;
269    private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
270    private boolean mTaskOverlay;
271    private boolean mTaskOverlayCanResume;
272    private AppTransitionAnimationSpec mAnimSpecs[];
273    private int mRotationAnimationHint = -1;
274    private Bundle mAppVerificationBundle;
275    private IAppTransitionAnimationSpecsFuture mSpecsFuture;
276
277    /**
278     * Create an ActivityOptions specifying a custom animation to run when
279     * the activity is displayed.
280     *
281     * @param context Who is defining this.  This is the application that the
282     * animation resources will be loaded from.
283     * @param enterResId A resource ID of the animation resource to use for
284     * the incoming activity.  Use 0 for no animation.
285     * @param exitResId A resource ID of the animation resource to use for
286     * the outgoing activity.  Use 0 for no animation.
287     * @return Returns a new ActivityOptions object that you can use to
288     * supply these options as the options Bundle when starting an activity.
289     */
290    public static ActivityOptions makeCustomAnimation(Context context,
291            int enterResId, int exitResId) {
292        return makeCustomAnimation(context, enterResId, exitResId, null, null);
293    }
294
295    /**
296     * Create an ActivityOptions specifying a custom animation to run when
297     * the activity is displayed.
298     *
299     * @param context Who is defining this.  This is the application that the
300     * animation resources will be loaded from.
301     * @param enterResId A resource ID of the animation resource to use for
302     * the incoming activity.  Use 0 for no animation.
303     * @param exitResId A resource ID of the animation resource to use for
304     * the outgoing activity.  Use 0 for no animation.
305     * @param handler If <var>listener</var> is non-null this must be a valid
306     * Handler on which to dispatch the callback; otherwise it should be null.
307     * @param listener Optional OnAnimationStartedListener to find out when the
308     * requested animation has started running.  If for some reason the animation
309     * is not executed, the callback will happen immediately.
310     * @return Returns a new ActivityOptions object that you can use to
311     * supply these options as the options Bundle when starting an activity.
312     * @hide
313     */
314    public static ActivityOptions makeCustomAnimation(Context context,
315            int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
316        ActivityOptions opts = new ActivityOptions();
317        opts.mPackageName = context.getPackageName();
318        opts.mAnimationType = ANIM_CUSTOM;
319        opts.mCustomEnterResId = enterResId;
320        opts.mCustomExitResId = exitResId;
321        opts.setOnAnimationStartedListener(handler, listener);
322        return opts;
323    }
324
325    /**
326     * Creates an ActivityOptions specifying a custom animation to run in place on an existing
327     * activity.
328     *
329     * @param context Who is defining this.  This is the application that the
330     * animation resources will be loaded from.
331     * @param animId A resource ID of the animation resource to use for
332     * the incoming activity.
333     * @return Returns a new ActivityOptions object that you can use to
334     * supply these options as the options Bundle when running an in-place animation.
335     * @hide
336     */
337    public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) {
338        if (animId == 0) {
339            throw new RuntimeException("You must specify a valid animation.");
340        }
341
342        ActivityOptions opts = new ActivityOptions();
343        opts.mPackageName = context.getPackageName();
344        opts.mAnimationType = ANIM_CUSTOM_IN_PLACE;
345        opts.mCustomInPlaceResId = animId;
346        return opts;
347    }
348
349    private void setOnAnimationStartedListener(final Handler handler,
350            final OnAnimationStartedListener listener) {
351        if (listener != null) {
352            mAnimationStartedListener = new IRemoteCallback.Stub() {
353                @Override
354                public void sendResult(Bundle data) throws RemoteException {
355                    handler.post(new Runnable() {
356                        @Override public void run() {
357                            listener.onAnimationStarted();
358                        }
359                    });
360                }
361            };
362        }
363    }
364
365    /**
366     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
367     * to find out when the given animation has started running.
368     * @hide
369     */
370    public interface OnAnimationStartedListener {
371        void onAnimationStarted();
372    }
373
374    private void setOnAnimationFinishedListener(final Handler handler,
375            final OnAnimationFinishedListener listener) {
376        if (listener != null) {
377            mAnimationFinishedListener = new IRemoteCallback.Stub() {
378                @Override
379                public void sendResult(Bundle data) throws RemoteException {
380                    handler.post(new Runnable() {
381                        @Override
382                        public void run() {
383                            listener.onAnimationFinished();
384                        }
385                    });
386                }
387            };
388        }
389    }
390
391    /**
392     * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation}
393     * to find out when the given animation has drawn its last frame.
394     * @hide
395     */
396    public interface OnAnimationFinishedListener {
397        void onAnimationFinished();
398    }
399
400    /**
401     * Create an ActivityOptions specifying an animation where the new
402     * activity is scaled from a small originating area of the screen to
403     * its final full representation.
404     *
405     * <p>If the Intent this is being used with has not set its
406     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
407     * those bounds will be filled in for you based on the initial
408     * bounds passed in here.
409     *
410     * @param source The View that the new activity is animating from.  This
411     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
412     * @param startX The x starting location of the new activity, relative to <var>source</var>.
413     * @param startY The y starting location of the activity, relative to <var>source</var>.
414     * @param width The initial width of the new activity.
415     * @param height The initial height of the new activity.
416     * @return Returns a new ActivityOptions object that you can use to
417     * supply these options as the options Bundle when starting an activity.
418     */
419    public static ActivityOptions makeScaleUpAnimation(View source,
420            int startX, int startY, int width, int height) {
421        ActivityOptions opts = new ActivityOptions();
422        opts.mPackageName = source.getContext().getPackageName();
423        opts.mAnimationType = ANIM_SCALE_UP;
424        int[] pts = new int[2];
425        source.getLocationOnScreen(pts);
426        opts.mStartX = pts[0] + startX;
427        opts.mStartY = pts[1] + startY;
428        opts.mWidth = width;
429        opts.mHeight = height;
430        return opts;
431    }
432
433    /**
434     * Create an ActivityOptions specifying an animation where the new
435     * activity is revealed from a small originating area of the screen to
436     * its final full representation.
437     *
438     * @param source The View that the new activity is animating from.  This
439     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
440     * @param startX The x starting location of the new activity, relative to <var>source</var>.
441     * @param startY The y starting location of the activity, relative to <var>source</var>.
442     * @param width The initial width of the new activity.
443     * @param height The initial height of the new activity.
444     * @return Returns a new ActivityOptions object that you can use to
445     * supply these options as the options Bundle when starting an activity.
446     */
447    public static ActivityOptions makeClipRevealAnimation(View source,
448            int startX, int startY, int width, int height) {
449        ActivityOptions opts = new ActivityOptions();
450        opts.mAnimationType = ANIM_CLIP_REVEAL;
451        int[] pts = new int[2];
452        source.getLocationOnScreen(pts);
453        opts.mStartX = pts[0] + startX;
454        opts.mStartY = pts[1] + startY;
455        opts.mWidth = width;
456        opts.mHeight = height;
457        return opts;
458    }
459
460    /**
461     * Create an ActivityOptions specifying an animation where a thumbnail
462     * is scaled from a given position to the new activity window that is
463     * being started.
464     *
465     * <p>If the Intent this is being used with has not set its
466     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
467     * those bounds will be filled in for you based on the initial
468     * thumbnail location and size provided here.
469     *
470     * @param source The View that this thumbnail is animating from.  This
471     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
472     * @param thumbnail The bitmap that will be shown as the initial thumbnail
473     * of the animation.
474     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
475     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
476     * @return Returns a new ActivityOptions object that you can use to
477     * supply these options as the options Bundle when starting an activity.
478     */
479    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
480            Bitmap thumbnail, int startX, int startY) {
481        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
482    }
483
484    /**
485     * Create an ActivityOptions specifying an animation where a thumbnail
486     * is scaled from a given position to the new activity window that is
487     * being started.
488     *
489     * @param source The View that this thumbnail is animating from.  This
490     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
491     * @param thumbnail The bitmap that will be shown as the initial thumbnail
492     * of the animation.
493     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
494     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
495     * @param listener Optional OnAnimationStartedListener to find out when the
496     * requested animation has started running.  If for some reason the animation
497     * is not executed, the callback will happen immediately.
498     * @return Returns a new ActivityOptions object that you can use to
499     * supply these options as the options Bundle when starting an activity.
500     */
501    private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
502            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
503        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
504    }
505
506    private static ActivityOptions makeThumbnailAnimation(View source,
507            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
508            boolean scaleUp) {
509        ActivityOptions opts = new ActivityOptions();
510        opts.mPackageName = source.getContext().getPackageName();
511        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
512        opts.mThumbnail = thumbnail;
513        int[] pts = new int[2];
514        source.getLocationOnScreen(pts);
515        opts.mStartX = pts[0] + startX;
516        opts.mStartY = pts[1] + startY;
517        opts.setOnAnimationStartedListener(source.getHandler(), listener);
518        return opts;
519    }
520
521    /**
522     * Create an ActivityOptions specifying an animation where a list of activity windows and
523     * thumbnails are aspect scaled to/from a new location.
524     * @hide
525     */
526    public static ActivityOptions makeMultiThumbFutureAspectScaleAnimation(Context context,
527            Handler handler, IAppTransitionAnimationSpecsFuture specsFuture,
528            OnAnimationStartedListener listener, boolean scaleUp) {
529        ActivityOptions opts = new ActivityOptions();
530        opts.mPackageName = context.getPackageName();
531        opts.mAnimationType = scaleUp
532                ? ANIM_THUMBNAIL_ASPECT_SCALE_UP
533                : ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
534        opts.mSpecsFuture = specsFuture;
535        opts.setOnAnimationStartedListener(handler, listener);
536        return opts;
537    }
538
539    /**
540     * Create an ActivityOptions specifying an animation where the new activity
541     * window and a thumbnail is aspect-scaled to a new location.
542     *
543     * @param source The View that this thumbnail is animating to.  This
544     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
545     * @param thumbnail The bitmap that will be shown as the final thumbnail
546     * of the animation.
547     * @param startX The x end location of the bitmap, relative to <var>source</var>.
548     * @param startY The y end location of the bitmap, relative to <var>source</var>.
549     * @param handler If <var>listener</var> is non-null this must be a valid
550     * Handler on which to dispatch the callback; otherwise it should be null.
551     * @param listener Optional OnAnimationStartedListener to find out when the
552     * requested animation has started running.  If for some reason the animation
553     * is not executed, the callback will happen immediately.
554     * @return Returns a new ActivityOptions object that you can use to
555     * supply these options as the options Bundle when starting an activity.
556     * @hide
557     */
558    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
559            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
560            Handler handler, OnAnimationStartedListener listener) {
561        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
562                targetWidth, targetHeight, handler, listener, false);
563    }
564
565    private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
566            int startX, int startY, int targetWidth, int targetHeight,
567            Handler handler, OnAnimationStartedListener listener, boolean scaleUp) {
568        ActivityOptions opts = new ActivityOptions();
569        opts.mPackageName = source.getContext().getPackageName();
570        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
571                ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
572        opts.mThumbnail = thumbnail;
573        int[] pts = new int[2];
574        source.getLocationOnScreen(pts);
575        opts.mStartX = pts[0] + startX;
576        opts.mStartY = pts[1] + startY;
577        opts.mWidth = targetWidth;
578        opts.mHeight = targetHeight;
579        opts.setOnAnimationStartedListener(handler, listener);
580        return opts;
581    }
582
583    /** @hide */
584    public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
585            AppTransitionAnimationSpec[] specs, Handler handler,
586            OnAnimationStartedListener onAnimationStartedListener,
587            OnAnimationFinishedListener onAnimationFinishedListener) {
588        ActivityOptions opts = new ActivityOptions();
589        opts.mPackageName = source.getContext().getPackageName();
590        opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
591        opts.mAnimSpecs = specs;
592        opts.setOnAnimationStartedListener(handler, onAnimationStartedListener);
593        opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener);
594        return opts;
595    }
596
597    /**
598     * Create an ActivityOptions to transition between Activities using cross-Activity scene
599     * animations. This method carries the position of one shared element to the started Activity.
600     * The position of <code>sharedElement</code> will be used as the epicenter for the
601     * exit Transition. The position of the shared element in the launched Activity will be the
602     * epicenter of its entering Transition.
603     *
604     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
605     * enabled on the calling Activity to cause an exit transition. The same must be in
606     * the called Activity to get an entering transition.</p>
607     * @param activity The Activity whose window contains the shared elements.
608     * @param sharedElement The View to transition to the started Activity.
609     * @param sharedElementName The shared element name as used in the target Activity. This
610     *                          must not be null.
611     * @return Returns a new ActivityOptions object that you can use to
612     *         supply these options as the options Bundle when starting an activity.
613     * @see android.transition.Transition#setEpicenterCallback(
614     *          android.transition.Transition.EpicenterCallback)
615     */
616    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
617            View sharedElement, String sharedElementName) {
618        return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
619    }
620
621    /**
622     * Create an ActivityOptions to transition between Activities using cross-Activity scene
623     * animations. This method carries the position of multiple shared elements to the started
624     * Activity. The position of the first element in sharedElements
625     * will be used as the epicenter for the exit Transition. The position of the associated
626     * shared element in the launched Activity will be the epicenter of its entering Transition.
627     *
628     * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be
629     * enabled on the calling Activity to cause an exit transition. The same must be in
630     * the called Activity to get an entering transition.</p>
631     * @param activity The Activity whose window contains the shared elements.
632     * @param sharedElements The names of the shared elements to transfer to the called
633     *                       Activity and their associated Views. The Views must each have
634     *                       a unique shared element name.
635     * @return Returns a new ActivityOptions object that you can use to
636     *         supply these options as the options Bundle when starting an activity.
637     * @see android.transition.Transition#setEpicenterCallback(
638     *          android.transition.Transition.EpicenterCallback)
639     */
640    @SafeVarargs
641    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
642            Pair<View, String>... sharedElements) {
643        ActivityOptions opts = new ActivityOptions();
644        makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
645                activity.mExitTransitionListener, sharedElements);
646        return opts;
647    }
648
649    /**
650     * Call this immediately prior to startActivity to begin a shared element transition
651     * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS.
652     * The exit transition will start immediately and the shared element transition will
653     * start once the launched Activity's shared element is ready.
654     * <p>
655     * When all transitions have completed and the shared element has been transfered,
656     * the window's decor View will have its visibility set to View.GONE.
657     *
658     * @hide
659     */
660    @SafeVarargs
661    public static ActivityOptions startSharedElementAnimation(Window window,
662            Pair<View, String>... sharedElements) {
663        ActivityOptions opts = new ActivityOptions();
664        final View decorView = window.getDecorView();
665        if (decorView == null) {
666            return opts;
667        }
668        final ExitTransitionCoordinator exit =
669                makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
670        if (exit != null) {
671            HideWindowListener listener = new HideWindowListener(window, exit);
672            exit.setHideSharedElementsCallback(listener);
673            exit.startExit();
674        }
675        return opts;
676    }
677
678    /**
679     * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
680     * animation must be stopped and the Views reset. This can happen if there was an error
681     * from startActivity or a springboard activity and the animation should stop and reset.
682     *
683     * @hide
684     */
685    public static void stopSharedElementAnimation(Window window) {
686        final View decorView = window.getDecorView();
687        if (decorView == null) {
688            return;
689        }
690        final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
691                decorView.getTag(com.android.internal.R.id.cross_task_transition);
692        if (exit != null) {
693            exit.cancelPendingTransitions();
694            decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
695            TransitionManager.endTransitions((ViewGroup) decorView);
696            exit.resetViews();
697            exit.clearState();
698            decorView.setVisibility(View.VISIBLE);
699        }
700    }
701
702    static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
703            ActivityOptions opts, SharedElementCallback callback,
704            Pair<View, String>[] sharedElements) {
705        if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
706            opts.mAnimationType = ANIM_DEFAULT;
707            return null;
708        }
709        opts.mAnimationType = ANIM_SCENE_TRANSITION;
710
711        ArrayList<String> names = new ArrayList<String>();
712        ArrayList<View> views = new ArrayList<View>();
713
714        if (sharedElements != null) {
715            for (int i = 0; i < sharedElements.length; i++) {
716                Pair<View, String> sharedElement = sharedElements[i];
717                String sharedElementName = sharedElement.second;
718                if (sharedElementName == null) {
719                    throw new IllegalArgumentException("Shared element name must not be null");
720                }
721                names.add(sharedElementName);
722                View view = sharedElement.first;
723                if (view == null) {
724                    throw new IllegalArgumentException("Shared element must not be null");
725                }
726                views.add(sharedElement.first);
727            }
728        }
729
730        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
731                callback, names, names, views, false);
732        opts.mTransitionReceiver = exit;
733        opts.mSharedElementNames = names;
734        opts.mIsReturning = (activity == null);
735        if (activity == null) {
736            opts.mExitCoordinatorIndex = -1;
737        } else {
738            opts.mExitCoordinatorIndex =
739                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
740        }
741        return exit;
742    }
743
744    /** @hide */
745    static ActivityOptions makeSceneTransitionAnimation(Activity activity,
746            ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
747            int resultCode, Intent resultData) {
748        ActivityOptions opts = new ActivityOptions();
749        opts.mAnimationType = ANIM_SCENE_TRANSITION;
750        opts.mSharedElementNames = sharedElementNames;
751        opts.mTransitionReceiver = exitCoordinator;
752        opts.mIsReturning = true;
753        opts.mResultCode = resultCode;
754        opts.mResultData = resultData;
755        opts.mExitCoordinatorIndex =
756                activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
757        return opts;
758    }
759
760    /**
761     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
762     * presented to the user but will instead be only available through the recents task list.
763     * In addition, the new task wil be affiliated with the launching activity's task.
764     * Affiliated tasks are grouped together in the recents task list.
765     *
766     * <p>This behavior is not supported for activities with {@link
767     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
768     * <code>singleInstance</code> or <code>singleTask</code>.
769     */
770    public static ActivityOptions makeTaskLaunchBehind() {
771        final ActivityOptions opts = new ActivityOptions();
772        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
773        return opts;
774    }
775
776    /**
777     * Create a basic ActivityOptions that has no special animation associated with it.
778     * Other options can still be set.
779     */
780    public static ActivityOptions makeBasic() {
781        final ActivityOptions opts = new ActivityOptions();
782        return opts;
783    }
784
785    /** @hide */
786    public boolean getLaunchTaskBehind() {
787        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
788    }
789
790    private ActivityOptions() {
791    }
792
793    /** @hide */
794    public ActivityOptions(Bundle opts) {
795        // If the remote side sent us bad parcelables, they won't get the
796        // results they want, which is their loss.
797        opts.setDefusable(true);
798
799        mPackageName = opts.getString(KEY_PACKAGE_NAME);
800        try {
801            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
802        } catch (RuntimeException e) {
803            Slog.w(TAG, e);
804        }
805        mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
806        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
807        switch (mAnimationType) {
808            case ANIM_CUSTOM:
809                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
810                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
811                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
812                        opts.getBinder(KEY_ANIM_START_LISTENER));
813                break;
814
815            case ANIM_CUSTOM_IN_PLACE:
816                mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0);
817                break;
818
819            case ANIM_SCALE_UP:
820            case ANIM_CLIP_REVEAL:
821                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
822                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
823                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
824                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
825                break;
826
827            case ANIM_THUMBNAIL_SCALE_UP:
828            case ANIM_THUMBNAIL_SCALE_DOWN:
829            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
830            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
831                // Unpackage the GraphicBuffer from the parceled thumbnail
832                final GraphicBuffer buffer = opts.getParcelable(KEY_ANIM_THUMBNAIL);
833                if (buffer != null) {
834                    mThumbnail = Bitmap.createHardwareBitmap(buffer);
835                }
836                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
837                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
838                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
839                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
840                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
841                        opts.getBinder(KEY_ANIM_START_LISTENER));
842                break;
843
844            case ANIM_SCENE_TRANSITION:
845                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
846                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
847                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
848                mResultData = opts.getParcelable(KEY_RESULT_DATA);
849                mResultCode = opts.getInt(KEY_RESULT_CODE);
850                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
851                break;
852        }
853        mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
854        mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
855        mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
856        mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
857        mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
858        mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
859        if (opts.containsKey(KEY_ANIM_SPECS)) {
860            Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
861            mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
862            for (int i = specs.length - 1; i >= 0; i--) {
863                mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
864            }
865        }
866        if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) {
867            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
868                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
869        }
870        mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
871        mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE);
872        if (opts.containsKey(KEY_SPECS_FUTURE)) {
873            mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
874                    KEY_SPECS_FUTURE));
875        }
876    }
877
878    /**
879     * Sets the bounds (window size) that the activity should be launched in.
880     * Rect position should be provided in pixels and in screen coordinates.
881     * Set to null explicitly for fullscreen.
882     * <p>
883     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
884     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
885     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
886     * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
887     */
888    public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
889        mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
890        return this;
891    }
892
893    /** @hide */
894    public String getPackageName() {
895        return mPackageName;
896    }
897
898    /**
899     * Returns the bounds that should be used to launch the activity.
900     * @see #setLaunchBounds(Rect)
901     * @return Bounds used to launch the activity.
902     */
903    @Nullable
904    public Rect getLaunchBounds() {
905        return mLaunchBounds;
906    }
907
908    /** @hide */
909    public int getAnimationType() {
910        return mAnimationType;
911    }
912
913    /** @hide */
914    public int getCustomEnterResId() {
915        return mCustomEnterResId;
916    }
917
918    /** @hide */
919    public int getCustomExitResId() {
920        return mCustomExitResId;
921    }
922
923    /** @hide */
924    public int getCustomInPlaceResId() {
925        return mCustomInPlaceResId;
926    }
927
928    /**
929     * The thumbnail is copied into a hardware bitmap when it is bundled and sent to the system, so
930     * it should always be backed by a GraphicBuffer on the other end.
931     *
932     * @hide
933     */
934    public GraphicBuffer getThumbnail() {
935        return mThumbnail != null ? mThumbnail.createGraphicBufferHandle() : null;
936    }
937
938    /** @hide */
939    public int getStartX() {
940        return mStartX;
941    }
942
943    /** @hide */
944    public int getStartY() {
945        return mStartY;
946    }
947
948    /** @hide */
949    public int getWidth() {
950        return mWidth;
951    }
952
953    /** @hide */
954    public int getHeight() {
955        return mHeight;
956    }
957
958    /** @hide */
959    public IRemoteCallback getOnAnimationStartListener() {
960        return mAnimationStartedListener;
961    }
962
963    /** @hide */
964    public IRemoteCallback getAnimationFinishedListener() {
965        return mAnimationFinishedListener;
966    }
967
968    /** @hide */
969    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
970
971    /** @hide */
972    public void abort() {
973        if (mAnimationStartedListener != null) {
974            try {
975                mAnimationStartedListener.sendResult(null);
976            } catch (RemoteException e) {
977            }
978        }
979    }
980
981    /** @hide */
982    public boolean isReturning() {
983        return mIsReturning;
984    }
985
986    /**
987     * Returns whether or not the ActivityOptions was created with
988     * {@link #startSharedElementAnimation(Window, Pair[])}.
989     *
990     * @hide
991     */
992    boolean isCrossTask() {
993        return mExitCoordinatorIndex < 0;
994    }
995
996    /** @hide */
997    public ArrayList<String> getSharedElementNames() {
998        return mSharedElementNames;
999    }
1000
1001    /** @hide */
1002    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
1003
1004    /** @hide */
1005    public int getResultCode() { return mResultCode; }
1006
1007    /** @hide */
1008    public Intent getResultData() { return mResultData; }
1009
1010    /** @hide */
1011    public PendingIntent getUsageTimeReport() {
1012        return mUsageTimeReport;
1013    }
1014
1015    /** @hide */
1016    public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
1017
1018    /** @hide */
1019    public IAppTransitionAnimationSpecsFuture getSpecsFuture() {
1020        return mSpecsFuture;
1021    }
1022
1023    /** @hide */
1024    public static ActivityOptions fromBundle(Bundle bOptions) {
1025        return bOptions != null ? new ActivityOptions(bOptions) : null;
1026    }
1027
1028    /** @hide */
1029    public static void abort(ActivityOptions options) {
1030        if (options != null) {
1031            options.abort();
1032        }
1033    }
1034
1035    /**
1036     * Gets the id of the display where activity should be launched.
1037     * @return The id of the display where activity should be launched,
1038     *         {@link android.view.Display#INVALID_DISPLAY} if not set.
1039     * @see #setLaunchDisplayId(int)
1040     */
1041    public int getLaunchDisplayId() {
1042        return mLaunchDisplayId;
1043    }
1044
1045    /**
1046     * Sets the id of the display where activity should be launched.
1047     * An app can launch activities on public displays or private displays that are owned by the app
1048     * or where an app already has activities. Otherwise, trying to launch on a private display
1049     * or providing an invalid display id will result in an exception.
1050     * <p>
1051     * Setting launch display id will be ignored on devices that don't have
1052     * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}.
1053     * @param launchDisplayId The id of the display where the activity should be launched.
1054     * @return {@code this} {@link ActivityOptions} instance.
1055     */
1056    public ActivityOptions setLaunchDisplayId(int launchDisplayId) {
1057        mLaunchDisplayId = launchDisplayId;
1058        return this;
1059    }
1060
1061    /** @hide */
1062    public int getLaunchStackId() {
1063        return mLaunchStackId;
1064    }
1065
1066    /** @hide */
1067    @TestApi
1068    public void setLaunchStackId(int launchStackId) {
1069        mLaunchStackId = launchStackId;
1070    }
1071
1072    /**
1073     * Sets the task the activity will be launched in.
1074     * @hide
1075     */
1076    @TestApi
1077    public void setLaunchTaskId(int taskId) {
1078        mLaunchTaskId = taskId;
1079    }
1080
1081    /**
1082     * @hide
1083     */
1084    public int getLaunchTaskId() {
1085        return mLaunchTaskId;
1086    }
1087
1088    /**
1089     * Set's whether the activity launched with this option should be a task overlay. That is the
1090     * activity will always be the top activity of the task.  If {@param canResume} is true, then
1091     * the task will also not be moved to the front of the stack.
1092     * @hide
1093     */
1094    @TestApi
1095    public void setTaskOverlay(boolean taskOverlay, boolean canResume) {
1096        mTaskOverlay = taskOverlay;
1097        mTaskOverlayCanResume = canResume;
1098    }
1099
1100    /**
1101     * @hide
1102     */
1103    public boolean getTaskOverlay() {
1104        return mTaskOverlay;
1105    }
1106
1107    /**
1108     * @hide
1109     */
1110    public boolean canTaskOverlayResume() {
1111        return mTaskOverlayCanResume;
1112    }
1113
1114    /** @hide */
1115    public int getDockCreateMode() {
1116        return mDockCreateMode;
1117    }
1118
1119    /** @hide */
1120    public void setDockCreateMode(int dockCreateMode) {
1121        mDockCreateMode = dockCreateMode;
1122    }
1123
1124    /**
1125     * Update the current values in this ActivityOptions from those supplied
1126     * in <var>otherOptions</var>.  Any values
1127     * defined in <var>otherOptions</var> replace those in the base options.
1128     */
1129    public void update(ActivityOptions otherOptions) {
1130        if (otherOptions.mPackageName != null) {
1131            mPackageName = otherOptions.mPackageName;
1132        }
1133        mUsageTimeReport = otherOptions.mUsageTimeReport;
1134        mTransitionReceiver = null;
1135        mSharedElementNames = null;
1136        mIsReturning = false;
1137        mResultData = null;
1138        mResultCode = 0;
1139        mExitCoordinatorIndex = 0;
1140        mAnimationType = otherOptions.mAnimationType;
1141        switch (otherOptions.mAnimationType) {
1142            case ANIM_CUSTOM:
1143                mCustomEnterResId = otherOptions.mCustomEnterResId;
1144                mCustomExitResId = otherOptions.mCustomExitResId;
1145                mThumbnail = null;
1146                if (mAnimationStartedListener != null) {
1147                    try {
1148                        mAnimationStartedListener.sendResult(null);
1149                    } catch (RemoteException e) {
1150                    }
1151                }
1152                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1153                break;
1154            case ANIM_CUSTOM_IN_PLACE:
1155                mCustomInPlaceResId = otherOptions.mCustomInPlaceResId;
1156                break;
1157            case ANIM_SCALE_UP:
1158                mStartX = otherOptions.mStartX;
1159                mStartY = otherOptions.mStartY;
1160                mWidth = otherOptions.mWidth;
1161                mHeight = otherOptions.mHeight;
1162                if (mAnimationStartedListener != null) {
1163                    try {
1164                        mAnimationStartedListener.sendResult(null);
1165                    } catch (RemoteException e) {
1166                    }
1167                }
1168                mAnimationStartedListener = null;
1169                break;
1170            case ANIM_THUMBNAIL_SCALE_UP:
1171            case ANIM_THUMBNAIL_SCALE_DOWN:
1172            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1173            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1174                mThumbnail = otherOptions.mThumbnail;
1175                mStartX = otherOptions.mStartX;
1176                mStartY = otherOptions.mStartY;
1177                mWidth = otherOptions.mWidth;
1178                mHeight = otherOptions.mHeight;
1179                if (mAnimationStartedListener != null) {
1180                    try {
1181                        mAnimationStartedListener.sendResult(null);
1182                    } catch (RemoteException e) {
1183                    }
1184                }
1185                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
1186                break;
1187            case ANIM_SCENE_TRANSITION:
1188                mTransitionReceiver = otherOptions.mTransitionReceiver;
1189                mSharedElementNames = otherOptions.mSharedElementNames;
1190                mIsReturning = otherOptions.mIsReturning;
1191                mThumbnail = null;
1192                mAnimationStartedListener = null;
1193                mResultData = otherOptions.mResultData;
1194                mResultCode = otherOptions.mResultCode;
1195                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
1196                break;
1197        }
1198        mAnimSpecs = otherOptions.mAnimSpecs;
1199        mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
1200        mSpecsFuture = otherOptions.mSpecsFuture;
1201    }
1202
1203    /**
1204     * Returns the created options as a Bundle, which can be passed to
1205     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
1206     * Context.startActivity(Intent, Bundle)} and related methods.
1207     * Note that the returned Bundle is still owned by the ActivityOptions
1208     * object; you must not modify it, but can supply it to the startActivity
1209     * methods that take an options Bundle.
1210     */
1211    public Bundle toBundle() {
1212        Bundle b = new Bundle();
1213        if (mPackageName != null) {
1214            b.putString(KEY_PACKAGE_NAME, mPackageName);
1215        }
1216        if (mLaunchBounds != null) {
1217            b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
1218        }
1219        b.putInt(KEY_ANIM_TYPE, mAnimationType);
1220        if (mUsageTimeReport != null) {
1221            b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
1222        }
1223        switch (mAnimationType) {
1224            case ANIM_CUSTOM:
1225                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
1226                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
1227                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1228                        != null ? mAnimationStartedListener.asBinder() : null);
1229                break;
1230            case ANIM_CUSTOM_IN_PLACE:
1231                b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId);
1232                break;
1233            case ANIM_SCALE_UP:
1234            case ANIM_CLIP_REVEAL:
1235                b.putInt(KEY_ANIM_START_X, mStartX);
1236                b.putInt(KEY_ANIM_START_Y, mStartY);
1237                b.putInt(KEY_ANIM_WIDTH, mWidth);
1238                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1239                break;
1240            case ANIM_THUMBNAIL_SCALE_UP:
1241            case ANIM_THUMBNAIL_SCALE_DOWN:
1242            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
1243            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
1244                // Once we parcel the thumbnail for transfering over to the system, create a copy of
1245                // the bitmap to a hardware bitmap and pass through the GraphicBuffer
1246                if (mThumbnail != null) {
1247                    final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, false /* isMutable */);
1248                    if (hwBitmap != null) {
1249                        b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle());
1250                    } else {
1251                        Slog.w(TAG, "Failed to copy thumbnail");
1252                    }
1253                }
1254                b.putInt(KEY_ANIM_START_X, mStartX);
1255                b.putInt(KEY_ANIM_START_Y, mStartY);
1256                b.putInt(KEY_ANIM_WIDTH, mWidth);
1257                b.putInt(KEY_ANIM_HEIGHT, mHeight);
1258                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
1259                        != null ? mAnimationStartedListener.asBinder() : null);
1260                break;
1261            case ANIM_SCENE_TRANSITION:
1262                if (mTransitionReceiver != null) {
1263                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
1264                }
1265                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
1266                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
1267                b.putParcelable(KEY_RESULT_DATA, mResultData);
1268                b.putInt(KEY_RESULT_CODE, mResultCode);
1269                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
1270                break;
1271        }
1272        b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
1273        b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
1274        b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
1275        b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
1276        b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
1277        b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
1278        if (mAnimSpecs != null) {
1279            b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
1280        }
1281        if (mAnimationFinishedListener != null) {
1282            b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder());
1283        }
1284        if (mSpecsFuture != null) {
1285            b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder());
1286        }
1287        b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
1288        if (mAppVerificationBundle != null) {
1289            b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
1290        }
1291
1292        return b;
1293    }
1294
1295    /**
1296     * Ask the the system track that time the user spends in the app being launched, and
1297     * report it back once done.  The report will be sent to the given receiver, with
1298     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
1299     * filled in.
1300     *
1301     * <p>The time interval tracked is from launching this activity until the user leaves
1302     * that activity's flow.  They are considered to stay in the flow as long as
1303     * new activities are being launched or returned to from the original flow,
1304     * even if this crosses package or task boundaries.  For example, if the originator
1305     * starts an activity to view an image, and while there the user selects to share,
1306     * which launches their email app in a new task, and they complete the share, the
1307     * time during that entire operation will be included until they finally hit back from
1308     * the original image viewer activity.</p>
1309     *
1310     * <p>The user is considered to complete a flow once they switch to another
1311     * activity that is not part of the tracked flow.  This may happen, for example, by
1312     * using the notification shade, launcher, or recents to launch or switch to another
1313     * app.  Simply going in to these navigation elements does not break the flow (although
1314     * the launcher and recents stops time tracking of the session); it is the act of
1315     * going somewhere else that completes the tracking.</p>
1316     *
1317     * @param receiver A broadcast receiver that willl receive the report.
1318     */
1319    public void requestUsageTimeReport(PendingIntent receiver) {
1320        mUsageTimeReport = receiver;
1321    }
1322
1323    /**
1324     * Return the filtered options only meant to be seen by the target activity itself
1325     * @hide
1326     */
1327    public ActivityOptions forTargetActivity() {
1328        if (mAnimationType == ANIM_SCENE_TRANSITION) {
1329            final ActivityOptions result = new ActivityOptions();
1330            result.update(this);
1331            return result;
1332        }
1333
1334        return null;
1335    }
1336
1337    /**
1338     * Returns the rotation animation set by {@link setRotationAnimationHint} or -1
1339     * if unspecified.
1340     * @hide
1341     */
1342    public int getRotationAnimationHint() {
1343        return mRotationAnimationHint;
1344    }
1345
1346
1347    /**
1348     * Set a rotation animation to be used if launching the activity
1349     * triggers an orientation change, or -1 to clear. See
1350     * {@link android.view.WindowManager.LayoutParams} for rotation
1351     * animation values.
1352     * @hide
1353     */
1354    public void setRotationAnimationHint(int hint) {
1355        mRotationAnimationHint = hint;
1356    }
1357
1358    /**
1359     * Pop the extra verification bundle for the installer.
1360     * This removes the bundle from the ActivityOptions to make sure the installer bundle
1361     * is only available once.
1362     * @hide
1363     */
1364    public Bundle popAppVerificationBundle() {
1365        Bundle out = mAppVerificationBundle;
1366        mAppVerificationBundle = null;
1367        return out;
1368    }
1369
1370    /**
1371     * Set the {@link Bundle} that is provided to the app installer for additional verification
1372     * if the call to {@link Context#startActivity} results in an app being installed.
1373     *
1374     * This Bundle is not provided to any other app besides the installer.
1375     */
1376    public ActivityOptions setAppVerificationBundle(Bundle bundle) {
1377        mAppVerificationBundle = bundle;
1378        return this;
1379
1380    }
1381
1382    /** @hide */
1383    @Override
1384    public String toString() {
1385        return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName
1386                + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
1387                + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
1388    }
1389
1390    private static class HideWindowListener extends TransitionListenerAdapter
1391        implements ExitTransitionCoordinator.HideSharedElementsCallback {
1392        private final Window mWindow;
1393        private final ExitTransitionCoordinator mExit;
1394        private final boolean mWaitingForTransition;
1395        private boolean mTransitionEnded;
1396        private boolean mSharedElementHidden;
1397        private ArrayList<View> mSharedElements;
1398
1399        public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
1400            mWindow = window;
1401            mExit = exit;
1402            mSharedElements = new ArrayList<>(exit.mSharedElements);
1403            Transition transition = mWindow.getExitTransition();
1404            if (transition != null) {
1405                transition.addListener(this);
1406                mWaitingForTransition = true;
1407            } else {
1408                mWaitingForTransition = false;
1409            }
1410            View decorView = mWindow.getDecorView();
1411            if (decorView != null) {
1412                if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
1413                    throw new IllegalStateException(
1414                            "Cannot start a transition while one is running");
1415                }
1416                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
1417            }
1418        }
1419
1420        @Override
1421        public void onTransitionEnd(Transition transition) {
1422            mTransitionEnded = true;
1423            hideWhenDone();
1424            transition.removeListener(this);
1425        }
1426
1427        @Override
1428        public void hideSharedElements() {
1429            mSharedElementHidden = true;
1430            hideWhenDone();
1431        }
1432
1433        private void hideWhenDone() {
1434            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
1435                mExit.resetViews();
1436                int numSharedElements = mSharedElements.size();
1437                for (int i = 0; i < numSharedElements; i++) {
1438                    View view = mSharedElements.get(i);
1439                    view.requestLayout();
1440                }
1441                View decorView = mWindow.getDecorView();
1442                if (decorView != null) {
1443                    decorView.setTagInternal(
1444                            com.android.internal.R.id.cross_task_transition, null);
1445                    decorView.setVisibility(View.GONE);
1446                }
1447            }
1448        }
1449    }
1450}
1451