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