Merge "Support Activity Transitions when activity stopped."
diff --git a/api/current.txt b/api/current.txt
index 5a25b12..799221a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3257,6 +3257,7 @@
     method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent);
     method public void onActionModeFinished(android.view.ActionMode);
     method public void onActionModeStarted(android.view.ActionMode);
+    method protected void onActivityReenter(int, android.content.Intent);
     method protected void onActivityResult(int, int, android.content.Intent);
     method public void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
@@ -3335,7 +3336,6 @@
     method public final boolean requestWindowFeature(int);
     method public final void runOnUiThread(java.lang.Runnable);
     method public void setActionBar(android.widget.Toolbar);
-    method public void setActivityTransitionListener(android.app.ActivityOptions.ActivityTransitionListener);
     method public void setContentTransitionManager(android.transition.TransitionManager);
     method public void setContentView(int);
     method public void setContentView(android.view.View);
@@ -3357,6 +3357,7 @@
     method public final void setResult(int);
     method public final void setResult(int, android.content.Intent);
     method public final void setSecondaryProgress(int);
+    method public void setSharedElementListener(android.app.SharedElementListener);
     method public void setTitle(java.lang.CharSequence);
     method public void setTitle(int);
     method public deprecated void setTitleColor(int);
@@ -3576,28 +3577,13 @@
   public class ActivityOptions {
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
     method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
-    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.view.View, java.lang.String);
-    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.app.ActivityOptions.ActivityTransitionListener);
+    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
+    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...);
     method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
   }
 
-  public static class ActivityOptions.ActivityTransitionListener {
-    ctor public ActivityOptions.ActivityTransitionListener();
-    method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping();
-    method public boolean handleRejectedSharedElements(java.util.List<android.view.View>);
-    method public void onCaptureSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
-    method public void onCaptureSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
-    method public void onEnterReady();
-    method public void onExitTransitionComplete();
-    method public void onRemoteExitComplete();
-    method public void onSharedElementExitTransitionComplete();
-    method public void onSharedElementTransferred(java.util.List<java.lang.String>, java.util.List<android.view.View>);
-    method public void onStartEnterTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
-    method public void onStartExitTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
-  }
-
   public class AlarmManager {
     method public void cancel(android.app.PendingIntent);
     method public void set(int, long, android.app.PendingIntent);
@@ -4821,6 +4807,14 @@
     field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
   }
 
+  public class SharedElementListener {
+    ctor public SharedElementListener();
+    method public void handleRejectedSharedElements(java.util.List<android.view.View>);
+    method public void remapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
+    method public void setSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+    method public void setSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+  }
+
   public deprecated class TabActivity extends android.app.ActivityGroup {
     ctor public TabActivity();
     method public android.widget.TabHost getTabHost();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4a30b05..36c36a8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -777,8 +777,9 @@
 
     private Thread mUiThread;
     final Handler mHandler = new Handler();
-    private ActivityOptions mCalledActivityOptions;
-    private EnterTransitionCoordinator mEnterTransitionCoordinator;
+
+    private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
+    SharedElementListener mTransitionListener = new SharedElementListener();
 
     /** Return the intent that started this activity. */
     public Intent getIntent() {
@@ -1100,9 +1101,6 @@
             mTitleReady = true;
             onTitleChanged(getTitle(), getTitleColor());
         }
-        if (mEnterTransitionCoordinator != null) {
-            mEnterTransitionCoordinator.readyToEnter();
-        }
         mCalled = true;
     }
 
@@ -1149,12 +1147,6 @@
         }
 
         getApplication().dispatchActivityStarted(this);
-
-        final ActivityOptions activityOptions = getActivityOptions();
-        if (activityOptions != null &&
-                activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-            mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this);
-        }
     }
 
     /**
@@ -1204,7 +1196,6 @@
     protected void onResume() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
         getApplication().dispatchActivityResumed(this);
-        mCalledActivityOptions = null;
         mCalled = true;
     }
 
@@ -1279,6 +1270,7 @@
     final void performSaveInstanceState(Bundle outState) {
         onSaveInstanceState(outState);
         saveManagedDialogs(outState);
+        mActivityTransitionState.saveState(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
     }
 
@@ -1549,10 +1541,7 @@
     protected void onStop() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
-        if (mCalledActivityOptions != null) {
-            mCalledActivityOptions.dispatchActivityStopped();
-            mCalledActivityOptions = null;
-        }
+        mActivityTransitionState.onStop();
         getApplication().dispatchActivityStopped(this);
         mTranslucentCallback = null;
         mCalled = true;
@@ -3650,7 +3639,7 @@
     public void startActivityForResult(Intent intent, int requestCode) {
         Bundle options = null;
         if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
-            options = ActivityOptions.makeSceneTransitionAnimation(mWindow, null).toBundle();
+            options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
         }
         startActivityForResult(intent, requestCode, options);
     }
@@ -3691,9 +3680,7 @@
      */
     public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
         if (options != null) {
-            ActivityOptions activityOptions = new ActivityOptions(options);
-            activityOptions.dispatchStartExit();
-            mCalledActivityOptions = activityOptions;
+            mActivityTransitionState.startExitOutTransition(this, options);
         }
         if (mParent == null) {
             Instrumentation.ActivityResult ar =
@@ -4559,13 +4546,10 @@
      * to reverse its exit Transition. When the exit Transition completes,
      * {@link #finish()} is called. If no entry Transition was used, finish() is called
      * immediately and the Activity exit Transition is run.
-     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
-     * android.app.ActivityOptions.ActivityTransitionListener)
+     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, android.util.Pair[])
      */
     public void finishWithTransition() {
-        if (mEnterTransitionCoordinator != null) {
-            mEnterTransitionCoordinator.startExit();
-        } else {
+        if (!mActivityTransitionState.startExitBackTransition(this)) {
             finish();
         }
     }
@@ -4643,6 +4627,27 @@
     }
 
     /**
+     * Called when an activity you launched with an activity transition exposes this
+     * Activity through a returning activity transition, giving you the resultCode
+     * and any additional data from it. This method will only be called if the activity
+     * set a result code other than {@link #RESULT_CANCELED} and it supports activity
+     * transitions with {@link Window#FEATURE_CONTENT_TRANSITIONS}.
+     *
+     * <p>The purpose of this function is to let the called Activity send a hint about
+     * its state so that this underlying Activity can prepare to be exposed. A call to
+     * this method does not guarantee that the called Activity has or will be exiting soon.
+     * It only indicates that it will expose this Activity's Window and it has
+     * some data to pass to prepare it.</p>
+     *
+     * @param resultCode The integer result code returned by the child activity
+     *                   through its setResult().
+     * @param data An Intent, which can return result data to the caller
+     *               (various data can be attached to Intent "extras").
+     */
+    protected void onActivityReenter(int resultCode, Intent data) {
+    }
+
+    /**
      * Create a new PendingIntent object which you can hand to others 
      * for them to use to send result data back to your 
      * {@link #onActivityResult} callback.  The created object will be either 
@@ -5246,7 +5251,8 @@
      * This call has no effect on non-translucent activities or on activities with the
      * {@link android.R.attr#windowIsFloating} attribute.
      *
-     * @see #convertToTranslucent(TranslucentConversionListener)
+     * @see #convertToTranslucent(android.app.Activity.TranslucentConversionListener,
+     * ActivityOptions)
      * @see TranslucentConversionListener
      *
      * @hide
@@ -5544,18 +5550,18 @@
     }
 
     /**
-     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
-     * android.app.ActivityOptions.ActivityTransitionListener)} was used to start an Activity,
-     * the Window will be triggered to enter with a Transition. <code>listener</code> allows
-     * The Activity to listen to events of the entering transition and control the mapping of
-     * shared elements. This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}.
+     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+     * android.view.View, String)} was used to start an Activity, <var>listener</var>
+     * will be called to handle shared elements. This requires
+     * {@link Window#FEATURE_CONTENT_TRANSITIONS}.
      *
-     * @param listener Used to listen to events in the entering transition.
+     * @param listener Used to manipulate how shared element transitions function.
      */
-    public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) {
-        if (mEnterTransitionCoordinator != null) {
-            mEnterTransitionCoordinator.setActivityTransitionListener(listener);
+    public void setSharedElementListener(SharedElementListener listener) {
+        if (listener == null) {
+            listener = new SharedElementListener();
         }
+        mTransitionListener = listener;
     }
 
     // ------------------ Internal API ------------------
@@ -5621,19 +5627,23 @@
         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
         mFragments.dispatchActivityCreated();
+        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
     }
 
     final void performCreate(Bundle icicle) {
         onCreate(icicle);
+        mActivityTransitionState.readState(icicle);
         performCreateCommon();
     }
 
     final void performCreate(Bundle icicle, PersistableBundle persistentState) {
         onCreate(icicle, persistentState);
+        mActivityTransitionState.readState(icicle);
         performCreateCommon();
     }
 
     final void performStart() {
+        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
         mFragments.noteStateNotSaved();
         mCalled = false;
         mFragments.execPendingActions();
@@ -5656,6 +5666,7 @@
                 lm.doReportStart();
             }
         }
+        mActivityTransitionState.enterReady(this);
     }
     
     final void performRestart() {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 692efd7..a057c3e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,18 +17,18 @@
 package android.app;
 
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.transition.Transition;
-import android.util.ArrayMap;
 import android.util.Pair;
 import android.view.View;
 import android.view.Window;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -108,6 +108,12 @@
     private static final String KEY_TRANSITION_COMPLETE_LISTENER
             = "android:transitionCompleteListener";
 
+    private static final String KEY_TRANSITION_IS_RETURNING = "android:transitionIsReturning";
+    private static final String KEY_TRANSITION_SHARED_ELEMENTS = "android:sharedElementNames";
+    private static final String KEY_LOCAL_SHARED_ELEMENTS = "android:localSharedElementNames";
+    private static final String KEY_RESULT_DATA = "android:resultData";
+    private static final String KEY_RESULT_CODE = "android:resultCode";
+
     /** @hide */
     public static final int ANIM_NONE = 0;
     /** @hide */
@@ -131,7 +137,12 @@
     private int mStartWidth;
     private int mStartHeight;
     private IRemoteCallback mAnimationStartedListener;
-    private ResultReceiver mExitReceiver;
+    private ResultReceiver mTransitionReceiver;
+    private boolean mIsReturning;
+    private ArrayList<String> mSharedElementNames;
+    private ArrayList<String> mLocalSharedElementNames;
+    private Intent mResultData;
+    private int mResultCode;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -334,7 +345,7 @@
      * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
      * enabled on the calling Activity to cause an exit transition. The same must be in
      * the called Activity to get an entering transition.</p>
-     * @param window The window containing shared elements.
+     * @param activity The Activity whose window contains the shared elements.
      * @param sharedElement The View to transition to the started Activity. sharedElement must
      *                      have a non-null sharedElementName.
      * @param sharedElementName The shared element name as used in the target Activity. This may
@@ -344,40 +355,70 @@
      * @see android.transition.Transition#setEpicenterCallback(
      *          android.transition.Transition.EpicenterCallback)
      */
