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