Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c2614ce authored by George Mount's avatar George Mount
Browse files

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

Bug 15539194

Change-Id: I8de9806b0bcdf8129eb3b5a555a2433f0c7be634
parent cdb4a446
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3339,6 +3339,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);
@@ -3394,6 +3395,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);
+28 −0
Original line number Diff line number Diff line
@@ -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) {
+12 −0
Original line number Diff line number Diff line
@@ -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);
@@ -269,6 +279,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) {
+33 −5
Original line number Diff line number Diff line
@@ -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);
                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;
+55 −16
Original line number Diff line number Diff line
@@ -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;
        }