blob: 45d1403bc59850ec1e3c0875103a38676fd16fb9 [file] [log] [blame]
Joe Onoratoc83bb732010-01-19 16:32:22 -08001/*
2 * Copyright (C) 2010 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 android.widget;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.content.BroadcastReceiver;
23import android.database.ContentObserver;
24import android.net.Uri;
25import android.os.Handler;
26import android.text.format.Time;
27import android.util.AttributeSet;
28import android.util.Log;
29import android.provider.Settings;
Joe Onoratoc83bb732010-01-19 16:32:22 -080030import android.widget.TextView;
31import android.widget.RemoteViews.RemoteView;
32
Joe Onoratoc83bb732010-01-19 16:32:22 -080033import java.text.DateFormat;
34import java.text.SimpleDateFormat;
35import java.util.Date;
36
37//
38// TODO
39// - listen for the next threshold time to update the view.
40// - listen for date format pref changed
41// - put the AM/PM in a smaller font
42//
43
44/**
45 * Displays a given time in a convenient human-readable foramt.
46 *
47 * @hide
48 */
49@RemoteView
50public class DateTimeView extends TextView {
51 private static final String TAG = "DateTimeView";
52
53 private static final long TWELVE_HOURS_IN_MINUTES = 12 * 60;
54 private static final long TWENTY_FOUR_HOURS_IN_MILLIS = 24 * 60 * 60 * 1000;
55
56 private static final int SHOW_TIME = 0;
57 private static final int SHOW_MONTH_DAY_YEAR = 1;
58
59 Date mTime;
60 long mTimeMillis;
61
62 int mLastDisplay = -1;
63 DateFormat mLastFormat;
64
65 private boolean mAttachedToWindow;
66 private long mUpdateTimeMillis;
67
68 public DateTimeView(Context context) {
69 super(context);
70 }
71
72 public DateTimeView(Context context, AttributeSet attrs) {
73 super(context, attrs);
74 }
75
76 @Override
77 protected void onAttachedToWindow() {
Daniel Sandler8d3c2342011-08-09 13:10:20 -040078 super.onAttachedToWindow();
Joe Onoratoc83bb732010-01-19 16:32:22 -080079 registerReceivers();
80 mAttachedToWindow = true;
81 }
82
83 @Override
84 protected void onDetachedFromWindow() {
85 super.onDetachedFromWindow();
86 unregisterReceivers();
87 mAttachedToWindow = false;
88 }
89
90 @android.view.RemotableViewMethod
91 public void setTime(long time) {
92 Time t = new Time();
93 t.set(time);
94 t.second = 0;
95 mTimeMillis = t.toMillis(false);
96 mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0);
97 update();
98 }
99
100 void update() {
101 if (mTime == null) {
102 return;
103 }
104
105 long start = System.nanoTime();
106
107 int display;
108 Date time = mTime;
109
110 Time t = new Time();
111 t.set(mTimeMillis);
112 t.second = 0;
113
114 t.hour -= 12;
115 long twelveHoursBefore = t.toMillis(false);
116 t.hour += 12;
117 long twelveHoursAfter = t.toMillis(false);
118 t.hour = 0;
119 t.minute = 0;
120 long midnightBefore = t.toMillis(false);
121 t.monthDay++;
122 long midnightAfter = t.toMillis(false);
123
124 long nowMillis = System.currentTimeMillis();
125 t.set(nowMillis);
126 t.second = 0;
127 nowMillis = t.normalize(false);
128
129 // Choose the display mode
130 choose_display: {
131 if ((nowMillis >= midnightBefore && nowMillis < midnightAfter)
132 || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) {
133 display = SHOW_TIME;
134 break choose_display;
135 }
136 // Else, show month day and year.
137 display = SHOW_MONTH_DAY_YEAR;
138 break choose_display;
139 }
140
141 // Choose the format
142 DateFormat format;
143 if (display == mLastDisplay && mLastFormat != null) {
144 // use cached format
145 format = mLastFormat;
146 } else {
147 switch (display) {
148 case SHOW_TIME:
149 format = getTimeFormat();
150 break;
151 case SHOW_MONTH_DAY_YEAR:
152 format = getDateFormat();
153 break;
154 default:
155 throw new RuntimeException("unknown display value: " + display);
156 }
157 mLastFormat = format;
158 }
159
160 // Set the text
161 String text = format.format(mTime);
162 setText(text);
163
164 // Schedule the next update
165 if (display == SHOW_TIME) {
166 // Currently showing the time, update at the later of twelve hours after or midnight.
167 mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter;
168 } else {
169 // Currently showing the date
170 if (mTimeMillis < nowMillis) {
171 // If the time is in the past, don't schedule an update
172 mUpdateTimeMillis = 0;
173 } else {
174 // If hte time is in the future, schedule one at the earlier of twelve hours
175 // before or midnight before.
176 mUpdateTimeMillis = twelveHoursBefore < midnightBefore
177 ? twelveHoursBefore : midnightBefore;
178 }
179 }
180 if (false) {
181 Log.d(TAG, "update needed for '" + time + "' at '" + new Date(mUpdateTimeMillis)
182 + "' - text=" + text);
183 }
184
185 long finish = System.nanoTime();
186 }
187
188 private DateFormat getTimeFormat() {
Elliott Hughes4caba612013-01-14 15:48:27 -0800189 return android.text.format.DateFormat.getTimeFormat(getContext());
Joe Onoratoc83bb732010-01-19 16:32:22 -0800190 }
191
192 private DateFormat getDateFormat() {
193 String format = Settings.System.getString(getContext().getContentResolver(),
194 Settings.System.DATE_FORMAT);
Joe Onorato77472102010-02-01 18:14:15 -0500195 if (format == null || "".equals(format)) {
Joe Onoratoc83bb732010-01-19 16:32:22 -0800196 return DateFormat.getDateInstance(DateFormat.SHORT);
197 } else {
Adam Powellf3311c92010-03-23 17:32:41 -0700198 try {
199 return new SimpleDateFormat(format);
200 } catch (IllegalArgumentException e) {
201 // If we tried to use a bad format string, fall back to a default.
202 return DateFormat.getDateInstance(DateFormat.SHORT);
203 }
Joe Onoratoc83bb732010-01-19 16:32:22 -0800204 }
205 }
206
207 private void registerReceivers() {
208 Context context = getContext();
209
210 IntentFilter filter = new IntentFilter();
211 filter.addAction(Intent.ACTION_TIME_TICK);
212 filter.addAction(Intent.ACTION_TIME_CHANGED);
213 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
214 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
215 context.registerReceiver(mBroadcastReceiver, filter);
216
217 Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
218 context.getContentResolver().registerContentObserver(uri, true, mContentObserver);
219 }
220
221 private void unregisterReceivers() {
222 Context context = getContext();
223 context.unregisterReceiver(mBroadcastReceiver);
224 context.getContentResolver().unregisterContentObserver(mContentObserver);
225 }
226
227 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
228 @Override
229 public void onReceive(Context context, Intent intent) {
230 String action = intent.getAction();
231 if (Intent.ACTION_TIME_TICK.equals(action)) {
232 if (System.currentTimeMillis() < mUpdateTimeMillis) {
233 // The update() function takes a few milliseconds to run because of
234 // all of the time conversions it needs to do, so we can't do that
235 // every minute.
236 return;
237 }
238 }
239 // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format.
240 mLastFormat = null;
241 update();
242 }
243 };
244
245 private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
246 @Override
247 public void onChange(boolean selfChange) {
248 mLastFormat = null;
249 update();
250 }
251 };
252}