1058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar/*
2058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar *
4058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * you may not use this file except in compliance with the License.
6058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * You may obtain a copy of the License at
7058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar *
8058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar *
10058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * See the License for the specific language governing permissions and
14058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar * limitations under the License.
15058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar */
16058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
1793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banespackage android.support.v7.graphics.drawable;
18058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
21058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.content.Context;
22058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.content.res.TypedArray;
23058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.Canvas;
24058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.ColorFilter;
25058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.Paint;
26058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.Path;
27058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.PixelFormat;
28058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.Rect;
29058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.graphics.drawable.Drawable;
30fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banesimport android.support.annotation.ColorInt;
3193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banesimport android.support.annotation.FloatRange;
3293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banesimport android.support.annotation.IntDef;
33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
34b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
35b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banesimport android.support.v4.view.ViewCompat;
36058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyarimport android.support.v7.appcompat.R;
37058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
3893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banesimport java.lang.annotation.Retention;
3993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banesimport java.lang.annotation.RetentionPolicy;
4093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
41058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar/**
4293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes * A drawable that can draw a "Drawer hamburger" menu or an arrow and animate between them.
4393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes * <p>
4493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes * The progress between the two states is controlled via {@link #setProgress(float)}.
4593011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes * </p>
46058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar */
4793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banespublic class DrawerArrowDrawable extends Drawable {
4893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
4993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
50b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * Direction to make the arrow point towards the left.
5193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     *
5293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * @see #setDirection(int)
5393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * @see #getDirection()
5493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
5593011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public static final int ARROW_DIRECTION_LEFT = 0;
5693011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
5793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
58b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * Direction to make the arrow point towards the right.
5993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     *
6093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * @see #setDirection(int)
6193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * @see #getDirection()
6293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
6393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public static final int ARROW_DIRECTION_RIGHT = 1;
6493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
65b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    /**
66b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * Direction to make the arrow point towards the start.
67b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     *
68b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * <p>When used in a view with a {@link ViewCompat#LAYOUT_DIRECTION_RTL RTL} layout direction,
69b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * this is the same as {@link #ARROW_DIRECTION_RIGHT}, otherwise it is the same as
70b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * {@link #ARROW_DIRECTION_LEFT}.</p>
71b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     *
72b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * @see #setDirection(int)
73b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * @see #getDirection()
74b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     */
75b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    public static final int ARROW_DIRECTION_START = 2;
76b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes
77b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    /**
78b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * Direction to make the arrow point to the end.
79b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     *
80b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * <p>When used in a view with a {@link ViewCompat#LAYOUT_DIRECTION_RTL RTL} layout direction,
81b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * this is the same as {@link #ARROW_DIRECTION_LEFT}, otherwise it is the same as
82b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * {@link #ARROW_DIRECTION_RIGHT}.</p>
83b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     *
84b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * @see #setDirection(int)
85b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     * @see #getDirection()
86b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes     */
87b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    public static final int ARROW_DIRECTION_END = 3;
88b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes
8993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /** @hide */
908e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
91b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    @IntDef({ARROW_DIRECTION_LEFT, ARROW_DIRECTION_RIGHT,
92b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            ARROW_DIRECTION_START, ARROW_DIRECTION_END})
9393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    @Retention(RetentionPolicy.SOURCE)
9493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public @interface ArrowDirection {}
95058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
96058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private final Paint mPaint = new Paint();
97058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
98c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas    // The angle in degrees that the arrow head is inclined at.
99058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
100058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The length of top and bottom bars when they merge into an arrow
101fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    private float mArrowHeadLength;
102058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The length of middle bar
103fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    private float mBarLength;
104058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The length of the middle bar when arrow is shaped
105fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    private float mArrowShaftLength;
106058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The space between bars when they are parallel
107fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    private float mBarGap;
108058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // Whether bars should spin or not during progress
109fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    private boolean mSpin;
110058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // Use Path instead of canvas operations so that if color has transparency, overlapping sections
111058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // wont look different
112058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private final Path mPath = new Path();
113058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The reported intrinsic size of the drawable.
114058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private final int mSize;
115058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // Whether we should mirror animation when animation is reversed.
116058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private boolean mVerticalMirror = false;
117058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    // The interpolated version of the original progress
118058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private float mProgress;
1199ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar    // the amount that overlaps w/ bar size when rotation is max
1209ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar    private float mMaxCutForBarSize;
12193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    // The arrow direction
122b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes    private int mDirection = ARROW_DIRECTION_START;
123058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
124058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    /**
125058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     * @param context used to get the configuration for the drawable from
126058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     */
12793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public DrawerArrowDrawable(Context context) {
128fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mPaint.setStyle(Paint.Style.STROKE);
129fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mPaint.setStrokeJoin(Paint.Join.MITER);
130fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mPaint.setStrokeCap(Paint.Cap.BUTT);
131058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPaint.setAntiAlias(true);
132fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
133fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final TypedArray a = context.getTheme().obtainStyledAttributes(null,
134fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes                R.styleable.DrawerArrowToggle, R.attr.drawerArrowStyle,
135fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes                R.style.Base_Widget_AppCompat_DrawerArrowToggle);
136fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
137fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        setColor(a.getColor(R.styleable.DrawerArrowToggle_color, 0));
138fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        setBarThickness(a.getDimension(R.styleable.DrawerArrowToggle_thickness, 0));
139fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        setSpinEnabled(a.getBoolean(R.styleable.DrawerArrowToggle_spinBars, true));
1409ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar        // round this because having this floating may cause bad measurements
141fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        setGapSize(Math.round(a.getDimension(R.styleable.DrawerArrowToggle_gapBetweenBars, 0)));
142fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
143fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mSize = a.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
1449ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar        // round this because having this floating may cause bad measurements
145fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mBarLength = Math.round(a.getDimension(R.styleable.DrawerArrowToggle_barLength, 0));
1469ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar        // round this because having this floating may cause bad measurements
147fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mArrowHeadLength = Math.round(a.getDimension(
148fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes                R.styleable.DrawerArrowToggle_arrowHeadLength, 0));
149fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mArrowShaftLength = a.getDimension(R.styleable.DrawerArrowToggle_arrowShaftLength, 0);
15093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        a.recycle();
151fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
152058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
153fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
154fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the length of the arrow head (from tip to edge, perpendicular to the shaft).
155fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
156fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param length the length in pixels
157fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
158fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setArrowHeadLength(float length) {
159fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (mArrowHeadLength != length) {
160fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mArrowHeadLength = length;
161fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
162fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
163fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
164fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
165fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
166fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns the length of the arrow head (from tip to edge, perpendicular to the shaft),
167fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * in pixels.
168fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
169fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public float getArrowHeadLength() {
170fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mArrowHeadLength;
171fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
172fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
173fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
174fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the arrow shaft length.
175fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
176fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param length the length in pixels
177fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
178fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setArrowShaftLength(float length) {
179fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (mArrowShaftLength != length) {
180fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mArrowShaftLength = length;
181fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
182fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
183fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
184fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
185fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
186fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns the arrow shaft length in pixels.
187fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
188fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public float getArrowShaftLength() {
189fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mArrowShaftLength;
190fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
191fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
192fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
193fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * The length of the bars when they are parallel to each other.
194fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
195fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public float getBarLength() {
196fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mBarLength;
197fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
198fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
199fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
200fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the length of the bars when they are parallel to each other.
201fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
202fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param length the length in pixels
203fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
204fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setBarLength(float length) {
205fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (mBarLength != length) {
206fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mBarLength = length;
207fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
208fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
209fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
210fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
211fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
212fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the color of the drawable.
213fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
214fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setColor(@ColorInt int color) {
215fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (color != mPaint.getColor()) {
216fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mPaint.setColor(color);
217fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
218fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
219fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
220fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
221fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
222fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns the color of the drawable.
223fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
224fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    @ColorInt
225fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public int getColor() {
226fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mPaint.getColor();
227fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
228fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
229fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
230fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the thickness (stroke size) for the bars.
231fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
232fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param width stroke width in pixels
233fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
234fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setBarThickness(float width) {
235fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (mPaint.getStrokeWidth() != width) {
236fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mPaint.setStrokeWidth(width);
237fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mMaxCutForBarSize = (float) (width / 2 * Math.cos(ARROW_HEAD_ANGLE));
238fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
239fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
240fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
241fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
242fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
243fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns the thickness (stroke width) of the bars.
244fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
245fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public float getBarThickness() {
246fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mPaint.getStrokeWidth();
247fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
248fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
249fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
250fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns the max gap between the bars when they are parallel to each other.
251fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
252fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @see #getGapSize()
253fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
254fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public float getGapSize() {
255fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mBarGap;
256fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
2579ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar
258fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
259fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Sets the max gap between the bars when they are parallel to each other.
260fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
261fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param gap the gap in pixels
262fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
263fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @see #getGapSize()
264fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
265fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setGapSize(float gap) {
266fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (gap != mBarGap) {
267fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mBarGap = gap;
268fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
269fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
270058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
271058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
27293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
27393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * Set the arrow direction.
27493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
27593011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public void setDirection(@ArrowDirection int direction) {
27693011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        if (direction != mDirection) {
27793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes            mDirection = direction;
27893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes            invalidateSelf();
27993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        }
28093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    }
28193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
28293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
283fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns whether the bars should rotate or not during the transition.
284fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
285fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @see #setSpinEnabled(boolean)
286fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
287fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public boolean isSpinEnabled() {
288fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        return mSpin;
289fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
290fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
291fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
292fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * Returns whether the bars should rotate or not during the transition.
293fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
294fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @param enabled true if the bars should rotate.
295fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     *
296fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     * @see #isSpinEnabled()
297fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes     */
298fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    public void setSpinEnabled(boolean enabled) {
299fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        if (mSpin != enabled) {
300fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            mSpin = enabled;
301fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes            invalidateSelf();
302fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        }
303fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    }
304fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
305fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes    /**
30693011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * Returns the arrow direction.
30793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
30893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    @ArrowDirection
30993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public int getDirection() {
31093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        return mDirection;
31193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    }
312058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
313058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    /**
314058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     * If set, canvas is flipped when progress reached to end and going back to start.
315058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     */
31693011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public void setVerticalMirror(boolean verticalMirror) {
31793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        if (mVerticalMirror != verticalMirror) {
31893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes            mVerticalMirror = verticalMirror;
31993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes            invalidateSelf();
32093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        }
321058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
322058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
323058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
324058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public void draw(Canvas canvas) {
325058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        Rect bounds = getBounds();
326b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes
327b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes        final boolean flipToPointRight;
328b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes        switch (mDirection) {
329b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            case ARROW_DIRECTION_LEFT:
330b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                flipToPointRight = false;
331b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                break;
332b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            case ARROW_DIRECTION_RIGHT:
333b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                flipToPointRight = true;
334b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                break;
335b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            case ARROW_DIRECTION_END:
336b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                flipToPointRight = DrawableCompat.getLayoutDirection(this)
337b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                        == ViewCompat.LAYOUT_DIRECTION_LTR;
338b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                break;
339b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            case ARROW_DIRECTION_START:
340b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes            default:
341b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                flipToPointRight = DrawableCompat.getLayoutDirection(this)
342b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                        == ViewCompat.LAYOUT_DIRECTION_RTL;
343b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes                break;
344b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes        }
345b01b01dddd64c945dd8dfa896648aa36dc47dde3Chris Banes
346058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // Interpolated widths of arrow bars
347fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
348fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        float arrowHeadBarLength = (float) Math.sqrt(mArrowHeadLength * mArrowHeadLength * 2);
349fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        arrowHeadBarLength = lerp(mBarLength, arrowHeadBarLength, mProgress);
350fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float arrowShaftLength = lerp(mBarLength, mArrowShaftLength, mProgress);
351058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // Interpolated size of middle bar
352fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float arrowShaftCut = Math.round(lerp(0, mMaxCutForBarSize, mProgress));
353058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // The rotation of the top and bottom bars (that make the arrow head)
354058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        final float rotation = lerp(0, ARROW_HEAD_ANGLE, mProgress);
355058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
356058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // The whole canvas rotates as the transition happens
35793011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        final float canvasRotate = lerp(flipToPointRight ? 0 : -180,
35893011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes                flipToPointRight ? 180 : 0, mProgress);
35993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes
360fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float arrowWidth = Math.round(arrowHeadBarLength * Math.cos(rotation));
361fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float arrowHeight = Math.round(arrowHeadBarLength * Math.sin(rotation));
3629ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar
363058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.rewind();
364fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float topBottomBarOffset = lerp(mBarGap + mPaint.getStrokeWidth(), -mMaxCutForBarSize,
3659ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar                mProgress);
366058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
367fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float arrowEdge = -arrowShaftLength / 2;
368058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // draw middle bar
369fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mPath.moveTo(arrowEdge + arrowShaftCut, 0);
370fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        mPath.rLineTo(arrowShaftLength - arrowShaftCut * 2, 0);
371058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
3729ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar        // bottom bar
373058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.moveTo(arrowEdge, topBottomBarOffset);
374058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.rLineTo(arrowWidth, arrowHeight);
375058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
3769ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar        // top bar
377058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.moveTo(arrowEdge, -topBottomBarOffset);
378058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.rLineTo(arrowWidth, -arrowHeight);
3799ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar
380058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPath.close();
381058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
382058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        canvas.save();
383fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
384058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // Rotate the whole canvas if spinning, if not, rotate it 180 to get
385058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        // the arrow pointing the other way for RTL.
386fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final float barThickness = mPaint.getStrokeWidth();
387fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        final int remainingSpace = (int) (bounds.height() - barThickness * 3 - mBarGap * 2);
388fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        float yOffset = (remainingSpace / 4) * 2; // making sure it is a multiple of 2.
389ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas        yOffset += barThickness * 1.5f + mBarGap;
390fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes
391fab671ee55f26d6f3c9381a3e6d444d23dd970d2Chris Banes        canvas.translate(bounds.centerX(), yOffset);
392058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        if (mSpin) {
39393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes            canvas.rotate(canvasRotate * ((mVerticalMirror ^ flipToPointRight) ? -1 : 1));
39493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes        } else if (flipToPointRight) {
3959ce2474543a32abfba8a0bdf65133416c5ba7a34Yigit Boyar            canvas.rotate(180);
396058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        }
397058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        canvas.drawPath(mPath, mPaint);
398058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
399058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        canvas.restore();
400058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
401058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
402058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
40393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public void setAlpha(int alpha) {
404486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes        if (alpha != mPaint.getAlpha()) {
405486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes            mPaint.setAlpha(alpha);
406486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes            invalidateSelf();
407486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes        }
408058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
409058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
410058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
411058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public void setColorFilter(ColorFilter colorFilter) {
412058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        mPaint.setColorFilter(colorFilter);
413486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes        invalidateSelf();
414058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
415058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
416058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
417058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public int getIntrinsicHeight() {
418058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        return mSize;
419058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
420058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
421058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
422058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public int getIntrinsicWidth() {
423058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        return mSize;
424058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
425058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
426058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    @Override
427058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public int getOpacity() {
428058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        return PixelFormat.TRANSLUCENT;
429058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
430058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
43193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
43293011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * Returns the current progress of the arrow.
43393011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
43493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    @FloatRange(from = 0.0, to = 1.0)
435058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    public float getProgress() {
436058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        return mProgress;
437058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
438058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
43993011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    /**
44093011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * Set the progress of the arrow.
44193011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     *
44244ece5ceaaee232f9139ccbde40ac4a5e642bdeeChristophe Beyls     * <p>A value of {@code 0.0} indicates that the arrow should be drawn in its starting
44344ece5ceaaee232f9139ccbde40ac4a5e642bdeeChristophe Beyls     * position. A value of {@code 1.0} indicates that the arrow should be drawn in its ending
44493011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     * position.</p>
44593011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes     */
44693011d6b0ca0622a6b098d3c3670ac7cc7bf9632Chris Banes    public void setProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
447486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes        if (mProgress != progress) {
448486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes            mProgress = progress;
449486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes            invalidateSelf();
450486ecff84a98c7b397e4a19c85a87f565d09f197Chris Banes        }
451058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
452058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar
453058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    /**
454403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes     * Returns the paint instance used for all drawing.
455403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes     */
456403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes    public final Paint getPaint() {
457403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes        return mPaint;
458403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes    }
459403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes
460403b20a282fa3d71b799bde1b4699eb8aade0c83Chris Banes    /**
461058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     * Linear interpolate between a and b with parameter t.
462058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar     */
463058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    private static float lerp(float a, float b, float t) {
464058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar        return a + (b - a) * t;
465058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar    }
466058467cbd6dd0acdfaec61e65ab9e352868ec5c9Yigit Boyar}