FileDocCategorySizeDatePackage
EnterTransitionCoordinator.javaAPI DocAndroid 5.1 API24909Thu Mar 12 22:22:10 GMT 2015android.app

EnterTransitionCoordinator

public class EnterTransitionCoordinator extends ActivityTransitionCoordinator
This ActivityTransitionCoordinator is created by the Activity to manage the enter scene and shared element transfer into the Scene, either during launch of an Activity or returning from a launched Activity.

Fields Summary
private static final String
TAG
private static final int
MIN_ANIMATION_FRAMES
private boolean
mSharedElementTransitionStarted
private Activity
mActivity
private boolean
mHasStopped
private boolean
mIsCanceled
private android.animation.ObjectAnimator
mBackgroundAnimator
private boolean
mIsExitTransitionComplete
private boolean
mIsReadyForTransition
private android.os.Bundle
mSharedElementsBundle
private boolean
mWasOpaque
private boolean
mAreViewsReady
private boolean
mIsViewsTransitionStarted
private boolean
mIsViewsTransitionComplete
private boolean
mIsSharedElementTransitionComplete
private android.transition.Transition
mEnterViewsTransition
Constructors Summary
public EnterTransitionCoordinator(Activity activity, android.os.ResultReceiver resultReceiver, ArrayList 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);
        final View decorView = getDecor();
        if (decorView != null) {
            decorView.getViewTreeObserver().addOnPreDrawListener(
                    new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            if (mIsReadyForTransition) {
                                decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                            }
                            return mIsReadyForTransition;
                        }
                    });
        }
    
Methods Summary
private booleanallowOverlappingTransitions()

        return mIsReturning ? getWindow().getAllowExitTransitionOverlap()
                : getWindow().getAllowEnterTransitionOverlap();
    
private android.transition.TransitionbeginTransition(android.view.ViewGroup decorView, boolean startEnterTransition, boolean startSharedElementTransition)

        Transition sharedElementTransition = null;
        if (startSharedElementTransition) {
            if (!mSharedElementNames.isEmpty()) {
                sharedElementTransition = configureTransition(getSharedElementTransition(), false);
            }
            if (sharedElementTransition == null) {
                sharedElementTransitionStarted();
                sharedElementTransitionComplete();
            } else {
                sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
                    @Override
                    public void onTransitionStart(Transition transition) {
                        sharedElementTransitionStarted();
                    }

                    @Override
                    public void onTransitionEnd(Transition transition) {
                        transition.removeListener(this);
                        sharedElementTransitionComplete();
                    }
                });
            }
        }
        Transition viewsTransition = null;
        if (startEnterTransition) {
            mIsViewsTransitionStarted = true;
            if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
                viewsTransition = configureTransition(getViewsTransition(), true);
                if (viewsTransition != null && !mIsReturning) {
                    stripOffscreenViews();
                }
            }
            if (viewsTransition == null) {
                viewTransitionComplete();
            } else {
                viewsTransition.forceVisibility(View.INVISIBLE, true);
                final ArrayList<View> transitioningViews = mTransitioningViews;
                viewsTransition.addListener(new ContinueTransitionListener() {
                    @Override
                    public void onTransitionStart(Transition transition) {
                        mEnterViewsTransition = transition;
                        if (transitioningViews != null) {
                            showViews(transitioningViews, false);
                        }
                        super.onTransitionStart(transition);
                    }

                    @Override
                    public void onTransitionEnd(Transition transition) {
                        mEnterViewsTransition = null;
                        transition.removeListener(this);
                        viewTransitionComplete();
                        super.onTransitionEnd(transition);
                    }
                });
            }
        }

        Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
        if (transition != null) {
            transition.addListener(new ContinueTransitionListener());
            TransitionManager.beginDelayedTransition(decorView, transition);
            if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
                mSharedElements.get(0).invalidate();
            } else if (startEnterTransition && mTransitioningViews != null &&
                    !mTransitioningViews.isEmpty()) {
                mTransitioningViews.get(0).invalidate();
            }
        } else {
            transitionStarted();
        }
        return transition;
    
