DO NOT MERGE Allow enter activity transitions to be delayed until data is ready.

Bug 15539194

Change-Id: I8de9806b0bcdf8129eb3b5a555a2433f0c7be634
This commit is contained in:
George Mount
2014-06-10 11:12:01 -07:00
parent 096fc544af
commit 28a8d10805
5 changed files with 130 additions and 21 deletions

View File

@@ -3333,6 +3333,7 @@ package android.app {
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
method public void registerForContextMenu(android.view.View);
method public final deprecated void removeDialog(int);
@@ -3388,6 +3389,7 @@ package android.app {
method public deprecated void startManagingCursor(android.database.Cursor);
method public boolean startNextMatchingActivity(android.content.Intent);
method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle);
method public void startPostponedEnterTransition();
method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean);
method public void stopLockTask();
method public deprecated void stopManagingCursor(android.database.Cursor);

View File

@@ -5615,6 +5615,34 @@ public class Activity extends ContextThemeWrapper
mExitTransitionListener = listener;
}
/**
* Postpone the entering activity transition when Activity was started with
* {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
* android.util.Pair[])}.
* <p>This method gives the Activity the ability to delay starting the entering and
* shared element transitions until all data is loaded. Until then, the Activity won't
* draw into its window, leaving the window transparent. This may also cause the
* returning animation to be delayed until data is ready. This method should be
* called in {@link #onCreate(android.os.Bundle)} or in
* {@link #onActivityReenter(int, android.content.Intent)}.
* {@link #startPostponedEnterTransition()} must be called to allow the Activity to
* start the transitions. If the Activity did not use
* {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
* android.util.Pair[])}, then this method does nothing.</p>
*/
public void postponeEnterTransition() {
mActivityTransitionState.postponeEnterTransition();
}
/**
* Begin postponed transitions after {@link #postponeEnterTransition()} was called.
* If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
* to have your Activity start drawing.
*/
public void startPostponedEnterTransition() {
mActivityTransitionState.startPostponedEnterTransition();
}
// ------------------ Internal API ------------------
final void setParent(Activity parent) {

View File

@@ -214,11 +214,21 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
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) {
super(new Handler());
mWindow = window;
mListener = listener;
mAllSharedElementNames = allSharedElementNames;
mIsReturning = isReturning;
}
protected void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
setSharedElements(accepted, localNames);
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
@@ -274,6 +284,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
return names;
}
public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
public static void setViewVisibility(Collection<View> views, int visibility) {
if (views != null) {
for (View view : views) {

View File

@@ -87,6 +87,11 @@ class ActivityTransitionState {
*/
private boolean mHasExited;
/**
* Postpone painting and starting the enter transition until this is false.
*/
private boolean mIsEnterPostponed;
public ActivityTransitionState() {
}
@@ -140,15 +145,38 @@ class ActivityTransitionState {
if (mEnterActivityOptions.isReturning()) {
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
}
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
if (!mIsEnterPostponed) {
startEnter();
}
}
public void postponeEnterTransition() {
mIsEnterPostponed = true;
}
public void startPostponedEnterTransition() {
if (mIsEnterPostponed) {
mIsEnterPostponed = false;
if (mEnterTransitionCoordinator != null) {
startEnter();
}
}
}
private void startEnter() {
if (mEnterActivityOptions.isReturning()) {
mEnterTransitionCoordinator.viewsReady(mExitingFrom, mExitingTo);
} else {
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, null, null);
mEnteringNames = sharedElementNames;
mEnterTransitionCoordinator.viewsReady(null, null);
mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
}
mExitingFrom = null;
mExitingTo = null;
mEnterActivityOptions = null;

View File

@@ -53,18 +53,37 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private boolean mIsCanceled;
private ObjectAnimator mBackgroundAnimator;
private boolean mIsExitTransitionComplete;
private boolean mIsReadyForTransition;
private Bundle mSharedElementsBundle;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames,
ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
getListener(activity, acceptedNames), acceptedNames != null);
ArrayList<String> sharedElementNames, boolean isReturning) {
super(activity.getWindow(), sharedElementNames,
getListener(activity, isReturning), isReturning);
mActivity = activity;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (mIsReadyForTransition) {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
}
return mIsReadyForTransition;
}
});
}
public void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
if (mIsReadyForTransition) {
return;
}
super.viewsReady(accepted, localNames);
mIsReadyForTransition = true;
if (mIsReturning) {
mHandler = new Handler() {
@Override
@@ -75,6 +94,13 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null);
}
setViewVisibility(mSharedElements, View.INVISIBLE);
if (getViewsTransition() != null) {
setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
if (mSharedElementsBundle != null) {
onTakeSharedElements();
}
}
private void sendSharedElementDestination() {
@@ -94,9 +120,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
}
private static SharedElementListener getListener(Activity activity,
ArrayList<String> acceptedNames) {
boolean isReturning = acceptedNames != null;
private static SharedElementListener getListener(Activity activity, boolean isReturning) {
return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
}
@@ -108,7 +132,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
if (mHandler != null) {
mHandler.removeMessages(MSG_CANCEL);
}
onTakeSharedElements(resultData);
mSharedElementsBundle = resultData;
onTakeSharedElements();
}
break;
case MSG_EXIT_TRANSITION_COMPLETE:
@@ -139,7 +164,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
mSharedElementNames.clear();
mSharedElements.clear();
mAllSharedElementNames.clear();
onTakeSharedElements(null);
startSharedElementTransition(null);
onRemoteExitTransitionComplete();
}
}
@@ -149,10 +174,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
protected void prepareEnter() {
setViewVisibility(mSharedElements, View.INVISIBLE);
if (getViewsTransition() != null) {
setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
mActivity.overridePendingTransition(0, 0);
if (!mIsReturning) {
mActivity.convertToTranslucent(null, null);
@@ -185,7 +206,25 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
}
protected void onTakeSharedElements(Bundle sharedElementState) {
protected void onTakeSharedElements() {
if (!mIsReadyForTransition || mSharedElementsBundle == null) {
return;
}
final Bundle sharedElementState = mSharedElementsBundle;
mSharedElementsBundle = null;
getDecor().getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
startSharedElementTransition(sharedElementState);
return false;
}
});
getDecor().invalidate();
}
private void startSharedElementTransition(Bundle sharedElementState) {
setEpicenter();
// Remove rejected shared elements
ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
@@ -299,8 +338,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
public void stop() {
makeOpaque();
mHasStopped = true;
mActivity = null;
mIsCanceled = true;
mResultReceiver = null;
if (mBackgroundAnimator != null) {
@@ -310,7 +349,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
private void makeOpaque() {
if (!mHasStopped) {
if (!mHasStopped && mActivity != null) {
mActivity.convertFromTranslucent();
mActivity = null;
}