-    public static ActivityOptions makeSceneTransitionAnimation(Window window,
+    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
             View sharedElement, String sharedElementName) {
-        return makeSceneTransitionAnimation(window,
-                new SharedElementMappingListener(sharedElement, sharedElementName));
+        return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
     }
 
     /**
      * Create an ActivityOptions to transition between Activities using cross-Activity scene
      * animations. This method carries the position of multiple shared elements to the started
-     * Activity. The position of the first element in the value returned from
-     * {@link android.app.ActivityOptions.ActivityTransitionListener#getSharedElementsMapping()}
+     * Activity. The position of the first element in sharedElements
      * will be used as the epicenter for the exit Transition. The position of the associated
      * shared element in the launched Activity will be the epicenter of its entering Transition.
      *
      * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
      * enabled on the calling Activity to cause an exit transition. The same must be in
      * the called Activity to get an entering transition.</p>
-     * @param window The window containing shared elements.
-     * @param listener The listener to use to monitor activity transition events.
+     * @param activity The Activity whose window contains the shared elements.
+     * @param sharedElements The names of the shared elements to transfer to the called
+     *                       Activity and their associated Views. The Views must each have
+     *                       a unique shared element name.
      * @return Returns a new ActivityOptions object that you can use to
      *         supply these options as the options Bundle when starting an activity.
      *         Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}.
      * @see android.transition.Transition#setEpicenterCallback(
      *          android.transition.Transition.EpicenterCallback)
      */
-    public static ActivityOptions makeSceneTransitionAnimation(Window window,
-            ActivityTransitionListener listener) {
-        if (!window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
+            Pair<View, String>... sharedElements) {
+        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
             return null;
         }
         ActivityOptions opts = new ActivityOptions();
         opts.mAnimationType = ANIM_SCENE_TRANSITION;
-        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(window, listener);
-        opts.mExitReceiver = exit;
+
+        ArrayList<String> names = new ArrayList<String>();
+        ArrayList<String> mappedNames = new ArrayList<String>();
+
+        if (sharedElements != null) {
+            for (int i = 0; i < sharedElements.length; i++) {
+                Pair<View, String> sharedElement = sharedElements[i];
+                names.add(sharedElement.second);
+                mappedNames.add(sharedElement.first.getViewName());
+            }
+        }
+
+        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
+                mappedNames, false);
+        opts.mTransitionReceiver = exit;
+        opts.mSharedElementNames = names;
+        opts.mLocalSharedElementNames = mappedNames;
+        opts.mIsReturning = false;
+        return opts;
+    }
+
+    /** @hide */
+    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
+            ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
+            int resultCode, Intent resultData) {
+        ActivityOptions opts = new ActivityOptions();
+        opts.mAnimationType = ANIM_SCENE_TRANSITION;
+        opts.mSharedElementNames = sharedElementNames;
+        opts.mTransitionReceiver = exitCoordinator;
+        opts.mIsReturning = true;
+        opts.mResultCode = resultCode;
+        opts.mResultData = resultData;
         return opts;
     }
 
@@ -413,7 +454,12 @@
                 break;
 
             case ANIM_SCENE_TRANSITION:
-                mExitReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
+                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
+                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
+                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
+                mLocalSharedElementNames = opts.getStringArrayList(KEY_LOCAL_SHARED_ELEMENTS);
+                mResultData = opts.getParcelable(KEY_RESULT_DATA);
+                mResultCode = opts.getInt(KEY_RESULT_CODE);
                 break;
         }
     }
@@ -470,15 +516,15 @@
 
     /** @hide */
     public void dispatchActivityStopped() {
-        if (mExitReceiver != null) {
-            mExitReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null);
+        if (mTransitionReceiver != null) {
+            mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null);
         }
     }
 
     /** @hide */
     public void dispatchStartExit() {
-        if (mExitReceiver != null) {
-            mExitReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null);
+        if (mTransitionReceiver != null) {
+            mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null);
         }
     }
 
@@ -493,21 +539,39 @@
     }
 
     /** @hide */
+    public void setReturning() {
+        mIsReturning = true;
+    }
+
+    /** @hide */
+    public boolean isReturning() {
+        return mIsReturning;
+    }
+
+    /** @hide */
+    public ArrayList<String> getSharedElementNames() {
+        return mSharedElementNames;
+    }
+
+    /** @hide */
+    public ArrayList<String> getLocalSharedElementNames() { return mLocalSharedElementNames; }
+
+    /** @hide */
+    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
+
+    /** @hide */
+    public int getResultCode() { return mResultCode; }
+
+    /** @hide */
+    public Intent getResultData() { return mResultData; }
+
+    /** @hide */
     public static void abort(Bundle options) {
         if (options != null) {
             (new ActivityOptions(options)).abort();
         }
     }
 
-    /** @hide */
-    public EnterTransitionCoordinator createEnterActivityTransition(Activity activity) {
-        EnterTransitionCoordinator coordinator = null;
-        if (mAnimationType == ANIM_SCENE_TRANSITION) {
-            coordinator = new EnterTransitionCoordinator(activity, mExitReceiver);
-        }
-        return coordinator;
-    }
-
     /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
@@ -517,7 +581,12 @@
         if (otherOptions.mPackageName != null) {
             mPackageName = otherOptions.mPackageName;
         }
-        mExitReceiver = null;
+        mTransitionReceiver = null;
+        mSharedElementNames = null;
+        mLocalSharedElementNames = null;
+        mIsReturning = false;
+        mResultData = null;
+        mResultCode = 0;
         switch (otherOptions.mAnimationType) {
             case ANIM_CUSTOM:
                 mAnimationType = otherOptions.mAnimationType;
@@ -562,9 +631,14 @@
                 break;
             case ANIM_SCENE_TRANSITION:
                 mAnimationType = otherOptions.mAnimationType;
-                mExitReceiver = otherOptions.mExitReceiver;
+                mTransitionReceiver = otherOptions.mTransitionReceiver;
+                mSharedElementNames = otherOptions.mSharedElementNames;
+                mLocalSharedElementNames = otherOptions.mLocalSharedElementNames;
+                mIsReturning = otherOptions.mIsReturning;
                 mThumbnail = null;
                 mAnimationStartedListener = null;
+                mResultData = otherOptions.mResultData;
+                mResultCode = otherOptions.mResultCode;
                 break;
         }
     }
@@ -608,9 +682,14 @@
                 break;
             case ANIM_SCENE_TRANSITION:
                 b.putInt(KEY_ANIM_TYPE, mAnimationType);
-                if (mExitReceiver != null) {
-                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mExitReceiver);
+                if (mTransitionReceiver != null) {
+                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
                 }
+                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
+                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
+                b.putStringArrayList(KEY_LOCAL_SHARED_ELEMENTS, mLocalSharedElementNames);
+                b.putParcelable(KEY_RESULT_DATA, mResultData);
+                b.putInt(KEY_RESULT_CODE, mResultCode);
                 break;
         }
         return b;
@@ -630,126 +709,4 @@
         return null;
     }
 
-    /**
-     * Listener provided in
-     * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
-     * android.app.ActivityOptions.ActivityTransitionListener)} or in
-     * {@link android.app.Activity#setActivityTransitionListener(
-     * android.app.ActivityOptions.ActivityTransitionListener)} to monitor the Activity transitions.
-     * The events can be used to customize or override Activity Transition behavior.
-     */
-    public static class ActivityTransitionListener {
-        /**
-         * Called when the enter Transition is ready to start, but hasn't started yet. If
-         * {@link android.view.Window#getEnterTransition()} is non-null,
-         * The entering views will be {@link View#INVISIBLE}.
-         */
-        public void onEnterReady() {}
-
-        /**
-         * Called when the remote exiting transition completes.
-         */
-        public void onRemoteExitComplete() {}
-
-        /**
-         * Called when the start state for shared elements is captured on enter.
-         *
-         * @param sharedElementNames The names of the shared elements that were accepted into
-         *                           the View hierarchy.
-         * @param sharedElements The shared elements that are part of the View hierarchy.
-         * @param sharedElementSnapshots The Views containing snap shots of the shared element
-         *                               from the launching Window. These elements will not
-         *                               be part of the scene, but will be positioned relative
-         *                               to the Window decor View.
-         */
-        public void onCaptureSharedElementStart(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots) {}
-
-        /**
-         * Called when the end state for shared elements is captured on enter.
-         *
-         * @param sharedElementNames The names of the shared elements that were accepted into
-         *                           the View hierarchy.
-         * @param sharedElements The shared elements that are part of the View hierarchy.
-         * @param sharedElementSnapshots The Views containing snap shots of the shared element
-         *                               from the launching Window. These elements will not
-         *                               be part of the scene, but will be positioned relative
-         *                               to the Window decor View.
-         */
-        public void onCaptureSharedElementEnd(List<String> sharedElementNames,
-                List<View> sharedElements, List<View> sharedElementSnapshots) {}
-
-        /**
-         * Called when the enter Transition has been started.
-         * @param sharedElementNames The names of shared elements that were transferred.
-         * @param sharedElements The shared elements that were transferred.
-         */
-        public void onStartEnterTransition(List<String> sharedElementNames,
-                List<View> sharedElements) {}
-
-        /**
-         * Called when the exit Transition has been started.
-         * @param sharedElementNames The names of all shared elements that will be transferred.
-         * @param sharedElements All shared elements that will be transferred.
-         */
-        public void onStartExitTransition(List<String> sharedElementNames,
-                List<View> sharedElements) {}
-
-        /**
-         * Called when the exiting shared element transition completes.
-         */
-        public void onSharedElementExitTransitionComplete() {}
-
-        /**
-         * Called on exit when the shared element has been transferred.
-         * @param sharedElementNames The names of all shared elements that were transferred.
-         * @param sharedElements All shared elements that will were transferred.
-         */
-        public void onSharedElementTransferred(List<String> sharedElementNames,
-                List<View> sharedElements) {}
-
-        /**
-         * Called when the exit transition has completed.
-         */
-        public void onExitTransitionComplete() {}
-
-        /**
-         * Returns a mapping from a View in the View hierarchy to the shared element name used
-         * in the call. This is called twice -- once when the view is
-         * entering and again when it exits. A null return value indicates that the
-         * View hierachy can be trusted without any remapping.
-         * @return A map from a View in the hierarchy to the shared element name used in the
-         * call.
-         */
-        public Pair<View, String>[] getSharedElementsMapping() { return null; }
-
-        /**
-         * Returns <code>true</code> if the ActivityTransitionListener will handle removing
-         * rejected shared elements from the scene. If <code>false</code> is returned, a default
-         * animation will be used to remove the rejected shared elements from the scene.
-         *
-         * @param rejectedSharedElements Views containing visual information of shared elements
-         *                               that are not part of the entering scene. These Views
-         *                               are positioned relative to the Window decor View.
-         * @return <code>false</code> if the default animation should be used to remove the
-         * rejected shared elements from the scene or <code>true</code> if the listener provides
-         * custom handling.
-         */
-        public boolean handleRejectedSharedElements(List<View> rejectedSharedElements) {
-            return false;
-        }
-    }
-
-    private static class SharedElementMappingListener extends ActivityTransitionListener {
-        Pair<View, String>[] mSharedElementsMapping = new Pair[1];
-
-        public SharedElementMappingListener(View view, String name) {
-            mSharedElementsMapping[0] = Pair.create(view, name);
-        }
-
-        @Override
-        public Pair<View, String>[] getSharedElementsMapping() {
-            return mSharedElementsMapping;
-        }
-    }
 }
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index ca64788..6c6a52f 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,27 +15,14 @@
  */
 package android.app;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.ResultReceiver;
 import android.transition.Transition;
-import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.util.ArrayMap;
-import android.util.Pair;
-import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroupOverlay;
-import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.widget.ImageView;
 
@@ -83,14 +70,13 @@
  *    - onActivityStopped() is called and all exited Views are made VISIBLE.
  *
  * Typical finishWithTransition goes like this:
