All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.widget.TextClock Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * 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;

/**
 * 

TextClock can display the current date and/or time as * a formatted string.

* *

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.

* *

It is possible to determine whether the system is currently in * 24-hour mode by calling {@link #is24HourModeEnabled()}.

* *

The rules used by this widget to decide how to format the date and * time are the following:

*
    *
  • In 24-hour mode: *
      *
    • Use the value returned by {@link #getFormat24Hour()} when non-null
    • *
    • Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null
    • *
    • Otherwise, use a default value appropriate for the user's locale, such as {@code h:mm a}
    • *
    *
  • *
  • In 12-hour mode: *
      *
    • Use the value returned by {@link #getFormat12Hour()} when non-null
    • *
    • Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null
    • *
    • Otherwise, use a default value appropriate for the user's locale, such as {@code HH:mm}
    • *
    *
  • *
* *

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.

* * @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; } /** *

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}.

* *

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.

* *

Note: 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.

* * * @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; } /** *

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}.

* *

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.

* *

Note: 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.

* * @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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy