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 17 package android.widget; 18 19 import static android.os.Process.myUserHandle; 20 import static android.view.ViewDebug.ExportedProperty; 21 import static android.widget.RemoteViews.RemoteView; 22 23 import android.annotation.NonNull; 24 import android.annotation.TestApi; 25 import android.app.ActivityManager; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.res.TypedArray; 32 import android.database.ContentObserver; 33 import android.icu.text.DateTimePatternGenerator; 34 import android.net.Uri; 35 import android.os.Build; 36 import android.os.Handler; 37 import android.os.UserHandle; 38 import android.provider.Settings; 39 import android.text.format.DateFormat; 40 import android.util.AttributeSet; 41 import android.view.RemotableViewMethod; 42 import android.view.ViewHierarchyEncoder; 43 import android.view.inspector.InspectableProperty; 44 45 import com.android.internal.R; 46 import com.android.internal.util.Preconditions; 47 48 import java.time.DateTimeException; 49 import java.time.Duration; 50 import java.time.Instant; 51 import java.time.ZoneId; 52 import java.time.ZonedDateTime; 53 import java.util.Calendar; 54 import java.util.TimeZone; 55 56 /** 57 * <p><code>TextClock</code> can display the current date and/or time as 58 * a formatted string.</p> 59 * 60 * <p>This view honors the 24-hour format system setting. As such, it is 61 * possible and recommended to provide two different formatting patterns: 62 * one to display the date/time in 24-hour mode and one to display the 63 * date/time in 12-hour mode. Most callers will want to use the defaults, 64 * though, which will be appropriate for the user's locale.</p> 65 * 66 * <p>It is possible to determine whether the system is currently in 67 * 24-hour mode by calling {@link #is24HourModeEnabled()}.</p> 68 * 69 * <p>The rules used by this widget to decide how to format the date and 70 * time are the following:</p> 71 * <ul> 72 * <li>In 24-hour mode: 73 * <ul> 74 * <li>Use the value returned by {@link #getFormat24Hour()} when non-null</li> 75 * <li>Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null</li> 76 * <li>Otherwise, use a default value appropriate for the user's locale, such as {@code h:mm a}</li> 77 * </ul> 78 * </li> 79 * <li>In 12-hour mode: 80 * <ul> 81 * <li>Use the value returned by {@link #getFormat12Hour()} when non-null</li> 82 * <li>Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null</li> 83 * <li>Otherwise, use a default value appropriate for the user's locale, such as {@code HH:mm}</li> 84 * </ul> 85 * </li> 86 * </ul> 87 * 88 * <p>The {@link CharSequence} instances used as formatting patterns when calling either 89 * {@link #setFormat24Hour(CharSequence)} or {@link #setFormat12Hour(CharSequence)} can 90 * contain styling information. To do so, use a {@link android.text.Spanned} object. 91 * Note that if you customize these strings, it is your responsibility to supply strings 92 * appropriate for formatting dates and/or times in the user's locale.</p> 93 * 94 * @attr ref android.R.styleable#TextClock_format12Hour 95 * @attr ref android.R.styleable#TextClock_format24Hour 96 * @attr ref android.R.styleable#TextClock_timeZone 97 */ 98 @RemoteView 99 public class TextClock extends TextView { 100 /** 101 * The default formatting pattern in 12-hour mode. This pattern is used 102 * if {@link #setFormat12Hour(CharSequence)} is called with a null pattern 103 * or if no pattern was specified when creating an instance of this class. 104 * 105 * This default pattern shows only the time, hours and minutes, and an am/pm 106 * indicator. 107 * 108 * @see #setFormat12Hour(CharSequence) 109 * @see #getFormat12Hour() 110 * 111 * @deprecated Let the system use locale-appropriate defaults instead. 112 */ 113 @Deprecated 114 public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm a"; 115 116 /** 117 * The default formatting pattern in 24-hour mode. This pattern is used 118 * if {@link #setFormat24Hour(CharSequence)} is called with a null pattern 119 * or if no pattern was specified when creating an instance of this class. 120 * 121 * This default pattern shows only the time, hours and minutes. 122 * 123 * @see #setFormat24Hour(CharSequence) 124 * @see #getFormat24Hour() 125 * 126 * @deprecated Let the system use locale-appropriate defaults instead. 127 */ 128 @Deprecated 129 public static final CharSequence DEFAULT_FORMAT_24_HOUR = "H:mm"; 130 131 private CharSequence mFormat12; 132 private CharSequence mFormat24; 133 private CharSequence mDescFormat12; 134 private CharSequence mDescFormat24; 135 136 @ExportedProperty 137 private CharSequence mFormat; 138 @ExportedProperty 139 private boolean mHasSeconds; 140 141 private CharSequence mDescFormat; 142 143 private boolean mRegistered; 144 private boolean mShouldRunTicker; 145 146 private ClockEventDelegate mClockEventDelegate; 147 148 private Calendar mTime; 149 private String mTimeZone; 150 151 private boolean mShowCurrentUserTime; 152 153 private ContentObserver mFormatChangeObserver; 154 // Used by tests to stop time change events from triggering the text update 155 private boolean mStopTicking; 156 157 private class FormatChangeObserver extends ContentObserver { 158 FormatChangeObserver(Handler handler)159 public FormatChangeObserver(Handler handler) { 160 super(handler); 161 } 162 163 @Override onChange(boolean selfChange)164 public void onChange(boolean selfChange) { 165 chooseFormat(); 166 onTimeChanged(); 167 } 168 169 @Override onChange(boolean selfChange, Uri uri)170 public void onChange(boolean selfChange, Uri uri) { 171 chooseFormat(); 172 onTimeChanged(); 173 } 174 }; 175 176 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 177 @Override 178 public void onReceive(Context context, Intent intent) { 179 if (mStopTicking) { 180 return; // Test disabled the clock ticks 181 } 182 if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { 183 final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE); 184 createTime(timeZone); 185 } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { 186 return; 187 } 188 onTimeChanged(); 189 } 190 }; 191 192 private final Runnable mTicker = new Runnable() { 193 public void run() { 194 removeCallbacks(this); 195 if (mStopTicking || !mShouldRunTicker) { 196 return; // Test disabled the clock ticks 197 } 198 onTimeChanged(); 199 200 Instant now = mTime.toInstant(); 201 ZoneId zone = mTime.getTimeZone().toZoneId(); 202 203 ZonedDateTime nextTick; 204 if (mHasSeconds) { 205 nextTick = now.atZone(zone).plusSeconds(1).withNano(0); 206 } else { 207 nextTick = now.atZone(zone).plusMinutes(1).withSecond(0).withNano(0); 208 } 209 210 long millisUntilNextTick = Duration.between(now, nextTick.toInstant()).toMillis(); 211 if (millisUntilNextTick <= 0) { 212 // This should never happen, but if it does, then tick again in a second. 213 millisUntilNextTick = 1000; 214 } 215 216 postDelayed(this, millisUntilNextTick); 217 } 218 }; 219 220 /** 221 * Creates a new clock using the default patterns for the current locale. 222 * 223 * @param context The Context the view is running in, through which it can 224 * access the current theme, resources, etc. 225 */ 226 @SuppressWarnings("UnusedDeclaration") TextClock(Context context)227 public TextClock(Context context) { 228 super(context); 229 init(); 230 } 231 232 /** 233 * Creates a new clock inflated from XML. This object's properties are 234 * intialized from the attributes specified in XML. 235 * 236 * This constructor uses a default style of 0, so the only attribute values 237 * applied are those in the Context's Theme and the given AttributeSet. 238 * 239 * @param context The Context the view is running in, through which it can 240 * access the current theme, resources, etc. 241 * @param attrs The attributes of the XML tag that is inflating the view 242 */ 243 @SuppressWarnings("UnusedDeclaration") TextClock(Context context, AttributeSet attrs)244 public TextClock(Context context, AttributeSet attrs) { 245 this(context, attrs, 0); 246 } 247 248 /** 249 * Creates a new clock inflated from XML. This object's properties are 250 * intialized from the attributes specified in XML. 251 * 252 * @param context The Context the view is running in, through which it can 253 * access the current theme, resources, etc. 254 * @param attrs The attributes of the XML tag that is inflating the view 255 * @param defStyleAttr An attribute in the current theme that contains a 256 * reference to a style resource that supplies default values for 257 * the view. Can be 0 to not look for defaults. 258 */ TextClock(Context context, AttributeSet attrs, int defStyleAttr)259 public TextClock(Context context, AttributeSet attrs, int defStyleAttr) { 260 this(context, attrs, defStyleAttr, 0); 261 } 262 TextClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)263 public TextClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 264 super(context, attrs, defStyleAttr, defStyleRes); 265 266 final TypedArray a = context.obtainStyledAttributes( 267 attrs, R.styleable.TextClock, defStyleAttr, defStyleRes); 268 saveAttributeDataForStyleable(context, R.styleable.TextClock, 269 attrs, a, defStyleAttr, defStyleRes); 270 try { 271 mFormat12 = a.getText(R.styleable.TextClock_format12Hour); 272 mFormat24 = a.getText(R.styleable.TextClock_format24Hour); 273 mTimeZone = a.getString(R.styleable.TextClock_timeZone); 274 } finally { 275 a.recycle(); 276 } 277 278 init(); 279 } 280 init()281 private void init() { 282 if (mFormat12 == null) { 283 mFormat12 = getBestDateTimePattern("hm"); 284 } 285 if (mFormat24 == null) { 286 mFormat24 = getBestDateTimePattern("Hm"); 287 } 288 mClockEventDelegate = new ClockEventDelegate(getContext()); 289 290 createTime(mTimeZone); 291 chooseFormat(); 292 } 293 createTime(String timeZone)294 private void createTime(String timeZone) { 295 TimeZone tz = null; 296 if (timeZone == null) { 297 tz = TimeZone.getDefault(); 298 // Note that mTimeZone should always be null if timeZone is. 299 } else { 300 tz = TimeZone.getTimeZone(timeZone); 301 try { 302 // Try converting this TZ to a zoneId to make sure it's valid. This 303 // performs a different set of checks than TimeZone.getTimeZone so 304 // we can avoid exceptions later when we do need this conversion. 305 tz.toZoneId(); 306 } catch (DateTimeException ex) { 307 // If we're here, the user supplied timezone is invalid, so reset 308 // mTimeZone to something sane. 309 tz = TimeZone.getDefault(); 310 mTimeZone = tz.getID(); 311 } 312 } 313 314 mTime = Calendar.getInstance(tz); 315 } 316 317 /** 318 * Returns the formatting pattern used to display the date and/or time 319 * in 12-hour mode. The formatting pattern syntax is described in 320 * {@link DateFormat}. 321 * 322 * @return A {@link CharSequence} or null. 323 * 324 * @see #setFormat12Hour(CharSequence) 325 * @see #is24HourModeEnabled() 326 */ 327 @InspectableProperty 328 @ExportedProperty getFormat12Hour()329 public CharSequence getFormat12Hour() { 330 return mFormat12; 331 } 332 333 /** 334 * <p>Specifies the formatting pattern used to display the date and/or time 335 * in 12-hour mode. The formatting pattern syntax is described in 336 * {@link DateFormat}.</p> 337 * 338 * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used 339 * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns 340 * are set to null, the default pattern for the current locale will be used 341 * instead.</p> 342 * 343 * <p><strong>Note:</strong> if styling is not needed, it is highly recommended 344 * you supply a format string generated by 345 * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method 346 * takes care of generating a format string adapted to the desired locale.</p> 347 * 348 * 349 * @param format A date/time formatting pattern as described in {@link DateFormat} 350 * 351 * @see #getFormat12Hour() 352 * @see #is24HourModeEnabled() 353 * @see DateFormat#getBestDateTimePattern(java.util.Locale, String) 354 * @see DateFormat 355 * 356 * @attr ref android.R.styleable#TextClock_format12Hour 357 */ 358 @RemotableViewMethod setFormat12Hour(CharSequence format)359 public void setFormat12Hour(CharSequence format) { 360 mFormat12 = format; 361 362 chooseFormat(); 363 onTimeChanged(); 364 } 365 366 /** 367 * Like setFormat12Hour, but for the content description. 368 * @hide 369 */ setContentDescriptionFormat12Hour(CharSequence format)370 public void setContentDescriptionFormat12Hour(CharSequence format) { 371 mDescFormat12 = format; 372 373 chooseFormat(); 374 onTimeChanged(); 375 } 376 377 /** 378 * Returns the formatting pattern used to display the date and/or time 379 * in 24-hour mode. The formatting pattern syntax is described in 380 * {@link DateFormat}. 381 * 382 * @return A {@link CharSequence} or null. 383 * 384 * @see #setFormat24Hour(CharSequence) 385 * @see #is24HourModeEnabled() 386 */ 387 @InspectableProperty 388 @ExportedProperty getFormat24Hour()389 public CharSequence getFormat24Hour() { 390 return mFormat24; 391 } 392 393 /** 394 * <p>Specifies the formatting pattern used to display the date and/or time 395 * in 24-hour mode. The formatting pattern syntax is described in 396 * {@link DateFormat}.</p> 397 * 398 * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used 399 * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns 400 * are set to null, the default pattern for the current locale will be used 401 * instead.</p> 402 * 403 * <p><strong>Note:</strong> if styling is not needed, it is highly recommended 404 * you supply a format string generated by 405 * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method 406 * takes care of generating a format string adapted to the desired locale.</p> 407 * 408 * @param format A date/time formatting pattern as described in {@link DateFormat} 409 * 410 * @see #getFormat24Hour() 411 * @see #is24HourModeEnabled() 412 * @see DateFormat#getBestDateTimePattern(java.util.Locale, String) 413 * @see DateFormat 414 * 415 * @attr ref android.R.styleable#TextClock_format24Hour 416 */ 417 @RemotableViewMethod setFormat24Hour(CharSequence format)418 public void setFormat24Hour(CharSequence format) { 419 mFormat24 = format; 420 421 chooseFormat(); 422 onTimeChanged(); 423 } 424 425 /** 426 * Like setFormat24Hour, but for the content description. 427 * @hide 428 */ setContentDescriptionFormat24Hour(CharSequence format)429 public void setContentDescriptionFormat24Hour(CharSequence format) { 430 mDescFormat24 = format; 431 432 chooseFormat(); 433 onTimeChanged(); 434 } 435 436 /** 437 * Sets whether this clock should always track the current user and not the user of the 438 * current process. This is used for single instance processes like the systemUI who need 439 * to display time for different users. 440 * 441 * @hide 442 */ setShowCurrentUserTime(boolean showCurrentUserTime)443 public void setShowCurrentUserTime(boolean showCurrentUserTime) { 444 mShowCurrentUserTime = showCurrentUserTime; 445 446 chooseFormat(); 447 onTimeChanged(); 448 unregisterObserver(); 449 registerObserver(); 450 } 451 452 /** 453 * Sets a delegate to handle clock event registration. This must be called before the view is 454 * attached to the window 455 * 456 * @hide 457 */ setClockEventDelegate(ClockEventDelegate delegate)458 public void setClockEventDelegate(ClockEventDelegate delegate) { 459 Preconditions.checkState(!mRegistered, "Clock events already registered"); 460 mClockEventDelegate = delegate; 461 } 462 463 /** 464 * Update the displayed time if necessary and invalidate the view. 465 */ refreshTime()466 public void refreshTime() { 467 onTimeChanged(); 468 invalidate(); 469 } 470 471 /** 472 * Indicates whether the system is currently using the 24-hour mode. 473 * 474 * When the system is in 24-hour mode, this view will use the pattern 475 * returned by {@link #getFormat24Hour()}. In 12-hour mode, the pattern 476 * returned by {@link #getFormat12Hour()} is used instead. 477 * 478 * If either one of the formats is null, the other format is used. If 479 * both formats are null, the default formats for the current locale are used. 480 * 481 * @return true if time should be displayed in 24-hour format, false if it 482 * should be displayed in 12-hour format. 483 * 484 * @see #setFormat12Hour(CharSequence) 485 * @see #getFormat12Hour() 486 * @see #setFormat24Hour(CharSequence) 487 * @see #getFormat24Hour() 488 */ 489 @InspectableProperty(hasAttributeId = false) is24HourModeEnabled()490 public boolean is24HourModeEnabled() { 491 if (mShowCurrentUserTime) { 492 return DateFormat.is24HourFormat(getContext(), ActivityManager.getCurrentUser()); 493 } else { 494 return DateFormat.is24HourFormat(getContext()); 495 } 496 } 497 498 /** 499 * Indicates which time zone is currently used by this view. 500 * 501 * @return The ID of the current time zone or null if the default time zone, 502 * as set by the user, must be used 503 * 504 * @see TimeZone 505 * @see java.util.TimeZone#getAvailableIDs() 506 * @see #setTimeZone(String) 507 */ 508 @InspectableProperty getTimeZone()509 public String getTimeZone() { 510 return mTimeZone; 511 } 512 513 /** 514 * Sets the specified time zone to use in this clock. When the time zone 515 * is set through this method, system time zone changes (when the user 516 * sets the time zone in settings for instance) will be ignored. 517 * 518 * @param timeZone The desired time zone's ID as specified in {@link TimeZone} 519 * or null to user the time zone specified by the user 520 * (system time zone) 521 * 522 * @see #getTimeZone() 523 * @see java.util.TimeZone#getAvailableIDs() 524 * @see TimeZone#getTimeZone(String) 525 * 526 * @attr ref android.R.styleable#TextClock_timeZone 527 */ 528 @RemotableViewMethod setTimeZone(String timeZone)529 public void setTimeZone(String timeZone) { 530 mTimeZone = timeZone; 531 532 createTime(timeZone); 533 onTimeChanged(); 534 } 535 536 /** 537 * Returns the current format string. Always valid after constructor has 538 * finished, and will never be {@code null}. 539 * 540 * @hide 541 */ 542 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getFormat()543 public CharSequence getFormat() { 544 return mFormat; 545 } 546 547 /** 548 * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} 549 * depending on whether the user has selected 24-hour format. 550 */ chooseFormat()551 private void chooseFormat() { 552 final boolean format24Requested = is24HourModeEnabled(); 553 554 if (format24Requested) { 555 mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm")); 556 mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat); 557 } else { 558 mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm")); 559 mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat); 560 } 561 562 boolean hadSeconds = mHasSeconds; 563 mHasSeconds = DateFormat.hasSeconds(mFormat); 564 565 if (mShouldRunTicker && hadSeconds != mHasSeconds) { 566 mTicker.run(); 567 } 568 } 569 getBestDateTimePattern(String skeleton)570 private String getBestDateTimePattern(String skeleton) { 571 DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance( 572 getContext().getResources().getConfiguration().locale); 573 return dtpg.getBestPattern(skeleton); 574 } 575 576 /** 577 * Returns a if not null, else return b if not null, else return c. 578 */ abc(CharSequence a, CharSequence b, CharSequence c)579 private static CharSequence abc(CharSequence a, CharSequence b, CharSequence c) { 580 return a == null ? (b == null ? c : b) : a; 581 } 582 583 @Override onAttachedToWindow()584 protected void onAttachedToWindow() { 585 super.onAttachedToWindow(); 586 587 if (!mRegistered) { 588 mRegistered = true; 589 590 mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler()); 591 registerObserver(); 592 593 createTime(mTimeZone); 594 } 595 } 596 597 @Override onVisibilityAggregated(boolean isVisible)598 public void onVisibilityAggregated(boolean isVisible) { 599 super.onVisibilityAggregated(isVisible); 600 601 if (!mShouldRunTicker && isVisible) { 602 mShouldRunTicker = true; 603 mTicker.run(); 604 } else if (mShouldRunTicker && !isVisible) { 605 mShouldRunTicker = false; 606 removeCallbacks(mTicker); 607 } 608 } 609 610 @Override onDetachedFromWindow()611 protected void onDetachedFromWindow() { 612 super.onDetachedFromWindow(); 613 614 if (mRegistered) { 615 mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver); 616 unregisterObserver(); 617 618 mRegistered = false; 619 } 620 } 621 622 /** 623 * Used by tests to stop the clock tick from updating the text. 624 * @hide 625 */ 626 @TestApi disableClockTick()627 public void disableClockTick() { 628 mStopTicking = true; 629 } 630 registerObserver()631 private void registerObserver() { 632 if (mRegistered) { 633 if (mFormatChangeObserver == null) { 634 mFormatChangeObserver = new FormatChangeObserver(getHandler()); 635 } 636 // UserHandle.myUserId() is needed. This class is supported by the 637 // remote views mechanism and as a part of that the remote views 638 // can be inflated by a context for another user without the app 639 // having interact users permission - just for loading resources. 640 // For example, when adding widgets from a managed profile to the 641 // home screen. Therefore, we register the ContentObserver with the user 642 // the app is running (e.g. the launcher) and not the user of the 643 // context (e.g. the widget's profile). 644 int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId(); 645 mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle); 646 } 647 } 648 unregisterObserver()649 private void unregisterObserver() { 650 if (mFormatChangeObserver != null) { 651 mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver); 652 } 653 } 654 655 /** 656 * Update the displayed time if this view and its ancestors and window is visible 657 */ 658 @UnsupportedAppUsage onTimeChanged()659 private void onTimeChanged() { 660 mTime.setTimeInMillis(System.currentTimeMillis()); 661 setText(DateFormat.format(mFormat, mTime)); 662 setContentDescription(DateFormat.format(mDescFormat, mTime)); 663 } 664 665 /** @hide */ 666 @Override encodeProperties(@onNull ViewHierarchyEncoder stream)667 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { 668 super.encodeProperties(stream); 669 670 CharSequence s = getFormat12Hour(); 671 stream.addProperty("format12Hour", s == null ? null : s.toString()); 672 673 s = getFormat24Hour(); 674 stream.addProperty("format24Hour", s == null ? null : s.toString()); 675 stream.addProperty("format", mFormat == null ? null : mFormat.toString()); 676 stream.addProperty("hasSeconds", mHasSeconds); 677 } 678 679 /** 680 * Utility class to delegate some system event handling to allow overring the default behavior 681 * 682 * @hide 683 */ 684 public static class ClockEventDelegate { 685 686 private final Context mContext; 687 ClockEventDelegate(Context context)688 public ClockEventDelegate(Context context) { 689 mContext = context; 690 } 691 692 /** 693 * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and 694 * {@link Intent#ACTION_TIMEZONE_CHANGED} 695 * 696 * OK, this is gross but needed. This class is supported by the remote views mechanism and 697 * as a part of that the remote views can be inflated by a context for another user without 698 * the app having interact users permission - just for loading resources. For example, 699 * when adding widgets from a managed profile to the home screen. Therefore, we register 700 * the receiver as the user the app is running as not the one the context is for. 701 */ registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler)702 public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) { 703 final IntentFilter filter = new IntentFilter(); 704 705 filter.addAction(Intent.ACTION_TIME_CHANGED); 706 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 707 708 mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler); 709 } 710 711 /** 712 * Unregisters a previously registered receiver 713 */ unregisterTimeChangeReceiver(BroadcastReceiver receiver)714 public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) { 715 mContext.unregisterReceiver(receiver); 716 } 717 718 /** 719 * Registers an observer for time format changes 720 */ registerFormatChangeObserver(ContentObserver observer, int userHandle)721 public void registerFormatChangeObserver(ContentObserver observer, int userHandle) { 722 Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24); 723 mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle); 724 } 725 726 /** 727 * Unregisters a previously registered observer 728 */ unregisterFormatChangeObserver(ContentObserver observer)729 public void unregisterFormatChangeObserver(ContentObserver observer) { 730 mContext.getContentResolver().unregisterContentObserver(observer); 731 } 732 } 733 } 734