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