ActivityOptions.java revision d6107a3170df61d9e776fcd5666acfc9135c6f16
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.IRemoteCallback;
25import android.os.RemoteException;
26import android.os.ResultReceiver;
27import android.transition.Transition;
28import android.util.Log;
29import android.util.Pair;
30import android.view.View;
31
32import java.util.ArrayList;
33import java.util.Map;
34
35/**
36 * Helper class for building an options Bundle that can be used with
37 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
38 * Context.startActivity(Intent, Bundle)} and related methods.
39 */
40public class ActivityOptions {
41    private static final String TAG = "ActivityOptions";
42
43    /**
44     * The package name that created the options.
45     * @hide
46     */
47    public static final String KEY_PACKAGE_NAME = "android:packageName";
48
49    /**
50     * Type of animation that arguments specify.
51     * @hide
52     */
53    public static final String KEY_ANIM_TYPE = "android:animType";
54
55    /**
56     * Custom enter animation resource ID.
57     * @hide
58     */
59    public static final String KEY_ANIM_ENTER_RES_ID = "android:animEnterRes";
60
61    /**
62     * Custom exit animation resource ID.
63     * @hide
64     */
65    public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes";
66
67    /**
68     * Bitmap for thumbnail animation.
69     * @hide
70     */
71    public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail";
72
73    /**
74     * Start X position of thumbnail animation.
75     * @hide
76     */
77    public static final String KEY_ANIM_START_X = "android:animStartX";
78
79    /**
80     * Start Y position of thumbnail animation.
81     * @hide
82     */
83    public static final String KEY_ANIM_START_Y = "android:animStartY";
84
85    /**
86     * Initial width of the animation.
87     * @hide
88     */
89    public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth";
90
91    /**
92     * Initial height of the animation.
93     * @hide
94     */
95    public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight";
96
97    /**
98     * Callback for when animation is started.
99     * @hide
100     */
101    public static final String KEY_ANIM_START_LISTENER = "android:animStartListener";
102
103    /**
104     * For Activity transitions, the calling Activity's TransitionListener used to
105     * notify the called Activity when the shared element and the exit transitions
106     * complete.
107     */
108    private static final String KEY_TRANSITION_COMPLETE_LISTENER
109            = "android:transitionCompleteListener";
110
111    /**
112     * For Activity transitions, the called Activity's listener to receive calls
113     * when transitions complete.
114     */
115    private static final String KEY_TRANSITION_TARGET_LISTENER = "android:transitionTargetListener";
116
117    /**
118     * The names of shared elements that are transitioned to the started Activity.
119     * This is also the name of shared elements that the started Activity accepted.
120     */
121    private static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names";
122
123    /**
124     * The shared elements names of the views in the calling Activity.
125     */
126    private static final String KEY_LOCAL_ELEMENT_NAMES = "android:local_element_names";
127
128    /** @hide */
129    public static final int ANIM_NONE = 0;
130    /** @hide */
131    public static final int ANIM_CUSTOM = 1;
132    /** @hide */
133    public static final int ANIM_SCALE_UP = 2;
134    /** @hide */
135    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
136    /** @hide */
137    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
138    /** @hide */
139    public static final int ANIM_SCENE_TRANSITION = 5;
140
141    private static final int MSG_SET_LISTENER = 100;
142    private static final int MSG_HIDE_SHARED_ELEMENTS = 101;
143    private static final int MSG_PREPARE_RESTORE = 102;
144    private static final int MSG_RESTORE = 103;
145
146    private String mPackageName;
147    private int mAnimationType = ANIM_NONE;
148    private int mCustomEnterResId;
149    private int mCustomExitResId;
150    private Bitmap mThumbnail;
151    private int mStartX;
152    private int mStartY;
153    private int mStartWidth;
154    private int mStartHeight;
155    private IRemoteCallback mAnimationStartedListener;
156    private ResultReceiver mTransitionCompleteListener;
157    private ArrayList<String> mSharedElementNames;
158    private ArrayList<String> mLocalElementNames;
159
160    /**
161     * Create an ActivityOptions specifying a custom animation to run when
162     * the activity is displayed.
163     *
164     * @param context Who is defining this.  This is the application that the
165     * animation resources will be loaded from.
166     * @param enterResId A resource ID of the animation resource to use for
167     * the incoming activity.  Use 0 for no animation.
168     * @param exitResId A resource ID of the animation resource to use for
169     * the outgoing activity.  Use 0 for no animation.
170     * @return Returns a new ActivityOptions object that you can use to
171     * supply these options as the options Bundle when starting an activity.
172     */
173    public static ActivityOptions makeCustomAnimation(Context context,
174            int enterResId, int exitResId) {
175        return makeCustomAnimation(context, enterResId, exitResId, null, null);
176    }
177
178    /**
179     * Create an ActivityOptions specifying a custom animation to run when
180     * the activity is displayed.
181     *
182     * @param context Who is defining this.  This is the application that the
183     * animation resources will be loaded from.
184     * @param enterResId A resource ID of the animation resource to use for
185     * the incoming activity.  Use 0 for no animation.
186     * @param exitResId A resource ID of the animation resource to use for
187     * the outgoing activity.  Use 0 for no animation.
188     * @param handler If <var>listener</var> is non-null this must be a valid
189     * Handler on which to dispatch the callback; otherwise it should be null.
190     * @param listener Optional OnAnimationStartedListener to find out when the
191     * requested animation has started running.  If for some reason the animation
192     * is not executed, the callback will happen immediately.
193     * @return Returns a new ActivityOptions object that you can use to
194     * supply these options as the options Bundle when starting an activity.
195     * @hide
196     */
197    public static ActivityOptions makeCustomAnimation(Context context,
198            int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
199        ActivityOptions opts = new ActivityOptions();
200        opts.mPackageName = context.getPackageName();
201        opts.mAnimationType = ANIM_CUSTOM;
202        opts.mCustomEnterResId = enterResId;
203        opts.mCustomExitResId = exitResId;
204        opts.setOnAnimationStartedListener(handler, listener);
205        return opts;
206    }
207
208    private void setOnAnimationStartedListener(Handler handler,
209            OnAnimationStartedListener listener) {
210        if (listener != null) {
211            final Handler h = handler;
212            final OnAnimationStartedListener finalListener = listener;
213            mAnimationStartedListener = new IRemoteCallback.Stub() {
214                @Override public void sendResult(Bundle data) throws RemoteException {
215                    h.post(new Runnable() {
216                        @Override public void run() {
217                            finalListener.onAnimationStarted();
218                        }
219                    });
220                }
221            };
222        }
223    }
224
225    /**
226     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
227     * to find out when the given animation has started running.
228     * @hide
229     */
230    public interface OnAnimationStartedListener {
231        void onAnimationStarted();
232    }
233
234    /** @hide */
235    public interface ActivityTransitionTarget {
236        void sharedElementTransitionComplete(Bundle transitionArgs);
237        void exitTransitionComplete();
238    }
239
240    /**
241     * Create an ActivityOptions specifying an animation where the new
242     * activity is scaled from a small originating area of the screen to
243     * its final full representation.
244     *
245     * <p>If the Intent this is being used with has not set its
246     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
247     * those bounds will be filled in for you based on the initial
248     * bounds passed in here.
249     *
250     * @param source The View that the new activity is animating from.  This
251     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
252     * @param startX The x starting location of the new activity, relative to <var>source</var>.
253     * @param startY The y starting location of the activity, relative to <var>source</var>.
254     * @param startWidth The initial width of the new activity.
255     * @param startHeight The initial height of the new activity.
256     * @return Returns a new ActivityOptions object that you can use to
257     * supply these options as the options Bundle when starting an activity.
258     */
259    public static ActivityOptions makeScaleUpAnimation(View source,
260            int startX, int startY, int startWidth, int startHeight) {
261        ActivityOptions opts = new ActivityOptions();
262        opts.mPackageName = source.getContext().getPackageName();
263        opts.mAnimationType = ANIM_SCALE_UP;
264        int[] pts = new int[2];
265        source.getLocationOnScreen(pts);
266        opts.mStartX = pts[0] + startX;
267        opts.mStartY = pts[1] + startY;
268        opts.mStartWidth = startWidth;
269        opts.mStartHeight = startHeight;
270        return opts;
271    }
272
273    /**
274     * Create an ActivityOptions specifying an animation where a thumbnail
275     * is scaled from a given position to the new activity window that is
276     * being started.
277     *
278     * <p>If the Intent this is being used with has not set its
279     * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
280     * those bounds will be filled in for you based on the initial
281     * thumbnail location and size provided here.
282     *
283     * @param source The View that this thumbnail is animating from.  This
284     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
285     * @param thumbnail The bitmap that will be shown as the initial thumbnail
286     * of the animation.
287     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
288     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
289     * @return Returns a new ActivityOptions object that you can use to
290     * supply these options as the options Bundle when starting an activity.
291     */
292    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
293            Bitmap thumbnail, int startX, int startY) {
294        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null);
295    }
296
297    /**
298     * Create an ActivityOptions specifying an animation where a thumbnail
299     * is scaled from a given position to the new activity window that is
300     * being started.
301     *
302     * @param source The View that this thumbnail is animating from.  This
303     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
304     * @param thumbnail The bitmap that will be shown as the initial thumbnail
305     * of the animation.
306     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
307     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
308     * @param listener Optional OnAnimationStartedListener to find out when the
309     * requested animation has started running.  If for some reason the animation
310     * is not executed, the callback will happen immediately.
311     * @return Returns a new ActivityOptions object that you can use to
312     * supply these options as the options Bundle when starting an activity.
313     * @hide
314     */
315    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
316            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
317        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true);
318    }
319
320    /**
321     * Create an ActivityOptions specifying an animation where an activity window
322     * is scaled from a given position to a thumbnail at a specified location.
323     *
324     * @param source The View that this thumbnail is animating to.  This
325     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
326     * @param thumbnail The bitmap that will be shown as the final thumbnail
327     * of the animation.
328     * @param startX The x end location of the bitmap, relative to <var>source</var>.
329     * @param startY The y end location of the bitmap, relative to <var>source</var>.
330     * @param listener Optional OnAnimationStartedListener to find out when the
331     * requested animation has started running.  If for some reason the animation
332     * is not executed, the callback will happen immediately.
333     * @return Returns a new ActivityOptions object that you can use to
334     * supply these options as the options Bundle when starting an activity.
335     * @hide
336     */
337    public static ActivityOptions makeThumbnailScaleDownAnimation(View source,
338            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
339        return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false);
340    }
341
342    private static ActivityOptions makeThumbnailAnimation(View source,
343            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
344            boolean scaleUp) {
345        ActivityOptions opts = new ActivityOptions();
346        opts.mPackageName = source.getContext().getPackageName();
347        opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
348        opts.mThumbnail = thumbnail;
349        int[] pts = new int[2];
350        source.getLocationOnScreen(pts);
351        opts.mStartX = pts[0] + startX;
352        opts.mStartY = pts[1] + startY;
353        opts.setOnAnimationStartedListener(source.getHandler(), listener);
354        return opts;
355    }
356
357    /**
358     * Create an ActivityOptions to transition between Activities using cross-Activity scene
359     * animations. This method carries the position of one shared element to the started Activity.
360     *
361     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
362     * enabled on the calling Activity to cause an exit transition. The same must be in
363     * the called Activity to get an entering transition.</p>
364     * @param sharedElement The View to transition to the started Activity. sharedElement must
365     *                      have a non-null sharedElementName.
366     * @param sharedElementName The shared element name as used in the target Activity. This may
367     *                          be null if it has the same name as sharedElement.
368     * @return Returns a new ActivityOptions object that you can use to
369     *         supply these options as the options Bundle when starting an activity.
370     */
371    public static ActivityOptions makeSceneTransitionAnimation(View sharedElement,
372            String sharedElementName) {
373        return makeSceneTransitionAnimation(
374                new Pair<View, String>(sharedElement, sharedElementName));
375    }
376
377    /**
378     * Create an ActivityOptions to transition between Activities using cross-Activity scene
379     * animations. This method carries the position of multiple shared elements to the started
380     * Activity.
381     *
382     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
383     * enabled on the calling Activity to cause an exit transition. The same must be in
384     * the called Activity to get an entering transition.</p>
385     * @param sharedElements The View to transition to the started Activity along with the
386     *                       shared element name as used in the started Activity. The view
387     *                       must have a non-null sharedElementName.
388     * @return Returns a new ActivityOptions object that you can use to
389     *         supply these options as the options Bundle when starting an activity.
390     */
391    public static ActivityOptions makeSceneTransitionAnimation(
392            Pair<View, String>... sharedElements) {
393        ActivityOptions opts = new ActivityOptions();
394        opts.mAnimationType = ANIM_SCENE_TRANSITION;
395        opts.mSharedElementNames = new ArrayList<String>();
396        opts.mLocalElementNames = new ArrayList<String>();
397
398        if (sharedElements != null) {
399            for (Pair<View, String> sharedElement : sharedElements) {
400                opts.addSharedElement(sharedElement.first, sharedElement.second);
401            }
402        }
403        return opts;
404    }
405
406    private ActivityOptions() {
407    }
408
409    /** @hide */
410    public ActivityOptions(Bundle opts) {
411        mPackageName = opts.getString(KEY_PACKAGE_NAME);
412        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
413        switch (mAnimationType) {
414            case ANIM_CUSTOM:
415                mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
416                mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
417                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
418                        opts.getBinder(KEY_ANIM_START_LISTENER));
419                break;
420
421            case ANIM_SCALE_UP:
422                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
423                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
424                mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
425                mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
426                break;
427
428            case ANIM_THUMBNAIL_SCALE_UP:
429            case ANIM_THUMBNAIL_SCALE_DOWN:
430                mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
431                mStartX = opts.getInt(KEY_ANIM_START_X, 0);
432                mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
433                mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
434                        opts.getBinder(KEY_ANIM_START_LISTENER));
435                break;
436
437            case ANIM_SCENE_TRANSITION:
438                mTransitionCompleteListener = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
439                mSharedElementNames = opts.getStringArrayList(KEY_SHARED_ELEMENT_NAMES);
440                mLocalElementNames = opts.getStringArrayList(KEY_LOCAL_ELEMENT_NAMES);
441                break;
442        }
443    }
444
445    /** @hide */
446    public String getPackageName() {
447        return mPackageName;
448    }
449
450    /** @hide */
451    public int getAnimationType() {
452        return mAnimationType;
453    }
454
455    /** @hide */
456    public int getCustomEnterResId() {
457        return mCustomEnterResId;
458    }
459
460    /** @hide */
461    public int getCustomExitResId() {
462        return mCustomExitResId;
463    }
464
465    /** @hide */
466    public Bitmap getThumbnail() {
467        return mThumbnail;
468    }
469
470    /** @hide */
471    public int getStartX() {
472        return mStartX;
473    }
474
475    /** @hide */
476    public int getStartY() {
477        return mStartY;
478    }
479
480    /** @hide */
481    public int getStartWidth() {
482        return mStartWidth;
483    }
484
485    /** @hide */
486    public int getStartHeight() {
487        return mStartHeight;
488    }
489
490    /** @hide */
491    public IRemoteCallback getOnAnimationStartListener() {
492        return mAnimationStartedListener;
493    }
494
495    /** @hide */
496    public ArrayList<String> getSharedElementNames() { return mSharedElementNames; }
497
498    /** @hide */
499    public ArrayList<String> getLocalElementNames() { return mLocalElementNames; }
500
501    /** @hide */
502    public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target,
503            ArrayList<String> sharedElementNames) {
504        if (mTransitionCompleteListener != null) {
505            IRemoteCallback callback = new IRemoteCallback.Stub() {
506                @Override
507                public void sendResult(Bundle data) throws RemoteException {
508                    if (data == null) {
509                        target.exitTransitionComplete();
510                    } else {
511                        target.sharedElementTransitionComplete(data);
512                    }
513                }
514            };
515            Bundle bundle = new Bundle();
516            bundle.putBinder(KEY_TRANSITION_TARGET_LISTENER, callback.asBinder());
517            bundle.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, sharedElementNames);
518            mTransitionCompleteListener.send(MSG_SET_LISTENER, bundle);
519        }
520    }
521
522    /** @hide */
523    public void dispatchSharedElementsReady() {
524        if (mTransitionCompleteListener != null) {
525            mTransitionCompleteListener.send(MSG_HIDE_SHARED_ELEMENTS, null);
526        }
527    }
528
529    /** @hide */
530    public void dispatchPrepareRestore() {
531        if (mTransitionCompleteListener != null) {
532            mTransitionCompleteListener.send(MSG_PREPARE_RESTORE, null);
533        }
534    }
535
536    /** @hide */
537    public void dispatchRestore(Bundle sharedElementsArgs) {
538        if (mTransitionCompleteListener != null) {
539            mTransitionCompleteListener.send(MSG_RESTORE, sharedElementsArgs);
540        }
541    }
542
543    /** @hide */
544    public void abort() {
545        if (mAnimationStartedListener != null) {
546            try {
547                mAnimationStartedListener.sendResult(null);
548            } catch (RemoteException e) {
549            }
550        }
551    }
552
553    /** @hide */
554    public static void abort(Bundle options) {
555        if (options != null) {
556            (new ActivityOptions(options)).abort();
557        }
558    }
559
560    /**
561     * Update the current values in this ActivityOptions from those supplied
562     * in <var>otherOptions</var>.  Any values
563     * defined in <var>otherOptions</var> replace those in the base options.
564     */
565    public void update(ActivityOptions otherOptions) {
566        if (otherOptions.mPackageName != null) {
567            mPackageName = otherOptions.mPackageName;
568        }
569        mSharedElementNames = null;
570        mLocalElementNames = null;
571        switch (otherOptions.mAnimationType) {
572            case ANIM_CUSTOM:
573                mAnimationType = otherOptions.mAnimationType;
574                mCustomEnterResId = otherOptions.mCustomEnterResId;
575                mCustomExitResId = otherOptions.mCustomExitResId;
576                mThumbnail = null;
577                if (mAnimationStartedListener != null) {
578                    try {
579                        mAnimationStartedListener.sendResult(null);
580                    } catch (RemoteException e) {
581                    }
582                }
583                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
584                mTransitionCompleteListener = null;
585                break;
586            case ANIM_SCALE_UP:
587                mAnimationType = otherOptions.mAnimationType;
588                mStartX = otherOptions.mStartX;
589                mStartY = otherOptions.mStartY;
590                mStartWidth = otherOptions.mStartWidth;
591                mStartHeight = otherOptions.mStartHeight;
592                if (mAnimationStartedListener != null) {
593                    try {
594                        mAnimationStartedListener.sendResult(null);
595                    } catch (RemoteException e) {
596                    }
597                }
598                mAnimationStartedListener = null;
599                mTransitionCompleteListener = null;
600                break;
601            case ANIM_THUMBNAIL_SCALE_UP:
602            case ANIM_THUMBNAIL_SCALE_DOWN:
603                mAnimationType = otherOptions.mAnimationType;
604                mThumbnail = otherOptions.mThumbnail;
605                mStartX = otherOptions.mStartX;
606                mStartY = otherOptions.mStartY;
607                if (mAnimationStartedListener != null) {
608                    try {
609                        mAnimationStartedListener.sendResult(null);
610                    } catch (RemoteException e) {
611                    }
612                }
613                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
614                mTransitionCompleteListener = null;
615                break;
616            case ANIM_SCENE_TRANSITION:
617                mAnimationType = otherOptions.mAnimationType;
618                mTransitionCompleteListener = otherOptions.mTransitionCompleteListener;
619                mThumbnail = null;
620                mAnimationStartedListener = null;
621                mSharedElementNames = otherOptions.mSharedElementNames;
622                mLocalElementNames = otherOptions.mLocalElementNames;
623                break;
624        }
625    }
626
627    /**
628     * Returns the created options as a Bundle, which can be passed to
629     * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle)
630     * Context.startActivity(Intent, Bundle)} and related methods.
631     * Note that the returned Bundle is still owned by the ActivityOptions
632     * object; you must not modify it, but can supply it to the startActivity
633     * methods that take an options Bundle.
634     */
635    public Bundle toBundle() {
636        Bundle b = new Bundle();
637        if (mPackageName != null) {
638            b.putString(KEY_PACKAGE_NAME, mPackageName);
639        }
640        switch (mAnimationType) {
641            case ANIM_CUSTOM:
642                b.putInt(KEY_ANIM_TYPE, mAnimationType);
643                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
644                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
645                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
646                        != null ? mAnimationStartedListener.asBinder() : null);
647                break;
648            case ANIM_SCALE_UP:
649                b.putInt(KEY_ANIM_TYPE, mAnimationType);
650                b.putInt(KEY_ANIM_START_X, mStartX);
651                b.putInt(KEY_ANIM_START_Y, mStartY);
652                b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
653                b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
654                break;
655            case ANIM_THUMBNAIL_SCALE_UP:
656            case ANIM_THUMBNAIL_SCALE_DOWN:
657                b.putInt(KEY_ANIM_TYPE, mAnimationType);
658                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
659                b.putInt(KEY_ANIM_START_X, mStartX);
660                b.putInt(KEY_ANIM_START_Y, mStartY);
661                b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
662                        != null ? mAnimationStartedListener.asBinder() : null);
663                break;
664            case ANIM_SCENE_TRANSITION:
665                b.putInt(KEY_ANIM_TYPE, mAnimationType);
666                if (mTransitionCompleteListener != null) {
667                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionCompleteListener);
668                }
669                b.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, mSharedElementNames);
670                b.putStringArrayList(KEY_LOCAL_ELEMENT_NAMES, mLocalElementNames);
671                break;
672        }
673        return b;
674    }
675
676    /**
677     * Return the filtered options only meant to be seen by the target activity itself
678     * @hide
679     */
680    public ActivityOptions forTargetActivity() {
681        if (mAnimationType == ANIM_SCENE_TRANSITION) {
682            final ActivityOptions result = new ActivityOptions();
683            result.update(this);
684            return result;
685        }
686
687        return null;
688    }
689
690    /** @hide */
691    public void addSharedElements(Map<String, View> sharedElements) {
692        for (Map.Entry<String, View> entry : sharedElements.entrySet()) {
693            addSharedElement(entry.getValue(), entry.getKey());
694        }
695    }
696
697    /** @hide */
698    public void updateSceneTransitionAnimation(Transition exitTransition,
699            Transition sharedElementTransition, SharedElementSource sharedElementSource) {
700        mTransitionCompleteListener = new ExitTransitionListener(exitTransition,
701                sharedElementTransition, sharedElementSource);
702    }
703
704    private void addSharedElement(View view, String name) {
705        String sharedElementName = view.getSharedElementName();
706        if (name == null) {
707            name = sharedElementName;
708        }
709        mSharedElementNames.add(name);
710        mLocalElementNames.add(sharedElementName);
711    }
712
713    /** @hide */
714    public interface SharedElementSource {
715        Bundle getSharedElementExitState();
716        void acceptedSharedElements(ArrayList<String> sharedElementNames);
717        void hideSharedElements();
718        void restore(Bundle sharedElementState);
719        void prepareForRestore();
720    }
721
722    private static class ExitTransitionListener extends ResultReceiver
723            implements Transition.TransitionListener {
724        private boolean mSharedElementNotified;
725        private IRemoteCallback mTransitionCompleteCallback;
726        private boolean mExitComplete;
727        private boolean mSharedElementComplete;
728        private SharedElementSource mSharedElementSource;
729
730        public ExitTransitionListener(Transition exitTransition, Transition sharedElementTransition,
731                SharedElementSource sharedElementSource) {
732            super(null);
733            mSharedElementSource = sharedElementSource;
734            exitTransition.addListener(this);
735            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
736                @Override
737                public void onTransitionEnd(Transition transition) {
738                    mSharedElementComplete = true;
739                    notifySharedElement();
740                    transition.removeListener(this);
741                }
742            });
743        }
744
745        @Override
746        protected void onReceiveResult(int resultCode, Bundle resultData) {
747            switch (resultCode) {
748                case MSG_SET_LISTENER:
749                    IBinder listener = resultData.getBinder(KEY_TRANSITION_TARGET_LISTENER);
750                    mTransitionCompleteCallback = IRemoteCallback.Stub.asInterface(listener);
751                    ArrayList<String> sharedElementNames
752                            = resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES);
753                    mSharedElementSource.acceptedSharedElements(sharedElementNames);
754                    notifySharedElement();
755                    notifyExit();
756                    break;
757                case MSG_HIDE_SHARED_ELEMENTS:
758                    mSharedElementSource.hideSharedElements();
759                    break;
760                case MSG_PREPARE_RESTORE:
761                    mSharedElementSource.prepareForRestore();
762                    break;
763                case MSG_RESTORE:
764                    mSharedElementSource.restore(resultData);
765                    break;
766            }
767        }
768
769        @Override
770        public void onTransitionStart(Transition transition) {
771        }
772
773        @Override
774        public void onTransitionEnd(Transition transition) {
775            mExitComplete = true;
776            notifyExit();
777            transition.removeListener(this);
778        }
779
780        @Override
781        public void onTransitionCancel(Transition transition) {
782            onTransitionEnd(transition);
783        }
784
785        @Override
786        public void onTransitionPause(Transition transition) {
787        }
788
789        @Override
790        public void onTransitionResume(Transition transition) {
791        }
792
793        private void notifySharedElement() {
794            if (!mSharedElementNotified && mSharedElementComplete
795                    && mTransitionCompleteCallback != null) {
796                mSharedElementNotified = true;
797                try {
798                    Bundle sharedElementState = mSharedElementSource.getSharedElementExitState();
799                    mTransitionCompleteCallback.sendResult(sharedElementState);
800                } catch (RemoteException e) {
801                    Log.w(TAG, "Couldn't notify that the transition ended", e);
802                }
803            }
804        }
805
806        private void notifyExit() {
807            if (mExitComplete && mTransitionCompleteCallback != null) {
808                try {
809                    mTransitionCompleteCallback.sendResult(null);
810                } catch (RemoteException e) {
811                    Log.w(TAG, "Couldn't notify that the transition ended", e);
812                }
813            }
814        }
815    }
816}
817