blob: 5731e502ae45a3596bde167d94fab8fb14a6e19e [file] [log] [blame]
Romain Guy3d1728c2012-10-31 20:31:58 -07001/*
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 android.widget;
18
Aurimas Liutikas99441c52016-10-11 16:48:32 -070019import static android.view.ViewDebug.ExportedProperty;
20import static android.widget.RemoteViews.RemoteView;
21
Siva Velusamy94a6d152015-05-05 15:07:00 -070022import android.annotation.NonNull;
George Mount1c9b3132017-12-20 13:36:10 -080023import android.annotation.TestApi;
Selim Cinek9c4a7072014-11-21 17:44:34 +010024import android.app.ActivityManager;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000025import android.compat.annotation.UnsupportedAppUsage;
Romain Guy3d1728c2012-10-31 20:31:58 -070026import android.content.BroadcastReceiver;
27import android.content.ContentResolver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.res.TypedArray;
32import android.database.ContentObserver;
33import android.net.Uri;
34import android.os.Handler;
35import android.os.SystemClock;
Selim Cinek9c4a7072014-11-21 17:44:34 +010036import android.os.UserHandle;
Romain Guy3d1728c2012-10-31 20:31:58 -070037import android.provider.Settings;
38import android.text.format.DateFormat;
39import android.util.AttributeSet;
Romain Guya9cfe672012-11-05 14:16:32 -080040import android.view.RemotableViewMethod;
Siva Velusamy94a6d152015-05-05 15:07:00 -070041import android.view.ViewHierarchyEncoder;
Ashley Rose55f9f922019-01-28 19:29:36 -050042import android.view.inspector.InspectableProperty;
Romain Guy3d1728c2012-10-31 20:31:58 -070043
44import com.android.internal.R;
45
Elliott Hughescdafd372013-03-18 17:21:33 -070046import libcore.icu.LocaleData;
47
Aurimas Liutikas99441c52016-10-11 16:48:32 -070048import java.util.Calendar;
49import java.util.TimeZone;
Romain Guy3d1728c2012-10-31 20:31:58 -070050
51/**
52 * <p><code>TextClock</code> can display the current date and/or time as
53 * a formatted string.</p>
Elliott Hughescdafd372013-03-18 17:21:33 -070054 *
Romain Guy3d1728c2012-10-31 20:31:58 -070055 * <p>This view honors the 24-hour format system setting. As such, it is
56 * possible and recommended to provide two different formatting patterns:
57 * one to display the date/time in 24-hour mode and one to display the
Elliott Hughescdafd372013-03-18 17:21:33 -070058 * date/time in 12-hour mode. Most callers will want to use the defaults,
59 * though, which will be appropriate for the user's locale.</p>
60 *
Romain Guy3d1728c2012-10-31 20:31:58 -070061 * <p>It is possible to determine whether the system is currently in
62 * 24-hour mode by calling {@link #is24HourModeEnabled()}.</p>
Elliott Hughescdafd372013-03-18 17:21:33 -070063 *
Romain Guy3d1728c2012-10-31 20:31:58 -070064 * <p>The rules used by this widget to decide how to format the date and
65 * time are the following:</p>
66 * <ul>
67 * <li>In 24-hour mode:
68 * <ul>
69 * <li>Use the value returned by {@link #getFormat24Hour()} when non-null</li>
70 * <li>Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null</li>
Elliott Hughescdafd372013-03-18 17:21:33 -070071 * <li>Otherwise, use a default value appropriate for the user's locale, such as {@code h:mm a}</li>
Romain Guy3d1728c2012-10-31 20:31:58 -070072 * </ul>
73 * </li>
74 * <li>In 12-hour mode:
75 * <ul>
76 * <li>Use the value returned by {@link #getFormat12Hour()} when non-null</li>
77 * <li>Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null</li>
Elliott Hughescdafd372013-03-18 17:21:33 -070078 * <li>Otherwise, use a default value appropriate for the user's locale, such as {@code HH:mm}</li>
Romain Guy3d1728c2012-10-31 20:31:58 -070079 * </ul>
80 * </li>
81 * </ul>
Elliott Hughescdafd372013-03-18 17:21:33 -070082 *
Romain Guy3d1728c2012-10-31 20:31:58 -070083 * <p>The {@link CharSequence} instances used as formatting patterns when calling either
84 * {@link #setFormat24Hour(CharSequence)} or {@link #setFormat12Hour(CharSequence)} can
Elliott Hughescdafd372013-03-18 17:21:33 -070085 * contain styling information. To do so, use a {@link android.text.Spanned} object.
86 * Note that if you customize these strings, it is your responsibility to supply strings
87 * appropriate for formatting dates and/or times in the user's locale.</p>
88 *
Romain Guy3d1728c2012-10-31 20:31:58 -070089 * @attr ref android.R.styleable#TextClock_format12Hour
90 * @attr ref android.R.styleable#TextClock_format24Hour
91 * @attr ref android.R.styleable#TextClock_timeZone
92 */
93@RemoteView
94public class TextClock extends TextView {
95 /**
Elliott Hughescdafd372013-03-18 17:21:33 -070096 * The default formatting pattern in 12-hour mode. This pattern is used
Romain Guy3d1728c2012-10-31 20:31:58 -070097 * if {@link #setFormat12Hour(CharSequence)} is called with a null pattern
98 * or if no pattern was specified when creating an instance of this class.
Elliott Hughescdafd372013-03-18 17:21:33 -070099 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700100 * This default pattern shows only the time, hours and minutes, and an am/pm
101 * indicator.
102 *
103 * @see #setFormat12Hour(CharSequence)
104 * @see #getFormat12Hour()
Romain Guy09b19942013-04-30 11:19:39 -0700105 *
Elliott Hughescdafd372013-03-18 17:21:33 -0700106 * @deprecated Let the system use locale-appropriate defaults instead.
Romain Guy3d1728c2012-10-31 20:31:58 -0700107 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700108 @Deprecated
Elliott Hughescdafd372013-03-18 17:21:33 -0700109 public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm a";
Romain Guy3d1728c2012-10-31 20:31:58 -0700110
111 /**
Elliott Hughescdafd372013-03-18 17:21:33 -0700112 * The default formatting pattern in 24-hour mode. This pattern is used
Romain Guy3d1728c2012-10-31 20:31:58 -0700113 * if {@link #setFormat24Hour(CharSequence)} is called with a null pattern
114 * or if no pattern was specified when creating an instance of this class.
115 *
116 * This default pattern shows only the time, hours and minutes.
Elliott Hughescdafd372013-03-18 17:21:33 -0700117 *
118 * @see #setFormat24Hour(CharSequence)
119 * @see #getFormat24Hour()
Romain Guy09b19942013-04-30 11:19:39 -0700120 *
Elliott Hughescdafd372013-03-18 17:21:33 -0700121 * @deprecated Let the system use locale-appropriate defaults instead.
Romain Guy3d1728c2012-10-31 20:31:58 -0700122 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700123 @Deprecated
Elliott Hughescdafd372013-03-18 17:21:33 -0700124 public static final CharSequence DEFAULT_FORMAT_24_HOUR = "H:mm";
Romain Guy3d1728c2012-10-31 20:31:58 -0700125
Elliott Hughescdafd372013-03-18 17:21:33 -0700126 private CharSequence mFormat12;
127 private CharSequence mFormat24;
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400128 private CharSequence mDescFormat12;
129 private CharSequence mDescFormat24;
Romain Guy3d1728c2012-10-31 20:31:58 -0700130
131 @ExportedProperty
132 private CharSequence mFormat;
133 @ExportedProperty
134 private boolean mHasSeconds;
135
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400136 private CharSequence mDescFormat;
137
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000138 private boolean mRegistered;
Ian Lake851957c2017-04-10 15:51:07 -0700139 private boolean mShouldRunTicker;
Romain Guy3d1728c2012-10-31 20:31:58 -0700140
141 private Calendar mTime;
142 private String mTimeZone;
143
Selim Cinek9c4a7072014-11-21 17:44:34 +0100144 private boolean mShowCurrentUserTime;
145
John Reckd0374c62015-10-20 13:25:01 -0700146 private ContentObserver mFormatChangeObserver;
George Mount1c9b3132017-12-20 13:36:10 -0800147 // Used by tests to stop time change events from triggering the text update
148 private boolean mStopTicking;
149
John Reckd0374c62015-10-20 13:25:01 -0700150 private class FormatChangeObserver extends ContentObserver {
151
152 public FormatChangeObserver(Handler handler) {
153 super(handler);
154 }
155
Romain Guy3d1728c2012-10-31 20:31:58 -0700156 @Override
157 public void onChange(boolean selfChange) {
158 chooseFormat();
159 onTimeChanged();
160 }
161
162 @Override
163 public void onChange(boolean selfChange, Uri uri) {
164 chooseFormat();
165 onTimeChanged();
166 }
167 };
168
169 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
170 @Override
171 public void onReceive(Context context, Intent intent) {
George Mount1c9b3132017-12-20 13:36:10 -0800172 if (mStopTicking) {
173 return; // Test disabled the clock ticks
174 }
Romain Guya76f7db2012-11-06 17:35:23 -0800175 if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
176 final String timeZone = intent.getStringExtra("time-zone");
177 createTime(timeZone);
Lucas Dupin01c78382019-05-20 09:29:31 -0700178 } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
179 || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
180 return;
Romain Guy3d1728c2012-10-31 20:31:58 -0700181 }
Romain Guya76f7db2012-11-06 17:35:23 -0800182 onTimeChanged();
Romain Guy3d1728c2012-10-31 20:31:58 -0700183 }
184 };
185
186 private final Runnable mTicker = new Runnable() {
187 public void run() {
George Mount1c9b3132017-12-20 13:36:10 -0800188 if (mStopTicking) {
189 return; // Test disabled the clock ticks
190 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700191 onTimeChanged();
192
193 long now = SystemClock.uptimeMillis();
194 long next = now + (1000 - now % 1000);
195
George Mountf0aff302019-05-10 09:29:03 -0700196 Handler handler = getHandler();
197 if (handler != null) {
198 handler.postAtTime(mTicker, next);
199 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700200 }
201 };
202
203 /**
Romain Guy09b19942013-04-30 11:19:39 -0700204 * Creates a new clock using the default patterns for the current locale.
Elliott Hughescdafd372013-03-18 17:21:33 -0700205 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700206 * @param context The Context the view is running in, through which it can
207 * access the current theme, resources, etc.
208 */
209 @SuppressWarnings("UnusedDeclaration")
210 public TextClock(Context context) {
211 super(context);
212 init();
213 }
214
215 /**
216 * Creates a new clock inflated from XML. This object's properties are
217 * intialized from the attributes specified in XML.
Elliott Hughescdafd372013-03-18 17:21:33 -0700218 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700219 * This constructor uses a default style of 0, so the only attribute values
220 * applied are those in the Context's Theme and the given AttributeSet.
221 *
222 * @param context The Context the view is running in, through which it can
223 * access the current theme, resources, etc.
224 * @param attrs The attributes of the XML tag that is inflating the view
225 */
226 @SuppressWarnings("UnusedDeclaration")
227 public TextClock(Context context, AttributeSet attrs) {
228 this(context, attrs, 0);
229 }
230
231 /**
232 * Creates a new clock inflated from XML. This object's properties are
233 * intialized from the attributes specified in XML.
234 *
235 * @param context The Context the view is running in, through which it can
236 * access the current theme, resources, etc.
237 * @param attrs The attributes of the XML tag that is inflating the view
Alan Viverette617feb92013-09-09 18:09:13 -0700238 * @param defStyleAttr An attribute in the current theme that contains a
239 * reference to a style resource that supplies default values for
240 * the view. Can be 0 to not look for defaults.
Romain Guy3d1728c2012-10-31 20:31:58 -0700241 */
Alan Viverette617feb92013-09-09 18:09:13 -0700242 public TextClock(Context context, AttributeSet attrs, int defStyleAttr) {
243 this(context, attrs, defStyleAttr, 0);
244 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700245
Alan Viverette617feb92013-09-09 18:09:13 -0700246 public TextClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
247 super(context, attrs, defStyleAttr, defStyleRes);
248
249 final TypedArray a = context.obtainStyledAttributes(
250 attrs, R.styleable.TextClock, defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800251 saveAttributeDataForStyleable(context, R.styleable.TextClock,
252 attrs, a, defStyleAttr, defStyleRes);
Romain Guy3d1728c2012-10-31 20:31:58 -0700253 try {
Elliott Hughescdafd372013-03-18 17:21:33 -0700254 mFormat12 = a.getText(R.styleable.TextClock_format12Hour);
255 mFormat24 = a.getText(R.styleable.TextClock_format24Hour);
Romain Guy3d1728c2012-10-31 20:31:58 -0700256 mTimeZone = a.getString(R.styleable.TextClock_timeZone);
257 } finally {
258 a.recycle();
259 }
260
261 init();
262 }
263
264 private void init() {
Elliott Hughescdafd372013-03-18 17:21:33 -0700265 if (mFormat12 == null || mFormat24 == null) {
266 LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
267 if (mFormat12 == null) {
Elliott Hughesf7d5e0a2014-10-23 11:06:43 -0700268 mFormat12 = ld.timeFormat_hm;
Elliott Hughescdafd372013-03-18 17:21:33 -0700269 }
270 if (mFormat24 == null) {
Elliott Hughesf7d5e0a2014-10-23 11:06:43 -0700271 mFormat24 = ld.timeFormat_Hm;
Elliott Hughescdafd372013-03-18 17:21:33 -0700272 }
273 }
274
Romain Guy3d1728c2012-10-31 20:31:58 -0700275 createTime(mTimeZone);
Ian Lake851957c2017-04-10 15:51:07 -0700276 chooseFormat();
Romain Guy3d1728c2012-10-31 20:31:58 -0700277 }
278
279 private void createTime(String timeZone) {
280 if (timeZone != null) {
281 mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
282 } else {
283 mTime = Calendar.getInstance();
284 }
285 }
286
287 /**
288 * Returns the formatting pattern used to display the date and/or time
289 * in 12-hour mode. The formatting pattern syntax is described in
290 * {@link DateFormat}.
Elliott Hughescdafd372013-03-18 17:21:33 -0700291 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700292 * @return A {@link CharSequence} or null.
Elliott Hughescdafd372013-03-18 17:21:33 -0700293 *
294 * @see #setFormat12Hour(CharSequence)
295 * @see #is24HourModeEnabled()
Romain Guy3d1728c2012-10-31 20:31:58 -0700296 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500297 @InspectableProperty
Romain Guy3d1728c2012-10-31 20:31:58 -0700298 @ExportedProperty
299 public CharSequence getFormat12Hour() {
300 return mFormat12;
301 }
302
303 /**
Romain Guy09b19942013-04-30 11:19:39 -0700304 * <p>Specifies the formatting pattern used to display the date and/or time
Romain Guy3d1728c2012-10-31 20:31:58 -0700305 * in 12-hour mode. The formatting pattern syntax is described in
Romain Guy09b19942013-04-30 11:19:39 -0700306 * {@link DateFormat}.</p>
Romain Guy3d1728c2012-10-31 20:31:58 -0700307 *
Romain Guy09b19942013-04-30 11:19:39 -0700308 * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used
Romain Guy3d1728c2012-10-31 20:31:58 -0700309 * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns
Romain Guy09b19942013-04-30 11:19:39 -0700310 * are set to null, the default pattern for the current locale will be used
311 * instead.</p>
312 *
313 * <p><strong>Note:</strong> if styling is not needed, it is highly recommended
314 * you supply a format string generated by
315 * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method
316 * takes care of generating a format string adapted to the desired locale.</p>
317 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700318 *
319 * @param format A date/time formatting pattern as described in {@link DateFormat}
Elliott Hughescdafd372013-03-18 17:21:33 -0700320 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700321 * @see #getFormat12Hour()
322 * @see #is24HourModeEnabled()
Romain Guy09b19942013-04-30 11:19:39 -0700323 * @see DateFormat#getBestDateTimePattern(java.util.Locale, String)
Romain Guy3d1728c2012-10-31 20:31:58 -0700324 * @see DateFormat
Elliott Hughescdafd372013-03-18 17:21:33 -0700325 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700326 * @attr ref android.R.styleable#TextClock_format12Hour
327 */
Romain Guya9cfe672012-11-05 14:16:32 -0800328 @RemotableViewMethod
Romain Guy3d1728c2012-10-31 20:31:58 -0700329 public void setFormat12Hour(CharSequence format) {
330 mFormat12 = format;
331
332 chooseFormat();
333 onTimeChanged();
334 }
335
336 /**
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400337 * Like setFormat12Hour, but for the content description.
338 * @hide
339 */
340 public void setContentDescriptionFormat12Hour(CharSequence format) {
341 mDescFormat12 = format;
342
343 chooseFormat();
344 onTimeChanged();
345 }
346
347 /**
Romain Guy3d1728c2012-10-31 20:31:58 -0700348 * Returns the formatting pattern used to display the date and/or time
349 * in 24-hour mode. The formatting pattern syntax is described in
350 * {@link DateFormat}.
351 *
352 * @return A {@link CharSequence} or null.
353 *
354 * @see #setFormat24Hour(CharSequence)
355 * @see #is24HourModeEnabled()
356 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500357 @InspectableProperty
Romain Guy3d1728c2012-10-31 20:31:58 -0700358 @ExportedProperty
359 public CharSequence getFormat24Hour() {
360 return mFormat24;
361 }
362
363 /**
Romain Guy09b19942013-04-30 11:19:39 -0700364 * <p>Specifies the formatting pattern used to display the date and/or time
Romain Guy3d1728c2012-10-31 20:31:58 -0700365 * in 24-hour mode. The formatting pattern syntax is described in
Romain Guy09b19942013-04-30 11:19:39 -0700366 * {@link DateFormat}.</p>
Elliott Hughescdafd372013-03-18 17:21:33 -0700367 *
Romain Guy09b19942013-04-30 11:19:39 -0700368 * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used
369 * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns
370 * are set to null, the default pattern for the current locale will be used
371 * instead.</p>
372 *
373 * <p><strong>Note:</strong> if styling is not needed, it is highly recommended
374 * you supply a format string generated by
375 * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method
376 * takes care of generating a format string adapted to the desired locale.</p>
Romain Guy3d1728c2012-10-31 20:31:58 -0700377 *
378 * @param format A date/time formatting pattern as described in {@link DateFormat}
379 *
380 * @see #getFormat24Hour()
Elliott Hughescdafd372013-03-18 17:21:33 -0700381 * @see #is24HourModeEnabled()
Romain Guy09b19942013-04-30 11:19:39 -0700382 * @see DateFormat#getBestDateTimePattern(java.util.Locale, String)
Romain Guy3d1728c2012-10-31 20:31:58 -0700383 * @see DateFormat
384 *
385 * @attr ref android.R.styleable#TextClock_format24Hour
386 */
Romain Guya9cfe672012-11-05 14:16:32 -0800387 @RemotableViewMethod
Romain Guy3d1728c2012-10-31 20:31:58 -0700388 public void setFormat24Hour(CharSequence format) {
389 mFormat24 = format;
390
391 chooseFormat();
392 onTimeChanged();
393 }
394
395 /**
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400396 * Like setFormat24Hour, but for the content description.
397 * @hide
398 */
399 public void setContentDescriptionFormat24Hour(CharSequence format) {
400 mDescFormat24 = format;
401
402 chooseFormat();
403 onTimeChanged();
404 }
405
406 /**
Selim Cinek9c4a7072014-11-21 17:44:34 +0100407 * Sets whether this clock should always track the current user and not the user of the
408 * current process. This is used for single instance processes like the systemUI who need
409 * to display time for different users.
410 *
411 * @hide
412 */
413 public void setShowCurrentUserTime(boolean showCurrentUserTime) {
414 mShowCurrentUserTime = showCurrentUserTime;
415
416 chooseFormat();
417 onTimeChanged();
418 unregisterObserver();
419 registerObserver();
420 }
421
422 /**
Lucas Dupinf9583c42018-04-05 22:20:44 -0700423 * Update the displayed time if necessary and invalidate the view.
Lucas Dupinf9583c42018-04-05 22:20:44 -0700424 */
425 public void refresh() {
426 onTimeChanged();
427 invalidate();
428 }
429
430 /**
Romain Guy3d1728c2012-10-31 20:31:58 -0700431 * Indicates whether the system is currently using the 24-hour mode.
Elliott Hughescdafd372013-03-18 17:21:33 -0700432 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700433 * When the system is in 24-hour mode, this view will use the pattern
434 * returned by {@link #getFormat24Hour()}. In 12-hour mode, the pattern
435 * returned by {@link #getFormat12Hour()} is used instead.
Elliott Hughescdafd372013-03-18 17:21:33 -0700436 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700437 * If either one of the formats is null, the other format is used. If
Romain Guy09b19942013-04-30 11:19:39 -0700438 * both formats are null, the default formats for the current locale are used.
Elliott Hughescdafd372013-03-18 17:21:33 -0700439 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700440 * @return true if time should be displayed in 24-hour format, false if it
441 * should be displayed in 12-hour format.
Elliott Hughescdafd372013-03-18 17:21:33 -0700442 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700443 * @see #setFormat12Hour(CharSequence)
Elliott Hughescdafd372013-03-18 17:21:33 -0700444 * @see #getFormat12Hour()
Romain Guy3d1728c2012-10-31 20:31:58 -0700445 * @see #setFormat24Hour(CharSequence)
Elliott Hughescdafd372013-03-18 17:21:33 -0700446 * @see #getFormat24Hour()
Romain Guy3d1728c2012-10-31 20:31:58 -0700447 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500448 @InspectableProperty(hasAttributeId = false)
Romain Guy3d1728c2012-10-31 20:31:58 -0700449 public boolean is24HourModeEnabled() {
Selim Cinek9c4a7072014-11-21 17:44:34 +0100450 if (mShowCurrentUserTime) {
451 return DateFormat.is24HourFormat(getContext(), ActivityManager.getCurrentUser());
452 } else {
453 return DateFormat.is24HourFormat(getContext());
454 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700455 }
456
457 /**
458 * Indicates which time zone is currently used by this view.
Elliott Hughescdafd372013-03-18 17:21:33 -0700459 *
Romain Guy3d1728c2012-10-31 20:31:58 -0700460 * @return The ID of the current time zone or null if the default time zone,
461 * as set by the user, must be used
462 *
463 * @see TimeZone
464 * @see java.util.TimeZone#getAvailableIDs()
Elliott Hughescdafd372013-03-18 17:21:33 -0700465 * @see #setTimeZone(String)
Romain Guy3d1728c2012-10-31 20:31:58 -0700466 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500467 @InspectableProperty
Romain Guy3d1728c2012-10-31 20:31:58 -0700468 public String getTimeZone() {
469 return mTimeZone;
470 }
471
472 /**
473 * Sets the specified time zone to use in this clock. When the time zone
474 * is set through this method, system time zone changes (when the user
475 * sets the time zone in settings for instance) will be ignored.
476 *
477 * @param timeZone The desired time zone's ID as specified in {@link TimeZone}
478 * or null to user the time zone specified by the user
479 * (system time zone)
480 *
481 * @see #getTimeZone()
482 * @see java.util.TimeZone#getAvailableIDs()
483 * @see TimeZone#getTimeZone(String)
484 *
485 * @attr ref android.R.styleable#TextClock_timeZone
486 */
Romain Guya9cfe672012-11-05 14:16:32 -0800487 @RemotableViewMethod
Romain Guy3d1728c2012-10-31 20:31:58 -0700488 public void setTimeZone(String timeZone) {
489 mTimeZone = timeZone;
490
491 createTime(timeZone);
492 onTimeChanged();
493 }
494
495 /**
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800496 * Returns the current format string. Always valid after constructor has
497 * finished, and will never be {@code null}.
498 *
499 * @hide
500 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100501 @UnsupportedAppUsage
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800502 public CharSequence getFormat() {
503 return mFormat;
504 }
505
506 /**
Romain Guy3d1728c2012-10-31 20:31:58 -0700507 * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
508 * depending on whether the user has selected 24-hour format.
Romain Guy3d1728c2012-10-31 20:31:58 -0700509 */
Ian Lake851957c2017-04-10 15:51:07 -0700510 private void chooseFormat() {
Romain Guy3d1728c2012-10-31 20:31:58 -0700511 final boolean format24Requested = is24HourModeEnabled();
512
Elliott Hughescdafd372013-03-18 17:21:33 -0700513 LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
514
Romain Guy3d1728c2012-10-31 20:31:58 -0700515 if (format24Requested) {
Elliott Hughesf7d5e0a2014-10-23 11:06:43 -0700516 mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400517 mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
Romain Guy3d1728c2012-10-31 20:31:58 -0700518 } else {
Elliott Hughesf7d5e0a2014-10-23 11:06:43 -0700519 mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
Dan Sandler25ffc7a2015-06-11 09:17:07 -0400520 mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
Romain Guy3d1728c2012-10-31 20:31:58 -0700521 }
522
523 boolean hadSeconds = mHasSeconds;
524 mHasSeconds = DateFormat.hasSeconds(mFormat);
525
Ian Lake851957c2017-04-10 15:51:07 -0700526 if (mShouldRunTicker && hadSeconds != mHasSeconds) {
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000527 if (hadSeconds) getHandler().removeCallbacks(mTicker);
528 else mTicker.run();
Romain Guy3d1728c2012-10-31 20:31:58 -0700529 }
530 }
531
532 /**
533 * Returns a if not null, else return b if not null, else return c.
534 */
535 private static CharSequence abc(CharSequence a, CharSequence b, CharSequence c) {
536 return a == null ? (b == null ? c : b) : a;
537 }
538
539 @Override
Ian Lake851957c2017-04-10 15:51:07 -0700540 protected void onAttachedToWindow() {
541 super.onAttachedToWindow();
542
543 if (!mRegistered) {
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000544 mRegistered = true;
Romain Guy3d1728c2012-10-31 20:31:58 -0700545
546 registerReceiver();
547 registerObserver();
548
549 createTime(mTimeZone);
Ian Lake851957c2017-04-10 15:51:07 -0700550 }
551 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700552
Ian Lake851957c2017-04-10 15:51:07 -0700553 @Override
554 public void onVisibilityAggregated(boolean isVisible) {
555 super.onVisibilityAggregated(isVisible);
556
557 if (!mShouldRunTicker && isVisible) {
558 mShouldRunTicker = true;
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000559 if (mHasSeconds) {
560 mTicker.run();
561 } else {
562 onTimeChanged();
Romain Guy3d1728c2012-10-31 20:31:58 -0700563 }
Ian Lake851957c2017-04-10 15:51:07 -0700564 } else if (mShouldRunTicker && !isVisible) {
565 mShouldRunTicker = false;
566 getHandler().removeCallbacks(mTicker);
567 }
568 }
569
570 @Override
571 protected void onDetachedFromWindow() {
572 super.onDetachedFromWindow();
573
574 if (mRegistered) {
Romain Guy3d1728c2012-10-31 20:31:58 -0700575 unregisterReceiver();
576 unregisterObserver();
577
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000578 mRegistered = false;
Romain Guy3d1728c2012-10-31 20:31:58 -0700579 }
580 }
581
George Mount1c9b3132017-12-20 13:36:10 -0800582 /**
583 * Used by tests to stop the clock tick from updating the text.
584 * @hide
585 */
586 @TestApi
587 public void disableClockTick() {
588 mStopTicking = true;
589 }
590
Romain Guy3d1728c2012-10-31 20:31:58 -0700591 private void registerReceiver() {
592 final IntentFilter filter = new IntentFilter();
593
594 filter.addAction(Intent.ACTION_TIME_TICK);
595 filter.addAction(Intent.ACTION_TIME_CHANGED);
596 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
597
Oleksandr Peletskyi5a331062016-04-11 12:27:51 +0200598 // OK, this is gross but needed. This class is supported by the
599 // remote views mechanism and as a part of that the remote views
600 // can be inflated by a context for another user without the app
601 // having interact users permission - just for loading resources.
602 // For example, when adding widgets from a managed profile to the
603 // home screen. Therefore, we register the receiver as the user
604 // the app is running as not the one the context is for.
605 getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
606 filter, null, getHandler());
Romain Guy3d1728c2012-10-31 20:31:58 -0700607 }
608
609 private void registerObserver() {
Ian Pedowitzcbba5f32017-03-22 02:09:13 +0000610 if (mRegistered) {
John Reckd0374c62015-10-20 13:25:01 -0700611 if (mFormatChangeObserver == null) {
612 mFormatChangeObserver = new FormatChangeObserver(getHandler());
613 }
614 final ContentResolver resolver = getContext().getContentResolver();
George Mount3e3a08e2017-12-07 07:34:07 -0800615 Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
John Reckd0374c62015-10-20 13:25:01 -0700616 if (mShowCurrentUserTime) {
George Mount3e3a08e2017-12-07 07:34:07 -0800617 resolver.registerContentObserver(uri, true,
John Reckd0374c62015-10-20 13:25:01 -0700618 mFormatChangeObserver, UserHandle.USER_ALL);
619 } else {
Bookatzea05bd52019-02-06 13:14:52 -0800620 // UserHandle.myUserId() is needed. This class is supported by the
621 // remote views mechanism and as a part of that the remote views
622 // can be inflated by a context for another user without the app
623 // having interact users permission - just for loading resources.
624 // For example, when adding widgets from a managed profile to the
625 // home screen. Therefore, we register the ContentObserver with the user
626 // the app is running (e.g. the launcher) and not the user of the
627 // context (e.g. the widget's profile).
George Mount3e3a08e2017-12-07 07:34:07 -0800628 resolver.registerContentObserver(uri, true,
Bookatzea05bd52019-02-06 13:14:52 -0800629 mFormatChangeObserver, UserHandle.myUserId());
John Reckd0374c62015-10-20 13:25:01 -0700630 }
Selim Cinek9c4a7072014-11-21 17:44:34 +0100631 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700632 }
633
634 private void unregisterReceiver() {
635 getContext().unregisterReceiver(mIntentReceiver);
636 }
637
638 private void unregisterObserver() {
John Reckd0374c62015-10-20 13:25:01 -0700639 if (mFormatChangeObserver != null) {
640 final ContentResolver resolver = getContext().getContentResolver();
641 resolver.unregisterContentObserver(mFormatChangeObserver);
642 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700643 }
644
Ian Lake851957c2017-04-10 15:51:07 -0700645 /**
646 * Update the displayed time if this view and its ancestors and window is visible
647 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100648 @UnsupportedAppUsage
Romain Guy3d1728c2012-10-31 20:31:58 -0700649 private void onTimeChanged() {
Lucas Dupin01c78382019-05-20 09:29:31 -0700650 mTime.setTimeInMillis(System.currentTimeMillis());
651 setText(DateFormat.format(mFormat, mTime));
652 setContentDescription(DateFormat.format(mDescFormat, mTime));
Romain Guy3d1728c2012-10-31 20:31:58 -0700653 }
Siva Velusamy94a6d152015-05-05 15:07:00 -0700654
655 /** @hide */
656 @Override
657 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
658 super.encodeProperties(stream);
659
660 CharSequence s = getFormat12Hour();
661 stream.addProperty("format12Hour", s == null ? null : s.toString());
662
663 s = getFormat24Hour();
664 stream.addProperty("format24Hour", s == null ? null : s.toString());
665 stream.addProperty("format", mFormat == null ? null : mFormat.toString());
666 stream.addProperty("hasSeconds", mHasSeconds);
667 }
Romain Guy3d1728c2012-10-31 20:31:58 -0700668}