- * 1) finishWithTransition() calls startExit()
- *    - The Window start transitioning to Translucent
+ * 1) finishWithTransition() creates an ExitTransitionCoordinator and calls startExit()
+ *    - The Window start transitioning to Translucent with a new ActivityOptions.
  *    - If no background exists, a black background is substituted
- *    - MSG_PREPARE_RESTORE is sent to the ExitTransitionCoordinator
  *    - The shared elements in the scene are matched against those shared elements
  *      that were sent by comparing the names.
  *    - The exit transition is started by setting Views to INVISIBLE.
- * 2) MSG_PREPARE_RESTORE is received by the EnterTransitionCoordinator
+ * 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created.
  *    - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped()
  *      was called
  * 3) The Window is made translucent and a callback is received
@@ -98,21 +84,21 @@
  * 4) The background alpha animation completes
  * 5) The shared element transition completes
  *    - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the
- *      ExitTransitionCoordinator
- * 6) MSG_TAKE_SHARED_ELEMENTS is received by ExitTransitionCoordinator
+ *      EnterTransitionCoordinator
+ * 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator
  *    - Shared elements are made VISIBLE
  *    - Shared elements positions and size are set to match the end state of the calling
  *      Activity.
  *    - The shared element transition is started
  *    - If the window allows overlapping transitions, the views transition is started by setting
  *      the entering Views to VISIBLE.
- *    - MSG_HIDE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator
- * 7) MSG_HIDE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator
+ *    - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
+ * 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
  *    - The shared elements are made INVISIBLE
  * 8) The exit transition completes in the finishing Activity.
- *    - MSG_EXIT_TRANSITION_COMPLETE is sent to the ExitTransitionCoordinator.
+ *    - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
  *    - finish() is called on the exiting Activity
- * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the ExitTransitionCoordinator.
+ * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
  *    - If the window doesn't allow overlapping enter transitions, the enter transition is started
  *      by setting entering views to VISIBLE.
  */
@@ -120,30 +106,24 @@
     private static final String TAG = "ActivityTransitionCoordinator";
 
     /**
-     * The names of shared elements that are transitioned to the started Activity.
-     * This is also the name of shared elements that the started Activity accepted.
-     */
-    public static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names";
-
-    public static final String KEY_SHARED_ELEMENT_STATE = "android:shared_element_state";
-
-    /**
      * For Activity transitions, the called Activity's listener to receive calls
      * when transitions complete.
      */
-    static final String KEY_TRANSITION_RESULTS_RECEIVER = "android:transitionTargetListener";
+    static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver";
 
-    private static final String KEY_SCREEN_X = "shared_element:screenX";
-    private static final String KEY_SCREEN_Y = "shared_element:screenY";
-    private static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
-    private static final String KEY_WIDTH = "shared_element:width";
-    private static final String KEY_HEIGHT = "shared_element:height";
-    private static final String KEY_NAME = "shared_element:name";
-    private static final String KEY_BITMAP = "shared_element:bitmap";
-    private static final String KEY_SCALE_TYPE = "shared_element:scaleType";
-    private static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
+    protected static final String KEY_SCREEN_X = "shared_element:screenX";
+    protected static final String KEY_SCREEN_Y = "shared_element:screenY";
+    protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
+    protected static final String KEY_WIDTH = "shared_element:width";
+    protected static final String KEY_HEIGHT = "shared_element:height";
+    protected static final String KEY_BITMAP = "shared_element:bitmap";
+    protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
+    protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
 
-    private static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
+    // The background fade in/out duration. 150ms is pretty quick, but not abrupt.
+    public static final int FADE_BACKGROUND_DURATION_MS = 150;
+
+    protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
 
     /**
      * Sent by the exiting coordinator (either EnterTransitionCoordinator
@@ -154,7 +134,7 @@
      * until this message is received, but may wait for
      * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
      */
-    public static final int MSG_SET_LISTENER = 100;
+    public static final int MSG_SET_REMOTE_RECEIVER = 100;
 
     /**
      * Sent by the entering coordinator to tell the exiting coordinator
@@ -165,17 +145,10 @@
     public static final int MSG_HIDE_SHARED_ELEMENTS = 101;
 
     /**
-     * Sent by the EnterTransitionCoordinator to tell the
-     * ExitTransitionCoordinator to hide all of its exited views after
-     * MSG_ACTIVITY_STOPPED has caused them all to show.
-     */
-    public static final int MSG_PREPARE_RESTORE = 102;
-
-    /**
      * Sent by the exiting Activity in ActivityOptions#dispatchActivityStopped
      * to leave the Activity in a good state after it has been hidden.
      */
-    public static final int MSG_ACTIVITY_STOPPED = 103;
+    public static final int MSG_ACTIVITY_STOPPED = 102;
 
     /**
      * Sent by the exiting coordinator (either EnterTransitionCoordinator
@@ -186,7 +159,7 @@
      * until this message is received, but may wait for
      * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
      */
-    public static final int MSG_TAKE_SHARED_ELEMENTS = 104;
+    public static final int MSG_TAKE_SHARED_ELEMENTS = 103;
 
     /**
      * Sent by the exiting coordinator (either
@@ -196,309 +169,41 @@
      * remote coordinator. If it is false, it will trigger the enter
      * transition to start.
      */
-    public static final int MSG_EXIT_TRANSITION_COMPLETE = 105;
+    public static final int MSG_EXIT_TRANSITION_COMPLETE = 104;
 
     /**
      * Sent by Activity#startActivity to begin the exit transition.
      */
-    public static final int MSG_START_EXIT_TRANSITION = 106;
+    public static final int MSG_START_EXIT_TRANSITION = 105;
 
-    private Window mWindow;
-    private ArrayList<View> mSharedElements = new ArrayList<View>();
-    private ArrayList<String> mTargetSharedNames = new ArrayList<String>();
-    private ActivityOptions.ActivityTransitionListener mListener =
-            new ActivityOptions.ActivityTransitionListener();
-    private ArrayList<View> mEnteringViews;
-    private ResultReceiver mRemoteResultReceiver;
-    private boolean mNotifiedSharedElementTransitionComplete;
-    private boolean mNotifiedExitTransitionComplete;
-    private boolean mSharedElementTransitionStarted;
+    /**
+     * It took too long for a message from the entering Activity, so we canceled the transition.
+     */
+    public static final int MSG_CANCEL = 106;
 
-    private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+    final private Window mWindow;
+    final protected ArrayList<String> mAllSharedElementNames;
+    final protected ArrayList<View> mSharedElements = new ArrayList<View>();
+    final protected ArrayList<String> mSharedElementNames = new ArrayList<String>();
+    final protected ArrayList<View> mTransitioningViews = new ArrayList<View>();
+    final protected SharedElementListener mListener;
+    protected ResultReceiver mResultReceiver;
+    final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
 
-    private Transition.TransitionListener mSharedElementListener =
-            new Transition.TransitionListenerAdapter() {
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            transition.removeListener(this);
-            onSharedElementTransitionEnd();
-        }
-    };
-
-    private Transition.TransitionListener mExitListener =
-            new Transition.TransitionListenerAdapter() {
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            transition.removeListener(this);
-            onExitTransitionEnd();
-        }
-    };
-
-    public ActivityTransitionCoordinator(Window window)
-    {
+    public ActivityTransitionCoordinator(Window window,
+            ArrayList<String> allSharedElementNames,
+            ArrayList<String> accepted, ArrayList<String> localNames,
+            SharedElementListener listener) {
         super(new Handler());
         mWindow = window;
-    }
-
-    // -------------------- ResultsReceiver Overrides ----------------------
-    @Override
-    protected void onReceiveResult(int resultCode, Bundle resultData) {
-        switch (resultCode) {
-            case MSG_SET_LISTENER:
-                ResultReceiver resultReceiver
-                        = resultData.getParcelable(KEY_TRANSITION_RESULTS_RECEIVER);
-                setRemoteResultReceiver(resultReceiver);
-                onSetResultReceiver();
-                break;
-            case MSG_HIDE_SHARED_ELEMENTS:
-                onHideSharedElements();
-                break;
-            case MSG_PREPARE_RESTORE:
-                onPrepareRestore();
-                break;
-            case MSG_EXIT_TRANSITION_COMPLETE:
-                if (!mSharedElementTransitionStarted) {
-                    send(resultCode, resultData);
-                } else {
-                    onRemoteSceneExitComplete();
-                }
-                break;
-            case MSG_TAKE_SHARED_ELEMENTS:
-                ArrayList<String> sharedElementNames
-                        = resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES);
-                Bundle sharedElementState = resultData.getBundle(KEY_SHARED_ELEMENT_STATE);
-                onTakeSharedElements(sharedElementNames, sharedElementState);
-                break;
-            case MSG_ACTIVITY_STOPPED:
-                onActivityStopped();
-                break;
-            case MSG_START_EXIT_TRANSITION:
-                startExit();
-                break;
-        }
-    }
-
-    // -------------------- calls that can be overridden by subclasses --------------------
-
-    /**
-     * Called when MSG_SET_LISTENER is received. This will only be received by
-     * ExitTransitionCoordinator.
-     */
-    protected void onSetResultReceiver() {}
-
-    /**
-     * Called when MSG_HIDE_SHARED_ELEMENTS is received
-     */
-    protected void onHideSharedElements() {
-        setViewVisibility(getSharedElements(), View.INVISIBLE);
-        mListener.onSharedElementTransferred(getSharedElementNames(), getSharedElements());
-    }
-
-    /**
-     * Called when MSG_PREPARE_RESTORE is called. This will only be received by
-     * ExitTransitionCoordinator.
-     */
-    protected void onPrepareRestore() {
-        mListener.onEnterReady();
-    }
-
-    /**
-     * Called when MSG_EXIT_TRANSITION_COMPLETE is received -- the remote coordinator has
-     * completed its exit transition. This can be called by the ExitTransitionCoordinator when
-     * starting an Activity or EnterTransitionCoordinator when called with finishWithTransition.
-     */
-    protected void onRemoteSceneExitComplete() {
-        if (!allowOverlappingTransitions()) {
-            Transition transition = beginTransition(mEnteringViews, false, true, true);
-            onStartEnterTransition(transition, mEnteringViews);
-        }
-        mListener.onRemoteExitComplete();
-    }
-
-    /**
-     * Called when MSG_TAKE_SHARED_ELEMENTS is received. This means that the shared elements are
-     * in a stable state and ready to move to the Window.
-     * @param sharedElementNames The names of the shared elements to move.
-     * @param state Contains the shared element states (size & position)
-     */
-    protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
-        setSharedElements();
-        reconcileSharedElements(sharedElementNames);
-        mEnteringViews.removeAll(mSharedElements);
-        final ArrayList<View> accepted = new ArrayList<View>();
-        final ArrayList<View> rejected = new ArrayList<View>();
-        createSharedElementImages(accepted, rejected, sharedElementNames, state);
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
-                setSharedElementState(state, accepted);
-        handleRejected(rejected);
-
+        mListener = listener;
+        mAllSharedElementNames = allSharedElementNames;
+        setSharedElements(accepted, localNames);
         if (getViewsTransition() != null) {
-            setViewVisibility(mEnteringViews, View.INVISIBLE);
+            getDecor().captureTransitioningViews(mTransitioningViews);
+            mTransitioningViews.removeAll(mSharedElements);
         }
