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

Commit 67d92434 authored by George Mount's avatar George Mount
Browse files

Better coordination of transition destination.

Bug 15470128
Bug 15470558

Wait for shared element destinations to be complete
before sending it to the calling activity.

Don't allow layout between setting final destination
and the transition values capture.

Use snapshots to hold the final position while waiting
for the called Activity to take the shared element.

Fixed problem with exiting views sometimes disappearing
when exiting.

Change-Id: Ibc655f7bb9e8dd6e8a15778c96931e3d845cc15c
parent ea54579b
Loading
Loading
Loading
Loading
+26 −1
Original line number Diff line number Diff line
@@ -206,6 +206,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
    protected ResultReceiver mResultReceiver;
    final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
    final protected boolean mIsReturning;
    private Runnable mPendingTransition;
    private boolean mIsStartingTransition;


    public ActivityTransitionCoordinator(Window window,
            ArrayList<String> allSharedElementNames,
@@ -523,7 +526,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
     * @param transitionArgs Bundle to store shared element placement information.
     * @param tempBounds     A temporary Rect for capturing the current location of views.
     */
    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
    protected static void captureSharedElementState(View view, String name, Bundle transitionArgs,
            Rect tempBounds) {
        Bundle sharedElementBundle = new Bundle();
        tempBounds.set(0, 0, view.getWidth(), view.getHeight());
@@ -559,6 +562,28 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        transitionArgs.putBundle(name, sharedElementBundle);
    }


    protected void startTransition(Runnable runnable) {
        if (mIsStartingTransition) {
            mPendingTransition = runnable;
        } else {
            mIsStartingTransition = true;
            runnable.run();
        }
    }

    protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
        @Override
        public void onTransitionStart(Transition transition) {
            mIsStartingTransition = false;
            Runnable pending = mPendingTransition;
            mPendingTransition = null;
            if (pending != null) {
                startTransition(pending);
            }
        }
    }

    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
            if (scaleType == SCALE_TYPE_VALUES[i]) {
+12 −1
Original line number Diff line number Diff line
@@ -106,7 +106,16 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {

    private void sendSharedElementDestination() {
        ViewGroup decor = getDecor();
        if (!decor.isLayoutRequested()) {
        boolean allReady = !decor.isLayoutRequested();
        if (allReady) {
            for (int i = 0; i < mSharedElements.size(); i++) {
                if (mSharedElements.get(i).isLayoutRequested()) {
                    allReady = false;
                    break;
                }
            }
        }
        if (allReady) {
            Bundle state = captureSharedElementState();
            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
        } else {
@@ -115,6 +124,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
                        @Override
                        public boolean onPreDraw() {
                            getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
                            Bundle state = captureSharedElementState();
                            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
                            return true;
                        }
                    });
+99 −36
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -27,6 +28,7 @@ import android.os.Message;
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;

import java.util.ArrayList;
@@ -60,10 +62,10 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {

    private boolean mIsHidden;

    private boolean mExitTransitionStarted;

    private Bundle mExitSharedElementBundle;

    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),
@@ -106,30 +108,81 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
                break;
            case MSG_SHARED_ELEMENT_DESTINATION:
                mExitSharedElementBundle = resultData;
                if (mExitTransitionStarted) {
                sharedElementExitBack();
                break;
        }
    }

    private void sharedElementExitBack() {
        if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
            startTransition(new Runnable() {
                public void run() {
                    startSharedElementExit();
                }
                break;
            });
        }
    }

    private void startSharedElementExit() {
        if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
        Transition transition = getSharedElementExitTransition();
            TransitionManager.beginDelayedTransition(getDecor(), transition);
            ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
        final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
                mSharedElementNames);
        mSharedElementSnapshots = createSnapshots(mExitSharedElementBundle, mSharedElementNames);
        transition.addListener(new Transition.TransitionListenerAdapter() {
            @Override
            public void onTransitionEnd(Transition transition) {
                setViewVisibility(mSharedElements, View.INVISIBLE);
                ViewGroupOverlay overlay = getDecor().getOverlay();
                for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
                    overlay.add(mSharedElementSnapshots.get(i));
                }
            }
        });
        getDecor().getViewTreeObserver()
                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
                        setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
                        return true;
                    }
                });
        TransitionManager.beginDelayedTransition(getDecor(), transition);
        getDecor().invalidate();
    }

    private static ArrayList<View> copySnapshots(ArrayList<View> snapshots) {
        ArrayList<View> copy = new ArrayList<View>(snapshots.size());
        for (int i = 0; i < snapshots.size(); i++) {
            View view = snapshots.get(i);
            View viewCopy = new View(view.getContext());
            viewCopy.setBackground(view.getBackground());
            copy.add(viewCopy);
        }
        return copy;
    }

    private void hideSharedElements() {
        if (!mIsHidden) {
            setViewVisibility(mSharedElements, View.INVISIBLE);
        }
        if (mSharedElementSnapshots != null) {
            ViewGroupOverlay overlay = getDecor().getOverlay();
            for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
                overlay.remove(mSharedElementSnapshots.get(i));
            }
            mSharedElementSnapshots = null;
        }
        finishIfNecessary();
    }

    public void startExit() {
        startTransition(new Runnable() {
            @Override
            public void run() {
                beginTransitions();
            }
        });
        setViewVisibility(mTransitioningViews, View.INVISIBLE);
    }

