Home | History | Annotate | Download | only in widget
      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 
     57     public AnalogClock(Context context) {
     58         this(context, null);
     59     }
     60 
     61     public AnalogClock(Context context, AttributeSet attrs) {
     62         this(context, attrs, 0);
     63     }
     64 
     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
     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
    120     protected void onDetachedFromWindow() {
    121         super.onDetachedFromWindow();
    122         if (mAttached) {
    123             getContext().unregisterReceiver(mIntentReceiver);
    124             mAttached = false;
    125         }
    126     }
    127 
    128     @Override
    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
    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
    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 
    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