-        setViewVisibility(mSharedElements, View.VISIBLE);
-        Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(),
-                true);
-        setOriginalImageViewState(originalImageViewState);
-
-        if (allowOverlappingTransitions()) {
-            onStartEnterTransition(transition, mEnteringViews);
-        }
-
-        mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
-    }
-
-    /**
-     * Called when MSG_ACTIVITY_STOPPED is received. This is received when Activity.onStop is
-     * called after running startActivity* is called using an Activity Transition.
-     */
-    protected void onActivityStopped() {}
-
-    /**
-     * Called when the start transition is ready to run. This may be immediately after
-     * MSG_TAKE_SHARED_ELEMENTS or MSG_EXIT_TRANSITION_COMPLETE, depending on whether
-     * overlapping transitions are allowed.
-     * @param transition The transition currently started.
-     * @param enteringViews The views entering the scene. This won't include shared elements.
-     */
-    protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) {
-        if (getViewsTransition() != null) {
-            setViewVisibility(enteringViews, View.VISIBLE);
-        }
-        mEnteringViews = null;
-        mListener.onStartEnterTransition(getSharedElementNames(), getSharedElements());
-    }
-
-    /**
-     * Called when the exit transition has started.
-     * @param exitingViews The views leaving the scene. This won't include shared elements.
-     */
-    protected void onStartExitTransition(ArrayList<View> exitingViews) {}
-
-    /**
-     * Called during the exit when the shared element transition has completed.
-     */
-    protected void onSharedElementTransitionEnd() {
-        Bundle bundle = new Bundle();
-        int[] tempLoc = new int[2];
-        for (int i = 0; i < mSharedElements.size(); i++) {
-            View sharedElement = mSharedElements.get(i);
-            String name = mTargetSharedNames.get(i);
-            captureSharedElementState(sharedElement, name, bundle, tempLoc);
-        }
-        Bundle allValues = new Bundle();
-        allValues.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, getSharedElementNames());
-        allValues.putBundle(KEY_SHARED_ELEMENT_STATE, bundle);
-        sharedElementTransitionComplete(allValues);
-        mListener.onSharedElementExitTransitionComplete();
-    }
-
-    /**
-     * Called after the shared element transition is complete to pass the shared element state
-     * to the remote coordinator.
-     * @param bundle The Bundle to send to the coordinator containing the shared element state.
-     */
-    protected abstract void sharedElementTransitionComplete(Bundle bundle);
-
-    /**
-     * Called when the exit transition finishes.
-     */
-    protected void onExitTransitionEnd() {
-        mListener.onExitTransitionComplete();
-    }
-
-    /**
-     * Called to start the exit transition. Launched from ActivityOptions#dispatchStartExit
-     */
-    protected abstract void startExit();
-
-    /**
-     * A non-null transition indicates that the Views of the Window should be made INVISIBLE.
-     * @return The Transition used to cause transitioning views to either enter or exit the scene.
-     */
-    protected abstract Transition getViewsTransition();
-
-    /**
-     * @return The Transition used to move the shared elements from the start position and size
-     * to the end position and size.
-     */
-    protected abstract Transition getSharedElementTransition();
-
-    /**
-     * @return When the enter transition should overlap with the exit transition of the
-     * remote controller.
-     */
-    protected abstract boolean allowOverlappingTransitions();
-
-    // called by subclasses
-
-    protected void notifySharedElementTransitionComplete(Bundle sharedElements) {
-        if (!mNotifiedSharedElementTransitionComplete) {
-            mNotifiedSharedElementTransitionComplete = true;
-            mRemoteResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, sharedElements);
-        }
-    }
-
-    protected void notifyExitTransitionComplete() {
-        if (!mNotifiedExitTransitionComplete) {
-            mNotifiedExitTransitionComplete = true;
-            mRemoteResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
-        }
-    }
-
-    protected void notifyPrepareRestore() {
-        mRemoteResultReceiver.send(MSG_PREPARE_RESTORE, null);
-    }
-
-    protected void setRemoteResultReceiver(ResultReceiver resultReceiver) {
-        mRemoteResultReceiver = resultReceiver;
-    }
-
-    protected void notifySetListener() {
-        Bundle bundle = new Bundle();
-        bundle.putParcelable(KEY_TRANSITION_RESULTS_RECEIVER, this);
-        mRemoteResultReceiver.send(MSG_SET_LISTENER, bundle);
-    }
-
-    protected void setEnteringViews(ArrayList<View> views) {
-        mEnteringViews = views;
-    }
-
-    protected void setSharedElements() {
-        Pair<View, String>[] sharedElements = mListener.getSharedElementsMapping();
-        mSharedElements.clear();
-        mTargetSharedNames.clear();
-        if (sharedElements == null) {
-            ArrayMap<String, View> map = new ArrayMap<String, View>();
-            if (getViewsTransition() != null) {
-                setViewVisibility(mEnteringViews, View.VISIBLE);
-            }
-            getDecor().findNamedViews(map);
-            if (getViewsTransition() != null) {
-                setViewVisibility(mEnteringViews, View.INVISIBLE);
-            }
-            for (int i = 0; i < map.size(); i++) {
-                View view = map.valueAt(i);
-                String name = map.keyAt(i);
-                mSharedElements.add(view);
-                mTargetSharedNames.add(name);
-            }
-        } else {
-            for (int i = 0; i < sharedElements.length; i++) {
-                Pair<View, String> viewStringPair = sharedElements[i];
-                View view = viewStringPair.first;
-                String name = viewStringPair.second;
-                mSharedElements.add(view);
-                mTargetSharedNames.add(name);
-            }
-        }
-    }
-
-    protected ArrayList<View> getSharedElements() {
-        return mSharedElements;
-    }
-
-    protected ArrayList<String> getSharedElementNames() {
-        return mTargetSharedNames;
+        setEpicenter();
     }
 
     protected Window getWindow() {
@@ -509,238 +214,43 @@
         return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
     }
 
-    protected void startExitTransition(ArrayList<String> sharedElements) {
-        setSharedElements();
-        reconcileSharedElements(sharedElements);
-        ArrayList<View> transitioningViews = captureTransitioningViews();
-        beginTransition(transitioningViews, true, true, false);
-        onStartExitTransition(transitioningViews);
-        if (getViewsTransition() != null) {
-            setViewVisibility(transitioningViews, View.INVISIBLE);
+    /**
+     * Sets the transition epicenter to the position of the first shared element.
+     */
+    protected void setEpicenter() {
+        View epicenter = null;
+        if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty() &&
+                mAllSharedElementNames.get(0).equals(mSharedElementNames.get(0))) {
+            epicenter = mSharedElements.get(0);
         }
-        mListener.onStartExitTransition(getSharedElementNames(), getSharedElements());
+        setEpicenter(epicenter);
     }
 
-    protected void clearConnections() {
-        mRemoteResultReceiver = null;
-    }
-
-    // public API
-
-    public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) {
-        if (listener == null) {
-            mListener = new ActivityOptions.ActivityTransitionListener();
+    private void setEpicenter(View view) {
+        if (view == null) {
+            mEpicenterCallback.setEpicenter(null);
         } else {
-            mListener = listener;
+            int[] loc = new int[2];
+            view.getLocationOnScreen(loc);
+            int left = loc[0] + Math.round(view.getTranslationX());
+            int top = loc[1] + Math.round(view.getTranslationY());
+            int right = left + view.getWidth();
+            int bottom = top + view.getHeight();
+            Rect epicenter = new Rect(left, top, right, bottom);
+            mEpicenterCallback.setEpicenter(epicenter);
         }
     }
 
-    // private methods
-
-    private Transition configureTransition(Transition transition) {
-        if (transition != null) {
-            transition = transition.clone();
-            transition.setEpicenterCallback(mEpicenterCallback);
-        }
-        return transition;
+    public ArrayList<String> getAcceptedNames() {
+        return mSharedElementNames;
     }
 
-    private void reconcileSharedElements(ArrayList<String> sharedElementNames) {
-        // keep only those that are in sharedElementNames.
-        int numSharedElements = sharedElementNames.size();
-        int targetIndex = 0;
-        for (int i = 0; i < numSharedElements; i++) {
-            String name = sharedElementNames.get(i);
-            int index = mTargetSharedNames.indexOf(name);
-            if (index >= 0) {
-                // Swap the items at the indexes if necessary.
-                if (index != targetIndex) {
-                    View temp = mSharedElements.get(index);
-                    mSharedElements.set(index, mSharedElements.get(targetIndex));
-                    mSharedElements.set(targetIndex, temp);
-                    mTargetSharedNames.set(index, mTargetSharedNames.get(targetIndex));
-                    mTargetSharedNames.set(targetIndex, name);
-                }
-                targetIndex++;
-            }
+    public ArrayList<String> getMappedNames() {
+        ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
+        for (int i = 0; i < mSharedElements.size(); i++) {
+            names.add(mSharedElements.get(i).getViewName());
         }
-        for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) {
-            mSharedElements.remove(i);
-            mTargetSharedNames.remove(i);
-        }
-        Rect epicenter = null;
-        if (!mTargetSharedNames.isEmpty()
-                && mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) {
-            epicenter = calcEpicenter(mSharedElements.get(0));
-        }
-        mEpicenterCallback.setEpicenter(epicenter);
-    }
-
-    private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
-            Bundle sharedElementState, final ArrayList<View> acceptedOverlayViews) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
-        final int[] tempLoc = new int[2];
-        if (sharedElementState != null) {
-            for (int i = 0; i < mSharedElements.size(); i++) {
-                View sharedElement = mSharedElements.get(i);
-                String name = mTargetSharedNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
-                        name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
-                View parent = (View) sharedElement.getParent();
-                parent.getLocationOnScreen(tempLoc);
-                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
-                sharedElement.requestLayout();
-            }
-        }
-        mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements,
-                acceptedOverlayViews);
-
-        getDecor().getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
-                        mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements,
-                                acceptedOverlayViews);
-                        mSharedElementTransitionStarted = true;
-                        return true;
-                    }
-                }
-        );
-        return originalImageState;
-    }
-
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
-            Bundle transitionArgs) {
-        if (!(view instanceof ImageView)) {
-            return null;
-        }
-        Bundle bundle = transitionArgs.getBundle(name);
-        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
-        if (scaleTypeInt < 0) {
-            return null;
-        }
-
-        ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
-        }
-
-        return Pair.create(originalScaleType, originalMatrix);
-    }
-
-    /**
-     * Sets the captured values from a previous
-     * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])}
-     * @param view The View to apply placement changes to.
-     * @param name The shared element name given from the source Activity.
-     * @param transitionArgs A <code>Bundle</code> containing all placementinformation for named
-     *                       shared elements in the scene.
-     * @param parentLoc The x and y coordinates of the parent's screen position.
-     */
-    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] parentLoc) {
-        Bundle sharedElementBundle = transitionArgs.getBundle(name);
-        if (sharedElementBundle == null) {
-            return;
-        }
-
-        if (view instanceof ImageView) {
-            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
-            if (scaleTypeInt >= 0) {
-                ImageView imageView = (ImageView) view;
-                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
-                imageView.setScaleType(scaleType);
-                if (scaleType == ImageView.ScaleType.MATRIX) {
-                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
-                    Matrix matrix = new Matrix();
-                    matrix.setValues(matrixValues);
-                    imageView.setImageMatrix(matrix);
-                }
-            }
-        }
-
-        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
-        view.setTranslationZ(z);
-
-        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
-        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
-        int width = sharedElementBundle.getInt(KEY_WIDTH);
-        int height = sharedElementBundle.getInt(KEY_HEIGHT);
-
-        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
-        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
-        view.measure(widthSpec, heightSpec);
-
-        int left = x - parentLoc[0];
-        int top = y - parentLoc[1];
-        int right = left + width;
-        int bottom = top + height;
-        view.layout(left, top, right, bottom);
-    }
-
-    /**
-     * Captures placement information for Views with a shared element name for
-     * Activity Transitions.
-     * @param view The View to capture the placement information for.
-     * @param name The shared element name in the target Activity to apply the placement
-     *             information for.
-     * @param transitionArgs Bundle to store shared element placement information.
-     * @param tempLoc A temporary int[2] for capturing the current location of views.
-     * @see #setSharedElementState(android.view.View, String, android.os.Bundle, int[])
-     */
-    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] tempLoc) {
-        Bundle sharedElementBundle = new Bundle();
-        view.getLocationOnScreen(tempLoc);
-        float scaleX = view.getScaleX();
-        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
-        int width = Math.round(view.getWidth() * scaleX);
-        sharedElementBundle.putInt(KEY_WIDTH, width);
-
-        float scaleY = view.getScaleY();
-        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
-        int height= Math.round(view.getHeight() * scaleY);
-        sharedElementBundle.putInt(KEY_HEIGHT, height);
-
-        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
-
-        sharedElementBundle.putString(KEY_NAME, view.getViewName());
-
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
-
-        if (view instanceof ImageView) {
-            ImageView imageView = (ImageView) view;
-            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
-            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
-            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
-                float[] matrix = new float[9];
-                imageView.getImageMatrix().getValues(matrix);
-                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
-            }
-        }
-
-        transitionArgs.putBundle(name, sharedElementBundle);
-    }
-
-    private static Rect calcEpicenter(View view) {
-        int[] loc = new int[2];
-        view.getLocationOnScreen(loc);
-        int left = loc[0] + Math.round(view.getTranslationX());
-        int top = loc[1] + Math.round(view.getTranslationY());
-        int right = left + view.getWidth();
-        int bottom = top + view.getHeight();
-        return new Rect(left, top, right, bottom);
+        return names;
     }
 
     public static void setViewVisibility(Collection<View> views, int visibility) {
@@ -751,12 +261,12 @@
         }
     }
 
