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