Activity Transitions: don't require transitionName.

Bug 15585623
Bug 15607591

Exit transitions now run because exit transitions are executed
with startActivity.

Change-Id: Ie55793a9514c64d96e2cf1abdd2d39c4d2606a23
This commit is contained in:
George Mount
2014-06-18 14:55:55 -07:00
parent 51fa6bcb22
commit 1fecfb2ddc
6 changed files with 152 additions and 123 deletions

View File

@@ -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;

View File

@@ -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<String> mSharedElementNames;
private ArrayList<String> 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<String> names = new ArrayList<String>();
ArrayList<String> mappedNames = new ArrayList<String>();
ArrayList<View> views = new ArrayList<View>();
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<String> 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;

View File

@@ -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<String> allSharedElementNames,
ArrayList<String> accepted, ArrayList<String> localNames,
SharedElementListener listener, boolean isReturning) {
this(window, allSharedElementNames, listener, isReturning);
viewsReady(accepted, localNames);
}
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
SharedElementListener listener, boolean isReturning) {
@@ -228,8 +213,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
mIsReturning = isReturning;
}
protected void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
setSharedElements(accepted, localNames);
protected void viewsReady(ArrayMap<String, View> 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<View> getMappedViews() {
return mSharedElements;
}
public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
public static void setViewVisibility(Collection<View> views, int visibility) {
@@ -335,36 +324,31 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
}
}
private void setSharedElements(ArrayList<String> accepted, ArrayList<String> localNames) {
protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
ArrayList<View> localViews) {
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
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);
}
}
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<String, View> 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);
}
}
}

View File

@@ -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<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.
*/
@@ -66,10 +54,15 @@ class ActivityTransitionState {
private ArrayList<String> 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<View> 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<WeakReference<ExitTransitionCoordinator>> mExitTransitionCoordinators;
/**
* Next key for mExitTransitionCoordinator.
*/
private int mExitTransitionCoordinatorsKey = 1;
public ActivityTransitionState() {
}
public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) {
if (mExitTransitionCoordinators == null) {
mExitTransitionCoordinators =
new SparseArray<WeakReference<ExitTransitionCoordinator>>();
}
WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator);
// clean up old references:
for (int i = mExitTransitionCoordinators.size() - 1; i >= 0; i--) {
WeakReference<ExitTransitionCoordinator> 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();
}
}
}
}
}

View File

@@ -79,12 +79,24 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
});
}
public void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
public void viewInstancesReady(ArrayList<String> accepted, ArrayList<View> localViews) {
if (mIsReadyForTransition) {
return;
}
super.viewsReady(accepted, localNames);
viewsReady(mapSharedElements(accepted, localViews));
}
public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
if (mIsReadyForTransition) {
return;
}
viewsReady(mapNamedElements(accepted, localNames));
}
@Override
protected void viewsReady(ArrayMap<String, View> sharedElements) {
super.viewsReady(sharedElements);
mIsReadyForTransition = true;
if (mIsReturning) {
mHandler = new Handler() {
@@ -105,6 +117,25 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
}
private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
ArrayList<String> localNames) {
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 != 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();

View File

@@ -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<View> mSharedElementSnapshots;
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
ArrayList<String> accepted, ArrayList<View> 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() {