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