Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e348b2a authored by Victor Chang's avatar Victor Chang Committed by vichang
Browse files

Move/Copy some libcore.icu classes to android.text.format

Bug: 160606356
Test: atest FrameworksCoreTests:android.text.format
Change-Id: I5a3e954419e8bd282ec5dd057b241c3572be6831
parent cdf14236
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -22,17 +22,17 @@ import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
import android.icu.util.Calendar;
import android.icu.util.ULocale;

import libcore.util.BasicLruCache;
import android.util.LruCache;

/**
 * A formatter that outputs a single date/time.
 *
 * @hide
 */
public class DateTimeFormat {
    private static final libcore.icu.DateTimeFormat.FormatterCache
        CACHED_FORMATTERS = new libcore.icu.DateTimeFormat.FormatterCache();
class DateTimeFormat {
    private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();

    static class FormatterCache extends BasicLruCache<String, DateFormat> {
    static class FormatterCache extends LruCache<String, DateFormat> {
        FormatterCache() {
            super(8);
        }
@@ -48,7 +48,8 @@ public class DateTimeFormat {
        synchronized (CACHED_FORMATTERS) {
            DateFormat formatter = CACHED_FORMATTERS.get(key);
            if (formatter == null) {
                DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(icuLocale);
                DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(
                        icuLocale);
                formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
                CACHED_FORMATTERS.put(key, formatter);
            }
+0 −1
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import com.android.internal.R;

import libcore.icu.DateIntervalFormat;
import libcore.icu.LocaleData;
import libcore.icu.RelativeDateTimeFormatter;

import java.io.IOException;
import java.util.Calendar;
+40 −30
Original line number Diff line number Diff line
@@ -16,17 +16,23 @@

package android.text.format;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;

import android.icu.util.Calendar;
import android.icu.util.GregorianCalendar;
import android.icu.util.TimeZone;
import android.icu.util.ULocale;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Common methods and constants for the various ICU formatters used to support
 * {@link android.text.format.DateUtils}.
 * Common methods and constants for the various ICU formatters used to support {@link
 * android.text.format.DateUtils}.
 *
 * @hide
 */
final class DateUtilsBridge {
@VisibleForTesting(visibility = PACKAGE)
public final class DateUtilsBridge {
    // These are all public API in DateUtils. There are others, but they're either for use with
    // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
    // or have never been implemented anyway.
@@ -47,17 +53,20 @@ final class DateUtilsBridge {
    public static final int FORMAT_ABBREV_ALL = 0x80000;

    /**
     * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time of
     * writing the libcore implementation is faster but restricted to 1902 - 2038.
     * Callers must not modify the {@code tz} after calling this method.
     * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time
     * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must
     * not modify the {@code tz} after calling this method.
     */
    public static android.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
        android.icu.util.TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
    public static TimeZone icuTimeZone(java.util.TimeZone tz) {
        TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
        icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
        return icuTimeZone;
    }

    public static Calendar createIcuCalendar(android.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
    /**
     * Create a GregorianCalendar based on the arguments
     */
    public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale,
            long timeInMillis) {
        Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
        calendar.setTimeInMillis(timeInMillis);
@@ -123,7 +132,8 @@ final class DateUtilsBridge {
            if ((flags & FORMAT_SHOW_YEAR) != 0) {
                // The caller explicitly wants us to show the year.
            } else if ((flags & FORMAT_NO_YEAR) != 0) {
                // The caller explicitly doesn't want us to show the year, even if we otherwise would.
                // The caller explicitly doesn't want us to show the year, even if we otherwise
                // would.
            } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
                flags |= FORMAT_SHOW_YEAR;
            }
@@ -157,8 +167,8 @@ final class DateUtilsBridge {
     * skeletons provided by {@link #toSkeleton}.
     */
    public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
        // All the skeletons returned by toSkeleton have minute precision (they may abbreviate 4:00 PM
        // to 4 PM but will still show the following minute as 4:01 PM).
        // All the skeletons returned by toSkeleton have minute precision (they may abbreviate
        // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM).
        return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
    }

@@ -167,9 +177,9 @@ final class DateUtilsBridge {
    }

    private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
        return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
            c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
            c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
        return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)
                || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)
                || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
    }

    private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
+92 −95
Original line number Diff line number Diff line
@@ -16,13 +16,6 @@

package android.text.format;

import java.util.Locale;
import libcore.util.BasicLruCache;

import android.icu.text.DisplayContext;
import android.icu.util.Calendar;
import android.icu.util.ULocale;

import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL;
import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
@@ -32,12 +25,24 @@ import static android.text.format.DateUtilsBridge.FORMAT_SHOW_DATE;
import static android.text.format.DateUtilsBridge.FORMAT_SHOW_TIME;
import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;

import android.icu.text.DisplayContext;
import android.icu.util.Calendar;
import android.icu.util.ULocale;
import android.util.LruCache;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Locale;

/**
 * Exposes icu4j's RelativeDateTimeFormatter.
 *
 * @hide
 */
final class RelativeDateTimeFormatter {
@VisibleForTesting(visibility = PACKAGE)
public final class RelativeDateTimeFormatter {

    public static final long SECOND_IN_MILLIS = 1000;
    public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
@@ -54,7 +59,7 @@ final class RelativeDateTimeFormatter {
    private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();

    static class FormatterCache
        extends BasicLruCache<String, android.icu.text.RelativeDateTimeFormatter> {
            extends LruCache<String, android.icu.text.RelativeDateTimeFormatter> {
        FormatterCache() {
            super(8);
        }
@@ -64,38 +69,36 @@ final class RelativeDateTimeFormatter {
    }

    /**
     * This is the internal API that implements the functionality of
     * DateUtils.getRelativeTimeSpanString(long, long, long, int), which is to
     * return a string describing 'time' as a time relative to 'now' such as
     * '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils'
     * doc.
     *
     * In the implementation below, it selects the appropriate time unit based on
     * the elapsed time between time' and 'now', e.g. minutes, days and etc.
     * Callers may also specify the desired minimum resolution to show in the
     * result. For example, '45 minutes ago' will become '0 hours ago' when
     * minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to
     * display, it calls icu4j's RelativeDateTimeFormatter to format the actual
     * string according to the given locale.
     *
     * Note that when minResolution is set to DAY_IN_MILLIS, it returns the
     * result depending on the actual date difference. For example, it will
     * return 'Yesterday' even if 'time' was less than 24 hours ago but falling
     * onto a different calendar day.
     *
     * It takes two additional parameters of Locale and TimeZone than the
     * DateUtils' API. Caller must specify the locale and timezone.
     * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
     * the abbreviated forms when available. When 'time' equals to 'now', it
     * always // returns a string like '0 seconds/minutes/... ago' according to
     * minResolution.
     * This is the internal API that implements the functionality of DateUtils
     * .getRelativeTimeSpanString(long,
     * long, long, int), which is to return a string describing 'time' as a time relative to 'now'
     * such as '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils' doc.
     * <p>
     * In the implementation below, it selects the appropriate time unit based on the elapsed time
     * between time' and 'now', e.g. minutes, days and etc. Callers may also specify the desired
     * minimum resolution to show in the result. For example, '45 minutes ago' will become '0 hours
     * ago' when minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to display, it
     * calls icu4j's RelativeDateTimeFormatter to format the actual string according to the given
     * locale.
     * <p>
     * Note that when minResolution is set to DAY_IN_MILLIS, it returns the result depending on the
     * actual date difference. For example, it will return 'Yesterday' even if 'time' was less than
     * 24 hours ago but falling onto a different calendar day.
     * <p>
     * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
     * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
     * in 'flags' to get the abbreviated forms when available. When 'time' equals to 'now', it
     * always // returns a string like '0 seconds/minutes/... ago' according to minResolution.
     */
    public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
            long now, long minResolution, int flags) {
        // Android has been inconsistent about capitalization in the past. e.g. bug http://b/20247811.
        // Android has been inconsistent about capitalization in the past. e.g. bug
        // http://b/20247811.
        // Now we capitalize everything consistently.
        final DisplayContext displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
        return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags, displayContext);
        final DisplayContext displayContext =
                DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
        return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags,
                displayContext);
    }

    public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
@@ -113,7 +116,8 @@ final class RelativeDateTimeFormatter {
    }

    private static String getRelativeTimeSpanString(ULocale icuLocale,
        android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution, int flags,
            android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution,
            int flags,
            DisplayContext displayContext) {

        long duration = Math.abs(now - time);
@@ -167,15 +171,13 @@ final class RelativeDateTimeFormatter {
                String str;
                if (past) {
                    synchronized (CACHED_FORMATTERS) {
                        str = getFormatter(icuLocale, style, displayContext)
                            .format(
                        str = getFormatter(icuLocale, style, displayContext).format(
                                android.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
                                android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
                    }
                } else {
                    synchronized (CACHED_FORMATTERS) {
                        str = getFormatter(icuLocale, style, displayContext)
                            .format(
                        str = getFormatter(icuLocale, style, displayContext).format(
                                android.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
                                android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
                    }
@@ -208,7 +210,8 @@ final class RelativeDateTimeFormatter {
            // formatDateRange() would determine that based on the current system
            // time and may give wrong results.
            if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
                Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
                Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale,
                        now);

                if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
                    flags |= FORMAT_SHOW_YEAR;
@@ -231,33 +234,28 @@ final class RelativeDateTimeFormatter {
    }

    /**
     * This is the internal API that implements
     * DateUtils.getRelativeDateTimeString(long, long, long, long, int), which is
     * to return a string describing 'time' as a time relative to 'now', formatted
     * like '[relative time/date], [time]'. More examples can be found in
     * DateUtils' doc.
     *
     * The function is similar to getRelativeTimeSpanString, but it always
     * appends the absolute time to the relative time string to return
     * '[relative time/date clause], [absolute time clause]'. It also takes an
     * extra parameter transitionResolution to determine the format of the date
     * clause. When the elapsed time is less than the transition resolution, it
     * displays the relative time string. Otherwise, it gives the absolute
     * numeric date string as the date clause. With the date and time clauses, it
     * relies on icu4j's RelativeDateTimeFormatter::combineDateAndTime() to
     * concatenate the two.
     *
     * It takes two additional parameters of Locale and TimeZone than the
     * DateUtils' API. Caller must specify the locale and timezone.
     * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
     * the abbreviated forms when they are available.
     *
     * Bug 5252772: Since the absolute time will always be part of the result,
     * minResolution will be set to at least DAY_IN_MILLIS to correctly indicate
     * the date difference. For example, when it's 1:30 AM, it will return
     * 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
     * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2
     * hours ago, 11:30 PM' even with minResolution being HOUR_IN_MILLIS.
     * This is the internal API that implements DateUtils.getRelativeDateTimeString(long, long,
     * long, long, int), which is to return a string describing 'time' as a time relative to 'now',
     * formatted like '[relative time/date], [time]'. More examples can be found in DateUtils' doc.
     * <p>
     * The function is similar to getRelativeTimeSpanString, but it always appends the absolute time
     * to the relative time string to return '[relative time/date clause], [absolute time clause]'.
     * It also takes an extra parameter transitionResolution to determine the format of the date
     * clause. When the elapsed time is less than the transition resolution, it displays the
     * relative time string. Otherwise, it gives the absolute numeric date string as the date
     * clause. With the date and time clauses, it relies on icu4j's
     * RelativeDateTimeFormatter::combineDateAndTime()
     * to concatenate the two.
     * <p>
     * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
     * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
     * in 'flags' to get the abbreviated forms when they are available.
     * <p>
     * Bug 5252772: Since the absolute time will always be part of the result, minResolution will be
     * set to at least DAY_IN_MILLIS to correctly indicate the date difference. For example, when
     * it's 1:30 AM, it will return 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
     * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2 hours ago, 11:30 PM'
     * even with minResolution being HOUR_IN_MILLIS.
     */
    public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
            long now, long minResolution, long transitionResolution, int flags) {
@@ -317,7 +315,8 @@ final class RelativeDateTimeFormatter {
        String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
                DisplayContext.CAPITALIZATION_NONE);

        // icu4j also has other options available to control the capitalization. We are currently using
        // icu4j also has other options available to control the capitalization. We are currently
        // using
        // the _NONE option only.
        DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;

@@ -329,12 +328,10 @@ final class RelativeDateTimeFormatter {
    }

    /**
     * getFormatter() caches the RelativeDateTimeFormatter instances based on
     * the combination of localeName, sytle and capitalizationContext. It
     * should always be used along with the action of the formatter in a
     * synchronized block, because otherwise the formatter returned by
     * getFormatter() may have been evicted by the time of the call to
     * formatter->action().
     * getFormatter() caches the RelativeDateTimeFormatter instances based on the combination of
     * localeName, sytle and capitalizationContext. It should always be used along with the action
     * of the formatter in a synchronized block, because otherwise the formatter returned by
     * getFormatter() may have been evicted by the time of the call to formatter->action().
     */
    private static android.icu.text.RelativeDateTimeFormatter getFormatter(
            ULocale locale, android.icu.text.RelativeDateTimeFormatter.Style style,
+301 −251

File changed.

Preview size limit exceeded, changes collapsed.