ClockView.java revision c1aa6a54a78972843899fd0d911f80a4aff4dd20
1/*
2 * Copyright (C) 2012 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
17package com.android.internal.policy.impl.keyguard;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.database.ContentObserver;
24import android.graphics.Typeface;
25import android.os.Handler;
26import android.provider.Settings;
27import android.text.format.DateFormat;
28import android.util.AttributeSet;
29import android.view.View;
30import android.widget.RelativeLayout;
31import android.widget.TextView;
32
33import java.lang.ref.WeakReference;
34import java.text.DateFormatSymbols;
35import java.util.Calendar;
36import com.android.internal.R;
37
38/**
39 * Displays the time
40 */
41public class ClockView extends RelativeLayout {
42    private static final String ANDROID_CLOCK_FONT_FILE = "/system/fonts/AndroidClock.ttf";
43    private final static String M12 = "h:mm";
44    private final static String M24 = "kk:mm";
45
46    private Calendar mCalendar;
47    private String mFormat;
48    private TextView mTimeView;
49    private AmPm mAmPm;
50    private ContentObserver mFormatChangeObserver;
51    private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced
52
53    /* called by system on minute ticks */
54    private final Handler mHandler = new Handler();
55    private BroadcastReceiver mIntentReceiver;
56
57    private static class TimeChangedReceiver extends BroadcastReceiver {
58        private WeakReference<ClockView> mClock;
59        private Context mContext;
60
61        public TimeChangedReceiver(ClockView clock) {
62            mClock = new WeakReference<ClockView>(clock);
63            mContext = clock.getContext();
64        }
65
66        @Override
67        public void onReceive(Context context, Intent intent) {
68            // Post a runnable to avoid blocking the broadcast.
69            final boolean timezoneChanged =
70                    intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
71            final ClockView clock = mClock.get();
72            if (clock != null) {
73                clock.mHandler.post(new Runnable() {
74                    public void run() {
75                        if (timezoneChanged) {
76                            clock.mCalendar = Calendar.getInstance();
77                        }
78                        clock.updateTime();
79                    }
80                });
81            } else {
82                try {
83                    mContext.unregisterReceiver(this);
84                } catch (RuntimeException e) {
85                    // Shouldn't happen
86                }
87            }
88        }
89    };
90
91    static class AmPm {
92        private TextView mAmPmTextView;
93        private String mAmString, mPmString;
94
95        AmPm(View parent, Typeface tf) {
96            // No longer used, uncomment if we decide to use AM/PM indicator again
97            // mAmPmTextView = (TextView) parent.findViewById(R.id.am_pm);
98            if (mAmPmTextView != null && tf != null) {
99                mAmPmTextView.setTypeface(tf);
100            }
101
102            String[] ampm = new DateFormatSymbols().getAmPmStrings();
103            mAmString = ampm[0];
104            mPmString = ampm[1];
105        }
106
107        void setShowAmPm(boolean show) {
108            if (mAmPmTextView != null) {
109                mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);
110            }
111        }
112
113        void setIsMorning(boolean isMorning) {
114            if (mAmPmTextView != null) {
115                mAmPmTextView.setText(isMorning ? mAmString : mPmString);
116            }
117        }
118    }
119
120    private static class FormatChangeObserver extends ContentObserver {
121        private WeakReference<ClockView> mClock;
122        private Context mContext;
123        public FormatChangeObserver(ClockView clock) {
124            super(new Handler());
125            mClock = new WeakReference<ClockView>(clock);
126            mContext = clock.getContext();
127        }
128        @Override
129        public void onChange(boolean selfChange) {
130            ClockView digitalClock = mClock.get();
131            if (digitalClock != null) {
132                digitalClock.setDateFormat();
133                digitalClock.updateTime();
134            } else {
135                try {
136                    mContext.getContentResolver().unregisterContentObserver(this);
137                } catch (RuntimeException e) {
138                    // Shouldn't happen
139                }
140            }
141        }
142    }
143
144    public ClockView(Context context) {
145        this(context, null);
146    }
147
148    public ClockView(Context context, AttributeSet attrs) {
149        super(context, attrs);
150    }
151
152    @Override
153    protected void onFinishInflate() {
154        super.onFinishInflate();
155        mTimeView = (TextView) findViewById(R.id.clock_text);
156        mTimeView.setTypeface(Typeface.createFromFile(ANDROID_CLOCK_FONT_FILE));
157        mAmPm = new AmPm(this, null);
158        mCalendar = Calendar.getInstance();
159        setDateFormat();
160    }
161
162    @Override
163    protected void onAttachedToWindow() {
164        super.onAttachedToWindow();
165
166        mAttached++;
167
168        /* monitor time ticks, time changed, timezone */
169        if (mIntentReceiver == null) {
170            mIntentReceiver = new TimeChangedReceiver(this);
171            IntentFilter filter = new IntentFilter();
172            filter.addAction(Intent.ACTION_TIME_TICK);
173            filter.addAction(Intent.ACTION_TIME_CHANGED);
174            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
175            mContext.registerReceiver(mIntentReceiver, filter);
176        }
177
178        /* monitor 12/24-hour display preference */
179        if (mFormatChangeObserver == null) {
180            mFormatChangeObserver = new FormatChangeObserver(this);
181            mContext.getContentResolver().registerContentObserver(
182                    Settings.System.CONTENT_URI, true, mFormatChangeObserver);
183        }
184
185        updateTime();
186    }
187
188    @Override
189    protected void onDetachedFromWindow() {
190        super.onDetachedFromWindow();
191
192        mAttached--;
193
194        if (mIntentReceiver != null) {
195            mContext.unregisterReceiver(mIntentReceiver);
196        }
197        if (mFormatChangeObserver != null) {
198            mContext.getContentResolver().unregisterContentObserver(
199                    mFormatChangeObserver);
200        }
201
202        mFormatChangeObserver = null;
203        mIntentReceiver = null;
204    }
205
206    void updateTime(Calendar c) {
207        mCalendar = c;
208        updateTime();
209    }
210
211    public void updateTime() {
212        mCalendar.setTimeInMillis(System.currentTimeMillis());
213
214        CharSequence newTime = DateFormat.format(mFormat, mCalendar);
215        mTimeView.setText(newTime);
216        mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
217    }
218
219    private void setDateFormat() {
220        mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) ? M24 : M12;
221        mAmPm.setShowAmPm(mFormat.equals(M12));
222    }
223}
224