-    private static Transition addTransitionTargets(Transition transition, Collection<View> views) {
+    protected static Transition addTargets(Transition transition, Collection<View> views) {
         if (transition == null || views == null || views.isEmpty()) {
             return null;
         }
         TransitionSet set = new TransitionSet();
-        set.addTransition(transition.clone());
+        set.addTransition(transition);
         if (views != null) {
             for (View view: views) {
                 set.addTarget(view);
@@ -765,152 +275,62 @@
         return set;
     }
 
-    private ArrayList<View> captureTransitioningViews() {
-        if (getViewsTransition() == null) {
-            return null;
-        }
-        ArrayList<View> transitioningViews = new ArrayList<View>();
-        getDecor().captureTransitioningViews(transitioningViews);
-        transitioningViews.removeAll(getSharedElements());
-        return transitioningViews;
-    }
-
-    private Transition getSharedElementTransition(boolean isEnter) {
-        Transition transition = getSharedElementTransition();
-        if (transition == null) {
-            return null;
-        }
-        transition = configureTransition(transition);
-        if (!isEnter) {
-            transition.addListener(mSharedElementListener);
-        }
-        return transition;
-    }
-
-    private Transition getViewsTransition(ArrayList<View> transitioningViews, boolean isEnter) {
-        Transition transition = getViewsTransition();
-        if (transition == null) {
-            return null;
-        }
-        transition = configureTransition(transition);
-        if (!isEnter) {
-            transition.addListener(mExitListener);
-        }
-        return addTransitionTargets(transition, transitioningViews);
-    }
-
-    private Transition beginTransition(ArrayList<View> transitioningViews,
-            boolean transitionSharedElement, boolean transitionViews, boolean isEnter) {
-        Transition sharedElementTransition = null;
-        if (transitionSharedElement) {
-            sharedElementTransition = getSharedElementTransition(isEnter);
-            if (!isEnter && sharedElementTransition == null) {
-                onSharedElementTransitionEnd();
-            }
-        }
-        Transition viewsTransition = null;
-        if (transitionViews) {
-            viewsTransition = getViewsTransition(transitioningViews, isEnter);
-            if (!isEnter && viewsTransition == null) {
-                onExitTransitionEnd();
-            }
-        }
-
-        Transition transition = null;
-        if (sharedElementTransition == null) {
-            transition = viewsTransition;
-        } else if (viewsTransition == null) {
-            transition = sharedElementTransition;
-        } else {
-            TransitionSet set = new TransitionSet();
-            set.addTransition(sharedElementTransition);
-            set.addTransition(viewsTransition);
-            transition = set;
-        }
+    protected Transition configureTransition(Transition transition) {
         if (transition != null) {
-            TransitionManager.beginDelayedTransition(getDecor(), transition);
-            if (transitionSharedElement && !mSharedElements.isEmpty()) {
-                mSharedElements.get(0).invalidate();
-            } else if (transitionViews && !transitioningViews.isEmpty()) {
-                transitioningViews.get(0).invalidate();
-            }
+            transition = transition.clone();
+            transition.setEpicenterCallback(mEpicenterCallback);
         }
         return transition;
     }
 
-    private void handleRejected(final ArrayList<View> rejected) {
-        int numRejected = rejected.size();
-        if (numRejected == 0) {
-            return;
+    protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
+        if (transition1 == null) {
+            return transition2;
+        } else if (transition2 == null) {
+            return transition1;
+        } else {
+            TransitionSet transitionSet = new TransitionSet();
+            transitionSet.addTransition(transition1);
+            transitionSet.addTransition(transition2);
+            return transitionSet;
         }
-        boolean rejectionHandled = mListener.handleRejectedSharedElements(rejected);
-        if (rejectionHandled) {
-            return;
-        }
-
-        ViewGroupOverlay overlay = getDecor().getOverlay();
-        ObjectAnimator animator = null;
-        for (int i = 0; i < numRejected; i++) {
-            View view = rejected.get(i);
-            overlay.add(view);
-            animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
-            animator.start();
-        }
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                ViewGroupOverlay overlay = getDecor().getOverlay();
-                for (int i = rejected.size() - 1; i >= 0; i--) {
-                    overlay.remove(rejected.get(i));
-                }
-            }
-        });
     }
 
-    private void createSharedElementImages(ArrayList<View> accepted, ArrayList<View> rejected,
-            ArrayList<String> sharedElementNames, Bundle state) {
-        int numSharedElements = sharedElementNames.size();
-        Context context = getWindow().getContext();
-        int[] parentLoc = new int[2];
-        getDecor().getLocationOnScreen(parentLoc);
-        for (int i = 0; i < numSharedElements; i++) {
-            String name = sharedElementNames.get(i);
-            Bundle sharedElementBundle = state.getBundle(name);
-            if (sharedElementBundle != null) {
-                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
-                ImageView imageView = new ImageView(context);
-                imageView.setId(com.android.internal.R.id.shared_element);
-                imageView.setScaleType(ImageView.ScaleType.CENTER);
-                imageView.setImageBitmap(bitmap);
-                imageView.setViewName(name);
-                setSharedElementState(imageView, name, state, parentLoc);
-                if (mTargetSharedNames.contains(name)) {
-                    accepted.add(imageView);
-                } else {
-                    rejected.add(imageView);
+    private void setSharedElements(ArrayList<String> accepted, ArrayList<String> localNames) {
+        if (!mAllSharedElementNames.isEmpty()) {
+            ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
+            getDecor().findNamedViews(sharedElements);
+            if (accepted != null) {
+                for (int i = 0; i < localNames.size(); i++) {
+                    String localName = localNames.get(i);
+                    String acceptedName = accepted.get(i);
+                    if (!localName.equals(acceptedName)) {
+                        View view = sharedElements.remove(localName);
+                        if (view != null) {
+                            sharedElements.put(acceptedName, view);
+                        }
+                    }
+                }
+            }
+            sharedElements.retainAll(mAllSharedElementNames);
+            mListener.remapSharedElements(mAllSharedElementNames, sharedElements);
+            sharedElements.retainAll(mAllSharedElementNames);
+            for (int i = 0; i < mAllSharedElementNames.size(); i++) {
+                String name = mAllSharedElementNames.get(i);
+                View sharedElement = sharedElements.get(name);
+                if (sharedElement != null) {
+                    mSharedElementNames.add(name);
+                    mSharedElements.add(sharedElement);
                 }
             }
         }
     }
 
-    private static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
-        for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
-        }
+    protected void setResultReceiver(ResultReceiver resultReceiver) {
+        mResultReceiver = resultReceiver;
     }
 
-    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
-        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
-            if (scaleType == SCALE_TYPE_VALUES[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
+    protected abstract Transition getViewsTransition();
 
     private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
         private Rect mEpicenter;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
new file mode 100644
index 0000000..63019b6
--- /dev/null
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.Window;
+
+import java.util.ArrayList;
+
+/**
+ * This class contains all persistence-related functionality for Activity Transitions.
+ * Activities start exit and enter Activity Transitions through this class.
+ */
+class ActivityTransitionState {
+
+    private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
+
+    private static final String ENTERING_MAPPED_FROM = "android:enteringMappedFrom";
+
+    private static final String ENTERING_MAPPED_TO = "android:enteringMappedTo";
+
+    private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";
+
+    private static final String EXITING_MAPPED_TO = "android:exitingMappedTo";
+
+    /**
+     * The shared elements that the calling Activity has said that they transferred to this
+     * Activity.
+     */
+    private ArrayList<String> mEnteringNames;
+
+    /**
+     * The shared elements that this Activity as accepted and mapped to local Views.
+     */
+    private ArrayList<String> mEnteringFrom;
+
+    /**
+     * The names of local Views that are mapped to those elements in mEnteringFrom.
+     */
+    private ArrayList<String> mEnteringTo;
+
+    /**
+     * The names of shared elements that were shared to the called Activity.
+     */
+    private ArrayList<String> mExitingFrom;
+
+    /**
+     * The names of local Views that were shared out, mapped to those elements in mExitingFrom.
+     */
+    private ArrayList<String> mExitingTo;
+
+    /**
+     * The ActivityOptions used to call an Activity. Used to make the elements restore
+     * Visibility of exited Views.
+     */
+    private ActivityOptions mCalledActivityOptions;
+
+    /**
+     * We must be able to cancel entering transitions to stop changing the Window to
+     * opaque when we exit before making the Window opaque.
+     */
+    private EnterTransitionCoordinator mEnterTransitionCoordinator;
+
+    /**
+     * ActivityOptions used on entering this Activity.
+     */
+    private ActivityOptions mEnterActivityOptions;
+
+    /**
+     * Has an exit transition been started? If so, we don't want to double-exit.
+     */
+    private boolean mHasExited;
+
+    public ActivityTransitionState() {
+    }
+
+    public void readState(Bundle bundle) {
+        if (bundle != null) {
+            if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
+                mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
+                mEnteringFrom = bundle.getStringArrayList(ENTERING_MAPPED_FROM);
+                mEnteringTo = bundle.getStringArrayList(ENTERING_MAPPED_TO);
+            }
+            if (mEnterTransitionCoordinator == null) {
+                mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
+                mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO);
+            }
+        }
+    }
+
+    public void saveState(Bundle bundle) {
+        if (mEnteringNames != null) {
+            bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
+            bundle.putStringArrayList(ENTERING_MAPPED_FROM, mEnteringFrom);
+            bundle.putStringArrayList(ENTERING_MAPPED_TO, mEnteringTo);
+        }
+        if (mExitingFrom != null) {
+            bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
+            bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo);
+        }
+    }
+
+    public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
+        if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)
+                && options != null && mEnterActivityOptions == null
+                && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+            mEnterActivityOptions = options;
+            if (mEnterActivityOptions.isReturning()) {
+                int result = mEnterActivityOptions.getResultCode();
+                if (result != 0) {
+                    activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
+                }
+            }
+        }
+    }
+
+    public void enterReady(Activity activity) {
+        if (mEnterActivityOptions == null) {
+            return;
+        }
+        mHasExited = false;
+        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
+        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
+        if (mEnterActivityOptions.isReturning()) {
+            if (mCalledActivityOptions != null) {
+                mCalledActivityOptions.dispatchActivityStopped();
+                mCalledActivityOptions = null;
+            }
+            activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
+            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
+                    resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
+        } else {
+            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
+                    resultReceiver, sharedElementNames, null, null);
+            mEnteringNames = sharedElementNames;
+            mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
+            mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
+        }
+        mExitingFrom = null;
+        mExitingTo = null;
+        mEnterActivityOptions = null;
+    }
+
+    public void onStop() {
+        if (mCalledActivityOptions != null) {
+            mCalledActivityOptions.dispatchActivityStopped();
+            mCalledActivityOptions = null;
+        }
+        if (mEnterTransitionCoordinator != null) {
+            mEnterTransitionCoordinator.stop();
+            mEnterTransitionCoordinator = null;
+        }
+    }
+
+    public boolean startExitBackTransition(Activity activity) {
+        if (mEnteringNames == null) {
+            return false;
+        } else {
+            if (!mHasExited) {
+                mHasExited = true;
+                if (mEnterTransitionCoordinator != null) {
+                    mEnterTransitionCoordinator.stop();
+                    mEnterTransitionCoordinator = null;
+                }
+                ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
+                activity.getWindow().getDecorView().findNamedViews(sharedElements);
+
+                ExitTransitionCoordinator exitCoordinator =
+                        new ExitTransitionCoordinator(activity, mEnteringNames, mEnteringFrom,
+                                mEnteringTo, true);
+                exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+            }
+            return true;
+        }
+    }
+
+    public void startExitOutTransition(Activity activity, Bundle options) {
+        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            return;
+        }
+        mCalledActivityOptions = new ActivityOptions(options);
+        if (mCalledActivityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+            mExitingFrom = mCalledActivityOptions.getSharedElementNames();
+            mExitingTo = mCalledActivityOptions.getLocalSharedElementNames();
+            mCalledActivityOptions.dispatchStartExit();
+        }
+    }
+}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index d2d8ed1..636205b 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,270 +18,414 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.graphics.drawable.ColorDrawable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.os.ResultReceiver;
 import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.View;
