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

Commit 40868e19 authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Add curved motion to Transitions."

parents 8510e28e ecd857be
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -825,6 +825,7 @@ package android {
    field public static final int maxRows = 16843059; // 0x1010133
    field public static final int maxSdkVersion = 16843377; // 0x1010271
    field public static final int maxWidth = 16843039; // 0x101011f
    field public static final int maximumAngle = 16843905; // 0x1010481
    field public static final int measureAllChildren = 16843018; // 0x101010a
    field public static final int measureWithLargestChild = 16843476; // 0x10102d4
    field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
@@ -840,6 +841,8 @@ package android {
    field public static final int minResizeWidth = 16843669; // 0x1010395
    field public static final int minSdkVersion = 16843276; // 0x101020c
    field public static final int minWidth = 16843071; // 0x101013f
    field public static final int minimumHorizontalAngle = 16843903; // 0x101047f
    field public static final int minimumVerticalAngle = 16843904; // 0x1010480
    field public static final int mipMap = 16843725; // 0x10103cd
    field public static final int mirrorForRtl = 16843726; // 0x10103ce
    field public static final int mode = 16843134; // 0x101017e
@@ -30450,12 +30453,26 @@ package android.text.util {
package android.transition {
  public class ArcMotion extends android.transition.PathMotion {
    ctor public ArcMotion();
    ctor public ArcMotion(android.content.Context, android.util.AttributeSet);
    method public float getMaximumAngle();
    method public float getMinimumHorizontalAngle();
    method public float getMinimumVerticalAngle();
    method public android.graphics.Path getPath(float, float, float, float);
    method public void setMaximumAngle(float);
    method public void setMinimumHorizontalAngle(float);
    method public void setMinimumVerticalAngle(float);
  }
  public class AutoTransition extends android.transition.TransitionSet {
    ctor public AutoTransition();
    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
  }
  public class ChangeBounds extends android.transition.Transition {
    ctor public ChangeBounds();
    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
    method public void setReparent(boolean);
@@ -30464,18 +30481,21 @@ package android.transition {
  public class ChangeClipBounds extends android.transition.Transition {
    ctor public ChangeClipBounds();
    ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
  public class ChangeImageTransform extends android.transition.Transition {
    ctor public ChangeImageTransform();
    ctor public ChangeImageTransform(android.content.Context, android.util.AttributeSet);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
  public class ChangeTransform extends android.transition.Transition {
    ctor public ChangeTransform();
    ctor public ChangeTransform(android.content.Context, android.util.AttributeSet);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
@@ -30488,15 +30508,32 @@ package android.transition {
  public class Explode extends android.transition.Visibility {
    ctor public Explode();
    ctor public Explode(android.content.Context, android.util.AttributeSet);
  }
  public class Fade extends android.transition.Visibility {
    ctor public Fade();
    ctor public Fade(int);
    ctor public Fade(android.content.Context, android.util.AttributeSet);
    field public static final int IN = 1; // 0x1
    field public static final int OUT = 2; // 0x2
  }
  public abstract class PathMotion {
    ctor public PathMotion();
    ctor public PathMotion(android.content.Context, android.util.AttributeSet);
    method public abstract android.graphics.Path getPath(float, float, float, float);
  }
  public class PatternMotion extends android.transition.PathMotion {
    ctor public PatternMotion();
    ctor public PatternMotion(android.content.Context, android.util.AttributeSet);
    ctor public PatternMotion(android.graphics.Path);
    method public android.graphics.Path getPath(float, float, float, float);
    method public android.graphics.Path getPattern();
    method public void setPattern(android.graphics.Path);
  }
  public final class Scene {
    ctor public Scene(android.view.ViewGroup);
    ctor public Scene(android.view.ViewGroup, android.view.View);
@@ -30519,11 +30556,14 @@ package android.transition {
  public class Slide extends android.transition.Visibility {
    ctor public Slide();
    ctor public Slide(int);
    ctor public Slide(android.content.Context, android.util.AttributeSet);
    method public int getSlideEdge();
    method public void setSlideEdge(int);
  }
  public abstract class Transition implements java.lang.Cloneable {
    ctor public Transition();
    ctor public Transition(android.content.Context, android.util.AttributeSet);
    method public android.transition.Transition addListener(android.transition.Transition.TransitionListener);
    method public android.transition.Transition addTarget(int);
    method public android.transition.Transition addTarget(java.lang.String);
@@ -30546,6 +30586,7 @@ package android.transition {
    method public android.transition.Transition.EpicenterCallback getEpicenterCallback();
    method public android.animation.TimeInterpolator getInterpolator();
    method public java.lang.String getName();
    method public android.transition.PathMotion getPathMotion();
    method public android.transition.TransitionPropagation getPropagation();
    method public long getStartDelay();
    method public java.util.List<java.lang.Integer> getTargetIds();
@@ -30563,6 +30604,7 @@ package android.transition {
    method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback);
    method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator);
    method public void setMatchOrder(int...);
    method public void setPathMotion(android.transition.PathMotion);
    method public void setPropagation(android.transition.TransitionPropagation);
    method public android.transition.Transition setStartDelay(long);
    field public static final int MATCH_ID = 3; // 0x3
@@ -30610,6 +30652,7 @@ package android.transition {
  public class TransitionSet extends android.transition.Transition {
    ctor public TransitionSet();
    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
    method public android.transition.TransitionSet addTransition(android.transition.Transition);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
@@ -30628,8 +30671,10 @@ package android.transition {
  public abstract class Visibility extends android.transition.Transition {
    ctor public Visibility();
    ctor public Visibility(android.content.Context, android.util.AttributeSet);
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
    method public int getMode();
    method public boolean isVisible(android.transition.TransitionValues);
    method public android.animation.Animator onAppear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int);
    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues);
+272 −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.content.Context;
import android.content.res.TypedArray;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.FloatMath;

/**
 * A PathMotion that generates a curved path along an arc on an imaginary circle containing
 * the two points. If the horizontal distance between the points is less than the vertical
 * distance, then the circle's center point will be horizontally aligned with the end point. If the
 * vertical distance is less than the horizontal distance then the circle's center point
 * will be vertically aligned with the end point.
 * <p>
 * When the two points are near horizontal or vertical, the curve of the motion will be
 * small as the center of the circle will be far from both points. To force curvature of
 * the path, {@link #setMinimumHorizontalAngle(float)} and
 * {@link #setMinimumVerticalAngle(float)} may be used to set the minimum angle of the
 * arc between two points.
 * </p>
 * <p>This may be used in XML as an element inside a transition.</p>
 * <pre>
 * {@code
 * &lt;changeBounds>
 *   &lt;arcMotion android:minimumHorizontalAngle="15"
 *     android:minimumVerticalAngle="0" android:maximumAngle="90"/>
 * &lt;/changeBounds>
 * }
 * </pre>
 */
public class ArcMotion extends PathMotion {

    private static final float DEFAULT_MIN_ANGLE_DEGREES = 0;
    private static final float DEFAULT_MAX_ANGLE_DEGREES = 70;
    private static final float DEFAULT_MAX_TANGENT = (float)
            Math.tan(Math.toRadians(DEFAULT_MAX_ANGLE_DEGREES/2));

    private float mMinimumHorizontalAngle = 0;
    private float mMinimumVerticalAngle = 0;
    private float mMaximumAngle = DEFAULT_MAX_ANGLE_DEGREES;
    private float mMinimumHorizontalTangent = 0;
    private float mMinimumVerticalTangent = 0;
    private float mMaximumTangent = DEFAULT_MAX_TANGENT;

    public ArcMotion() {}

    public ArcMotion(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ArcMotion);
        float minimumVerticalAngle = a.getFloat(R.styleable.ArcMotion_minimumVerticalAngle,
                DEFAULT_MIN_ANGLE_DEGREES);
        setMinimumVerticalAngle(minimumVerticalAngle);
        float minimumHorizontalAngle = a.getFloat(R.styleable.ArcMotion_minimumHorizontalAngle,
                DEFAULT_MIN_ANGLE_DEGREES);
        setMinimumHorizontalAngle(minimumHorizontalAngle);
        float maximumAngle = a.getFloat(R.styleable.ArcMotion_maximumAngle,
                DEFAULT_MAX_ANGLE_DEGREES);
        setMaximumAngle(maximumAngle);
        a.recycle();
    }

    /**
     * Sets the minimum arc along the circle between two points aligned near horizontally.
     * When start and end points are close to horizontal, the calculated center point of the
     * circle will be far from both points, giving a near straight path between the points.
     * By setting a minimum angle, this forces the center point to be closer and give an
     * exaggerated curve to the path.
     * <p>The default value is 0.</p>
     *
     * @param angleInDegrees The minimum angle of the arc on a circle describing the Path
     *                       between two nearly horizontally-separated points.
     * @attr ref android.R.styleable#ArcMotion_minimumHorizontalAngle
     */
    public void setMinimumHorizontalAngle(float angleInDegrees) {
        mMinimumHorizontalAngle = angleInDegrees;
        mMinimumHorizontalTangent = toTangent(angleInDegrees);
    }

    /**
     * Returns the minimum arc along the circle between two points aligned near horizontally.
     * When start and end points are close to horizontal, the calculated center point of the
     * circle will be far from both points, giving a near straight path between the points.
     * By setting a minimum angle, this forces the center point to be closer and give an
     * exaggerated curve to the path.
     * <p>The default value is 0.</p>
     *
     * @return  The minimum arc along the circle between two points aligned near horizontally.
     * @attr ref android.R.styleable#ArcMotion_minimumHorizontalAngle
     */
    public float getMinimumHorizontalAngle() {
        return mMinimumHorizontalAngle;
    }

    /**
     * Sets the minimum arc along the circle between two points aligned near vertically.
     * When start and end points are close to vertical, the calculated center point of the
     * circle will be far from both points, giving a near straight path between the points.
     * By setting a minimum angle, this forces the center point to be closer and give an
     * exaggerated curve to the path.
     * <p>The default value is 0.</p>
     *
     * @param angleInDegrees The minimum angle of the arc on a circle describing the Path
     *                       between two nearly vertically-separated points.
     * @attr ref android.R.styleable#ArcMotion_minimumVerticalAngle
     */
    public void setMinimumVerticalAngle(float angleInDegrees) {
        mMinimumVerticalAngle = angleInDegrees;
        mMinimumVerticalTangent = toTangent(angleInDegrees);
    }

    /**
     * Returns the minimum arc along the circle between two points aligned near vertically.
     * When start and end points are close to vertical, the calculated center point of the
     * circle will be far from both points, giving a near straight path between the points.
     * By setting a minimum angle, this forces the center point to be closer and give an
     * exaggerated curve to the path.
     * <p>The default value is 0.</p>
     *
     * @return The minimum angle of the arc on a circle describing the Path
     *         between two nearly vertically-separated points.
     * @attr ref android.R.styleable#ArcMotion_minimumVerticalAngle
     */
    public float getMinimumVerticalAngle() {
        return mMinimumVerticalAngle;
    }

    /**
     * Sets the maximum arc along the circle between two points. When start and end points
     * have close to equal x and y differences, the curve between them is large. This forces
     * the curved path to have an arc of at most the given angle.
     * <p>The default value is 70 degrees.</p>
     *
     * @param angleInDegrees The maximum angle of the arc on a circle describing the Path
     *                       between the start and end points.
     * @attr ref android.R.styleable#ArcMotion_maximumAngle
     */
    public void setMaximumAngle(float angleInDegrees) {
        mMaximumAngle = angleInDegrees;
        mMaximumTangent = toTangent(angleInDegrees);
    }

    /**
     * Returns the maximum arc along the circle between two points. When start and end points
     * have close to equal x and y differences, the curve between them is large. This forces
     * the curved path to have an arc of at most the given angle.
     * <p>The default value is 70 degrees.</p>
     *
     * @return The maximum angle of the arc on a circle describing the Path
     *         between the start and end points.
     * @attr ref android.R.styleable#ArcMotion_maximumAngle
     */
    public float getMaximumAngle() {
        return mMaximumAngle;
    }

    private static float toTangent(float arcInDegrees) {
        if (arcInDegrees < 0 || arcInDegrees > 90) {
            throw new IllegalArgumentException("Arc must be between 0 and 90 degrees");
        }
        return (float) Math.tan(Math.toRadians(arcInDegrees / 2));
    }

    @Override
    public Path getPath(float startX, float startY, float endX, float endY) {
        // Here's a little ascii art to show how this is calculated:
        // c---------- b
        //  \        / |
        //    \     d  |
        //      \  /   e
        //        a----f
        // This diagram assumes that the horizontal distance is less than the vertical
        // distance between The start point (a) and end point (b).
        // d is the midpoint between a and b. c is the center point of the circle with
        // This path is formed by assuming that start and end points are in
        // an arc on a circle. The end point is centered in the circle vertically
        // and start is a point on the circle.

        // Triangles bfa and bde form similar right triangles. The control points
        // for the cubic Bezier arc path are the midpoints between a and e and e and b.

        Path path = new Path();
        path.moveTo(startX, startY);

        float ex;
        float ey;
        if (startY == endY) {
            ex = (startX + endX) / 2;
            ey = startY + mMinimumHorizontalTangent * Math.abs(endX - startX) / 2;
        } else if (startX == endX) {
            ex = startX + mMinimumVerticalTangent * Math.abs(endY - startY) / 2;
            ey = (startY + endY) / 2;
        } else {
            float deltaX = endX - startX;
            float deltaY = startY - endY; // Y is inverted compared to diagram above.
            // hypotenuse squared.
            float h2 = deltaX * deltaX + deltaY * deltaY;

            // Midpoint between start and end
            float dx = (startX + endX) / 2;
            float dy = (startY + endY) / 2;

            // Distance squared between end point and mid point is (1/2 hypotenuse)^2
            float midDist2 = h2 * 0.25f;

            float minimumArcDist2 = 0;

            if (Math.abs(deltaX) < Math.abs(deltaY)) {
                // Similar triangles bfa and bde mean that (ab/fb = eb/bd)
                // Therefore, eb = ab * bd / fb
                // ab = hypotenuse
                // bd = hypotenuse/2
                // fb = deltaY
                float eDistY = h2 / (2 * deltaY);
                ey = endY + eDistY;
                ex = endX;

                minimumArcDist2 = midDist2 * mMinimumVerticalTangent
                        * mMinimumVerticalTangent;
            } else {
                // Same as above, but flip X & Y
                float eDistX = h2 / (2 * deltaX);
                ex = endX + eDistX;
                ey = endY;

                minimumArcDist2 = midDist2 * mMinimumHorizontalTangent
                        * mMinimumHorizontalTangent;
            }
            float arcDistX = dx - ex;
            float arcDistY = dy - ey;
            float arcDist2 = arcDistX * arcDistX + arcDistY * arcDistY;

            float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;

            float newArcDistance2 = 0;
            if (arcDist2 < minimumArcDist2) {
                newArcDistance2 = minimumArcDist2;
            } else if (arcDist2 > maximumArcDist2) {
                newArcDistance2 = maximumArcDist2;
            }
            if (newArcDistance2 != 0) {
                float ratio2 = newArcDistance2 / arcDist2;
                float ratio = FloatMath.sqrt(ratio2);
                ex = dx + (ratio * (ex - dx));
                ey = dy + (ratio * (ey - dy));
            }
        }
        float controlX1 = (startX + ex) / 2;
        float controlY1 = (startY + ey) / 2;
        float controlX2 = (ex + endX) / 2;
        float controlY2 = (ey + endY) / 2;
        path.cubicTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
        return path;
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.transition;

import android.content.Context;
import android.util.AttributeSet;

/**
 * Utility class for creating a default transition that automatically fades,
 * moves, and resizes views during a scene change.
@@ -33,6 +36,15 @@ public class AutoTransition extends TransitionSet {
     *
     */
    public AutoTransition() {
        init();
    }

    public AutoTransition(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setOrdering(ORDERING_SEQUENTIAL);
        addTransition(new Fade(Fade.OUT)).
                addTransition(new ChangeBounds()).
+73 −39

File changed.

Preview size limit exceeded, changes collapsed.

+8 −0
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package android.transition;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

@@ -37,6 +39,12 @@ public class ChangeClipBounds extends Transition {
            PROPNAME_CLIP,
    };

    public ChangeClipBounds() {}

    public ChangeClipBounds(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public String[] getTransitionProperties() {
        return sTransitionProperties;
Loading