private voidcancel()

        if (!mIsCanceled) {
            mIsCanceled = true;
            if (getViewsTransition() == null || mIsViewsTransitionStarted) {
                showViews(mSharedElements, true);
            } else if (mTransitioningViews != null) {
                mTransitioningViews.addAll(mSharedElements);
            }
            mSharedElementNames.clear();
            mSharedElements.clear();
            mAllSharedElementNames.clear();
            startSharedElementTransition(null);
            onRemoteExitTransitionComplete();
        }
    
public booleancancelEnter()
Cancels the enter transition.

return
True if the enter transition is still pending capturing the target state. If so, any transition started on the decor will do nothing.

        setGhostVisibility(View.INVISIBLE);
        mHasStopped = true;
        mIsCanceled = true;
        mResultReceiver = null;
        if (mBackgroundAnimator != null) {
            mBackgroundAnimator.cancel();
            mBackgroundAnimator = null;
        }
        mActivity = null;
        clearState();
        return super.cancelPendingTransitions();
    
public android.transition.TransitiongetEnterViewsTransition()

        return mEnterViewsTransition;
    
private static SharedElementCallbackgetListener(Activity activity, boolean isReturning)

        return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
    
protected android.transition.TransitiongetSharedElementTransition()

        Window window = getWindow();
        if (window == null) {
            return null;
        }
        if (mIsReturning) {
            return window.getSharedElementReenterTransition();
        } else {
            return window.getSharedElementEnterTransition();
        }
    
protected android.transition.TransitiongetViewsTransition()

        Window window = getWindow();
        if (window == null) {
            return null;
        }
        if (mIsReturning) {
            return window.getReenterTransition();
        } else {
            return window.getEnterTransition();
        }
    
public booleanisReturning()

        return mIsReturning;
    
private voidmakeOpaque()

        if (!mHasStopped && mActivity != null) {
            if (mWasOpaque) {
                mActivity.convertFromTranslucent();
            }
            mActivity = null;
        }
    
private android.util.ArrayMapmapNamedElements(java.util.ArrayList accepted, java.util.ArrayList localNames)

        ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
        ViewGroup decorView = getDecor();
        if (decorView != null) {
            decorView.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;
    
public voidnamedViewsReady(java.util.ArrayList accepted, java.util.ArrayList localNames)

        triggerViewsReady(mapNamedElements(accepted, localNames));
    
protected voidonReceiveResult(int resultCode, android.os.Bundle resultData)

        switch (resultCode) {
            case MSG_TAKE_SHARED_ELEMENTS:
                if (!mIsCanceled) {
                    mSharedElementsBundle = resultData;
                    onTakeSharedElements();
                }
                break;
            case MSG_EXIT_TRANSITION_COMPLETE:
                if (!mIsCanceled) {
                    mIsExitTransitionComplete = true;
                    if (mSharedElementTransitionStarted) {
                        onRemoteExitTransitionComplete();
                    }
                }
                break;
            case MSG_CANCEL:
                cancel();
                break;
        }
    
protected voidonRemoteExitTransitionComplete()

        if (!allowOverlappingTransitions()) {
            startEnterTransitionOnly();
        }
    
private voidonTakeSharedElements()

        if (!mIsReadyForTransition || mSharedElementsBundle == null) {
            return;
        }
        final Bundle sharedElementState = mSharedElementsBundle;
        mSharedElementsBundle = null;
        final View decorView = getDecor();
        if (decorView != null) {
            decorView.getViewTreeObserver()
                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                            startTransition(new Runnable() {
                                @Override
                                public void run() {
                                    startSharedElementTransition(sharedElementState);
                                }
                            });
                            return false;
                        }
                    });
            decorView.invalidate();
        }
    
protected voidprepareEnter()

        ViewGroup decorView = getDecor();
        if (mActivity == null || decorView == null) {
            return;
        }
        mActivity.overridePendingTransition(0, 0);
        if (!mIsReturning) {
            mWasOpaque = mActivity.convertToTranslucent(null, null);
            Drawable background = decorView.getBackground();
            if (background != null) {
                getWindow().setBackgroundDrawable(null);
                background = background.mutate();
                background.setAlpha(0);
                getWindow().setBackgroundDrawable(background);
            }
        } else {
            mActivity = null; // all done with it now.
        }
    