+import android.view.ViewGroupOverlay;
 import android.view.ViewTreeObserver;
-import android.view.Window;
+import android.widget.ImageView;
 
 import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * This ActivityTransitionCoordinator is created by the Activity to manage
- * the enter scene and shared element transfer as well as Activity#finishWithTransition
- * exiting the Scene and transferring shared elements back to the called Activity.
+ * the enter scene and shared element transfer into the Scene, either during
+ * launch of an Activity or returning from a launched Activity.
  */
-class EnterTransitionCoordinator extends ActivityTransitionCoordinator
-        implements ViewTreeObserver.OnPreDrawListener {
+class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
     private static final String TAG = "EnterTransitionCoordinator";
 
-    // The background fade in/out duration. 150ms is pretty quick, but not abrupt.
-    private static final int FADE_BACKGROUND_DURATION_MS = 150;
+    private static final long MAX_WAIT_MS = 1500;
 
-    /**
-     * The shared element names sent by the ExitTransitionCoordinator and may be
-     * shared when exiting back.
-     */
-    private ArrayList<String> mEnteringSharedElementNames;
-
-    /**
-     * The Activity that has created this coordinator. This is used solely to make the
-     * Window translucent/opaque.
-     */
+    private boolean mSharedElementTransitionStarted;
+    private boolean mIsReturning;
     private Activity mActivity;
+    private boolean mHasStopped;
+    private Handler mHandler;
+    private boolean mIsCanceled;
 
-    /**
-     * True if the Window was opaque at the start and we should make it opaque again after
-     * enter transitions have completed.
-     */
-    private boolean mWasOpaque;
-
-    /**
-     * During exit, is the background alpha == 0?
-     */
-    private boolean mBackgroundFadedOut;
-
-    /**
-     * During exit, has the shared element transition completed?
-     */
-    private boolean mSharedElementTransitionComplete;
-
-    /**
-     * Has the exit started? We don't want to accidentally exit multiple times. e.g. when
-     * back is hit twice during the exit animation.
-     */
-    private boolean mExitTransitionStarted;
-
-    /**
-     * Has the exit transition ended?
-     */
-    private boolean mExitTransitionComplete;
-
-    /**
-     * We only want to make the Window transparent and set the background alpha once. After that,
-     * the Activity won't want the same enter transition.
-     */
-    private boolean mMadeReady;
-
-    /**
-     * True if Window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS) -- this means that
-     * enter and exit transitions should be active.
-     */
-    private boolean mSupportsTransition;
-
-    /**
-     * Background alpha animations may complete prior to receiving the callback for
-     * onTranslucentConversionComplete. If so, we need to immediately call to make the Window
-     * opaque.
-     */
-    private boolean mMakeOpaque;
-
-    public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver) {
-        super(activity.getWindow());
+    public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
+            ArrayList<String> sharedElementNames,
+            ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
+        super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
+                activity.mTransitionListener);
         mActivity = activity;
-        setRemoteResultReceiver(resultReceiver);
-    }
-
-    public void readyToEnter() {
-        if (!mMadeReady) {
-            mMadeReady = true;
-            mSupportsTransition = getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS);
-            if (mSupportsTransition) {
-                Window window = getWindow();
-                window.getDecorView().getViewTreeObserver().addOnPreDrawListener(this);
-                mActivity.overridePendingTransition(0, 0);
-                mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
-                    @Override
-                    public void onTranslucentConversionComplete(boolean drawComplete) {
-                        mWasOpaque = true;
-                        if (mMakeOpaque) {
-                            mActivity.convertFromTranslucent();
-                        }
-                    }
-                }, null);
-                Drawable background = getDecor().getBackground();
-                if (background != null) {
-                    window.setBackgroundDrawable(null);
-                    background.setAlpha(0);
-                    window.setBackgroundDrawable(background);
+        mIsReturning = acceptedNames != null;
+        setResultReceiver(resultReceiver);
+        prepareEnter();
+        Bundle resultReceiverBundle = new Bundle();
+        resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
+        mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
+        if (mIsReturning) {
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    cancel();
                 }
-            }
+            };
+            mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
         }
     }
 
     @Override
-    protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
-        mEnteringSharedElementNames = new ArrayList<String>();
-        mEnteringSharedElementNames.addAll(sharedElementNames);
-        super.onTakeSharedElements(sharedElementNames, state);
+    protected void onReceiveResult(int resultCode, Bundle resultData) {
+        switch (resultCode) {
+            case MSG_TAKE_SHARED_ELEMENTS:
+                if (!mIsCanceled) {
+                    if (mHandler != null) {
+                        mHandler.removeMessages(MSG_CANCEL);
+                    }
+                    onTakeSharedElements(resultData);
+                }
+                break;
+            case MSG_EXIT_TRANSITION_COMPLETE:
+                if (!mIsCanceled) {
+                    if (!mSharedElementTransitionStarted) {
+                        send(resultCode, resultData);
+                    } else {
+                        onRemoteExitTransitionComplete();
+                    }
+                }
+                break;
+            case MSG_CANCEL:
+                cancel();
+                break;
+        }
     }
 
