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 
17 package android.app;
18 
19 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
21 import static android.view.Display.INVALID_DISPLAY;
22 
23 import android.annotation.Nullable;
24 import android.annotation.TestApi;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.graphics.Bitmap;
28 import android.graphics.Bitmap.Config;
29 import android.graphics.GraphicBuffer;
30 import android.graphics.Rect;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.IRemoteCallback;
34 import android.os.Parcelable;
35 import android.os.RemoteException;
36 import android.os.ResultReceiver;
37 import android.transition.Transition;
38 import android.transition.TransitionListenerAdapter;
39 import android.transition.TransitionManager;
40 import android.util.Pair;
41 import android.util.Slog;
42 import android.view.AppTransitionAnimationSpec;
43 import android.view.IAppTransitionAnimationSpecsFuture;
44 import android.view.View;
45 import android.view.ViewGroup;
46 import android.view.Window;
47 
48 import 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  */
55 public 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      */
makeCustomAnimation(Context context, int enterResId, int exitResId)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      */
makeCustomAnimation(Context context, int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener)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      */
makeCustomInPlaceAnimation(Context context, int animId)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 
setOnAnimationStartedListener(final Handler handler, final OnAnimationStartedListener listener)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 {
onAnimationStarted()371         void onAnimationStarted();
372     }
373 
setOnAnimationFinishedListener(final Handler handler, final OnAnimationFinishedListener listener)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 {
onAnimationFinished()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      */
makeScaleUpAnimation(View source, int startX, int startY, int width, int height)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      */
makeClipRevealAnimation(View source, int startX, int startY, int width, int height)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      */
makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)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      */
makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener)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 
makeThumbnailAnimation(View source, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp)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      */
makeMultiThumbFutureAspectScaleAnimation(Context context, Handler handler, IAppTransitionAnimationSpecsFuture specsFuture, OnAnimationStartedListener listener, boolean scaleUp)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      */
makeThumbnailAspectScaleDownAnimation(View source, Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, Handler handler, OnAnimationStartedListener listener)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 
makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, Handler handler, OnAnimationStartedListener listener, boolean scaleUp)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 */
makeThumbnailAspectScaleDownAnimation(View source, AppTransitionAnimationSpec[] specs, Handler handler, OnAnimationStartedListener onAnimationStartedListener, OnAnimationFinishedListener onAnimationFinishedListener)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      */
makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)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
makeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElements)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
startSharedElementAnimation(Window window, Pair<View, String>... sharedElements)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      */
stopSharedElementAnimation(Window window)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 
makeSceneTransitionAnimation(Activity activity, Window window, ActivityOptions opts, SharedElementCallback callback, Pair<View, String>[] sharedElements)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 */
makeSceneTransitionAnimation(Activity activity, ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames, int resultCode, Intent resultData)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      */
makeTaskLaunchBehind()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      */
makeBasic()780     public static ActivityOptions makeBasic() {
781         final ActivityOptions opts = new ActivityOptions();
782         return opts;
783     }
784 
785     /** @hide */
getLaunchTaskBehind()786     public boolean getLaunchTaskBehind() {
787         return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
788     }
789 
ActivityOptions()790     private ActivityOptions() {
791     }
792 
793     /** @hide */
ActivityOptions(Bundle opts)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      */
setLaunchBounds(@ullable Rect screenSpacePixelRect)888     public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
889         mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null;
890         return this;
891     }
892 
893     /** @hide */
getPackageName()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
getLaunchBounds()904     public Rect getLaunchBounds() {
905         return mLaunchBounds;
906     }
907 
908     /** @hide */
getAnimationType()909     public int getAnimationType() {
910         return mAnimationType;
911     }
912 
913     /** @hide */
getCustomEnterResId()914     public int getCustomEnterResId() {
915         return mCustomEnterResId;
916     }
917 
918     /** @hide */
getCustomExitResId()919     public int getCustomExitResId() {
920         return mCustomExitResId;
921     }
922 
923     /** @hide */
getCustomInPlaceResId()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      */
getThumbnail()934     public GraphicBuffer getThumbnail() {
935         return mThumbnail != null ? mThumbnail.createGraphicBufferHandle() : null;
936     }
937 
938     /** @hide */
getStartX()939     public int getStartX() {
940         return mStartX;
941     }
942 
943     /** @hide */
getStartY()944     public int getStartY() {
945         return mStartY;
946     }
947 
948     /** @hide */
getWidth()949     public int getWidth() {
950         return mWidth;
951     }
952 
953     /** @hide */
getHeight()954     public int getHeight() {
955         return mHeight;
956     }
957 
958     /** @hide */
getOnAnimationStartListener()959     public IRemoteCallback getOnAnimationStartListener() {
960         return mAnimationStartedListener;
961     }
962 
963     /** @hide */
getAnimationFinishedListener()964     public IRemoteCallback getAnimationFinishedListener() {
965         return mAnimationFinishedListener;
966     }
967 
968     /** @hide */
getExitCoordinatorKey()969     public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
970 
971     /** @hide */
abort()972     public void abort() {
973         if (mAnimationStartedListener != null) {
974             try {
975                 mAnimationStartedListener.sendResult(null);
976             } catch (RemoteException e) {
977             }
978         }
979     }
980 
981     /** @hide */
isReturning()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      */
isCrossTask()992     boolean isCrossTask() {
993         return mExitCoordinatorIndex < 0;
994     }
995 
996     /** @hide */
getSharedElementNames()997     public ArrayList<String> getSharedElementNames() {
998         return mSharedElementNames;
999     }
1000 
1001     /** @hide */
getResultReceiver()1002     public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
1003 
1004     /** @hide */
getResultCode()1005     public int getResultCode() { return mResultCode; }
1006 
1007     /** @hide */
getResultData()1008     public Intent getResultData() { return mResultData; }
1009 
1010     /** @hide */
getUsageTimeReport()1011     public PendingIntent getUsageTimeReport() {
1012         return mUsageTimeReport;
1013     }
1014 
1015     /** @hide */
getAnimSpecs()1016     public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
1017 
1018     /** @hide */
getSpecsFuture()1019     public IAppTransitionAnimationSpecsFuture getSpecsFuture() {
1020         return mSpecsFuture;
1021     }
1022 
1023     /** @hide */
fromBundle(Bundle bOptions)1024     public static ActivityOptions fromBundle(Bundle bOptions) {
1025         return bOptions != null ? new ActivityOptions(bOptions) : null;
1026     }
1027 
1028     /** @hide */
abort(ActivityOptions options)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      */
getLaunchDisplayId()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      */
setLaunchDisplayId(int launchDisplayId)1056     public ActivityOptions setLaunchDisplayId(int launchDisplayId) {
1057         mLaunchDisplayId = launchDisplayId;
1058         return this;
1059     }
1060 
1061     /** @hide */
getLaunchStackId()1062     public int getLaunchStackId() {
1063         return mLaunchStackId;
1064     }
1065 
1066     /** @hide */
1067     @TestApi
setLaunchStackId(int launchStackId)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
setLaunchTaskId(int taskId)1077     public void setLaunchTaskId(int taskId) {
1078         mLaunchTaskId = taskId;
1079     }
1080 
1081     /**
1082      * @hide
1083      */
getLaunchTaskId()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
setTaskOverlay(boolean taskOverlay, boolean canResume)1095     public void setTaskOverlay(boolean taskOverlay, boolean canResume) {
1096         mTaskOverlay = taskOverlay;
1097         mTaskOverlayCanResume = canResume;
1098     }
1099 
1100     /**
1101      * @hide
1102      */
getTaskOverlay()1103     public boolean getTaskOverlay() {
1104         return mTaskOverlay;
1105     }
1106 
1107     /**
1108      * @hide
1109      */
canTaskOverlayResume()1110     public boolean canTaskOverlayResume() {
1111         return mTaskOverlayCanResume;
1112     }
1113 
1114     /** @hide */
getDockCreateMode()1115     public int getDockCreateMode() {
1116         return mDockCreateMode;
1117     }
1118 
1119     /** @hide */
setDockCreateMode(int dockCreateMode)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      */
update(ActivityOptions otherOptions)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      */
toBundle()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      */
requestUsageTimeReport(PendingIntent receiver)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      */
forTargetActivity()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      */
getRotationAnimationHint()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      */
setRotationAnimationHint(int hint)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      */
popAppVerificationBundle()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      */
setAppVerificationBundle(Bundle bundle)1376     public ActivityOptions setAppVerificationBundle(Bundle bundle) {
1377         mAppVerificationBundle = bundle;
1378         return this;
1379 
1380     }
1381 
1382     /** @hide */
1383     @Override
toString()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 
HideWindowListener(Window window, ExitTransitionCoordinator exit)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
onTransitionEnd(Transition transition)1421         public void onTransitionEnd(Transition transition) {
1422             mTransitionEnded = true;
1423             hideWhenDone();
1424             transition.removeListener(this);
1425         }
1426 
1427         @Override
hideSharedElements()1428         public void hideSharedElements() {
1429             mSharedElementHidden = true;
1430             hideWhenDone();
1431         }
1432 
hideWhenDone()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