private static voidremoveNullViews(java.util.ArrayList views)

        if (views != null) {
            for (int i = views.size() - 1; i >= 0; i--) {
                if (views.get(i) == null) {
                    views.remove(i);
                }
            }
        }
    
private voidrequestLayoutForSharedElements()

        int numSharedElements = mSharedElements.size();
        for (int i = 0; i < numSharedElements; i++) {
            mSharedElements.get(i).requestLayout();
        }
    
private voidsendSharedElementDestination()

        boolean allReady;
        final View decorView = getDecor();
        if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
            allReady = false;
        } else if (decorView == null) {
            allReady = true;
        } else {
            allReady = !decorView.isLayoutRequested();
            if (allReady) {
                for (int i = 0; i < mSharedElements.size(); i++) {
                    if (mSharedElements.get(i).isLayoutRequested()) {
                        allReady = false;
                        break;
                    }
                }
            }
        }
        if (allReady) {
            Bundle state = captureSharedElementState();
            moveSharedElementsToOverlay();
            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
        } else if (decorView != null) {
            decorView.getViewTreeObserver()
                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                            if (mResultReceiver != null) {
                                Bundle state = captureSharedElementState();
                                moveSharedElementsToOverlay();
                                mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
                            }
                            return true;
                        }
                    });
        }
        if (allowOverlappingTransitions()) {
            startEnterTransitionOnly();
        }
    
private voidsharedElementTransitionComplete()

        mIsSharedElementTransitionComplete = true;
        if (mIsViewsTransitionComplete) {
            moveSharedElementsFromOverlay();
        }
    
private voidsharedElementTransitionStarted()

        mSharedElementTransitionStarted = true;
        if (mIsExitTransitionComplete) {
            send(MSG_EXIT_TRANSITION_COMPLETE, null);
        }
    
private voidstartEnterTransition(android.transition.Transition transition)

        ViewGroup decorView = getDecor();
        if (!mIsReturning && decorView != null) {
            Drawable background = decorView.getBackground();
            if (background != null) {
                background = background.mutate();
                getWindow().setBackgroundDrawable(background);
                mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
                mBackgroundAnimator.setDuration(getFadeDuration());
                mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        makeOpaque();
                    }
                });
                mBackgroundAnimator.start();
            } else if (transition != null) {
                transition.addListener(new Transition.TransitionListenerAdapter() {
                    @Override
                    public void onTransitionEnd(Transition transition) {
                        transition.removeListener(this);
                        makeOpaque();
                    }
                });
            } else {
                makeOpaque();
            }
        }
    
private voidstartEnterTransitionOnly()

        startTransition(new Runnable() {
            @Override
            public void run() {
                boolean startEnterTransition = true;
                boolean startSharedElementTransition = false;
                ViewGroup decorView = getDecor();
                if (decorView != null) {
                    Transition transition = beginTransition(decorView, startEnterTransition,
                            startSharedElementTransition);
                    startEnterTransition(transition);
                }
            }
        });
    
private voidstartRejectedAnimations(java.util.ArrayList rejectedSnapshots)

        if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
            return;
        }
        final ViewGroup decorView = getDecor();
        if (decorView != null) {
            ViewGroupOverlay overlay = decorView.getOverlay();
            ObjectAnimator animator = null;
            int numRejected = rejectedSnapshots.size();
            for (int i = 0; i < numRejected; i++) {
                View snapshot = rejectedSnapshots.get(i);
                overlay.add(snapshot);
                animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
                animator.start();
            }
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    ViewGroupOverlay overlay = decorView.getOverlay();
                    int numRejected = rejectedSnapshots.size();
                    for (int i = 0; i < numRejected; i++) {
                        overlay.remove(rejectedSnapshots.get(i));
                    }
                }
            });
        }
    