-    @Override
-    protected void sharedElementTransitionComplete(Bundle bundle) {
-        notifySharedElementTransitionComplete(bundle);
-        exitAfterSharedElementTransition();
+    private void cancel() {
+        if (!mIsCanceled) {
+            mIsCanceled = true;
+            if (getViewsTransition() == null) {
+                setViewVisibility(mSharedElements, View.VISIBLE);
+            } else {
+                mTransitioningViews.addAll(mSharedElements);
+            }
+            mSharedElementNames.clear();
+            mSharedElements.clear();
+            mAllSharedElementNames.clear();
+            onTakeSharedElements(null);
+            onRemoteExitTransitionComplete();
+        }
     }
 
-    @Override
-    public boolean onPreDraw() {
-        getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this);
-        setEnteringViews(readyEnteringViews());
-        notifySetListener();
-        onPrepareRestore();
-        return false;
+    public boolean isReturning() {
+        return mIsReturning;
     }
 
-    @Override
-    public void startExit() {
-        if (!mExitTransitionStarted) {
-            mExitTransitionStarted = true;
-            startExitTransition(mEnteringSharedElementNames);
+    protected void prepareEnter() {
+        setViewVisibility(mSharedElements, View.INVISIBLE);
+        if (getViewsTransition() != null) {
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+        }
+        mActivity.overridePendingTransition(0, 0);
+        if (!mIsReturning) {
+            mActivity.convertToTranslucent(null, null);
+            Drawable background = getDecor().getBackground();
+            if (background != null) {
+                getWindow().setBackgroundDrawable(null);
+                background = background.mutate();
+                background.setAlpha(0);
+                getWindow().setBackgroundDrawable(background);
+            }
+        } else {
+            mActivity = null; // all done with it now.
+        }
+    }
+
+    protected void onTakeSharedElements(Bundle sharedElementState) {
+        setEpicenter();
+        // Remove rejected shared elements
+        ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
+        rejectedNames.removeAll(mSharedElementNames);
+        ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
+        mListener.handleRejectedSharedElements(rejectedSnapshots);
+        startRejectedAnimations(rejectedSnapshots);
+
+        // Now start shared element transition
+        ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
+                mSharedElementNames);
+        setViewVisibility(mSharedElements, View.VISIBLE);
+        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
+                setSharedElementState(sharedElementState, sharedElementSnapshots);
+
+        boolean startEnterTransition = allowOverlappingTransitions();
+        boolean startSharedElementTransition = true;
+        Transition transition = beginTransition(startEnterTransition, startSharedElementTransition);
+
+        if (startEnterTransition) {
+            startEnterTransition(transition);
+        }
+
+        setOriginalImageViewState(originalImageViewState);
+
+        if (mResultReceiver != null) {
+            mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
+        }
+        mResultReceiver = null; // all done sending messages.
+    }
+
+    private Transition beginTransition(boolean startEnterTransition,
+            boolean startSharedElementTransition) {
+        Transition sharedElementTransition = null;
+        if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
+            sharedElementTransition = configureTransition(getSharedElementTransition());
+        }
+        Transition viewsTransition = null;
+        if (startEnterTransition && !mTransitioningViews.isEmpty()) {
+            viewsTransition = configureTransition(getViewsTransition());
+            viewsTransition = addTargets(viewsTransition, mTransitioningViews);
+        }
+
+        Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+        if (transition != null) {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
+                mSharedElements.get(0).invalidate();
+            } else if (startEnterTransition && !mTransitioningViews.isEmpty()) {
+                mTransitioningViews.get(0).invalidate();
+            }
+        }
+        return transition;
+    }
+
+    private void startEnterTransition(Transition transition) {
+        setViewVisibility(mTransitioningViews, View.VISIBLE);
+        if (!mIsReturning) {
+            Drawable background = getDecor().getBackground();
+            if (background != null) {
+                background = background.mutate();
+                ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255);
+                animator.setDuration(FADE_BACKGROUND_DURATION_MS);
+                animator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        makeOpaque();
+                    }
+                });
+                animator.start();
+            } else if (transition != null) {
+                transition.addListener(new Transition.TransitionListenerAdapter() {
+                    @Override
+                    public void onTransitionEnd(Transition transition) {
+                        transition.removeListener(this);
+                        makeOpaque();
+                    }
+                });
+            } else {
+                makeOpaque();
+            }
+        }
+    }
+
+    public void stop() {
+        mHasStopped = true;
+        mActivity = null;
+        mIsCanceled = true;
+        mResultReceiver = null;
+    }
+
+    private void makeOpaque() {
+        if (!mHasStopped) {
+            mActivity.convertFromTranslucent();
+            mActivity = null;
+        }
+    }
+
+    private boolean allowOverlappingTransitions() {
+        return mIsReturning ? getWindow().getAllowExitTransitionOverlap()
+                : getWindow().getAllowEnterTransitionOverlap();
+    }
+
+    private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
+        if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
+            return;
+        }
+        ViewGroupOverlay overlay = getDecor().getOverlay();
+        ObjectAnimator animator = null;
+        int numRejected = rejectedSnapshots.size();
+        for (int i = 0; i < numRejected; i++) {
+            View snapshot = rejectedSnapshots.get(i);
+            overlay.add(snapshot);
+            animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
+            animator.start();
+        }
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                ViewGroupOverlay overlay = getDecor().getOverlay();
+                int numRejected = rejectedSnapshots.size();
+                for (int i = 0; i < numRejected; i++) {
+                    overlay.remove(rejectedSnapshots.get(i));
+                }
+            }
+        });
+    }
+
+    protected void onRemoteExitTransitionComplete() {
+        if (!allowOverlappingTransitions()) {
+            boolean startEnterTransition = true;
+            boolean startSharedElementTransition = false;
+            Transition transition = beginTransition(startEnterTransition,
+                    startSharedElementTransition);
+            startEnterTransition(transition);
+        }
+    }
+
+    private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
+        int numSharedElements = names.size();
+        if (numSharedElements == 0) {
+            return null;
+        }
+        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
+        Context context = getWindow().getContext();
+        int[] parentLoc = new int[2];
+        getDecor().getLocationOnScreen(parentLoc);
+        for (String name: names) {
+            Bundle sharedElementBundle = state.getBundle(name);
+            if (sharedElementBundle != null) {
+                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+                View snapshot = new View(context);
+                snapshot.setId(com.android.internal.R.id.shared_element);
+                Resources resources = getWindow().getContext().getResources();
+                snapshot.setBackground(new BitmapDrawable(resources, bitmap));
+                snapshot.setViewName(name);
+                setSharedElementState(snapshot, name, state, parentLoc);
+                snapshots.add(snapshot);
+            }
+        }
+        return snapshots;
+    }
+
+    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] parentLoc) {
+        Bundle sharedElementBundle = transitionArgs.getBundle(name);
+        if (sharedElementBundle == null) {
+            return;
+        }
+
+        if (view instanceof ImageView) {
+            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+            if (scaleTypeInt >= 0) {
+                ImageView imageView = (ImageView) view;
+                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+                imageView.setScaleType(scaleType);
+                if (scaleType == ImageView.ScaleType.MATRIX) {
+                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+                    Matrix matrix = new Matrix();
+                    matrix.setValues(matrixValues);
+                    imageView.setImageMatrix(matrix);
+                }
+            }
+        }
+
+        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
+        view.setTranslationZ(z);
+
+        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
+        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
+        int width = sharedElementBundle.getInt(KEY_WIDTH);
+        int height = sharedElementBundle.getInt(KEY_HEIGHT);
+
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+        view.measure(widthSpec, heightSpec);
+
+        int left = x - parentLoc[0];
+        int top = y - parentLoc[1];
+        int right = left + width;
+        int bottom = top + height;
+        view.layout(left, top, right, bottom);
+    }
+
+    private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+            Bundle sharedElementState, final ArrayList<View> snapshots) {
+        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        if (sharedElementState != null) {
+            int[] tempLoc = new int[2];
+            for (int i = 0; i < mSharedElementNames.size(); i++) {
+                View sharedElement = mSharedElements.get(i);
+                String name = mSharedElementNames.get(i);
+                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                        name, sharedElementState);
+                if (originalState != null) {
+                    originalImageState.put((ImageView) sharedElement, originalState);
+                }
+                View parent = (View) sharedElement.getParent();
+                parent.getLocationOnScreen(tempLoc);
+                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+                sharedElement.requestLayout();
+            }
+        }
+        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
+
+        getDecor().getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
+                                snapshots);
+                        mSharedElementTransitionStarted = true;
+                        return true;
+                    }
+                }
+        );
+        return originalImageState;
+    }
+
+    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+            Bundle transitionArgs) {
+        if (!(view instanceof ImageView)) {
+            return null;
+        }
+        Bundle bundle = transitionArgs.getBundle(name);
+        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+        if (scaleTypeInt < 0) {
+            return null;
+        }
+
+        ImageView imageView = (ImageView) view;
+        ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+        Matrix originalMatrix = null;
+        if (originalScaleType == ImageView.ScaleType.MATRIX) {
+            originalMatrix = new Matrix(imageView.getImageMatrix());
+        }
+
+        return Pair.create(originalScaleType, originalMatrix);
+    }
+
+    private static void setOriginalImageViewState(
+            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+        for (int i = 0; i < originalState.size(); i++) {
+            ImageView imageView = originalState.keyAt(i);
+            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+            imageView.setScaleType(state.first);
+            imageView.setImageMatrix(state.second);
         }
     }
 
     @Override
     protected Transition getViewsTransition() {
-        if (!mSupportsTransition) {
-            return null;
-        }
         return getWindow().getEnterTransition();
     }
 
-    @Override
     protected Transition getSharedElementTransition() {
-        if (!mSupportsTransition) {
-            return null;
-        }
         return getWindow().getSharedElementEnterTransition();
     }
-
-    @Override
-    protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) {
-        Drawable background = getDecor().getBackground();
-        if (background != null) {
-            ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255);
-            animator.setDuration(FADE_BACKGROUND_DURATION_MS);
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mMakeOpaque = true;
-                    if (mWasOpaque) {
-                        mActivity.convertFromTranslucent();
-                    }
-                }
-            });
-            animator.start();
-        } else if (mWasOpaque) {
-            transition.addListener(new Transition.TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    mMakeOpaque = true;
-                    mActivity.convertFromTranslucent();
-                }
-            });
-        }
-        super.onStartEnterTransition(transition, enteringViews);
-    }
-
-    public ArrayList<View> readyEnteringViews() {
-        ArrayList<View> enteringViews = new ArrayList<View>();
-        getDecor().captureTransitioningViews(enteringViews);
-        if (getViewsTransition() != null) {
-            setViewVisibility(enteringViews, View.INVISIBLE);
-        }
-        return enteringViews;
-    }
-
-    @Override
-    protected void startExitTransition(ArrayList<String> sharedElements) {
-        mMakeOpaque = false;
-        notifyPrepareRestore();
-
-        if (getDecor().getBackground() == null) {
-            ColorDrawable black = new ColorDrawable(0xFF000000);
-            getWindow().setBackgroundDrawable(black);
-        }
-        if (mWasOpaque) {
-            mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
-                @Override
-                public void onTranslucentConversionComplete(boolean drawComplete) {
-                    fadeOutBackground();
-                }
-            }, null);
-        } else {
-            fadeOutBackground();
-        }
-
-        super.startExitTransition(sharedElements);
-    }
-
-    private void fadeOutBackground() {
-        ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(),
-                "alpha", 0);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mBackgroundFadedOut = true;
-                if (mSharedElementTransitionComplete) {
-                    EnterTransitionCoordinator.super.onSharedElementTransitionEnd();
-                }
-            }
-        });
-        animator.setDuration(FADE_BACKGROUND_DURATION_MS);
-        animator.start();
-    }
-
-    @Override
-    protected void onExitTransitionEnd() {
-        mExitTransitionComplete = true;
-        exitAfterSharedElementTransition();
-        super.onExitTransitionEnd();
-    }
-
-    @Override
-    protected void onSharedElementTransitionEnd() {
-        mSharedElementTransitionComplete = true;
-        if (mBackgroundFadedOut) {
-            super.onSharedElementTransitionEnd();
-        }
-    }
-
-    @Override
-    protected boolean allowOverlappingTransitions() {
-        return getWindow().getAllowEnterTransitionOverlap();
-    }
-
-    private void exitAfterSharedElementTransition() {
-        if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) {
-            mActivity.finish();
-            if (mSupportsTransition) {
-                mActivity.overridePendingTransition(0, 0);
-            }
-            notifyExitTransitionComplete();
-            clearConnections();
-        }
-    }
 }
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index d920787..43a60a3 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -15,11 +15,20 @@
  */
 package android.app;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.transition.Transition;
-import android.util.Pair;
+import android.transition.TransitionManager;
 import android.view.View;
-import android.view.Window;
+import android.widget.ImageView;
 
 import java.util.ArrayList;
 
@@ -30,142 +39,245 @@
  */
 class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
     private static final String TAG = "ExitTransitionCoordinator";
+    private static final long MAX_WAIT_MS = 1500;
 
-    /**
-     * The Views that have exited and need to be restored to VISIBLE when returning to the
-     * normal state.
-     */
-    private ArrayList<View> mTransitioningViews;
-
-    /**
-     * Has the exit started? We don't want to accidentally exit multiple times.
-     */
-    private boolean mExitStarted;
-
-    /**
-     * Has the called Activity's ResultReceiver been set?
-     */
-    private boolean mIsResultReceiverSet;
-
-    /**
-     * Has the exit transition completed? If so, we can notify as soon as the ResultReceiver
-     * has been set.
-     */
     private boolean mExitComplete;
 
-    /**
-     * Has the shared element transition completed? If so, we can notify as soon as the
-     * ResultReceiver has been set.
-     */
-    private Bundle mSharedElements;
+    private Bundle mSharedElementBundle;
 
-    /**
-     * Has the shared element transition completed?
-     */
-    private boolean mSharedElementsComplete;
+    private boolean mExitNotified;
 
-    public ExitTransitionCoordinator(Window window,
-            ActivityOptions.ActivityTransitionListener listener) {
-        super(window);
-        setActivityTransitionListener(listener);
+    private boolean mSharedElementNotified;
+
+    private Activity mActivity;
+
+    private boolean mIsBackgroundReady;
+
+    private boolean mIsReturning;
+
+    private boolean mIsCanceled;
+
+    private Handler mHandler;
+
+    public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
+            ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
+        super(activity.getWindow(), names, accepted, mapped, activity.mTransitionListener);
+        mIsReturning = isReturning;
+        mIsBackgroundReady = !mIsReturning;
+        mActivity = activity;
     }
 
     @Override