@@ -159,29 +212,25 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
                }
            }
        }, options);
        startTransition(new Runnable() {
            @Override
            public void run() {
                startExitTransition();
            }
        });
    }

    private void startExitTransition() {
        Transition sharedElementTransition = mSharedElements.isEmpty()
                ? null : getSharedElementTransition();
        if (sharedElementTransition == null) {
            sharedElementTransitionComplete();
        }
        Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
        if (transition == null) {
            mExitTransitionStarted = true;
        } else {
        Transition transition = mergeTransitions(sharedElementTransition,
                getExitTransition());
        if (transition != null) {
            TransitionManager.beginDelayedTransition(getDecor(), transition);
            setViewVisibility(mTransitioningViews, View.INVISIBLE);
            getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
                    mExitTransitionStarted = true;
                    if (mExitSharedElementBundle != null) {
                        startSharedElementExit();
                    }
                    notifyComplete();
                    return true;
                }
            });
        }
    }

@@ -212,7 +261,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
        if (viewsTransition == null) {
            exitTransitionComplete();
        } else {
            viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
            viewsTransition.addListener(new ContinueTransitionListener() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    exitTransitionComplete();
@@ -238,7 +287,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
        if (sharedElementTransition == null) {
            sharedElementTransitionComplete();
        } else {
            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
            sharedElementTransition.addListener(new ContinueTransitionListener() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    sharedElementTransitionComplete();
@@ -257,7 +306,6 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
        Transition viewsTransition = getExitTransition();

        Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
        mExitTransitionStarted = true;
        if (transition != null) {
            TransitionManager.beginDelayedTransition(getDecor(), transition);
        }
@@ -269,15 +317,31 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
    }

    protected boolean isReadyToNotify() {
        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
                && mExitTransitionStarted;
        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
    }

    private void sharedElementTransitionComplete() {
        mSharedElementBundle = captureSharedElementState();
        mSharedElementBundle = mExitSharedElementBundle == null
                ? captureSharedElementState() : captureExitSharedElementsState();
        notifyComplete();
    }

    private Bundle captureExitSharedElementsState() {
        Bundle bundle = new Bundle();
        Rect bounds = new Rect();
        for (int i = 0; i < mSharedElementNames.size(); i++) {
            String name = mSharedElementNames.get(i);
            Bundle sharedElementState = mExitSharedElementBundle.getBundle(name);
            if (sharedElementState != null) {
                bundle.putBundle(name, sharedElementState);
            } else {
                View view = mSharedElements.get(i);
                captureSharedElementState(view, name, bundle, bounds);
            }
        }
        return bundle;
    }

    protected void notifyComplete() {
        if (isReadyToNotify()) {
            if (!mSharedElementNotified) {
@@ -294,8 +358,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
    }

    private void finishIfNecessary() {
        if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
                || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
        if (mIsReturning && mExitNotified && mActivity != null && mSharedElementSnapshots == null) {
            mActivity.finish();
            mActivity.overridePendingTransition(0, 0);
            mActivity = null;