• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.deskclock;
2 
3 import android.content.Context;
4 import android.content.SharedPreferences;
5 import android.content.res.Configuration;
6 import android.content.res.Resources;
7 import android.graphics.Canvas;
8 import android.graphics.Paint;
9 import android.graphics.RectF;
10 import android.util.AttributeSet;
11 import android.view.View;
12 
13 import com.android.deskclock.stopwatch.Stopwatches;
14 
15 /**
16  * TODO: Insert description here. (generated by isaackatz)
17  */
18 public class CircleTimerView extends View {
19 
20 
21     private int mRedColor;
22     private int mWhiteColor;
23     private long mIntervalTime = 0;
24     private long mIntervalStartTime = -1;
25     private long mMarkerTime = -1;
26     private long mCurrentIntervalTime = 0;
27     private long mAccumulatedTime = 0;
28     private boolean mPaused = false;
29     private boolean mAnimate = false;
30     private static float mCircleXCenterLeftPadding = 0;
31     private static float mStrokeSize = 4;
32     private static float mDiamondStrokeSize = 12;
33     private static float mMarkerStrokeSize = 2;
34     private final Paint mPaint = new Paint();
35     private final Paint mFill = new Paint();
36     private final RectF mArcRect = new RectF();
37     private float mRectHalfWidth = 6f;
38     private Resources mResources;
39     private float mRadiusOffset;   // amount to remove from radius to account for markers on circle
40     private float mScreenDensity;
41 
42     // Class has 2 modes:
43     // Timer mode - counting down. in this mode the animation is counter-clockwise and stops at 0
44     // Stop watch mode - counting up - in this mode the animation is clockwise and will keep the
45     //                   animation until stopped.
46     private boolean mTimerMode = false; // default is stop watch view
47 
CircleTimerView(Context context)48     public CircleTimerView(Context context) {
49         this(context, null);
50     }
51 
CircleTimerView(Context context, AttributeSet attrs)52     public CircleTimerView(Context context, AttributeSet attrs) {
53         super(context, attrs);
54         init(context);
55     }
56 
setIntervalTime(long t)57     public void setIntervalTime(long t) {
58         mIntervalTime = t;
59         postInvalidate();
60     }
61 
setMarkerTime(long t)62     public void setMarkerTime(long t) {
63         mMarkerTime = t;
64         postInvalidate();
65     }
66 
reset()67     public void reset() {
68         mIntervalStartTime = -1;
69         mMarkerTime = -1;
70         postInvalidate();
71     }
startIntervalAnimation()72     public void startIntervalAnimation() {
73         mIntervalStartTime = Utils.getTimeNow();
74         mAnimate = true;
75         invalidate();
76         mPaused = false;
77     }
stopIntervalAnimation()78     public void stopIntervalAnimation() {
79         mAnimate = false;
80         mIntervalStartTime = -1;
81         mAccumulatedTime = 0;
82     }
83 
isAnimating()84     public boolean isAnimating() {
85         return (mIntervalStartTime != -1);
86     }
87 
pauseIntervalAnimation()88     public void pauseIntervalAnimation() {
89         mAnimate = false;
90         mAccumulatedTime += Utils.getTimeNow() - mIntervalStartTime;
91         mPaused = true;
92     }
93 
abortIntervalAnimation()94     public void abortIntervalAnimation() {
95         mAnimate = false;
96     }
97 
setPassedTime(long time, boolean drawRed)98     public void setPassedTime(long time, boolean drawRed) {
99         // The onDraw() method checks if mIntervalStartTime has been set before drawing any red.
100         // Without drawRed, mIntervalStartTime should not be set here at all, and would remain at -1
101         // when the state is reconfigured after exiting and re-entering the application.
102         // If the timer is currently running, this drawRed will not be set, and will have no effect
103         // because mIntervalStartTime will be set when the thread next runs.
104         // When the timer is not running, mIntervalStartTime will not be set upon loading the state,
105         // and no red will be drawn, so drawRed is used to force onDraw() to draw the red portion,
106         // despite the timer not running.
107         mCurrentIntervalTime = mAccumulatedTime = time;
108         if (drawRed) {
109             mIntervalStartTime = Utils.getTimeNow();
110         }
111         postInvalidate();
112     }
113 
114 
115 
init(Context c)116     private void init(Context c) {
117 
118         mResources = c.getResources();
119         mCircleXCenterLeftPadding = (mResources.getDimension(R.dimen.timer_circle_width)
120                 - mResources.getDimension(R.dimen.timer_circle_diameter)) / 2;
121         mStrokeSize = mResources.getDimension(R.dimen.circletimer_circle_size);
122         mDiamondStrokeSize = mResources.getDimension(R.dimen.circletimer_diamond_size);
123         mMarkerStrokeSize = mResources.getDimension(R.dimen.circletimer_marker_size);
124         mRadiusOffset = Utils.calculateRadiusOffset(
125                 mStrokeSize, mDiamondStrokeSize, mMarkerStrokeSize);
126         mPaint.setAntiAlias(true);
127         mPaint.setStyle(Paint.Style.STROKE);
128         mWhiteColor = mResources.getColor(R.color.clock_white);
129         mRedColor = mResources.getColor(R.color.clock_red);
130         mScreenDensity = mResources.getDisplayMetrics().density;
131         mFill.setAntiAlias(true);
132         mFill.setStyle(Paint.Style.FILL);
133         mFill.setColor(mRedColor);
134         mRectHalfWidth = mDiamondStrokeSize / 2f;
135     }
136 
setTimerMode(boolean mode)137     public void setTimerMode(boolean mode) {
138         mTimerMode = mode;
139     }
140 
141     @Override
onDraw(Canvas canvas)142     public void onDraw(Canvas canvas) {
143         int xCenter = getWidth() / 2 + 1;
144         int yCenter = getHeight() / 2;
145 
146         mPaint.setStrokeWidth(mStrokeSize);
147         float radius = Math.min(xCenter, yCenter) - mRadiusOffset;
148 
149         if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
150             xCenter = (int) (radius + mRadiusOffset);
151             if (mTimerMode) {
152                 xCenter += mCircleXCenterLeftPadding;
153             }
154         }
155 
156         if (mIntervalStartTime == -1) {
157             // just draw a complete white circle, no red arc needed
158             mPaint.setColor(mWhiteColor);
159             canvas.drawCircle (xCenter, yCenter, radius, mPaint);
160             if (mTimerMode) {
161                 drawRedDiamond(canvas, 0f, xCenter, yCenter, radius);
162             }
163         } else {
164             if (mAnimate) {
165                 mCurrentIntervalTime = Utils.getTimeNow() - mIntervalStartTime + mAccumulatedTime;
166             }
167             //draw a combination of red and white arcs to create a circle
168             mArcRect.top = yCenter - radius;
169             mArcRect.bottom = yCenter + radius;
170             mArcRect.left =  xCenter - radius;
171             mArcRect.right = xCenter + radius;
172             float redPercent = (float)mCurrentIntervalTime / (float)mIntervalTime;
173             // prevent timer from doing more than one full circle
174             redPercent = (redPercent > 1 && mTimerMode) ? 1 : redPercent;
175 
176             float whitePercent = 1 - (redPercent > 1 ? 1 : redPercent);
177             // draw red arc here
178             mPaint.setColor(mRedColor);
179             if (mTimerMode){
180                 canvas.drawArc (mArcRect, 270, - redPercent * 360 , false, mPaint);
181             } else {
182                 canvas.drawArc (mArcRect, 270, + redPercent * 360 , false, mPaint);
183             }
184 
185             // draw white arc here
186             mPaint.setStrokeWidth(mStrokeSize);
187             mPaint.setColor(mWhiteColor);
188             if (mTimerMode) {
189                 canvas.drawArc(mArcRect, 270, + whitePercent * 360, false, mPaint);
190             } else {
191                 canvas.drawArc(mArcRect, 270 + (1 - whitePercent) * 360,
192                         whitePercent * 360, false, mPaint);
193             }
194 
195             if (mMarkerTime != -1 && radius > 0 && mIntervalTime != 0) {
196                 mPaint.setStrokeWidth(mMarkerStrokeSize);
197                 float angle = (float)(mMarkerTime % mIntervalTime) / (float)mIntervalTime * 360;
198                 // draw 2dips thick marker
199                 // the formula to draw the marker 1 unit thick is:
200                 // 180 / (radius * Math.PI)
201                 // after that we have to scale it by the screen density
202                 canvas.drawArc (mArcRect, 270 + angle, mScreenDensity *
203                         (float) (360 / (radius * Math.PI)) , false, mPaint);
204             }
205             drawRedDiamond(canvas, redPercent, xCenter, yCenter, radius);
206         }
207         if (mAnimate) {
208             invalidate();
209         }
210    }
211 
drawRedDiamond( Canvas canvas, float degrees, int xCenter, int yCenter, float radius)212     protected void drawRedDiamond(
213             Canvas canvas, float degrees, int xCenter, int yCenter, float radius) {
214         mPaint.setColor(mRedColor);
215         float diamondPercent;
216         if (mTimerMode) {
217             diamondPercent = 270 - degrees * 360;
218         } else {
219             diamondPercent = 270 + degrees * 360;
220         }
221 
222         canvas.save();
223         final double diamondRadians = Math.toRadians(diamondPercent);
224         canvas.translate(xCenter + (float) (radius * Math.cos(diamondRadians)),
225                 yCenter + (float) (radius * Math.sin(diamondRadians)));
226         canvas.rotate(diamondPercent + 45f);
227         canvas.drawRect(-mRectHalfWidth, -mRectHalfWidth, mRectHalfWidth, mRectHalfWidth, mFill);
228         canvas.restore();
229     }
230 
231     public static final String PREF_CTV_PAUSED  = "_ctv_paused";
232     public static final String PREF_CTV_INTERVAL  = "_ctv_interval";
233     public static final String PREF_CTV_INTERVAL_START = "_ctv_interval_start";
234     public static final String PREF_CTV_CURRENT_INTERVAL = "_ctv_current_interval";
235     public static final String PREF_CTV_ACCUM_TIME = "_ctv_accum_time";
236     public static final String PREF_CTV_TIMER_MODE = "_ctv_timer_mode";
237     public static final String PREF_CTV_MARKER_TIME = "_ctv_marker_time";
238 
239     // Since this view is used in multiple places, use the key to save different instances
writeToSharedPref(SharedPreferences prefs, String key)240     public void writeToSharedPref(SharedPreferences prefs, String key) {
241         SharedPreferences.Editor editor = prefs.edit();
242         editor.putBoolean (key + PREF_CTV_PAUSED, mPaused);
243         editor.putLong (key + PREF_CTV_INTERVAL, mIntervalTime);
244         editor.putLong (key + PREF_CTV_INTERVAL_START, mIntervalStartTime);
245         editor.putLong (key + PREF_CTV_CURRENT_INTERVAL, mCurrentIntervalTime);
246         editor.putLong (key + PREF_CTV_ACCUM_TIME, mAccumulatedTime);
247         editor.putLong (key + PREF_CTV_MARKER_TIME, mMarkerTime);
248         editor.putBoolean (key + PREF_CTV_TIMER_MODE, mTimerMode);
249         editor.apply();
250     }
251 
readFromSharedPref(SharedPreferences prefs, String key)252     public void readFromSharedPref(SharedPreferences prefs, String key) {
253         mPaused = prefs.getBoolean(key + PREF_CTV_PAUSED, false);
254         mIntervalTime = prefs.getLong(key + PREF_CTV_INTERVAL, 0);
255         mIntervalStartTime = prefs.getLong(key + PREF_CTV_INTERVAL_START, -1);
256         mCurrentIntervalTime = prefs.getLong(key + PREF_CTV_CURRENT_INTERVAL, 0);
257         mAccumulatedTime = prefs.getLong(key + PREF_CTV_ACCUM_TIME, 0);
258         mMarkerTime = prefs.getLong(key + PREF_CTV_MARKER_TIME, -1);
259         mTimerMode = prefs.getBoolean(key + PREF_CTV_TIMER_MODE, false);
260         mAnimate = (mIntervalStartTime != -1 && !mPaused);
261     }
262 
clearSharedPref(SharedPreferences prefs, String key)263     public void clearSharedPref(SharedPreferences prefs, String key) {
264         SharedPreferences.Editor editor = prefs.edit();
265         editor.remove (Stopwatches.PREF_START_TIME);
266         editor.remove (Stopwatches.PREF_ACCUM_TIME);
267         editor.remove (Stopwatches.PREF_STATE);
268         editor.remove (key + PREF_CTV_PAUSED);
269         editor.remove (key + PREF_CTV_INTERVAL);
270         editor.remove (key + PREF_CTV_INTERVAL_START);
271         editor.remove (key + PREF_CTV_CURRENT_INTERVAL);
272         editor.remove (key + PREF_CTV_ACCUM_TIME);
273         editor.remove (key + PREF_CTV_MARKER_TIME);
274         editor.remove (key + PREF_CTV_TIMER_MODE);
275         editor.apply();
276     }
277 }
278