-    protected void onSetResultReceiver() {
-        mIsResultReceiverSet = true;
-        notifyCompletions();
-    }
-
-    @Override
-    protected void onPrepareRestore() {
-        makeTransitioningViewsInvisible();
-        setEnteringViews(mTransitioningViews);
-        mTransitioningViews = null;
-        super.onPrepareRestore();
-    }
-
-    @Override
-    protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
-        super.onTakeSharedElements(sharedElementNames, state);
-        clearConnections();
-    }
-
-    @Override
-    protected void onActivityStopped() {
-        if (getViewsTransition() != null) {
-            setViewVisibility(mTransitioningViews, View.VISIBLE);
-        }
-        super.onActivityStopped();
-    }
-
-    @Override
-    protected void sharedElementTransitionComplete(Bundle bundle) {
-        mSharedElements = bundle;
-        mSharedElementsComplete = true;
-        notifyCompletions();
-    }
-
-    @Override
-    protected void onExitTransitionEnd() {
-        mExitComplete = true;
-        notifyCompletions();
-        super.onExitTransitionEnd();
-    }
-
-    private void notifyCompletions() {
-        if (mIsResultReceiverSet && mSharedElementsComplete) {
-            if (mSharedElements != null) {
-                notifySharedElementTransitionComplete(mSharedElements);
-                mSharedElements = null;
-            }
-            if (mExitComplete) {
-                notifyExitTransitionComplete();
-            }
+    protected void onReceiveResult(int resultCode, Bundle resultData) {
+        switch (resultCode) {
+            case MSG_SET_REMOTE_RECEIVER:
+                mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
+                if (mIsCanceled) {
+                    mResultReceiver.send(MSG_CANCEL, null);
+                    mResultReceiver = null;
+                } else {
+                    if (mHandler != null) {
+                        mHandler.removeMessages(MSG_CANCEL);
+                    }
+                    notifyComplete();
+                }
+                break;
+            case MSG_HIDE_SHARED_ELEMENTS:
+                if (!mIsCanceled) {
+                    hideSharedElements();
+                }
+                break;
+            case MSG_START_EXIT_TRANSITION:
+                startExit();
+                break;
+            case MSG_ACTIVITY_STOPPED:
+                setViewVisibility(mTransitioningViews, View.VISIBLE);
+                setViewVisibility(mSharedElements, View.VISIBLE);
+                break;
         }
     }
 
-    @Override
+    private void hideSharedElements() {
+        setViewVisibility(mSharedElements, View.INVISIBLE);
+    }
+
     public void startExit() {
-        if (!mExitStarted) {
-            mExitStarted = true;
-            setSharedElements();
-            startExitTransition(getSharedElementNames());
+        beginTransition();
+        setViewVisibility(mTransitioningViews, View.INVISIBLE);
+    }
+
+    public void startExit(int resultCode, Intent data) {
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                mIsCanceled = true;
+                mActivity.finish();
+                mActivity = null;
+            }
+        };
+        mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+        if (getDecor().getBackground() == null) {
+            ColorDrawable black = new ColorDrawable(0xFF000000);
+            black.setAlpha(0);
+            getWindow().setBackgroundDrawable(black);
+            black.setAlpha(255);
         }
+        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
+                mAllSharedElementNames, resultCode, data);
+        mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
+            @Override
+            public void onTranslucentConversionComplete(boolean drawComplete) {
+                if (!mIsCanceled) {
+                    fadeOutBackground();
+                }
+            }
+        }, options);
+        startExit();
+    }
+
+    private void fadeOutBackground() {
+        ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(),
+                "alpha", 0);
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIsBackgroundReady = true;
+                notifyComplete();
+            }
+        });
+        animator.setDuration(FADE_BACKGROUND_DURATION_MS);
+        animator.start();
+    }
+
+    private void beginTransition() {
+        Transition sharedElementTransition = configureTransition(getSharedElementTransition());
+        Transition viewsTransition = configureTransition(getViewsTransition());
+        viewsTransition = addTargets(viewsTransition, mTransitioningViews);
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        } else {
+            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    sharedElementTransitionComplete();
+                }
+            });
+        }
+        if (viewsTransition == null) {
+            exitTransitionComplete();
+        } else {
+            viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    exitTransitionComplete();
+                }
+            });
+        }
+
+        Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+        TransitionManager.beginDelayedTransition(getDecor(), transition);
+    }
+
+    private void exitTransitionComplete() {
+        mExitComplete = true;
+        notifyComplete();
+    }
+
+    protected boolean isReadyToNotify() {
+        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
+    }
+
+    private void sharedElementTransitionComplete() {
+        Bundle bundle = new Bundle();
+        int[] tempLoc = new int[2];
+        for (int i = 0; i < mSharedElementNames.size(); i++) {
+            View sharedElement = mSharedElements.get(i);
+            String name = mSharedElementNames.get(i);
+            captureSharedElementState(sharedElement, name, bundle, tempLoc);
+        }
+        mSharedElementBundle = bundle;
+        notifyComplete();
+    }
+
+    protected void notifyComplete() {
+        if (isReadyToNotify()) {
+            if (!mSharedElementNotified) {
+                mSharedElementNotified = true;
+                mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
+            }
+            if (!mExitNotified && mExitComplete) {
+                mExitNotified = true;
+                mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
+                mResultReceiver = null; // done talking
+                if (mIsReturning) {
+                    mActivity.finish();
+                    mActivity.overridePendingTransition(0, 0);
+                }
+                mActivity = null;
+            }
+        }
+    }
+
+    /**
+     * Captures placement information for Views with a shared element name for
+     * Activity Transitions.
+     *
+     * @param view           The View to capture the placement information for.
+     * @param name           The shared element name in the target Activity to apply the placement
+     *                       information for.
+     * @param transitionArgs Bundle to store shared element placement information.
+     * @param tempLoc        A temporary int[2] for capturing the current location of views.
+     */
+    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] tempLoc) {
+        Bundle sharedElementBundle = new Bundle();
+        view.getLocationOnScreen(tempLoc);
+        float scaleX = view.getScaleX();
+        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
+        int width = Math.round(view.getWidth() * scaleX);
+        sharedElementBundle.putInt(KEY_WIDTH, width);
+
+        float scaleY = view.getScaleY();
+        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
+        int height = Math.round(view.getHeight() * scaleY);
+        sharedElementBundle.putInt(KEY_HEIGHT, height);
+
+        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
+
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        view.draw(canvas);
+        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+
+        if (view instanceof ImageView) {
+            ImageView imageView = (ImageView) view;
+            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+                float[] matrix = new float[9];
+                imageView.getImageMatrix().getValues(matrix);
+                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+            }
+        }
+
+        transitionArgs.putBundle(name, sharedElementBundle);
+    }
+
+    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+            if (scaleType == SCALE_TYPE_VALUES[i]) {
+                return i;
+            }
+        }
+        return -1;
     }
 
     @Override
     protected Transition getViewsTransition() {
-        if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
-            return null;
-        }
         return getWindow().getExitTransition();
     }
 
-    @Override
     protected Transition getSharedElementTransition() {
-        if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
-            return null;
-        }
         return getWindow().getSharedElementExitTransition();
     }
-
-    private void makeTransitioningViewsInvisible() {
-        if (getViewsTransition() != null) {
-            setViewVisibility(mTransitioningViews, View.INVISIBLE);
-        }
-    }
-
-    @Override
-    protected void onStartExitTransition(ArrayList<View> exitingViews) {
-        mTransitioningViews = new ArrayList<View>();
-        if (exitingViews != null) {
-            mTransitioningViews.addAll(exitingViews);
-        }
-        mTransitioningViews.addAll(getSharedElements());
-    }
-
-    @Override
-    protected boolean allowOverlappingTransitions() {
-        return getWindow().getAllowExitTransitionOverlap();
-    }
 }
diff --git a/core/java/android/app/SharedElementListener.java b/core/java/android/app/SharedElementListener.java
new file mode 100644
index 0000000..d4bc019
--- /dev/null
+++ b/core/java/android/app/SharedElementListener.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.view.View;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Listener provided in
+ * {@link Activity#setSharedElementListener(SharedElementListener)}
+ * to monitor the Activity transitions. The events can be used to customize or override Activity
+ * Transition behavior.
+ */
+public class SharedElementListener {
+    /**
+     * Called to allow the listener to customize the start state of the shared element for
+     * the shared element entering transition. By default, the shared element is placed in
+     * the position and with the size of the shared element in the calling Activity or Fragment.
+     *
+     * @param sharedElementNames The names of the shared elements that were accepted into
+     *                           the View hierarchy.
+     * @param sharedElements The shared elements that are part of the View hierarchy.
+     * @param sharedElementSnapshots The Views containing snap shots of the shared element
+     *                               from the launching Window. These elements will not
+     *                               be part of the scene, but will be positioned relative
+     *                               to the Window decor View.
+     */
+    public void setSharedElementStart(List<String> sharedElementNames,
+            List<View> sharedElements, List<View> sharedElementSnapshots) {}
+
+    /**
+     * Called to allow the listener to customize the end state of the shared element for
+     * the shared element entering transition.
+     *
+     * @param sharedElementNames The names of the shared elements that were accepted into
+     *                           the View hierarchy.
+     * @param sharedElements The shared elements that are part of the View hierarchy.
+     * @param sharedElementSnapshots The Views containing snap shots of the shared element
+     *                               from the launching Window. These elements will not
+     *                               be part of the scene, but will be positioned relative
+     *                               to the Window decor View.
+     */
+    public void setSharedElementEnd(List<String> sharedElementNames,
+            List<View> sharedElements, List<View> sharedElementSnapshots) {}
+
+    /**
+     * If nothing is done, all shared elements that were not accepted by
+     * {@link #remapSharedElements(java.util.List, java.util.Map)} will be Transitioned
+     * out of the entering scene automatically. Any elements removed from
+     * rejectedSharedElements must be handled by the ActivityTransitionListener.
+     * <p>Views in rejectedSharedElements will have their position and size set to the
+     * position of the calling shared element, relative to the Window decor View. This
+     * view may be safely added to the decor View's overlay to remain in position.</p>
+     *
+     * @param rejectedSharedElements Views containing visual information of shared elements
+     *                               that are not part of the entering scene. These Views
+     *                               are positioned relative to the Window decor View. A
+     *                               View removed from this list will not be transitioned
+     *                               automatically.
+     */
+    public void handleRejectedSharedElements(List<View> rejectedSharedElements) {}
+
+    /**
+     * Lets the ActivityTransitionListener adjust the mapping of shared element names to
+     * Views.
+     * @param names The names of all shared elements transferred from the calling Activity
+     *              to the started Activity.
+     * @param sharedElements The mapping of shared element names to Views. The best guess
+     *                       will be filled into sharedElements based on the View names.
+     */
+    public void remapSharedElements(List<String> names, Map<String, View> sharedElements) {}
+}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 92a42b7..b821a3e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2318,8 +2318,8 @@
      *                          in Activity transitions. If false, the ViewGroup won't transition,
      *                          only its children. If true, the entire ViewGroup will transition
      *                          together.
-     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
-     * android.app.ActivityOptions.ActivityTransitionListener)
+     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
+     * android.util.Pair[])
      */
     public void setTransitionGroup(boolean isTransitionGroup) {
         mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9e684c7..a06a3fd 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2477,7 +2477,7 @@
              when doing an Activity transition. Typically, the elements inside a
              ViewGroup are each transitioned from the scene individually. The default
              for a ViewGroup is false unless it has a background. See
-             {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
+             {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
              android.view.View, String)} for more information. Corresponds to
              {@link android.view.ViewGroup#setTransitionGroup(boolean)}.-->
         <attr name="transitionGroup" format="boolean" />