Home | History | Annotate | Download | only in deskclock
      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 
     48     public CircleTimerView(Context context) {
     49         this(context, null);
     50     }
     51 
     52     public CircleTimerView(Context context, AttributeSet attrs) {
     53         super(context, attrs);
     54         init(context);
     55     }
     56 
     57     public void setIntervalTime(long t) {
     58         mIntervalTime = t;
     59         postInvalidate();
     60     }
     61 
     62     public void setMarkerTime(long t) {
     63         mMarkerTime = t;
     64         postInvalidate();
     65     }
     66 
     67     public void reset() {
     68         mIntervalStartTime = -1;
     69         mMarkerTime = -1;
     70         postInvalidate();
     71     }
     72     public void startIntervalAnimation() {
     73         mIntervalStartTime = Utils.getTimeNow();
     74         mAnimate = true;
     75         invalidate();
     76         mPaused = false;
     77     }
     78     public void stopIntervalAnimation() {
     79         mAnimate = false;
     80         mIntervalStartTime = -1;
     81         mAccumulatedTime = 0;
     82     }
     83 
     84     public boolean isAnimating() {
     85         return (mIntervalStartTime != -1);
     86     }
     87 
     88     public void pauseIntervalAnimation() {
     89         mAnimate = false;
     90         mAccumulatedTime += Utils.getTimeNow() - mIntervalStartTime;
     91         mPaused = true;
     92     }
     93 
     94     public void abortIntervalAnimation() {
     95         mAnimate = false;
     96     }
     97 
     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 
    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 
    137     public void setTimerMode(boolean mode) {
    138         mTimerMode = mode;
    139     }
    140 
    141     @Override
    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 
    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
    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 
    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 
    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