Skip to content

Instantly share code, notes, and snippets.

@iamrajendra
Created December 18, 2019 14:32
Show Gist options
  • Save iamrajendra/baf0f8f151787fc12c0c2c1e306de792 to your computer and use it in GitHub Desktop.
Save iamrajendra/baf0f8f151787fc12c0c2c1e306de792 to your computer and use it in GitHub Desktop.
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