blob: f847bc399b5a97237ae237ca4ca1ef649c0f5cbc [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.widget;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.content.BroadcastReceiver;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.Canvas;
26import android.graphics.drawable.Drawable;
27import android.os.Handler;
28import android.text.format.Time;
29import android.util.AttributeSet;
30import android.view.View;
31import android.widget.RemoteViews.RemoteView;
32
33import java.util.TimeZone;
34
35/**
36 * This widget display an analogic clock with two hands for hours and
37 * minutes.
38 */
39@RemoteView
40public class AnalogClock extends View {
41 private Time mCalendar;
42
43 private Drawable mHourHand;
44 private Drawable mMinuteHand;
45 private Drawable mDial;
46
47 private int mDialWidth;
48 private int mDialHeight;
49
50 private boolean mAttached;
51
52 private final Handler mHandler = new Handler();
53 private float mMinutes;
54 private float mHour;
55 private boolean mChanged;
56
57 public AnalogClock(Context context) {
58 this(context, null);
59 }
60
61 public AnalogClock(Context context, AttributeSet attrs) {
62 this(context, attrs, 0);
63 }
64
65 public AnalogClock(Context context, AttributeSet attrs,
66 int defStyle) {
67 super(context, attrs, defStyle);
68 Resources r = mContext.getResources();
69 TypedArray a =
70 context.obtainStyledAttributes(
71 attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0);
72
73 mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial);
74 if (mDial == null) {
75 mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial);
76 }
77
78 mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour);
79 if (mHourHand == null) {
80 mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour);
81 }
82
83 mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute);
84 if (mMinuteHand == null) {
85 mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
86 }
87
88 mCalendar = new Time();
89
90 mDialWidth = mDial.getIntrinsicWidth();
91 mDialHeight = mDial.getIntrinsicHeight();
92 }
93
94 @Override
95 protected void onAttachedToWindow() {
96 super.onAttachedToWindow();
97
98 if (!mAttached) {
99 mAttached = true;
100 IntentFilter filter = new IntentFilter();
101
102 filter.addAction(Intent.ACTION_TIME_TICK);
103 filter.addAction(Intent.ACTION_TIME_CHANGED);
104 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
105
106 getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
107 }
108
109 // NOTE: It's safe to do these after registering the receiver since the receiver always runs
110 // in the main thread, therefore the receiver can't run before this method returns.
111
112 // The time zone may have changed while the receiver wasn't registered, so update the Time
113 mCalendar = new Time();
114
115 // Make sure we update to the current time
116 onTimeChanged();
117 }
118
119 @Override
120 protected void onDetachedFromWindow() {
121 super.onDetachedFromWindow();
122 if (mAttached) {
123 getContext().unregisterReceiver(mIntentReceiver);
124 mAttached = false;
125 }
126 }
127
128 @Override
129 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
130
131 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
132 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
133 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
134 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
135
136 float hScale = 1.0f;
137 float vScale = 1.0f;
138
139 if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
140 hScale = (float) widthSize / (float) mDialWidth;
141 }
142
143 if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
144 vScale = (float )heightSize / (float) mDialHeight;
145 }
146
147 float scale = Math.min(hScale, vScale);
148
149 setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
150 resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
151 }
152
153 @Override
154 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
155 super.onSizeChanged(w, h, oldw, oldh);
156 mChanged = true;
157 }
158
159 @Override
160 protected void onDraw(Canvas canvas) {
161 super.onDraw(canvas);
162
163 boolean changed = mChanged;
164 if (changed) {
165 mChanged = false;
166 }
167
168 int availableWidth = mRight - mLeft;
169 int availableHeight = mBottom - mTop;
170
171 int x = availableWidth / 2;
172 int y = availableHeight / 2;
173
174 final Drawable dial = mDial;
175 int w = dial.getIntrinsicWidth();
176 int h = dial.getIntrinsicHeight();
177
178 boolean scaled = false;
179
180 if (availableWidth < w || availableHeight < h) {
181 scaled = true;
182 float scale = Math.min((float) availableWidth / (float) w,
183 (float) availableHeight / (float) h);
184 canvas.save();
185 canvas.scale(scale, scale, x, y);
186 }
187
188 if (changed) {
189 dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
190 }
191 dial.draw(canvas);
192
193 canvas.save();
194 canvas.rotate(mHour / 12.0f * 360.0f, x, y);
195 final Drawable hourHand = mHourHand;
196 if (changed) {
197 w = hourHand.getIntrinsicWidth();
198 h = hourHand.getIntrinsicHeight();
199 hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
200 }
201 hourHand.draw(canvas);
202 canvas.restore();
203
204 canvas.save();
205 canvas.rotate(mMinutes / 60.0f * 360.0f, x, y);
206
207 final Drawable minuteHand = mMinuteHand;
208 if (changed) {
209 w = minuteHand.getIntrinsicWidth();
210 h = minuteHand.getIntrinsicHeight();
211 minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
212 }
213 minuteHand.draw(canvas);
214 canvas.restore();
215
216 if (scaled) {
217 canvas.restore();
218 }
219 }
220
221 private void onTimeChanged() {
222 mCalendar.setToNow();
223
224 int hour = mCalendar.hour;
225 int minute = mCalendar.minute;
226 int second = mCalendar.second;
227
228 mMinutes = minute + second / 60.0f;
229 mHour = hour + mMinutes / 60.0f;
230 mChanged = true;
231 }
232
233 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
234 @Override
235 public void onReceive(Context context, Intent intent) {
236 if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
237 String tz = intent.getStringExtra("time-zone");
238 mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
239 }
240
241 onTimeChanged();
242
243 invalidate();
244 }
245 };
246}