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.DateUtils; 29 import android.text.format.Time; 30 import android.util.AttributeSet; 31 import android.view.View; 32 import android.widget.RemoteViews.RemoteView; 33 34 import java.util.TimeZone; 35 36 /** 37 * This widget display an analogic clock with two hands for hours and 38 * minutes. 39 * 40 * @attr ref android.R.styleable#AnalogClock_dial 41 * @attr ref android.R.styleable#AnalogClock_hand_hour 42 * @attr ref android.R.styleable#AnalogClock_hand_minute 43 * @deprecated This widget is no longer supported. 44 */ 45 @RemoteView 46 @Deprecated 47 public class AnalogClock extends View { 48 private Time mCalendar; 49 50 private Drawable mHourHand; 51 private Drawable mMinuteHand; 52 private Drawable mDial; 53 54 private int mDialWidth; 55 private int mDialHeight; 56 57 private boolean mAttached; 58 59 private final Handler mHandler = new Handler(); 60 private float mMinutes; 61 private float mHour; 62 private boolean mChanged; 63 AnalogClock(Context context)64 public AnalogClock(Context context) { 65 this(context, null); 66 } 67 AnalogClock(Context context, AttributeSet attrs)68 public AnalogClock(Context context, AttributeSet attrs) { 69 this(context, attrs, 0); 70 } 71 AnalogClock(Context context, AttributeSet attrs, int defStyleAttr)72 public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { 73 this(context, attrs, defStyleAttr, 0); 74 } 75 AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)76 public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 77 super(context, attrs, defStyleAttr, defStyleRes); 78 79 final Resources r = context.getResources(); 80 final TypedArray a = context.obtainStyledAttributes( 81 attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes); 82 83 mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial); 84 if (mDial == null) { 85 mDial = context.getDrawable(com.android.internal.R.drawable.clock_dial); 86 } 87 88 mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); 89 if (mHourHand == null) { 90 mHourHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_hour); 91 } 92 93 mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); 94 if (mMinuteHand == null) { 95 mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); 96 } 97 98 mCalendar = new Time(); 99 100 mDialWidth = mDial.getIntrinsicWidth(); 101 mDialHeight = mDial.getIntrinsicHeight(); 102 } 103 104 @Override onAttachedToWindow()105 protected void onAttachedToWindow() { 106 super.onAttachedToWindow(); 107 108 if (!mAttached) { 109 mAttached = true; 110 IntentFilter filter = new IntentFilter(); 111 112 filter.addAction(Intent.ACTION_TIME_TICK); 113 filter.addAction(Intent.ACTION_TIME_CHANGED); 114 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 115 116 // OK, this is gross but needed. This class is supported by the 117 // remote views machanism and as a part of that the remote views 118 // can be inflated by a context for another user without the app 119 // having interact users permission - just for loading resources. 120 // For exmaple, when adding widgets from a user profile to the 121 // home screen. Therefore, we register the receiver as the current 122 // user not the one the context is for. 123 getContext().registerReceiverAsUser(mIntentReceiver, 124 android.os.Process.myUserHandle(), filter, null, mHandler); 125 } 126 127 // NOTE: It's safe to do these after registering the receiver since the receiver always runs 128 // in the main thread, therefore the receiver can't run before this method returns. 129 130 // The time zone may have changed while the receiver wasn't registered, so update the Time 131 mCalendar = new Time(); 132 133 // Make sure we update to the current time 134 onTimeChanged(); 135 } 136 137 @Override onDetachedFromWindow()138 protected void onDetachedFromWindow() { 139 super.onDetachedFromWindow(); 140 if (mAttached) { 141 getContext().unregisterReceiver(mIntentReceiver); 142 mAttached = false; 143 } 144 } 145 146 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)147 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 148 149 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 150 int widthSize = MeasureSpec.getSize(widthMeasureSpec); 151 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 152 int heightSize = MeasureSpec.getSize(heightMeasureSpec); 153 154 float hScale = 1.0f; 155 float vScale = 1.0f; 156 157 if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) { 158 hScale = (float) widthSize / (float) mDialWidth; 159 } 160 161 if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) { 162 vScale = (float )heightSize / (float) mDialHeight; 163 } 164 165 float scale = Math.min(hScale, vScale); 166 167 setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0), 168 resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0)); 169 } 170 171 @Override onSizeChanged(int w, int h, int oldw, int oldh)172 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 173 super.onSizeChanged(w, h, oldw, oldh); 174 mChanged = true; 175 } 176 177 @Override onDraw(Canvas canvas)178 protected void onDraw(Canvas canvas) { 179 super.onDraw(canvas); 180 181 boolean changed = mChanged; 182 if (changed) { 183 mChanged = false; 184 } 185 186 int availableWidth = mRight - mLeft; 187 int availableHeight = mBottom - mTop; 188 189 int x = availableWidth / 2; 190 int y = availableHeight / 2; 191 192 final Drawable dial = mDial; 193 int w = dial.getIntrinsicWidth(); 194 int h = dial.getIntrinsicHeight(); 195 196 boolean scaled = false; 197 198 if (availableWidth < w || availableHeight < h) { 199 scaled = true; 200 float scale = Math.min((float) availableWidth / (float) w, 201 (float) availableHeight / (float) h); 202 canvas.save(); 203 canvas.scale(scale, scale, x, y); 204 } 205 206 if (changed) { 207 dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); 208 } 209 dial.draw(canvas); 210 211 canvas.save(); 212 canvas.rotate(mHour / 12.0f * 360.0f, x, y); 213 final Drawable hourHand = mHourHand; 214 if (changed) { 215 w = hourHand.getIntrinsicWidth(); 216 h = hourHand.getIntrinsicHeight(); 217 hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); 218 } 219 hourHand.draw(canvas); 220 canvas.restore(); 221 222 canvas.save(); 223 canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); 224 225 final Drawable minuteHand = mMinuteHand; 226 if (changed) { 227 w = minuteHand.getIntrinsicWidth(); 228 h = minuteHand.getIntrinsicHeight(); 229 minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); 230 } 231 minuteHand.draw(canvas); 232 canvas.restore(); 233 234 if (scaled) { 235 canvas.restore(); 236 } 237 } 238 onTimeChanged()239 private void onTimeChanged() { 240 mCalendar.setToNow(); 241 242 int hour = mCalendar.hour; 243 int minute = mCalendar.minute; 244 int second = mCalendar.second; 245 246 mMinutes = minute + second / 60.0f; 247 mHour = hour + mMinutes / 60.0f; 248 mChanged = true; 249 250 updateContentDescription(mCalendar); 251 } 252 253 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 254 @Override 255 public void onReceive(Context context, Intent intent) { 256 if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { 257 String tz = intent.getStringExtra("time-zone"); 258 mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); 259 } 260 261 onTimeChanged(); 262 263 invalidate(); 264 } 265 }; 266 updateContentDescription(Time time)267 private void updateContentDescription(Time time) { 268 final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; 269 String contentDescription = DateUtils.formatDateTime(mContext, 270 time.toMillis(false), flags); 271 setContentDescription(contentDescription); 272 } 273 } 274