private voidstartSharedElementTransition(android.os.Bundle sharedElementState)

        ViewGroup decorView = getDecor();
        if (decorView == null) {
            return;
        }
        // Remove rejected shared elements
        ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
        rejectedNames.removeAll(mSharedElementNames);
        ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
        if (mListener != null) {
            mListener.onRejectSharedElements(rejectedSnapshots);
        }
        removeNullViews(rejectedSnapshots);
        startRejectedAnimations(rejectedSnapshots);

        // Now start shared element transition
        ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
                mSharedElementNames);
        showViews(mSharedElements, true);
        scheduleSetSharedElementEnd(sharedElementSnapshots);
        ArrayList<SharedElementOriginalState> originalImageViewState =
                setSharedElementState(sharedElementState, sharedElementSnapshots);
        requestLayoutForSharedElements();

        boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
        boolean startSharedElementTransition = true;
        setGhostVisibility(View.INVISIBLE);
        scheduleGhostVisibilityChange(View.INVISIBLE);
        Transition transition = beginTransition(decorView, startEnterTransition,
                startSharedElementTransition);
        scheduleGhostVisibilityChange(View.VISIBLE);
        setGhostVisibility(View.VISIBLE);

        if (startEnterTransition) {
            startEnterTransition(transition);
        }

        setOriginalSharedElementState(mSharedElements, originalImageViewState);

        if (mResultReceiver != null) {
            // We can't trust that the view will disappear on the same frame that the shared
            // element appears here. Assure that we get at least 2 frames for double-buffering.
            decorView.postOnAnimation(new Runnable() {
                int mAnimations;

                @Override
                public void run() {
                    if (mAnimations++ < MIN_ANIMATION_FRAMES) {
                        View decorView = getDecor();
                        if (decorView != null) {
                            decorView.postOnAnimation(this);
                        }
                    } else if (mResultReceiver != null) {
                        mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
                        mResultReceiver = null; // all done sending messages.
                    }
                }
            });
        }
    
public voidstop()

        // Restore the background to its previous state since the
        // Activity is stopping.
        if (mBackgroundAnimator != null) {
            mBackgroundAnimator.end();
            mBackgroundAnimator = null;
        } else if (mWasOpaque) {
            ViewGroup decorView = getDecor();
            if (decorView != null) {
                Drawable drawable = decorView.getBackground();
                if (drawable != null) {
                    drawable.setAlpha(1);
                }
            }
        }
        makeOpaque();
        mIsCanceled = true;
        mResultReceiver = null;
        mActivity = null;
        moveSharedElementsFromOverlay();
        if (mTransitioningViews != null) {
            showViews(mTransitioningViews, true);
        }
        showViews(mSharedElements, true);
        clearState();
    
private voidtriggerViewsReady(android.util.ArrayMap sharedElements)

        if (mAreViewsReady) {
            return;
        }
        mAreViewsReady = true;
        final ViewGroup decor = getDecor();
        // Ensure the views have been laid out before capturing the views -- we need the epicenter.
        if (decor == null || (decor.isAttachedToWindow() &&
                (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
            viewsReady(sharedElements);
        } else {
            decor.getViewTreeObserver()
                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    decor.getViewTreeObserver().removeOnPreDrawListener(this);
                    viewsReady(sharedElements);
                    return true;
                }
            });
        }
    
public voidviewInstancesReady(java.util.ArrayList accepted, java.util.ArrayList localNames, java.util.ArrayList localViews)

        boolean remap = false;
        for (int i = 0; i < localViews.size(); i++) {
            View view = localViews.get(i);
            if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
                    || !view.isAttachedToWindow()) {
                remap = true;
                break;
            }
        }
        if (remap) {
            triggerViewsReady(mapNamedElements(accepted, localNames));
        } else {
            triggerViewsReady(mapSharedElements(accepted, localViews));
        }
    
private voidviewTransitionComplete()

        mIsViewsTransitionComplete = true;
        if (mIsSharedElementTransitionComplete) {
            moveSharedElementsFromOverlay();
        }
    
protected voidviewsReady(android.util.ArrayMap sharedElements)

        super.viewsReady(sharedElements);
        mIsReadyForTransition = true;
        hideViews(mSharedElements);
        if (getViewsTransition() != null && mTransitioningViews != null) {
            hideViews(mTransitioningViews);
        }
        if (mIsReturning) {
            sendSharedElementDestination();
        } else {
            moveSharedElementsToOverlay();
        }
        if (mSharedElementsBundle != null) {
            onTakeSharedElements();
        }