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

Commit 1f8c0118 authored by George Mount's avatar George Mount
Browse files

Handle interruptions in Explode and Slide transitions.

Bug 15618842

Change-Id: I22431b59c6516ee330d897791b79fa9ed17ba727
parent 52cd83e6
Loading
Loading
Loading
Loading
+34 −95
Original line number Diff line number Diff line
@@ -15,20 +15,16 @@
 */
package android.transition;

import com.android.internal.R;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.FloatMath;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;

/**
 * This transition tracks changes to the visibility of target views in the
 * start and end scenes and moves views in or out from the edges of the
@@ -44,8 +40,7 @@ public class Explode extends Visibility {
    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
    private static final String TAG = "Explode";

    private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds";
    private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";

    private int[] mTempLoc = new int[2];

@@ -56,8 +51,8 @@ public class Explode extends Visibility {
    private void captureValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        view.getLocationOnScreen(mTempLoc);
        int left = mTempLoc[0] + Math.round(view.getTranslationX());
        int top = mTempLoc[1] + Math.round(view.getTranslationY());
        int left = mTempLoc[0];
        int top = mTempLoc[1];
        int right = left + view.getWidth();
        int bottom = top + view.getHeight();
        transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
@@ -75,27 +70,6 @@ public class Explode extends Visibility {
        captureValues(transitionValues);
    }

    private Animator createAnimation(final View view, float startX, float startY, float endX,
            float endY, float terminalX, float terminalY, TimeInterpolator interpolator) {
        view.setTranslationX(startX);
        view.setTranslationY(startY);
        if (startY == endY && startX == endX) {
            return null;
        }
        Path path = new Path();
        path.moveTo(startX, startY);
        path.lineTo(endX, endY);
        ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
                View.TRANSLATION_Y, path);
        pathAnimator.setInterpolator(interpolator);
        OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY,
                endX, endY);
        pathAnimator.addListener(listener);
        pathAnimator.addPauseListener(listener);

        return pathAnimator;
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
@@ -103,29 +77,43 @@ public class Explode extends Visibility {
            return null;
        }
        Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
        float endX = view.getTranslationX();
        float endY = view.getTranslationY();
        calculateOut(sceneRoot, bounds, mTempLoc);
        float startX = endX + mTempLoc[0];
        float startY = endY + mTempLoc[1];

        final float endX = view.getTranslationX();
        final float startX = endX + mTempLoc[0];
        final float endY = view.getTranslationY();
        final float startY = endY + mTempLoc[1];

        return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate);
        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
                startX, startY, endX, endY, sDecelerate);
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null) {
            return null;
        }
        Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
        int viewPosX = bounds.left;
        int viewPosY = bounds.top;
        float startX = view.getTranslationX();
        float startY = view.getTranslationY();
        float endX = startX;
        float endY = startY;
        int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition);
        if (interruptedPosition != null) {
            // We want to have the end position relative to the interrupted position, not
            // the position it was supposed to start at.
            endX += interruptedPosition[0] - bounds.left;
            endY += interruptedPosition[1] - bounds.top;
            bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
        }
        calculateOut(sceneRoot, bounds, mTempLoc);
        endX += mTempLoc[0];
        endY += mTempLoc[1];

        final float startX = view.getTranslationX();
        final float endX = startX + mTempLoc[0];
        final float startY = view.getTranslationY();
        final float endY = startY + mTempLoc[1];

        return createAnimation(view, startX, startY, endX, endY, startX, startY,
                sAccelerate);
        return TranslationAnimationCreator.createAnimation(view, startValues,
                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
    }

    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
@@ -176,53 +164,4 @@ public class Explode extends Visibility {
    private static float calculateDistance(float x, float y) {
        return FloatMath.sqrt((x * x) + (y * y));
    }

    private static class OutAnimatorListener extends AnimatorListenerAdapter {
        private final View mView;
        private boolean mCanceled = false;
        private float mPausedX;
        private float mPausedY;
        private final float mTerminalX;
        private final float mTerminalY;
        private final float mEndX;
        private final float mEndY;

        public OutAnimatorListener(View view, float terminalX, float terminalY,
                float endX, float endY) {
            mView = view;
            mTerminalX = terminalX;
            mTerminalY = terminalY;
            mEndX = endX;
            mEndY = endY;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            mView.setTranslationX(mTerminalX);
            mView.setTranslationY(mTerminalY);
            mCanceled = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            if (!mCanceled) {
                mView.setTranslationX(mTerminalX);
                mView.setTranslationY(mTerminalY);
            }
        }

        @Override
        public void onAnimationPause(Animator animator) {
            mPausedX = mView.getTranslationX();
            mPausedY = mView.getTranslationY();
            mView.setTranslationY(mEndX);
            mView.setTranslationY(mEndY);
        }

        @Override
        public void onAnimationResume(Animator animator) {
            mView.setTranslationX(mPausedX);
            mView.setTranslationY(mPausedY);
        }
    }
}
+51 −91
Original line number Diff line number Diff line
@@ -16,14 +16,7 @@
package android.transition;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.util.Log;
import android.util.Property;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -41,71 +34,60 @@ import android.view.animation.DecelerateInterpolator;
 */
