From 1fecfb2ddcdf4335ff543bdd549b8e4d36139da8 Mon Sep 17 00:00:00 2001 From: George Mount Date: Wed, 18 Jun 2014 14:55:55 -0700 Subject: [PATCH] Activity Transitions: don't require transitionName. Bug 15585623 Bug 15607591 Exit transitions now run because exit transitions are executed with startActivity. Change-Id: Ie55793a9514c64d96e2cf1abdd2d39c4d2606a23 --- core/java/android/app/Activity.java | 2 +- core/java/android/app/ActivityOptions.java | 54 ++++------- .../app/ActivityTransitionCoordinator.java | 70 ++++++------- .../android/app/ActivityTransitionState.java | 97 ++++++++++++------- .../app/EnterTransitionCoordinator.java | 35 ++++++- .../app/ExitTransitionCoordinator.java | 17 ++-- 6 files changed, 152 insertions(+), 123 deletions(-) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d0d028908bf0d..36a4c3039807c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -780,7 +780,7 @@ public class Activity extends ContextThemeWrapper private Thread mUiThread; final Handler mHandler = new Handler(); - private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState(); + ActivityTransitionState mActivityTransitionState = new ActivityTransitionState(); SharedElementListener mEnterTransitionListener = SharedElementListener.NULL_LISTENER; SharedElementListener mExitTransitionListener = SharedElementListener.NULL_LISTENER; diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 0ecead9bdf575..6b5549d8ca91f 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -108,9 +108,9 @@ public class ActivityOptions { 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"; + private static final String KEY_EXIT_COORDINATOR_INDEX = "android:exitCoordinatorIndex"; /** @hide */ public static final int ANIM_NONE = 0; @@ -138,9 +138,9 @@ public class ActivityOptions { private ResultReceiver mTransitionReceiver; private boolean mIsReturning; private ArrayList mSharedElementNames; - private ArrayList mLocalSharedElementNames; private Intent mResultData; private int mResultCode; + private int mExitCoordinatorIndex; /** * Create an ActivityOptions specifying a custom animation to run when @@ -387,7 +387,7 @@ public class ActivityOptions { opts.mAnimationType = ANIM_SCENE_TRANSITION; ArrayList names = new ArrayList(); - ArrayList mappedNames = new ArrayList(); + ArrayList views = new ArrayList(); if (sharedElements != null) { for (int i = 0; i < sharedElements.length; i++) { @@ -396,23 +396,22 @@ public class ActivityOptions { if (sharedElementName == null) { throw new IllegalArgumentException("Shared element name must not be null"); } - String name = sharedElement.first.getTransitionName(); - if (name == null) { - throw new IllegalArgumentException("Shared elements must have non-null " + - "transitionNames"); - } - names.add(sharedElementName); - mappedNames.add(name); + View view = sharedElement.first; + if (view == null) { + throw new IllegalArgumentException("Shared element must not be null"); + } + views.add(sharedElement.first); } } ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names, - mappedNames, false); + views, false); opts.mTransitionReceiver = exit; opts.mSharedElementNames = names; - opts.mLocalSharedElementNames = mappedNames; opts.mIsReturning = false; + opts.mExitCoordinatorIndex = + activity.mActivityTransitionState.addExitTransitionCoordinator(exit); return opts; } @@ -427,6 +426,8 @@ public class ActivityOptions { opts.mIsReturning = true; opts.mResultCode = resultCode; opts.mResultData = resultData; + opts.mExitCoordinatorIndex = + activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator); return opts; } @@ -465,9 +466,9 @@ public class ActivityOptions { 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); + mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX); break; } } @@ -523,18 +524,7 @@ public class ActivityOptions { } /** @hide */ - public void dispatchActivityStopped() { - if (mTransitionReceiver != null) { - mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null); - } - } - - /** @hide */ - public void dispatchStartExit() { - if (mTransitionReceiver != null) { - mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null); - } - } + public int getExitCoordinatorKey() { return mExitCoordinatorIndex; } /** @hide */ public void abort() { @@ -546,11 +536,6 @@ public class ActivityOptions { } } - /** @hide */ - public void setReturning() { - mIsReturning = true; - } - /** @hide */ public boolean isReturning() { return mIsReturning; @@ -561,9 +546,6 @@ public class ActivityOptions { return mSharedElementNames; } - /** @hide */ - public ArrayList getLocalSharedElementNames() { return mLocalSharedElementNames; } - /** @hide */ public ResultReceiver getResultReceiver() { return mTransitionReceiver; } @@ -591,10 +573,10 @@ public class ActivityOptions { } mTransitionReceiver = null; mSharedElementNames = null; - mLocalSharedElementNames = null; mIsReturning = false; mResultData = null; mResultCode = 0; + mExitCoordinatorIndex = 0; switch (otherOptions.mAnimationType) { case ANIM_CUSTOM: mAnimationType = otherOptions.mAnimationType; @@ -641,12 +623,12 @@ public class ActivityOptions { mAnimationType = otherOptions.mAnimationType; mTransitionReceiver = otherOptions.mTransitionReceiver; mSharedElementNames = otherOptions.mSharedElementNames; - mLocalSharedElementNames = otherOptions.mLocalSharedElementNames; mIsReturning = otherOptions.mIsReturning; mThumbnail = null; mAnimationStartedListener = null; mResultData = otherOptions.mResultData; mResultCode = otherOptions.mResultCode; + mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex; break; } } @@ -695,9 +677,9 @@ public class ActivityOptions { } 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); + b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex); break; } return b; diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index c351cd5f9c21f..e3f55a21fddfc 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -150,12 +150,6 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { */ public static final int MSG_HIDE_SHARED_ELEMENTS = 101; - /** - * 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 = 102; - /** * Sent by the exiting coordinator (either EnterTransitionCoordinator * or ExitTransitionCoordinator) after the shared elements have @@ -209,15 +203,6 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { private Runnable mPendingTransition; private boolean mIsStartingTransition; - - public ActivityTransitionCoordinator(Window window, - ArrayList allSharedElementNames, - ArrayList accepted, ArrayList localNames, - SharedElementListener listener, boolean isReturning) { - this(window, allSharedElementNames, listener, isReturning); - viewsReady(accepted, localNames); - } - public ActivityTransitionCoordinator(Window window, ArrayList allSharedElementNames, SharedElementListener listener, boolean isReturning) { @@ -228,8 +213,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { mIsReturning = isReturning; } - protected void viewsReady(ArrayList accepted, ArrayList localNames) { - setSharedElements(accepted, localNames); + protected void viewsReady(ArrayMap sharedElements) { + setSharedElements(sharedElements); if (getViewsTransition() != null) { getDecor().captureTransitioningViews(mTransitioningViews); mTransitioningViews.removeAll(mSharedElements); @@ -286,6 +271,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return names; } + public ArrayList getMappedViews() { + return mSharedElements; + } + public ArrayList getAllSharedElementNames() { return mAllSharedElementNames; } public static void setViewVisibility(Collection views, int visibility) { @@ -335,36 +324,31 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } } - private void setSharedElements(ArrayList accepted, ArrayList localNames) { + protected ArrayMap mapSharedElements(ArrayList accepted, + ArrayList localViews) { + ArrayMap sharedElements = new ArrayMap(); if (!mAllSharedElementNames.isEmpty()) { - ArrayMap sharedElements = new ArrayMap(); - 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); - } - } + for (int i = 0; i < accepted.size(); i++) { + sharedElements.put(accepted.get(i), localViews.get(i)); } + } else { + getDecor().findNamedViews(sharedElements); } - 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) { - if (sharedElement.getTransitionName() == null) { - throw new IllegalArgumentException("Shared elements must have " + - "non-null transitionNames"); - } - mSharedElementNames.add(name); - mSharedElements.add(sharedElement); - } + } + return sharedElements; + } + + private void setSharedElements(ArrayMap sharedElements) { + 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); } } } diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index d94dadda055f0..1e1781963ac59 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -18,9 +18,11 @@ package android.app; import android.os.Bundle; import android.os.ResultReceiver; import android.util.ArrayMap; +import android.util.SparseArray; import android.view.View; import android.view.Window; +import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -31,10 +33,6 @@ 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"; @@ -45,16 +43,6 @@ class ActivityTransitionState { */ private ArrayList mEnteringNames; - /** - * The shared elements that this Activity as accepted and mapped to local Views. - */ - private ArrayList mEnteringFrom; - - /** - * The names of local Views that are mapped to those elements in mEnteringFrom. - */ - private ArrayList mEnteringTo; - /** * The names of shared elements that were shared to the called Activity. */ @@ -66,10 +54,15 @@ class ActivityTransitionState { private ArrayList mExitingTo; /** - * The ActivityOptions used to call an Activity. Used to make the elements restore + * The local Views that were shared out, mapped to those elements in mExitingFrom. + */ + private ArrayList mExitingToView; + + /** + * The ExitTransitionCoordinator used to start an Activity. Used to make the elements restore * Visibility of exited Views. */ - private ActivityOptions mCalledActivityOptions; + private ExitTransitionCoordinator mCalledExitCoordinator; /** * We must be able to cancel entering transitions to stop changing the Window to @@ -92,15 +85,42 @@ class ActivityTransitionState { */ private boolean mIsEnterPostponed; + /** + * Potential exit transition coordinators. + */ + private SparseArray> mExitTransitionCoordinators; + + /** + * Next key for mExitTransitionCoordinator. + */ + private int mExitTransitionCoordinatorsKey = 1; + public ActivityTransitionState() { } + public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) { + if (mExitTransitionCoordinators == null) { + mExitTransitionCoordinators = + new SparseArray>(); + } + WeakReference ref = new WeakReference(exitTransitionCoordinator); + // clean up old references: + for (int i = mExitTransitionCoordinators.size() - 1; i >= 0; i--) { + WeakReference oldRef + = mExitTransitionCoordinators.valueAt(i); + if (oldRef.get() == null) { + mExitTransitionCoordinators.removeAt(i); + } + } + int newKey = mExitTransitionCoordinatorsKey++; + mExitTransitionCoordinators.append(newKey, ref); + return newKey; + } + 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); @@ -112,8 +132,6 @@ class ActivityTransitionState { 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); @@ -169,16 +187,19 @@ class ActivityTransitionState { private void startEnter() { if (mEnterActivityOptions.isReturning()) { - mEnterTransitionCoordinator.viewsReady(mExitingFrom, mExitingTo); + if (mExitingToView != null) { + mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingToView); + } else { + mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo); + } } else { - mEnterTransitionCoordinator.viewsReady(null, null); + mEnterTransitionCoordinator.namedViewsReady(null, null); mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames(); - mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames(); - mEnteringTo = mEnterTransitionCoordinator.getMappedNames(); } mExitingFrom = null; mExitingTo = null; + mExitingToView = null; mEnterActivityOptions = null; } @@ -195,9 +216,9 @@ class ActivityTransitionState { } private void restoreExitedViews() { - if (mCalledActivityOptions != null) { - mCalledActivityOptions.dispatchActivityStopped(); - mCalledActivityOptions = null; + if (mCalledExitCoordinator != null) { + mCalledExitCoordinator.resetViews(); + mCalledExitCoordinator = null; } } @@ -215,8 +236,7 @@ class ActivityTransitionState { activity.getWindow().getDecorView().findNamedViews(sharedElements); ExitTransitionCoordinator exitCoordinator = - new ExitTransitionCoordinator(activity, mEnteringNames, mEnteringFrom, - mEnteringTo, true); + new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true); exitCoordinator.startExit(activity.mResultCode, activity.mResultData); } return true; @@ -227,11 +247,20 @@ class ActivityTransitionState { 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(); + ActivityOptions activityOptions = new ActivityOptions(options); + if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + int key = activityOptions.getExitCoordinatorKey(); + int index = mExitTransitionCoordinators.indexOfKey(key); + if (index >= 0) { + mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get(); + mExitTransitionCoordinators.removeAt(index); + if (mCalledExitCoordinator != null) { + mExitingFrom = mCalledExitCoordinator.getAcceptedNames(); + mExitingTo = mCalledExitCoordinator.getMappedNames(); + mExitingToView = mCalledExitCoordinator.getMappedViews(); + mCalledExitCoordinator.startExit(); + } + } } } } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 1d7a0ec8c98ba..389f42cf9032b 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -79,12 +79,24 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { }); } - public void viewsReady(ArrayList accepted, ArrayList localNames) { + public void viewInstancesReady(ArrayList accepted, ArrayList localViews) { if (mIsReadyForTransition) { return; } - super.viewsReady(accepted, localNames); + viewsReady(mapSharedElements(accepted, localViews)); + } + public void namedViewsReady(ArrayList accepted, ArrayList localNames) { + if (mIsReadyForTransition) { + return; + } + + viewsReady(mapNamedElements(accepted, localNames)); + } + + @Override + protected void viewsReady(ArrayMap sharedElements) { + super.viewsReady(sharedElements); mIsReadyForTransition = true; if (mIsReturning) { mHandler = new Handler() { @@ -105,6 +117,25 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } + private ArrayMap mapNamedElements(ArrayList accepted, + ArrayList localNames) { + ArrayMap sharedElements = new ArrayMap(); + 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 != null && !localName.equals(acceptedName)) { + View view = sharedElements.remove(localName); + if (view != null) { + sharedElements.put(acceptedName, view); + } + } + } + } + return sharedElements; + } + private void sendSharedElementDestination() { ViewGroup decor = getDecor(); boolean allReady = !decor.isLayoutRequested(); diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 9f3dbdcf21cfe..1033248d048c0 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -27,6 +27,7 @@ import android.os.Handler; import android.os.Message; import android.transition.Transition; import android.transition.TransitionManager; +import android.util.ArrayMap; import android.view.View; import android.view.ViewGroupOverlay; import android.view.ViewTreeObserver; @@ -67,9 +68,10 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private ArrayList mSharedElementSnapshots; public ExitTransitionCoordinator(Activity activity, ArrayList names, - ArrayList accepted, ArrayList mapped, boolean isReturning) { - super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning), + ArrayList accepted, ArrayList mapped, boolean isReturning) { + super(activity.getWindow(), names, getListener(activity, isReturning), isReturning); + viewsReady(mapSharedElements(accepted, mapped)); mIsBackgroundReady = !isReturning; mActivity = activity; } @@ -101,11 +103,6 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { case MSG_START_EXIT_TRANSITION: startExit(); break; - case MSG_ACTIVITY_STOPPED: - setViewVisibility(mTransitioningViews, View.VISIBLE); - setViewVisibility(mSharedElements, View.VISIBLE); - mIsHidden = true; - break; case MSG_SHARED_ELEMENT_DESTINATION: mExitSharedElementBundle = resultData; sharedElementExitBack(); @@ -113,6 +110,12 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } + public void resetViews() { + setViewVisibility(mTransitioningViews, View.VISIBLE); + setViewVisibility(mSharedElements, View.VISIBLE); + mIsHidden = true; + } + private void sharedElementExitBack() { if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) { startTransition(new Runnable() {