1/*
2 * Copyright (C) 2008 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.deskclock;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.database.ContentObserver;
24import android.os.Handler;
25import android.provider.Settings;
26import android.text.format.DateFormat;
27import android.util.AttributeSet;
28import android.view.View;
29import android.widget.RelativeLayout;
30
31import java.text.DateFormatSymbols;
32import java.util.Calendar;
33
34/**
35 * Displays the time
36 */
37public class DigitalClock extends RelativeLayout {
38
39    private final static String M12 = "h:mm";
40
41    private Calendar mCalendar;
42    private String mFormat;
43    private AndroidClockTextView mTimeDisplay;
44    private AmPm mAmPm;
45    private ContentObserver mFormatChangeObserver;
46    private boolean mLive = true;
47    private boolean mAttached;
48
49    /* called by system on minute ticks */
50    private final Handler mHandler = new Handler();
51    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
52            @Override
53            public void onReceive(Context context, Intent intent) {
54                if (mLive && intent.getAction().equals(
55                            Intent.ACTION_TIMEZONE_CHANGED)) {
56                    mCalendar = Calendar.getInstance();
57                }
58                // Post a runnable to avoid blocking the broadcast.
59                mHandler.post(new Runnable() {
60                        public void run() {
61                            updateTime();
62                        }
63                });
64            }
65        };
66
67    static class AmPm {
68        private AndroidClockTextView mAmPm;
69        private String mAmString, mPmString;
70
71        AmPm(View parent) {
72            mAmPm = (AndroidClockTextView) parent.findViewById(R.id.am_pm);
73
74            String[] ampm = new DateFormatSymbols().getAmPmStrings();
75            mAmString = ampm[0];
76            mPmString = ampm[1];
77        }
78
79        void setShowAmPm(boolean show) {
80            mAmPm.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
81        }
82
83        void setIsMorning(boolean isMorning) {
84            mAmPm.setText(isMorning ? mAmString : mPmString);
85        }
86    }
87
88    private class FormatChangeObserver extends ContentObserver {
89        public FormatChangeObserver() {
90            super(new Handler());
91        }
92        @Override
93        public void onChange(boolean selfChange) {
94            setDateFormat();
95            updateTime();
96        }
97    }
98
99    public DigitalClock(Context context) {
100        this(context, null);
101    }
102
103    public DigitalClock(Context context, AttributeSet attrs) {
104        super(context, attrs);
105    }
106
107    @Override
108    protected void onFinishInflate() {
109        super.onFinishInflate();
110
111        mTimeDisplay = (AndroidClockTextView) findViewById(R.id.timeDisplay);
112        mAmPm = new AmPm(this);
113        mCalendar = Calendar.getInstance();
114
115        setDateFormat();
116    }
117
118    @Override
119    protected void onAttachedToWindow() {
120        super.onAttachedToWindow();
121
122        if (Log.LOGV) Log.v("onAttachedToWindow " + this);
123
124        if (mAttached) return;
125        mAttached = true;
126
127        if (mLive) {
128            /* monitor time ticks, time changed, timezone */
129            IntentFilter filter = new IntentFilter();
130            filter.addAction(Intent.ACTION_TIME_TICK);
131            filter.addAction(Intent.ACTION_TIME_CHANGED);
132            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
133            getContext().registerReceiver(mIntentReceiver, filter);
134        }
135
136        /* monitor 12/24-hour display preference */
137        mFormatChangeObserver = new FormatChangeObserver();
138        getContext().getContentResolver().registerContentObserver(
139                Settings.System.CONTENT_URI, true, mFormatChangeObserver);
140
141        updateTime();
142    }
143
144    @Override
145    protected void onDetachedFromWindow() {
146        super.onDetachedFromWindow();
147
148        if (!mAttached) return;
149        mAttached = false;
150
151        if (mLive) {
152            getContext().unregisterReceiver(mIntentReceiver);
153        }
154        getContext().getContentResolver().unregisterContentObserver(
155                mFormatChangeObserver);
156    }
157
158
159    void updateTime(Calendar c) {
160        mCalendar = c;
161        updateTime();
162    }
163
164    private void updateTime() {
165        if (mLive) {
166            mCalendar.setTimeInMillis(System.currentTimeMillis());
167        }
168
169        CharSequence newTime = DateFormat.format(mFormat, mCalendar);
170        mTimeDisplay.setText(newTime);
171        mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
172    }
173
174    private void setDateFormat() {
175        mFormat = Alarms.get24HourMode(getContext()) ? Alarms.M24 : M12;
176        mAmPm.setShowAmPm(mFormat == M12);
177    }
178
179    void setLive(boolean live) {
180        mLive = live;
181    }
182}
183