public class Slide extends Visibility {
    private static final String TAG = "Slide";

    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();

    private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
    private CalculateSlide mSlideCalculator = sCalculateBottom;

    private interface CalculateSlide {
        /** Returns the translation value for view when it out of the scene */
        float getGone(ViewGroup sceneRoot, View view);

        /** Returns the translation value for view when it is in the scene */
        float getHere(View view);
        /** Returns the translation value for view when it goes out of the scene */
        float getGoneX(ViewGroup sceneRoot, View view);

        /** Returns the property to animate translation */
        Property<View, Float> getProperty();
        /** Returns the translation value for view when it goes out of the scene */
        float getGoneY(ViewGroup sceneRoot, View view);
    }

    private static abstract class CalculateSlideHorizontal implements CalculateSlide {
        @Override
        public float getHere(View view) {
            return view.getTranslationX();
        }

        @Override
        public Property<View, Float> getProperty() {
            return View.TRANSLATION_X;
        public float getGoneY(ViewGroup sceneRoot, View view) {
            return view.getTranslationY();
        }
    }

    private static abstract class CalculateSlideVertical implements CalculateSlide {
        @Override
        public float getHere(View view) {
            return view.getTranslationY();
        }

        @Override
        public Property<View, Float> getProperty() {
            return View.TRANSLATION_Y;
        public float getGoneX(ViewGroup sceneRoot, View view) {
            return view.getTranslationX();
        }
    }

    private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
        @Override
        public float getGone(ViewGroup sceneRoot, View view) {
        public float getGoneX(ViewGroup sceneRoot, View view) {
            return view.getTranslationX() - sceneRoot.getWidth();
        }
    };

    private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
        @Override
        public float getGone(ViewGroup sceneRoot, View view) {
        public float getGoneY(ViewGroup sceneRoot, View view) {
            return view.getTranslationY() - sceneRoot.getHeight();
        }
    };

    private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
        @Override
        public float getGone(ViewGroup sceneRoot, View view) {
        public float getGoneX(ViewGroup sceneRoot, View view) {
            return view.getTranslationX() + sceneRoot.getWidth();
        }
    };

    private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
        @Override
        public float getGone(ViewGroup sceneRoot, View view) {
        public float getGoneY(ViewGroup sceneRoot, View view) {
            return view.getTranslationY() + sceneRoot.getHeight();
        }
    };
@@ -125,8 +107,28 @@ public class Slide extends Visibility {
        setSlideEdge(slideEdge);
    }

    private void captureValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        int[] position = new int[2];
        view.getLocationOnScreen(position);
        transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
        captureValues(transitionValues);
    }

    /**
     * Change the edge that Views appear and disappear from.
     *
     * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
     *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
@@ -153,77 +155,35 @@ public class Slide extends Visibility {
        setPropagation(propagation);
    }

    private Animator createAnimation(final View view, Property<View, Float> property,
            float start, float end, float terminalValue, TimeInterpolator interpolator) {
        view.setTranslationY(start);
        if (start == end) {
            return null;
        }
        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);

        SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end);
        anim.addListener(listener);
        anim.addPauseListener(listener);
        anim.setInterpolator(interpolator);
        return anim;
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
        if (endValues == null) {
            return null;
        }
        float end = mSlideCalculator.getHere(view);
        float start = mSlideCalculator.getGone(sceneRoot, view);
        return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate);
        int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
        float endX = view.getTranslationX();
        float endY = view.getTranslationY();
        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
        return TranslationAnimationCreator
                .createAnimation(view, endValues, position[0], position[1],
                        startX, startY, endX, endY, sDecelerate);
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
        float start = mSlideCalculator.getHere(view);
        float end = mSlideCalculator.getGone(sceneRoot, view);

        return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
                sAccelerate);
    }

    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
        private boolean mCanceled = false;
        private float mPausedY;
        private final View mView;
        private final float mEndY;
        private final float mTerminalY;

        public SlideAnimatorListener(View view, float terminalY, float endY) {
            mView = view;
            mTerminalY = terminalY;
            mEndY = endY;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            mView.setTranslationY(mTerminalY);
            mCanceled = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            if (!mCanceled) {
                mView.setTranslationY(mTerminalY);
            }
        }

        @Override
        public void onAnimationPause(Animator animator) {
            mPausedY = mView.getTranslationY();
            mView.setTranslationY(mEndY);
        }

        @Override
        public void onAnimationResume(Animator animator) {
            mView.setTranslationY(mPausedY);
        if (startValues == null) {
            return null;
        }
        int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
        float startX = view.getTranslationX();
        float startY = view.getTranslationY();
        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
        return TranslationAnimationCreator
                .createAnimation(view, startValues, position[0], position[1],
                        startX, startY, endX, endY, sAccelerate);
    }
}
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.transition;

import com.android.internal.R;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Path;
import android.view.View;

/**
 * This class is used by Slide and Explode to create an animator that goes from the start
 * position to the end position. It takes into account the canceled position so that it
 * will not blink out or shift suddenly when the transition is interrupted.
 */
class TranslationAnimationCreator {

