Home | History | Annotate | Download | only in deskclock
      1 /*
      2  * Copyright (C) 2015 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 com.android.deskclock;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.support.v7.widget.AppCompatImageView;
     24 import android.text.format.DateFormat;
     25 import android.util.AttributeSet;
     26 import android.widget.FrameLayout;
     27 import android.widget.ImageView;
     28 
     29 import java.text.SimpleDateFormat;
     30 import java.util.Calendar;
     31 import java.util.TimeZone;
     32 
     33 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
     34 
     35 /**
     36  * This widget display an analog clock with two hands for hours and minutes.
     37  */
     38 public class AnalogClock extends FrameLayout {
     39 
     40     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
     41         @Override
     42         public void onReceive(Context context, Intent intent) {
     43             if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
     44                 final String tz = intent.getStringExtra("time-zone");
     45                 mTime = Calendar.getInstance(TimeZone.getTimeZone(tz));
     46             }
     47             onTimeChanged();
     48         }
     49     };
     50 
     51     private final Runnable mClockTick = new Runnable() {
     52         @Override
     53         public void run() {
     54             onTimeChanged();
     55 
     56             if (mEnableSeconds) {
     57                 final long now = System.currentTimeMillis();
     58                 final long delay = SECOND_IN_MILLIS - now % SECOND_IN_MILLIS;
     59                 postDelayed(this, delay);
     60             }
     61         }
     62     };
     63 
     64     private final ImageView mHourHand;
     65     private final ImageView mMinuteHand;
     66     private final ImageView mSecondHand;
     67 
     68     private Calendar mTime;
     69     private String mDescFormat;
     70     private TimeZone mTimeZone;
     71     private boolean mEnableSeconds = true;
     72 
     73     public AnalogClock(Context context) {
     74         this(context, null /* attrs */);
     75     }
     76 
     77     public AnalogClock(Context context, AttributeSet attrs) {
     78         this(context, attrs, 0 /* defStyleAttr */);
     79     }
     80 
     81     public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr) {
     82         super(context, attrs, defStyleAttr);
     83 
     84         mTime = Calendar.getInstance();
     85         mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
     86 
     87         // Must call mutate on these instances, otherwise the drawables will blur, because they're
     88         // sharing their size characteristics with the (smaller) world cities analog clocks.
     89         final ImageView dial = new AppCompatImageView(context);
     90         dial.setImageResource(R.drawable.clock_analog_dial);
     91         dial.getDrawable().mutate();
     92         addView(dial);
     93 
     94         mHourHand = new AppCompatImageView(context);
     95         mHourHand.setImageResource(R.drawable.clock_analog_hour);
     96         mHourHand.getDrawable().mutate();
     97         addView(mHourHand);
     98 
     99         mMinuteHand = new AppCompatImageView(context);
    100         mMinuteHand.setImageResource(R.drawable.clock_analog_minute);
    101         mMinuteHand.getDrawable().mutate();
    102         addView(mMinuteHand);
    103 
    104         mSecondHand = new AppCompatImageView(context);
    105         mSecondHand.setImageResource(R.drawable.clock_analog_second);
    106         mSecondHand.getDrawable().mutate();
    107         addView(mSecondHand);
    108     }
    109 
    110     @Override
    111     protected void onAttachedToWindow() {
    112         super.onAttachedToWindow();
    113 
    114         final IntentFilter filter = new IntentFilter();
    115         filter.addAction(Intent.ACTION_TIME_TICK);
    116         filter.addAction(Intent.ACTION_TIME_CHANGED);
    117         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
    118         getContext().registerReceiver(mIntentReceiver, filter);
    119 
    120         // Refresh the calendar instance since the time zone may have changed while the receiver
    121         // wasn't registered.
    122         mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
    123         onTimeChanged();
    124 
    125         // Tick every second.
    126         if (mEnableSeconds) {
    127             mClockTick.run();
    128         }
    129     }
    130 
    131     @Override
    132     protected void onDetachedFromWindow() {
    133         super.onDetachedFromWindow();
    134 
    135         getContext().unregisterReceiver(mIntentReceiver);
    136         removeCallbacks(mClockTick);
    137     }
    138 
    139     private void onTimeChanged() {
    140         mTime.setTimeInMillis(System.currentTimeMillis());
    141         final float hourAngle = mTime.get(Calendar.HOUR) * 30f;
    142         mHourHand.setRotation(hourAngle);
    143         final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
    144         mMinuteHand.setRotation(minuteAngle);
    145         if (mEnableSeconds) {
    146             final float secondAngle = mTime.get(Calendar.SECOND) * 6f;
    147             mSecondHand.setRotation(secondAngle);
    148         }
    149         setContentDescription(DateFormat.format(mDescFormat, mTime));
    150         invalidate();
    151     }
    152 
    153     public void setTimeZone(String id) {
    154         mTimeZone = TimeZone.getTimeZone(id);
    155         mTime.setTimeZone(mTimeZone);
    156         onTimeChanged();
    157     }
    158 
    159     public void enableSeconds(boolean enable) {
    160         mEnableSeconds = enable;
    161         if (mEnableSeconds) {
    162             mSecondHand.setVisibility(VISIBLE);
    163             mClockTick.run();
    164         } else {
    165             mSecondHand.setVisibility(GONE);
    166         }
    167     }
    168 }
    169