• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.widget;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.content.BroadcastReceiver;
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.graphics.Canvas;
26 import android.graphics.drawable.Drawable;
27 import android.os.Handler;
28 import android.text.format.Time;
29 import android.util.AttributeSet;
30 import android.view.View;
31 import android.widget.RemoteViews.RemoteView;
32 
33 import java.util.TimeZone;
34 
35 /**
36  * This widget display an analogic clock with two hands for hours and
37  * minutes.
38  */
39 @RemoteView
40 public 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 
AnalogClock(Context context)57     public AnalogClock(Context context) {
58         this(context, null);
59     }
60 
AnalogClock(Context context, AttributeSet attrs)61     public AnalogClock(Context context, AttributeSet attrs) {
62         this(context, attrs, 0);
63     }
64 
AnalogClock(Context context, AttributeSet attrs, int defStyle)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
onAttachedToWindow()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
onDetachedFromWindow()120     protected void onDetachedFromWindow() {
121         super.onDetachedFromWindow();
122         if (mAttached) {
123             getContext().unregisterReceiver(mIntentReceiver);
124             mAttached = false;
125         }
126     }
127 
128     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
onSizeChanged(int w, int h, int oldw, int oldh)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
onDraw(Canvas canvas)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 
onTimeChanged()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 }
247