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