Created
December 18, 2019 14:32
-
-
Save iamrajendra/baf0f8f151787fc12c0c2c1e306de792 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.iamrajendra.analogclock.customview; | |
import android.content.BroadcastReceiver; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.IntentFilter; | |
import android.content.res.Resources; | |
import android.content.res.TypedArray; | |
import android.graphics.Canvas; | |
import android.graphics.drawable.Drawable; | |
import android.os.Build; | |
import android.text.format.DateFormat; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import java.text.SimpleDateFormat; | |
import java.util.Calendar; | |
import java.util.TimeZone; | |
import static android.text.format.DateUtils.SECOND_IN_MILLIS; | |
public class AnalogClock extends View { | |
/* | |
* this receiver will registered on window attached and remove on window un attached | |
* */ | |
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { | |
@Override | |
public void onReceive(Context context, Intent intent) { | |
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { | |
final String tz = intent.getStringExtra("time-zone"); | |
mTime = Calendar.getInstance(TimeZone.getTimeZone(tz)); | |
} | |
onTimeChanged(); | |
} | |
}; | |
/* | |
* this is for the second hand and it registered it self for one second delay and un registered on window unattached | |
* | |
* */ | |
private final Runnable mClockTick = new Runnable() { | |
@Override | |
public void run() { | |
onTimeChanged(); | |
if (mEnableSeconds) { | |
final long now = System.currentTimeMillis(); | |
final long delay = SECOND_IN_MILLIS - now % SECOND_IN_MILLIS; | |
postDelayed(this, delay); | |
} | |
} | |
}; | |
//required drawable | |
private Drawable mDial; | |
private Drawable mHourHand; | |
private Drawable mMinuteHand; | |
private Drawable mSecondHand; | |
// time things | |
private Calendar mTime; | |
private String mDescFormat; | |
private TimeZone mTimeZone; | |
private boolean mEnableSeconds = true; | |
public AnalogClock(Context context) { | |
this(context, null); | |
} | |
public AnalogClock(Context context, AttributeSet attrs) { | |
this(context, attrs, 0); | |
} | |
/* | |
* It initialize the resource which is given in xml. like min,hour, and second | |
* | |
* */ | |
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
final Resources r = context.getResources(); | |
final TypedArray a = context.obtainStyledAttributes(attrs, com.iamrajendra.analogclock.R.styleable.AnalogClock); | |
mTime = Calendar.getInstance(); | |
mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); | |
mEnableSeconds = a.getBoolean(com.iamrajendra.analogclock.R.styleable.AnalogClock_showSecondHand, true); | |
mDial = a.getDrawable(com.iamrajendra.analogclock.R.styleable.AnalogClock_dial); | |
if (mDial == null) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
mDial = context.getDrawable(com.iamrajendra.analogclock.R.drawable.dial); | |
} else { | |
mDial = r.getDrawable(com.iamrajendra.analogclock.R.drawable.dial); | |
} | |
} | |
mHourHand = a.getDrawable(com.iamrajendra.analogclock.R.styleable.AnalogClock_hour); | |
if (mHourHand == null) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
mHourHand = context.getDrawable(com.iamrajendra.analogclock.R.drawable.hour); | |
} else { | |
mHourHand = r.getDrawable(com.iamrajendra.analogclock.R.drawable.hour); | |
} | |
} | |
mMinuteHand = a.getDrawable(com.iamrajendra.analogclock.R.styleable.AnalogClock_minute); | |
if (mMinuteHand == null) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
mMinuteHand = context.getDrawable(com.iamrajendra.analogclock.R.drawable.minute); | |
} else { | |
mMinuteHand = r.getDrawable(com.iamrajendra.analogclock.R.drawable.minute); | |
} | |
} | |
mSecondHand = a.getDrawable(com.iamrajendra.analogclock.R.styleable.AnalogClock_second); | |
if (mSecondHand == null) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
mSecondHand = context.getDrawable(com.iamrajendra.analogclock.R.drawable.second); | |
} else { | |
mSecondHand = r.getDrawable(com.iamrajendra.analogclock.R.drawable.second); | |
} | |
} | |
initDrawable(context, mDial); | |
initDrawable(context, mHourHand); | |
initDrawable(context, mMinuteHand); | |
initDrawable(context, mSecondHand); | |
} | |
/* | |
* register the receiver and initialize the calenders | |
* */ | |
@Override | |
protected void onAttachedToWindow() { | |
super.onAttachedToWindow(); | |
final IntentFilter filter = new IntentFilter(); | |
filter.addAction(Intent.ACTION_TIME_TICK); | |
filter.addAction(Intent.ACTION_TIME_CHANGED); | |
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); | |
getContext().registerReceiver(mIntentReceiver, filter); | |
mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); | |
onTimeChanged(); | |
if (mEnableSeconds) { | |
mClockTick.run(); | |
} | |
} | |
/* | |
* unattached the receiver and second callback | |
* */ | |
@Override | |
protected void onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
getContext().unregisterReceiver(mIntentReceiver); | |
removeCallbacks(mClockTick); | |
} | |
/* | |
* get the width and height and set the view accordingly | |
* */ | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
final int minWidth = Math.max(mDial.getIntrinsicWidth(), getSuggestedMinimumWidth()); | |
final int minHeight = Math.max(mDial.getIntrinsicHeight(), getSuggestedMinimumHeight()); | |
setMeasuredDimension(getDefaultSize(minWidth, widthMeasureSpec), | |
getDefaultSize(minHeight, heightMeasureSpec)); | |
} | |
/* | |
* draw | |
* */ | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
final int w = getWidth(); | |
final int h = getHeight(); | |
final int saveCount = canvas.save(); | |
/*it draw a dial*/ | |
canvas.translate(w / 2, h / 2); | |
final float scale = Math.min((float) w / mDial.getIntrinsicWidth(), | |
(float) h / mDial.getIntrinsicHeight()); | |
if (scale < 1f) { | |
canvas.scale(scale, scale, 0f, 0f); | |
} | |
mDial.draw(canvas); | |
/* | |
* it draw a hour | |
* */ | |
final float hourAngle = mTime.get(Calendar.HOUR) * 30f; | |
canvas.rotate(hourAngle, 0f, 0f); | |
mHourHand.draw(canvas); | |
/* | |
* it draw min | |
* */ | |
final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f; | |
canvas.rotate(minuteAngle - hourAngle, 0f, 0f); | |
mMinuteHand.draw(canvas); | |
/* | |
* it draw a second | |
* */ | |
if (mEnableSeconds) { | |
final float secondAngle = mTime.get(Calendar.SECOND) * 6f; | |
canvas.rotate(secondAngle - minuteAngle, 0f, 0f); | |
mSecondHand.draw(canvas); | |
} | |
canvas.restoreToCount(saveCount); | |
} | |
@Override | |
protected boolean verifyDrawable(Drawable who) { | |
return mDial == who | |
|| mHourHand == who | |
|| mMinuteHand == who | |
|| mSecondHand == who | |
|| super.verifyDrawable(who); | |
} | |
private void initDrawable(Context context, Drawable drawable) { | |
final int midX = drawable.getIntrinsicWidth() / 2; | |
final int midY = drawable.getIntrinsicHeight() / 2; | |
drawable.setBounds(-midX, -midY, midX, midY); | |
} | |
private void onTimeChanged() { | |
mTime.setTimeInMillis(System.currentTimeMillis()); | |
setContentDescription(DateFormat.format(mDescFormat, mTime)); | |
invalidate(); | |
} | |
public void setTimeZone(String id) { | |
mTimeZone = TimeZone.getTimeZone(id); | |
mTime.setTimeZone(mTimeZone); | |
onTimeChanged(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment