blob: c07a010daa0cbef4aca860686ea6f0b3aefdacca [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.wallpaper.widget;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.graphics.Canvas;
22import android.graphics.Color;
23import android.graphics.ColorFilter;
24import android.graphics.Paint;
25import android.graphics.Paint.Style;
26import android.graphics.Path;
27import android.graphics.PixelFormat;
28import android.graphics.Rect;
29import android.graphics.RectF;
30import android.graphics.drawable.Animatable;
31import android.graphics.drawable.Drawable;
32import android.support.annotation.IntDef;
33import android.support.annotation.NonNull;
34import android.support.v4.view.animation.FastOutSlowInInterpolator;
35import android.util.DisplayMetrics;
36import android.view.View;
37import android.view.animation.Animation;
38import android.view.animation.Interpolator;
39import android.view.animation.LinearInterpolator;
40import android.view.animation.Transformation;
41
42import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import java.util.ArrayList;
45
46/**
47 * Fancy progress indicator for Material theme.
48 * <p>
49 * Copied from //frameworks/support/v4/java/android/support/v4/widget.
50 */
51public class MaterialProgressDrawable extends Drawable implements Animatable {
52 // Maps to ProgressBar.Large style
53 public static final int LARGE = 0;
54 // Maps to ProgressBar default style
55 public static final int DEFAULT = 1;
56 private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
57 private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
58 private static final float FULL_ROTATION = 1080.0f;
59 // Maps to ProgressBar default style
60 private static final int CIRCLE_DIAMETER = 40;
61 private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
62 private static final float STROKE_WIDTH = 2.5f;
63 // Maps to ProgressBar.Large style
64 private static final int CIRCLE_DIAMETER_LARGE = 56;
65 private static final float CENTER_RADIUS_LARGE = 12.5f;
66 private static final float STROKE_WIDTH_LARGE = 3f;
67 /**
68 * The value in the linear interpolator for animating the drawable at which
69 * the color transition should start
70 */
71 private static final float COLOR_START_DELAY_OFFSET = 0.75f;
72 private static final float END_TRIM_START_DELAY_OFFSET = 0.5f;
73 private static final float START_TRIM_DURATION_OFFSET = 0.5f;
74 /**
75 * The duration of a single progress spin in milliseconds.
76 */
77 private static final int ANIMATION_DURATION = 1332;
78 /**
79 * The number of points in the progress "star".
80 */
81 private static final float NUM_POINTS = 5f;
82 /**
83 * Layout info for the arrowhead in dp
84 */
85 private static final int ARROW_WIDTH = 10;
86 private static final int ARROW_HEIGHT = 5;
87 private static final float ARROW_OFFSET_ANGLE = 5;
88 /**
89 * Layout info for the arrowhead for the large spinner in dp
90 */
91 private static final int ARROW_WIDTH_LARGE = 12;
92 private static final int ARROW_HEIGHT_LARGE = 6;
93 private static final float MAX_PROGRESS_ARC = .8f;
94 private final int[] colors = new int[]{
95 Color.BLACK
96 };
97 /**
98 * The list of animators operating on this drawable.
99 */
100 private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();
101 /**
102 * The indicator ring, used to manage animation state.
103 */
104 private final Ring mRing;
105 private final Callback mCallback = new Callback() {
106 @Override
107 public void invalidateDrawable(Drawable d) {
108 invalidateSelf();
109 }
110
111 @Override
112 public void scheduleDrawable(Drawable d, Runnable what, long when) {
113 scheduleSelf(what, when);
114 }
115
116 @Override
117 public void unscheduleDrawable(Drawable d, Runnable what) {
118 unscheduleSelf(what);
119 }
120 };
121 boolean mFinishing;
122 /**
123 * Canvas rotation in degrees.
124 */
125 private float mRotation;
126 private Resources mResources;
127 private View mParent;
128 private Animation mAnimation;
129 private float mRotationCount;
130 private double mWidth;
131 private double mHeight;
132
133 public MaterialProgressDrawable(Context context, View parent) {
134 mParent = parent;
135 mResources = context.getResources();
136
137 mRing = new Ring(mCallback);
138 mRing.setColors(colors);
139
140 updateSizes(DEFAULT);
141 setupAnimators();
142 }
143
144 private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
145 double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
146 final Ring ring = mRing;
147 final DisplayMetrics metrics = mResources.getDisplayMetrics();
148 final float screenDensity = metrics.density;
149
150 mWidth = progressCircleWidth * screenDensity;
151 mHeight = progressCircleHeight * screenDensity;
152 ring.setStrokeWidth((float) strokeWidth * screenDensity);
153 ring.setCenterRadius(centerRadius * screenDensity);
154 ring.setColorIndex(0);
155 ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
156 ring.setInsets((int) mWidth, (int) mHeight);
157 }
158
159 /**
160 * Set the overall size for the progress spinner. This updates the radius
161 * and stroke width of the ring.
162 *
163 * @param size One of {@link MaterialProgressDrawable.LARGE} or
164 * {@link MaterialProgressDrawable.DEFAULT}
165 */
166 public void updateSizes(@ProgressDrawableSize int size) {
167 if (size == LARGE) {
168 setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
169 STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
170 } else {
171 setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
172 ARROW_WIDTH, ARROW_HEIGHT);
173 }
174 }
175
176 /**
177 * @param show Set to true to display the arrowhead on the progress spinner.
178 */
179 public void showArrow(boolean show) {
180 mRing.setShowArrow(show);
181 }
182
183 /**
184 * @param scale Set the scale of the arrowhead for the spinner.
185 */
186 public void setArrowScale(float scale) {
187 mRing.setArrowScale(scale);
188 }
189
190 /**
191 * Set the start and end trim for the progress spinner arc.
192 *
193 * @param startAngle start angle
194 * @param endAngle end angle
195 */
196 public void setStartEndTrim(float startAngle, float endAngle) {
197 mRing.setStartTrim(startAngle);
198 mRing.setEndTrim(endAngle);
199 }
200
201 /**
202 * Set the amount of rotation to apply to the progress spinner.
203 *
204 * @param rotation Rotation is from [0..1]
205 */
206 public void setProgressRotation(float rotation) {
207 mRing.setRotation(rotation);
208 }
209
210 /**
211 * Update the background color of the circle image view.
212 */
213 public void setBackgroundColor(int color) {
214 mRing.setBackgroundColor(color);
215 }
216
217 /**
218 * Set the colors used in the progress animation from color resources.
219 * The first color will also be the color of the bar that grows in response
220 * to a user swipe gesture.
221 *
222 * @param colors
223 */
224 public void setColorSchemeColors(int... colors) {
225 mRing.setColors(colors);
226 mRing.setColorIndex(0);
227 }
228
229 @Override
230 public int getIntrinsicHeight() {
231 return (int) mHeight;
232 }
233
234 @Override
235 public int getIntrinsicWidth() {
236 return (int) mWidth;
237 }
238
239 @Override
240 public void draw(Canvas c) {
241 final Rect bounds = getBounds();
242 final int saveCount = c.save();
243 c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
244 mRing.draw(c, bounds);
245 c.restoreToCount(saveCount);
246 }
247
248 public int getAlpha() {
249 return mRing.getAlpha();
250 }
251
252 @Override
253 public void setAlpha(int alpha) {
254 mRing.setAlpha(alpha);
255 }
256
257 @Override
258 public void setColorFilter(ColorFilter colorFilter) {
259 mRing.setColorFilter(colorFilter);
260 }
261
262 @SuppressWarnings("unused")
263 private float getRotation() {
264 return mRotation;
265 }
266
267 @SuppressWarnings("unused")
268 void setRotation(float rotation) {
269 mRotation = rotation;
270 invalidateSelf();
271 }
272
273 @Override
274 public int getOpacity() {
275 return PixelFormat.TRANSLUCENT;
276 }
277
278 @Override
279 public boolean isRunning() {
280 final ArrayList<Animation> animators = mAnimators;
281 final int n = animators.size();
282 for (int i = 0; i < n; i++) {
283 final Animation animator = animators.get(i);
284 if (animator.hasStarted() && !animator.hasEnded()) {
285 return true;
286 }
287 }
288 return false;
289 }
290
291 @Override
292 public void start() {
293 mAnimation.reset();
294 mRing.storeOriginals();
295 // Already showing some part of the ring
296 if (mRing.getEndTrim() != mRing.getStartTrim()) {
297 mFinishing = true;
298 mAnimation.setDuration(ANIMATION_DURATION / 2);
299 mParent.startAnimation(mAnimation);
300 } else {
301 mRing.setColorIndex(0);
302 mRing.resetOriginals();
303 mAnimation.setDuration(ANIMATION_DURATION);
304 mParent.startAnimation(mAnimation);
305 }
306 }
307
308 @Override
309 public void stop() {
310 mParent.clearAnimation();
311 setRotation(0);
312 mRing.setShowArrow(false);
313 mRing.setColorIndex(0);
314 mRing.resetOriginals();
315 }
316
317 private float getMinProgressArc(Ring ring) {
318 return (float) Math.toRadians(
319 ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));
320 }
321
322 // Adapted from ArgbEvaluator.java
323 private int evaluateColorChange(float fraction, int startValue, int endValue) {
324 int startInt = (Integer) startValue;
325 int startA = (startInt >> 24) & 0xff;
326 int startR = (startInt >> 16) & 0xff;
327 int startG = (startInt >> 8) & 0xff;
328 int startB = startInt & 0xff;
329
330 int endInt = (Integer) endValue;
331 int endA = (endInt >> 24) & 0xff;
332 int endR = (endInt >> 16) & 0xff;
333 int endG = (endInt >> 8) & 0xff;
334 int endB = endInt & 0xff;
335
336 return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
337 | (int) ((startR + (int) (fraction * (endR - startR))) << 16)
338 | (int) ((startG + (int) (fraction * (endG - startG))) << 8)
339 | (int) ((startB + (int) (fraction * (endB - startB))));
340 }
341
342 /**
343 * Update the ring color if this is within the last 25% of the animation.
344 * The new ring color will be a translation from the starting ring color to
345 * the next color.
346 */
347 private void updateRingColor(float interpolatedTime, Ring ring) {
348 if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
349 // scale the interpolatedTime so that the full
350 // transformation from 0 - 1 takes place in the
351 // remaining time
352 ring.setColor(evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
353 / (1.0f - COLOR_START_DELAY_OFFSET), ring.getStartingColor(),
354 ring.getNextColor()));
355 }
356 }
357
358 private void applyFinishTranslation(float interpolatedTime, Ring ring) {
359 // shrink back down and complete a full rotation before
360 // starting other circles
361 // Rotation goes between [0..1].
362 updateRingColor(interpolatedTime, ring);
363 float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
364 + 1f);
365 final float minProgressArc = getMinProgressArc(ring);
366 final float startTrim = ring.getStartingStartTrim()
367 + (ring.getStartingEndTrim() - minProgressArc - ring.getStartingStartTrim())
368 * interpolatedTime;
369 ring.setStartTrim(startTrim);
370 ring.setEndTrim(ring.getStartingEndTrim());
371 final float rotation = ring.getStartingRotation()
372 + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
373 ring.setRotation(rotation);
374 }
375
376 private void setupAnimators() {
377 final Ring ring = mRing;
378 final Animation animation = new Animation() {
379 @Override
380 public void applyTransformation(float interpolatedTime, Transformation t) {
381 if (mFinishing) {
382 applyFinishTranslation(interpolatedTime, ring);
383 } else {
384 // The minProgressArc is calculated from 0 to create an
385 // angle that matches the stroke width.
386 final float minProgressArc = getMinProgressArc(ring);
387 final float startingEndTrim = ring.getStartingEndTrim();
388 final float startingTrim = ring.getStartingStartTrim();
389 final float startingRotation = ring.getStartingRotation();
390
391 updateRingColor(interpolatedTime, ring);
392
393 // Moving the start trim only occurs in the first 50% of a
394 // single ring animation
395 if (interpolatedTime <= START_TRIM_DURATION_OFFSET) {
396 // scale the interpolatedTime so that the full
397 // transformation from 0 - 1 takes place in the
398 // remaining time
399 final float scaledTime = (interpolatedTime)
400 / (1.0f - START_TRIM_DURATION_OFFSET);
401 final float startTrim = startingTrim
402 + ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR
403 .getInterpolation(scaledTime));
404 ring.setStartTrim(startTrim);
405 }
406
407 // Moving the end trim starts after 50% of a single ring
408 // animation completes
409 if (interpolatedTime > END_TRIM_START_DELAY_OFFSET) {
410 // scale the interpolatedTime so that the full
411 // transformation from 0 - 1 takes place in the
412 // remaining time
413 final float minArc = MAX_PROGRESS_ARC - minProgressArc;
414 float scaledTime = (interpolatedTime - START_TRIM_DURATION_OFFSET)
415 / (1.0f - START_TRIM_DURATION_OFFSET);
416 final float endTrim = startingEndTrim
417 + (minArc * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));
418 ring.setEndTrim(endTrim);
419 }
420
421 final float rotation = startingRotation + (0.25f * interpolatedTime);
422 ring.setRotation(rotation);
423
424 float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)
425 + (FULL_ROTATION * (mRotationCount / NUM_POINTS));
426 setRotation(groupRotation);
427 }
428 }
429 };
430 animation.setRepeatCount(Animation.INFINITE);
431 animation.setRepeatMode(Animation.RESTART);
432 animation.setInterpolator(LINEAR_INTERPOLATOR);
433 animation.setAnimationListener(new Animation.AnimationListener() {
434
435 @Override
436 public void onAnimationStart(Animation animation) {
437 mRotationCount = 0;
438 }
439
440 @Override
441 public void onAnimationEnd(Animation animation) {
442 // do nothing
443 }
444
445 @Override
446 public void onAnimationRepeat(Animation animation) {
447 ring.storeOriginals();
448 ring.goToNextColor();
449 ring.setStartTrim(ring.getEndTrim());
450 if (mFinishing) {
451 // finished closing the last ring from the swipe gesture; go
452 // into progress mode
453 mFinishing = false;
454 animation.setDuration(ANIMATION_DURATION);
455 ring.setShowArrow(false);
456 } else {
457 mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
458 }
459 }
460 });
461 mAnimation = animation;
462 }
463
464 /**
465 * Progress drawable size.
466 */
467 @Retention(RetentionPolicy.CLASS)
468 @IntDef({LARGE, DEFAULT})
469 public @interface ProgressDrawableSize {
470 }
471
472 private static class Ring {
473 private final RectF mTempBounds = new RectF();
474 private final Paint mPaint = new Paint();
475 private final Paint mArrowPaint = new Paint();
476
477 private final Callback mCallback;
478 private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
479 private float mStartTrim = 0.0f;
480 private float mEndTrim = 0.0f;
481 private float mRotation = 0.0f;
482 private float mStrokeWidth = 5.0f;
483 private float mStrokeInset = 2.5f;
484 private int[] mColors;
485 // mColorIndex represents the offset into the available mColors that the
486 // progress circle should currently display. As the progress circle is
487 // animating, the mColorIndex moves by one to the next available color.
488 private int mColorIndex;
489 private float mStartingStartTrim;
490 private float mStartingEndTrim;
491 private float mStartingRotation;
492 private boolean mShowArrow;
493 private Path mArrow;
494 private float mArrowScale;
495 private double mRingCenterRadius;
496 private int mArrowWidth;
497 private int mArrowHeight;
498 private int mAlpha;
499 private int mBackgroundColor;
500 private int mCurrentColor;
501
502 public Ring(Callback callback) {
503 mCallback = callback;
504
505 mPaint.setStrokeCap(Paint.Cap.SQUARE);
506 mPaint.setAntiAlias(true);
507 mPaint.setStyle(Style.STROKE);
508
509 mArrowPaint.setStyle(Paint.Style.FILL);
510 mArrowPaint.setAntiAlias(true);
511 }
512
513 public void setBackgroundColor(int color) {
514 mBackgroundColor = color;
515 }
516
517 /**
518 * Set the dimensions of the arrowhead.
519 *
520 * @param width Width of the hypotenuse of the arrow head
521 * @param height Height of the arrow point
522 */
523 public void setArrowDimensions(float width, float height) {
524 mArrowWidth = (int) width;
525 mArrowHeight = (int) height;
526 }
527
528 /**
529 * Draw the progress spinner
530 */
531 public void draw(Canvas c, Rect bounds) {
532 final RectF arcBounds = mTempBounds;
533 arcBounds.set(bounds);
534 arcBounds.inset(mStrokeInset, mStrokeInset);
535
536 final float startAngle = (mStartTrim + mRotation) * 360;
537 final float endAngle = (mEndTrim + mRotation) * 360;
538 float sweepAngle = endAngle - startAngle;
539
540 mPaint.setColor(mCurrentColor);
541 c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
542
543 drawTriangle(c, startAngle, sweepAngle, bounds);
544
545 if (mAlpha < 255) {
546 mCirclePaint.setColor(mBackgroundColor);
547 mCirclePaint.setAlpha(255 - mAlpha);
548 c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
549 mCirclePaint);
550 }
551 }
552
553 private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
554 if (mShowArrow) {
555 if (mArrow == null) {
556 mArrow = new android.graphics.Path();
557 mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
558 } else {
559 mArrow.reset();
560 }
561
562 // Adjust the position of the triangle so that it is inset as
563 // much as the arc, but also centered on the arc.
564 float inset = (int) mStrokeInset / 2 * mArrowScale;
565 float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
566 float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
567
568 // Update the path each time. This works around an issue in SKIA
569 // where concatenating a rotation matrix to a scale matrix
570 // ignored a starting negative rotation. This appears to have
571 // been fixed as of API 21.
572 mArrow.moveTo(0, 0);
573 mArrow.lineTo(mArrowWidth * mArrowScale, 0);
574 mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
575 * mArrowScale));
576 mArrow.offset(x - inset, y);
577 mArrow.close();
578 // draw a triangle
579 mArrowPaint.setColor(mCurrentColor);
580 c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
581 bounds.exactCenterY());
582 c.drawPath(mArrow, mArrowPaint);
583 }
584 }
585
586 /**
587 * Set the colors the progress spinner alternates between.
588 *
589 * @param colors Array of integers describing the colors. Must be non-<code>null</code>.
590 */
591 public void setColors(@NonNull int[] colors) {
592 mColors = colors;
593 // if colors are reset, make sure to reset the color index as well
594 setColorIndex(0);
595 }
596
597 /**
598 * Set the absolute color of the progress spinner. This is should only
599 * be used when animating between current and next color when the
600 * spinner is rotating.
601 *
602 * @param color int describing the color.
603 */
604 public void setColor(int color) {
605 mCurrentColor = color;
606 }
607
608 /**
609 * @param index Index into the color array of the color to display in
610 * the progress spinner.
611 */
612 public void setColorIndex(int index) {
613 mColorIndex = index;
614 mCurrentColor = mColors[mColorIndex];
615 }
616
617 /**
618 * @return int describing the next color the progress spinner should use when drawing.
619 */
620 public int getNextColor() {
621 return mColors[getNextColorIndex()];
622 }
623
624 private int getNextColorIndex() {
625 return (mColorIndex + 1) % (mColors.length);
626 }
627
628 /**
629 * Proceed to the next available ring color. This will automatically
630 * wrap back to the beginning of colors.
631 */
632 public void goToNextColor() {
633 setColorIndex(getNextColorIndex());
634 }
635
636 public void setColorFilter(ColorFilter filter) {
637 mPaint.setColorFilter(filter);
638 invalidateSelf();
639 }
640
641 /**
642 * @return Current alpha of the progress spinner and arrowhead.
643 */
644 public int getAlpha() {
645 return mAlpha;
646 }
647
648 /**
649 * @param alpha Set the alpha of the progress spinner and associated arrowhead.
650 */
651 public void setAlpha(int alpha) {
652 mAlpha = alpha;
653 }
654
655 @SuppressWarnings("unused")
656 public float getStrokeWidth() {
657 return mStrokeWidth;
658 }
659
660 /**
661 * @param strokeWidth Set the stroke width of the progress spinner in pixels.
662 */
663 public void setStrokeWidth(float strokeWidth) {
664 mStrokeWidth = strokeWidth;
665 mPaint.setStrokeWidth(strokeWidth);
666 invalidateSelf();
667 }
668
669 @SuppressWarnings("unused")
670 public float getStartTrim() {
671 return mStartTrim;
672 }
673
674 @SuppressWarnings("unused")
675 public void setStartTrim(float startTrim) {
676 mStartTrim = startTrim;
677 invalidateSelf();
678 }
679
680 public float getStartingStartTrim() {
681 return mStartingStartTrim;
682 }
683
684 public float getStartingEndTrim() {
685 return mStartingEndTrim;
686 }
687
688 public int getStartingColor() {
689 return mColors[mColorIndex];
690 }
691
692 @SuppressWarnings("unused")
693 public float getEndTrim() {
694 return mEndTrim;
695 }
696
697 @SuppressWarnings("unused")
698 public void setEndTrim(float endTrim) {
699 mEndTrim = endTrim;
700 invalidateSelf();
701 }
702
703 @SuppressWarnings("unused")
704 public float getRotation() {
705 return mRotation;
706 }
707
708 @SuppressWarnings("unused")
709 public void setRotation(float rotation) {
710 mRotation = rotation;
711 invalidateSelf();
712 }
713
714 public void setInsets(int width, int height) {
715 final float minEdge = (float) Math.min(width, height);
716 float insets;
717 if (mRingCenterRadius <= 0 || minEdge < 0) {
718 insets = (float) Math.ceil(mStrokeWidth / 2.0f);
719 } else {
720 insets = (float) (minEdge / 2.0f - mRingCenterRadius);
721 }
722 mStrokeInset = insets;
723 }
724
725 @SuppressWarnings("unused")
726 public float getInsets() {
727 return mStrokeInset;
728 }
729
730 public double getCenterRadius() {
731 return mRingCenterRadius;
732 }
733
734 /**
735 * @param centerRadius Inner radius in px of the circle the progress
736 * spinner arc traces.
737 */
738 public void setCenterRadius(double centerRadius) {
739 mRingCenterRadius = centerRadius;
740 }
741
742 /**
743 * @param show Set to true to show the arrow head on the progress spinner.
744 */
745 public void setShowArrow(boolean show) {
746 if (mShowArrow != show) {
747 mShowArrow = show;
748 invalidateSelf();
749 }
750 }
751
752 /**
753 * @param scale Set the scale of the arrowhead for the spinner.
754 */
755 public void setArrowScale(float scale) {
756 if (scale != mArrowScale) {
757 mArrowScale = scale;
758 invalidateSelf();
759 }
760 }
761
762 /**
763 * @return The amount the progress spinner is currently rotated, between [0..1].
764 */
765 public float getStartingRotation() {
766 return mStartingRotation;
767 }
768
769 /**
770 * If the start / end trim are offset to begin with, store them so that
771 * animation starts from that offset.
772 */
773 public void storeOriginals() {
774 mStartingStartTrim = mStartTrim;
775 mStartingEndTrim = mEndTrim;
776 mStartingRotation = mRotation;
777 }
778
779 /**
780 * Reset the progress spinner to default rotation, start and end angles.
781 */
782 public void resetOriginals() {
783 mStartingStartTrim = 0;
784 mStartingEndTrim = 0;
785 mStartingRotation = 0;
786 setStartTrim(0);
787 setEndTrim(0);
788 setRotation(0);
789 }
790
791 private void invalidateSelf() {
792 mCallback.invalidateDrawable(null);
793 }
794 }
795}