|  | /* | 
|  | * Copyright (C) 2012 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package android.widget; | 
|  |  | 
|  | import static android.view.ViewDebug.ExportedProperty; | 
|  | import static android.widget.RemoteViews.RemoteView; | 
|  |  | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.TestApi; | 
|  | import android.app.ActivityManager; | 
|  | import android.compat.annotation.UnsupportedAppUsage; | 
|  | import android.content.BroadcastReceiver; | 
|  | import android.content.ContentResolver; | 
|  | import android.content.Context; | 
|  | import android.content.Intent; | 
|  | import android.content.IntentFilter; | 
|  | import android.content.res.TypedArray; | 
|  | import android.database.ContentObserver; | 
|  | import android.icu.text.DateTimePatternGenerator; | 
|  | import android.net.Uri; | 
|  | import android.os.Build; | 
|  | import android.os.Handler; | 
|  | import android.os.UserHandle; | 
|  | import android.provider.Settings; | 
|  | import android.text.format.DateFormat; | 
|  | import android.util.AttributeSet; | 
|  | import android.view.RemotableViewMethod; | 
|  | import android.view.ViewHierarchyEncoder; | 
|  | import android.view.inspector.InspectableProperty; | 
|  |  | 
|  | import com.android.internal.R; | 
|  |  | 
|  | import java.time.Duration; | 
|  | import java.time.Instant; | 
|  | import java.time.ZoneId; | 
|  | import java.time.ZonedDateTime; | 
|  | import java.util.Calendar; | 
|  | import java.util.TimeZone; | 
|  |  | 
|  | /** | 
|  | * <p><code>TextClock</code> can display the current date and/or time as | 
|  | * a formatted string.</p> | 
|  | * | 
|  | * <p>This view honors the 24-hour format system setting. As such, it is | 
|  | * possible and recommended to provide two different formatting patterns: | 
|  | * one to display the date/time in 24-hour mode and one to display the | 
|  | * date/time in 12-hour mode. Most callers will want to use the defaults, | 
|  | * though, which will be appropriate for the user's locale.</p> | 
|  | * | 
|  | * <p>It is possible to determine whether the system is currently in | 
|  | * 24-hour mode by calling {@link #is24HourModeEnabled()}.</p> | 
|  | * | 
|  | * <p>The rules used by this widget to decide how to format the date and | 
|  | * time are the following:</p> | 
|  | * <ul> | 
|  | *     <li>In 24-hour mode: | 
|  | *         <ul> | 
|  | *             <li>Use the value returned by {@link #getFormat24Hour()} when non-null</li> | 
|  | *             <li>Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null</li> | 
|  | *             <li>Otherwise, use a default value appropriate for the user's locale, such as {@code h:mm a}</li> | 
|  | *         </ul> | 
|  | *     </li> | 
|  | *     <li>In 12-hour mode: | 
|  | *         <ul> | 
|  | *             <li>Use the value returned by {@link #getFormat12Hour()} when non-null</li> | 
|  | *             <li>Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null</li> | 
|  | *             <li>Otherwise, use a default value appropriate for the user's locale, such as {@code HH:mm}</li> | 
|  | *         </ul> | 
|  | *     </li> | 
|  | * </ul> | 
|  | * | 
|  | * <p>The {@link CharSequence} instances used as formatting patterns when calling either | 
|  | * {@link #setFormat24Hour(CharSequence)} or {@link #setFormat12Hour(CharSequence)} can | 
|  | * contain styling information. To do so, use a {@link android.text.Spanned} object. | 
|  | * Note that if you customize these strings, it is your responsibility to supply strings | 
|  | * appropriate for formatting dates and/or times in the user's locale.</p> | 
|  | * | 
|  | * @attr ref android.R.styleable#TextClock_format12Hour | 
|  | * @attr ref android.R.styleable#TextClock_format24Hour | 
|  | * @attr ref android.R.styleable#TextClock_timeZone | 
|  | */ | 
|  | @RemoteView | 
|  | public class TextClock extends TextView { | 
|  | /** | 
|  | * The default formatting pattern in 12-hour mode. This pattern is used | 
|  | * if {@link #setFormat12Hour(CharSequence)} is called with a null pattern | 
|  | * or if no pattern was specified when creating an instance of this class. | 
|  | * | 
|  | * This default pattern shows only the time, hours and minutes, and an am/pm | 
|  | * indicator. | 
|  | * | 
|  | * @see #setFormat12Hour(CharSequence) | 
|  | * @see #getFormat12Hour() | 
|  | * | 
|  | * @deprecated Let the system use locale-appropriate defaults instead. | 
|  | */ | 
|  | @Deprecated | 
|  | public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm a"; | 
|  |  | 
|  | /** | 
|  | * The default formatting pattern in 24-hour mode. This pattern is used | 
|  | * if {@link #setFormat24Hour(CharSequence)} is called with a null pattern | 
|  | * or if no pattern was specified when creating an instance of this class. | 
|  | * | 
|  | * This default pattern shows only the time, hours and minutes. | 
|  | * | 
|  | * @see #setFormat24Hour(CharSequence) | 
|  | * @see #getFormat24Hour() | 
|  | * | 
|  | * @deprecated Let the system use locale-appropriate defaults instead. | 
|  | */ | 
|  | @Deprecated | 
|  | public static final CharSequence DEFAULT_FORMAT_24_HOUR = "H:mm"; | 
|  |  | 
|  | private CharSequence mFormat12; | 
|  | private CharSequence mFormat24; | 
|  | private CharSequence mDescFormat12; | 
|  | private CharSequence mDescFormat24; | 
|  |  | 
|  | @ExportedProperty | 
|  | private CharSequence mFormat; | 
|  | @ExportedProperty | 
|  | private boolean mHasSeconds; | 
|  |  | 
|  | private CharSequence mDescFormat; | 
|  |  | 
|  | private boolean mRegistered; | 
|  | private boolean mShouldRunTicker; | 
|  |  | 
|  | private Calendar mTime; | 
|  | private String mTimeZone; | 
|  |  | 
|  | private boolean mShowCurrentUserTime; | 
|  |  | 
|  | private ContentObserver mFormatChangeObserver; | 
|  | // Used by tests to stop time change events from triggering the text update | 
|  | private boolean mStopTicking; | 
|  |  | 
|  | private class FormatChangeObserver extends ContentObserver { | 
|  |  | 
|  | public FormatChangeObserver(Handler handler) { | 
|  | super(handler); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onChange(boolean selfChange) { | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onChange(boolean selfChange, Uri uri) { | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { | 
|  | @Override | 
|  | public void onReceive(Context context, Intent intent) { | 
|  | if (mStopTicking) { | 
|  | return; // Test disabled the clock ticks | 
|  | } | 
|  | if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { | 
|  | final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE); | 
|  | createTime(timeZone); | 
|  | } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction()) | 
|  | || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) { | 
|  | return; | 
|  | } | 
|  | onTimeChanged(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | private final Runnable mTicker = new Runnable() { | 
|  | public void run() { | 
|  | removeCallbacks(this); | 
|  | if (mStopTicking || !mShouldRunTicker) { | 
|  | return; // Test disabled the clock ticks | 
|  | } | 
|  | onTimeChanged(); | 
|  |  | 
|  | Instant now = mTime.toInstant(); | 
|  | ZoneId zone = mTime.getTimeZone().toZoneId(); | 
|  |  | 
|  | ZonedDateTime nextTick; | 
|  | if (mHasSeconds) { | 
|  | nextTick = now.atZone(zone).plusSeconds(1).withNano(0); | 
|  | } else { | 
|  | nextTick = now.atZone(zone).plusMinutes(1).withSecond(0).withNano(0); | 
|  | } | 
|  |  | 
|  | long millisUntilNextTick = Duration.between(now, nextTick.toInstant()).toMillis(); | 
|  | if (millisUntilNextTick <= 0) { | 
|  | // This should never happen, but if it does, then tick again in a second. | 
|  | millisUntilNextTick = 1000; | 
|  | } | 
|  |  | 
|  | postDelayed(this, millisUntilNextTick); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Creates a new clock using the default patterns for the current locale. | 
|  | * | 
|  | * @param context The Context the view is running in, through which it can | 
|  | *        access the current theme, resources, etc. | 
|  | */ | 
|  | @SuppressWarnings("UnusedDeclaration") | 
|  | public TextClock(Context context) { | 
|  | super(context); | 
|  | init(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Creates a new clock inflated from XML. This object's properties are | 
|  | * intialized from the attributes specified in XML. | 
|  | * | 
|  | * This constructor uses a default style of 0, so the only attribute values | 
|  | * applied are those in the Context's Theme and the given AttributeSet. | 
|  | * | 
|  | * @param context The Context the view is running in, through which it can | 
|  | *        access the current theme, resources, etc. | 
|  | * @param attrs The attributes of the XML tag that is inflating the view | 
|  | */ | 
|  | @SuppressWarnings("UnusedDeclaration") | 
|  | public TextClock(Context context, AttributeSet attrs) { | 
|  | this(context, attrs, 0); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Creates a new clock inflated from XML. This object's properties are | 
|  | * intialized from the attributes specified in XML. | 
|  | * | 
|  | * @param context The Context the view is running in, through which it can | 
|  | *        access the current theme, resources, etc. | 
|  | * @param attrs The attributes of the XML tag that is inflating the view | 
|  | * @param defStyleAttr An attribute in the current theme that contains a | 
|  | *        reference to a style resource that supplies default values for | 
|  | *        the view. Can be 0 to not look for defaults. | 
|  | */ | 
|  | public TextClock(Context context, AttributeSet attrs, int defStyleAttr) { | 
|  | this(context, attrs, defStyleAttr, 0); | 
|  | } | 
|  |  | 
|  | public TextClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | 
|  | super(context, attrs, defStyleAttr, defStyleRes); | 
|  |  | 
|  | final TypedArray a = context.obtainStyledAttributes( | 
|  | attrs, R.styleable.TextClock, defStyleAttr, defStyleRes); | 
|  | saveAttributeDataForStyleable(context, R.styleable.TextClock, | 
|  | attrs, a, defStyleAttr, defStyleRes); | 
|  | try { | 
|  | mFormat12 = a.getText(R.styleable.TextClock_format12Hour); | 
|  | mFormat24 = a.getText(R.styleable.TextClock_format24Hour); | 
|  | mTimeZone = a.getString(R.styleable.TextClock_timeZone); | 
|  | } finally { | 
|  | a.recycle(); | 
|  | } | 
|  |  | 
|  | init(); | 
|  | } | 
|  |  | 
|  | private void init() { | 
|  | if (mFormat12 == null) { | 
|  | mFormat12 = getBestDateTimePattern("hm"); | 
|  | } | 
|  | if (mFormat24 == null) { | 
|  | mFormat24 = getBestDateTimePattern("Hm"); | 
|  | } | 
|  |  | 
|  | createTime(mTimeZone); | 
|  | chooseFormat(); | 
|  | } | 
|  |  | 
|  | private void createTime(String timeZone) { | 
|  | if (timeZone != null) { | 
|  | mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone)); | 
|  | } else { | 
|  | mTime = Calendar.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the formatting pattern used to display the date and/or time | 
|  | * in 12-hour mode. The formatting pattern syntax is described in | 
|  | * {@link DateFormat}. | 
|  | * | 
|  | * @return A {@link CharSequence} or null. | 
|  | * | 
|  | * @see #setFormat12Hour(CharSequence) | 
|  | * @see #is24HourModeEnabled() | 
|  | */ | 
|  | @InspectableProperty | 
|  | @ExportedProperty | 
|  | public CharSequence getFormat12Hour() { | 
|  | return mFormat12; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * <p>Specifies the formatting pattern used to display the date and/or time | 
|  | * in 12-hour mode. The formatting pattern syntax is described in | 
|  | * {@link DateFormat}.</p> | 
|  | * | 
|  | * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used | 
|  | * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns | 
|  | * are set to null, the default pattern for the current locale will be used | 
|  | * instead.</p> | 
|  | * | 
|  | * <p><strong>Note:</strong> if styling is not needed, it is highly recommended | 
|  | * you supply a format string generated by | 
|  | * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method | 
|  | * takes care of generating a format string adapted to the desired locale.</p> | 
|  | * | 
|  | * | 
|  | * @param format A date/time formatting pattern as described in {@link DateFormat} | 
|  | * | 
|  | * @see #getFormat12Hour() | 
|  | * @see #is24HourModeEnabled() | 
|  | * @see DateFormat#getBestDateTimePattern(java.util.Locale, String) | 
|  | * @see DateFormat | 
|  | * | 
|  | * @attr ref android.R.styleable#TextClock_format12Hour | 
|  | */ | 
|  | @RemotableViewMethod | 
|  | public void setFormat12Hour(CharSequence format) { | 
|  | mFormat12 = format; | 
|  |  | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Like setFormat12Hour, but for the content description. | 
|  | * @hide | 
|  | */ | 
|  | public void setContentDescriptionFormat12Hour(CharSequence format) { | 
|  | mDescFormat12 = format; | 
|  |  | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the formatting pattern used to display the date and/or time | 
|  | * in 24-hour mode. The formatting pattern syntax is described in | 
|  | * {@link DateFormat}. | 
|  | * | 
|  | * @return A {@link CharSequence} or null. | 
|  | * | 
|  | * @see #setFormat24Hour(CharSequence) | 
|  | * @see #is24HourModeEnabled() | 
|  | */ | 
|  | @InspectableProperty | 
|  | @ExportedProperty | 
|  | public CharSequence getFormat24Hour() { | 
|  | return mFormat24; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * <p>Specifies the formatting pattern used to display the date and/or time | 
|  | * in 24-hour mode. The formatting pattern syntax is described in | 
|  | * {@link DateFormat}.</p> | 
|  | * | 
|  | * <p>If this pattern is set to null, {@link #getFormat24Hour()} will be used | 
|  | * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns | 
|  | * are set to null, the default pattern for the current locale will be used | 
|  | * instead.</p> | 
|  | * | 
|  | * <p><strong>Note:</strong> if styling is not needed, it is highly recommended | 
|  | * you supply a format string generated by | 
|  | * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}. This method | 
|  | * takes care of generating a format string adapted to the desired locale.</p> | 
|  | * | 
|  | * @param format A date/time formatting pattern as described in {@link DateFormat} | 
|  | * | 
|  | * @see #getFormat24Hour() | 
|  | * @see #is24HourModeEnabled() | 
|  | * @see DateFormat#getBestDateTimePattern(java.util.Locale, String) | 
|  | * @see DateFormat | 
|  | * | 
|  | * @attr ref android.R.styleable#TextClock_format24Hour | 
|  | */ | 
|  | @RemotableViewMethod | 
|  | public void setFormat24Hour(CharSequence format) { | 
|  | mFormat24 = format; | 
|  |  | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Like setFormat24Hour, but for the content description. | 
|  | * @hide | 
|  | */ | 
|  | public void setContentDescriptionFormat24Hour(CharSequence format) { | 
|  | mDescFormat24 = format; | 
|  |  | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets whether this clock should always track the current user and not the user of the | 
|  | * current process. This is used for single instance processes like the systemUI who need | 
|  | * to display time for different users. | 
|  | * | 
|  | * @hide | 
|  | */ | 
|  | public void setShowCurrentUserTime(boolean showCurrentUserTime) { | 
|  | mShowCurrentUserTime = showCurrentUserTime; | 
|  |  | 
|  | chooseFormat(); | 
|  | onTimeChanged(); | 
|  | unregisterObserver(); | 
|  | registerObserver(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Update the displayed time if necessary and invalidate the view. | 
|  | */ | 
|  | public void refreshTime() { | 
|  | onTimeChanged(); | 
|  | invalidate(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Indicates whether the system is currently using the 24-hour mode. | 
|  | * | 
|  | * When the system is in 24-hour mode, this view will use the pattern | 
|  | * returned by {@link #getFormat24Hour()}. In 12-hour mode, the pattern | 
|  | * returned by {@link #getFormat12Hour()} is used instead. | 
|  | * | 
|  | * If either one of the formats is null, the other format is used. If | 
|  | * both formats are null, the default formats for the current locale are used. | 
|  | * | 
|  | * @return true if time should be displayed in 24-hour format, false if it | 
|  | *         should be displayed in 12-hour format. | 
|  | * | 
|  | * @see #setFormat12Hour(CharSequence) | 
|  | * @see #getFormat12Hour() | 
|  | * @see #setFormat24Hour(CharSequence) | 
|  | * @see #getFormat24Hour() | 
|  | */ | 
|  | @InspectableProperty(hasAttributeId = false) | 
|  | public boolean is24HourModeEnabled() { | 
|  | if (mShowCurrentUserTime) { | 
|  | return DateFormat.is24HourFormat(getContext(), ActivityManager.getCurrentUser()); | 
|  | } else { | 
|  | return DateFormat.is24HourFormat(getContext()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Indicates which time zone is currently used by this view. | 
|  | * | 
|  | * @return The ID of the current time zone or null if the default time zone, | 
|  | *         as set by the user, must be used | 
|  | * | 
|  | * @see TimeZone | 
|  | * @see java.util.TimeZone#getAvailableIDs() | 
|  | * @see #setTimeZone(String) | 
|  | */ | 
|  | @InspectableProperty | 
|  | public String getTimeZone() { | 
|  | return mTimeZone; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the specified time zone to use in this clock. When the time zone | 
|  | * is set through this method, system time zone changes (when the user | 
|  | * sets the time zone in settings for instance) will be ignored. | 
|  | * | 
|  | * @param timeZone The desired time zone's ID as specified in {@link TimeZone} | 
|  | *                 or null to user the time zone specified by the user | 
|  | *                 (system time zone) | 
|  | * | 
|  | * @see #getTimeZone() | 
|  | * @see java.util.TimeZone#getAvailableIDs() | 
|  | * @see TimeZone#getTimeZone(String) | 
|  | * | 
|  | * @attr ref android.R.styleable#TextClock_timeZone | 
|  | */ | 
|  | @RemotableViewMethod | 
|  | public void setTimeZone(String timeZone) { | 
|  | mTimeZone = timeZone; | 
|  |  | 
|  | createTime(timeZone); | 
|  | onTimeChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the current format string. Always valid after constructor has | 
|  | * finished, and will never be {@code null}. | 
|  | * | 
|  | * @hide | 
|  | */ | 
|  | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | public CharSequence getFormat() { | 
|  | return mFormat; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} | 
|  | * depending on whether the user has selected 24-hour format. | 
|  | */ | 
|  | private void chooseFormat() { | 
|  | final boolean format24Requested = is24HourModeEnabled(); | 
|  |  | 
|  | if (format24Requested) { | 
|  | mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm")); | 
|  | mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat); | 
|  | } else { | 
|  | mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm")); | 
|  | mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat); | 
|  | } | 
|  |  | 
|  | boolean hadSeconds = mHasSeconds; | 
|  | mHasSeconds = DateFormat.hasSeconds(mFormat); | 
|  |  | 
|  | if (mShouldRunTicker && hadSeconds != mHasSeconds) { | 
|  | mTicker.run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private String getBestDateTimePattern(String skeleton) { | 
|  | DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance( | 
|  | getContext().getResources().getConfiguration().locale); | 
|  | return dtpg.getBestPattern(skeleton); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a if not null, else return b if not null, else return c. | 
|  | */ | 
|  | private static CharSequence abc(CharSequence a, CharSequence b, CharSequence c) { | 
|  | return a == null ? (b == null ? c : b) : a; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected void onAttachedToWindow() { | 
|  | super.onAttachedToWindow(); | 
|  |  | 
|  | if (!mRegistered) { | 
|  | mRegistered = true; | 
|  |  | 
|  | registerReceiver(); | 
|  | registerObserver(); | 
|  |  | 
|  | createTime(mTimeZone); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onVisibilityAggregated(boolean isVisible) { | 
|  | super.onVisibilityAggregated(isVisible); | 
|  |  | 
|  | if (!mShouldRunTicker && isVisible) { | 
|  | mShouldRunTicker = true; | 
|  | mTicker.run(); | 
|  | } else if (mShouldRunTicker && !isVisible) { | 
|  | mShouldRunTicker = false; | 
|  | removeCallbacks(mTicker); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected void onDetachedFromWindow() { | 
|  | super.onDetachedFromWindow(); | 
|  |  | 
|  | if (mRegistered) { | 
|  | unregisterReceiver(); | 
|  | unregisterObserver(); | 
|  |  | 
|  | mRegistered = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Used by tests to stop the clock tick from updating the text. | 
|  | * @hide | 
|  | */ | 
|  | @TestApi | 
|  | public void disableClockTick() { | 
|  | mStopTicking = true; | 
|  | } | 
|  |  | 
|  | private void registerReceiver() { | 
|  | final IntentFilter filter = new IntentFilter(); | 
|  |  | 
|  | filter.addAction(Intent.ACTION_TIME_CHANGED); | 
|  | filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); | 
|  |  | 
|  | // OK, this is gross but needed. This class is supported by the | 
|  | // remote views mechanism and as a part of that the remote views | 
|  | // can be inflated by a context for another user without the app | 
|  | // having interact users permission - just for loading resources. | 
|  | // For example, when adding widgets from a managed profile to the | 
|  | // home screen. Therefore, we register the receiver as the user | 
|  | // the app is running as not the one the context is for. | 
|  | getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(), | 
|  | filter, null, getHandler()); | 
|  | } | 
|  |  | 
|  | private void registerObserver() { | 
|  | if (mRegistered) { | 
|  | if (mFormatChangeObserver == null) { | 
|  | mFormatChangeObserver = new FormatChangeObserver(getHandler()); | 
|  | } | 
|  | final ContentResolver resolver = getContext().getContentResolver(); | 
|  | Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24); | 
|  | if (mShowCurrentUserTime) { | 
|  | resolver.registerContentObserver(uri, true, | 
|  | mFormatChangeObserver, UserHandle.USER_ALL); | 
|  | } else { | 
|  | // UserHandle.myUserId() is needed. This class is supported by the | 
|  | // remote views mechanism and as a part of that the remote views | 
|  | // can be inflated by a context for another user without the app | 
|  | // having interact users permission - just for loading resources. | 
|  | // For example, when adding widgets from a managed profile to the | 
|  | // home screen. Therefore, we register the ContentObserver with the user | 
|  | // the app is running (e.g. the launcher) and not the user of the | 
|  | // context (e.g. the widget's profile). | 
|  | resolver.registerContentObserver(uri, true, | 
|  | mFormatChangeObserver, UserHandle.myUserId()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void unregisterReceiver() { | 
|  | getContext().unregisterReceiver(mIntentReceiver); | 
|  | } | 
|  |  | 
|  | private void unregisterObserver() { | 
|  | if (mFormatChangeObserver != null) { | 
|  | final ContentResolver resolver = getContext().getContentResolver(); | 
|  | resolver.unregisterContentObserver(mFormatChangeObserver); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Update the displayed time if this view and its ancestors and window is visible | 
|  | */ | 
|  | @UnsupportedAppUsage | 
|  | private void onTimeChanged() { | 
|  | mTime.setTimeInMillis(System.currentTimeMillis()); | 
|  | setText(DateFormat.format(mFormat, mTime)); | 
|  | setContentDescription(DateFormat.format(mDescFormat, mTime)); | 
|  | } | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { | 
|  | super.encodeProperties(stream); | 
|  |  | 
|  | CharSequence s = getFormat12Hour(); | 
|  | stream.addProperty("format12Hour", s == null ? null : s.toString()); | 
|  |  | 
|  | s = getFormat24Hour(); | 
|  | stream.addProperty("format24Hour", s == null ? null : s.toString()); | 
|  | stream.addProperty("format", mFormat == null ? null : mFormat.toString()); | 
|  | stream.addProperty("hasSeconds", mHasSeconds); | 
|  | } | 
|  | } |