    /**
     * Creates an animator that can be used for x and/or y translations. When interrupted,
     * it sets a tag to keep track of the position so that it may be continued from position.
     *
     * @param view The view being moved. This may be in the overlay for onDisappear.
     * @param values The values containing the view in the view hierarchy.
     * @param viewPosX The x screen coordinate of view
     * @param viewPosY The y screen coordinate of view
     * @param startX The start translation x of view
     * @param startY The start translation y of view
     * @param endX The end translation x of view
     * @param endY The end translation y of view
     * @param interpolator The interpolator to use with this animator.
     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
     * a previous interruption, in which case it moves from the current position to (endX, endY).
     */
    static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
            float startX, float startY, float endX, float endY, TimeInterpolator interpolator) {
        float terminalX = view.getTranslationX();
        float terminalY = view.getTranslationY();
        int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
        if (startPosition != null) {
            startX = startPosition[0] - viewPosX + terminalX;
            startY = startPosition[1] - viewPosY + terminalY;
        }
        // Initial position is at translation startX, startY, so position is offset by that amount
        int startPosX = viewPosX + Math.round(startX - terminalX);
        int startPosY = viewPosY + Math.round(startY - terminalY);

        view.setTranslationX(startX);
        view.setTranslationY(startY);
        if (startX == endX && startY == endY) {
            return null;
        }
        Path path = new Path();
        path.moveTo(startX, startY);
        path.lineTo(endX, endY);
        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
                path);

        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
                startPosX, startPosY, terminalX, terminalY);
        anim.addListener(listener);
        anim.addPauseListener(listener);
        anim.setInterpolator(interpolator);
        return anim;
    }

    private static class TransitionPositionListener extends AnimatorListenerAdapter {

        private final View mViewInHierarchy;
        private final View mMovingView;
        private final int mStartX;
        private final int mStartY;
        private int[] mTransitionPosition;
        private float mPausedX;
        private float mPausedY;
        private final float mTerminalX;
        private final float mTerminalY;

        private TransitionPositionListener(View movingView, View viewInHierarchy,
                int startX, int startY, float terminalX, float terminalY) {
            mMovingView = movingView;
            mViewInHierarchy = viewInHierarchy;
            mStartX = startX - Math.round(mMovingView.getTranslationX());
            mStartY = startY - Math.round(mMovingView.getTranslationY());
            mTerminalX = terminalX;
            mTerminalY = terminalY;
            mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
            if (mTransitionPosition != null) {
                mViewInHierarchy.setTagInternal(R.id.transitionPosition, null);
            }
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            if (mTransitionPosition == null) {
                mTransitionPosition = new int[2];
            }
            mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
            mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
            mViewInHierarchy.setTagInternal(R.id.transitionPosition, mTransitionPosition);
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            mMovingView.setTranslationX(mTerminalX);
            mMovingView.setTranslationY(mTerminalY);
        }

        @Override
        public void onAnimationPause(Animator animator) {
            mPausedX = mMovingView.getTranslationX();
            mPausedY = mMovingView.getTranslationY();
            mMovingView.setTranslationX(mTerminalX);
            mMovingView.setTranslationY(mTerminalY);
        }

        @Override
        public void onAnimationResume(Animator animator) {
            mMovingView.setTranslationX(mPausedX);
            mMovingView.setTranslationY(mPausedY);
        }
    }

}
+1 −0
Original line number Diff line number Diff line
@@ -84,4 +84,5 @@
  <item type="id" name="current_scene" />
  <item type="id" name="scene_layoutid_cache" />
  <item type="id" name="mask" />
  <item type="id" name="transitionPosition" />
</resources>
+1 −0
Original line number Diff line number Diff line
@@ -218,6 +218,7 @@
  <java-symbol type="id" name="pin_error_message" />
  <java-symbol type="id" name="timePickerLayout" />
  <java-symbol type="id" name="profile_icon" />
  <java-symbol type="id" name="transitionPosition" />

  <java-symbol type="attr" name="actionModeShareDrawable" />
  <java-symbol type="attr" name="alertDialogCenterButtons" />