diff options
Diffstat (limited to 'luni/src/main')
-rw-r--r-- | luni/src/main/java/libcore/icu/DateIntervalFormat.java | 12 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java | 370 | ||||
-rw-r--r-- | luni/src/main/native/Register.cpp | 1 | ||||
-rw-r--r-- | luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp | 109 | ||||
-rw-r--r-- | luni/src/main/native/sub.mk | 1 |
5 files changed, 492 insertions, 1 deletions
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java index 3855654..fbef89a 100644 --- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java +++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java @@ -224,10 +224,20 @@ public final class DateIntervalFormat { return c.get(Calendar.YEAR) == now.get(Calendar.YEAR); } - private static int dayDistance(Calendar c1, Calendar c2) { + // Return the date difference for the two times in a given timezone. + public static int dayDistance(TimeZone tz, long startTime, long endTime) { + return julianDay(tz, endTime) - julianDay(tz, startTime); + } + + public static int dayDistance(Calendar c1, Calendar c2) { return julianDay(c2) - julianDay(c1); } + private static int julianDay(TimeZone tz, long time) { + long utcMs = time + tz.getOffset(time); + return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY; + } + private static int julianDay(Calendar c) { long utcMs = c.getTimeInMillis() + c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET); return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY; diff --git a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java new file mode 100644 index 0000000..0e715b6 --- /dev/null +++ b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2015 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 libcore.icu; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; +import libcore.util.BasicLruCache; +import libcore.icu.DateIntervalFormat; + +/** + * Exposes icu4c's RelativeDateTimeFormatter. + */ +public final class RelativeDateTimeFormatter { + + // Values from public API in DateUtils to be used in this class. They must + // match the ones in DateUtils.java. + public static final int FORMAT_SHOW_TIME = 0x00001; + public static final int FORMAT_SHOW_YEAR = 0x00004; + public static final int FORMAT_SHOW_DATE = 0x00010; + public static final int FORMAT_ABBREV_MONTH = 0x10000; + public static final int FORMAT_NUMERIC_DATE = 0x20000; + public static final int FORMAT_ABBREV_RELATIVE = 0x40000; + public static final int FORMAT_ABBREV_ALL = 0x80000; + + public static final long SECOND_IN_MILLIS = 1000; + public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; + public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; + public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; + public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + // YEAR_IN_MILLIS considers 364 days as a year. However, since this + // constant comes from public API in DateUtils, it cannot be fixed here. + public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52; + + // Values from icu4c UDateRelativeUnit enum in unicode/reldatefmt.h. + // The following U* constants must agree with the ones in icu4c. + private static final int UDAT_RELATIVE_SECONDS = 0; + private static final int UDAT_RELATIVE_MINUTES = 1; + private static final int UDAT_RELATIVE_HOURS = 2; + private static final int UDAT_RELATIVE_DAYS = 3; + private static final int UDAT_RELATIVE_WEEKS = 4; + private static final int UDAT_RELATIVE_MONTHS = 5; + private static final int UDAT_RELATIVE_YEARS = 6; + + // Values from icu4c UDateAbsoluteUnit enum in unicode/reldatefmt.h. + private static final int UDAT_ABSOLUTE_DAY = 7; + + // Values from icu4c UDisplayContext enum in unicode/udisplaycontext.h. + private static final int UDISPCTX_CAPITALIZATION_NONE = 1 << 8; + private static final int UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE = (1 << 8) + 2; + + // Values from icu4c UDateDirection enum in unicode/reldatefmt.h. + private static final int UDAT_DIRECTION_LAST_2 = 0; + private static final int UDAT_DIRECTION_LAST = 1; + private static final int UDAT_DIRECTION_THIS = 2; + private static final int UDAT_DIRECTION_NEXT = 3; + private static final int UDAT_DIRECTION_NEXT_2 = 4; + private static final int UDAT_DIRECTION_PLAIN = 5; + + // Values from icu4c UDateRelativeDateTimeFormatterStyle enum in + // unicode/reldatefmt.h. + private static final int UDAT_STYLE_LONG = 0; + private static final int UDAT_STYLE_SHORT = 1; + private static final int UDAT_STYLE_NARROW = 2; + + private static final FormatterCache CACHED_FORMATTERS = new FormatterCache(); + private static final int EPOCH_JULIAN_DAY = 2440588; + + static class FormatterCache extends BasicLruCache<String, Long> { + FormatterCache() { + super(8); + } + + protected void entryEvicted(String key, Long value) { + destroyRelativeDateTimeFormatter(value); + } + }; + + private 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 icu4c'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. + */ + public static String getRelativeTimeSpanString(Locale locale, TimeZone tz, long time, + long now, long minResolution, int flags) { + if (locale == null) { + throw new NullPointerException("locale == null"); + } + if (tz == null) { + throw new NullPointerException("tz == null"); + } + long duration = Math.abs(now - time); + boolean past = (now >= time); + + // Use UDAT_STYLE_SHORT or UDAT_STYLE_LONG. + int style; + if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) { + style = UDAT_STYLE_SHORT; + } else { + style = UDAT_STYLE_LONG; + } + + // We are currently using the _NONE and _FOR_BEGINNING_OF_SENTENCE for the + // capitalization. We use _NONE for relative time strings, and the latter + // to capitalize the first letter of strings that don't contain + // quantities, such as "Yesterday", "Today" and etc. This is for backward + // compatibility (see b/14493853). + int capitalizationContext = UDISPCTX_CAPITALIZATION_NONE; + + // Use UDAT_DIRECTION_LAST or UDAT_DIRECTION_NEXT. + int direction; + if (past) { + direction = UDAT_DIRECTION_LAST; + } else { + direction = UDAT_DIRECTION_NEXT; + } + + // 'relative' defaults to true as we are generating relative time span + // string. It will be set to false when we try to display strings without + // a quantity, such as 'Yesterday', etc. + boolean relative = true; + int count; + int unit; + + if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) { + count = (int)(duration / SECOND_IN_MILLIS); + unit = UDAT_RELATIVE_SECONDS; + } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) { + count = (int)(duration / MINUTE_IN_MILLIS); + unit = UDAT_RELATIVE_MINUTES; + } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) { + // Even if 'time' actually happened yesterday, we don't format it as + // "Yesterday" in this case. Unless the duration is longer than a day, + // or minResolution is specified as DAY_IN_MILLIS by user. + count = (int)(duration / HOUR_IN_MILLIS); + unit = UDAT_RELATIVE_HOURS; + } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) { + count = Math.abs(DateIntervalFormat.dayDistance(tz, time, now)); + unit = UDAT_RELATIVE_DAYS; + + if (count == 2) { + // Some locales have special terms for "2 days ago". Return them if + // available. Note that we cannot set up direction and unit here and + // make it fall through to use the call near the end of the function, + // because for locales that don't have special terms for "2 days ago", + // icu4c returns an empty string instead of falling back to strings + // like "2 days ago". + capitalizationContext = UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE; + String str; + if (past) { + synchronized (CACHED_FORMATTERS) { + str = formatWithAbsoluteUnit(getFormatter(locale.toString(), style, + capitalizationContext), + UDAT_DIRECTION_LAST_2, UDAT_ABSOLUTE_DAY); + } + } else { + synchronized (CACHED_FORMATTERS) { + str = formatWithAbsoluteUnit(getFormatter(locale.toString(), style, + capitalizationContext), + UDAT_DIRECTION_NEXT_2, UDAT_ABSOLUTE_DAY); + } + } + if (!str.isEmpty()) { + return str; + } + // Fall back to show something like "2 days ago". Reset the + // capitalization setting. + capitalizationContext = UDISPCTX_CAPITALIZATION_NONE; + } else if (count == 1) { + // Show "Yesterday / Tomorrow" instead of "1 day ago / in 1 day". + unit = UDAT_ABSOLUTE_DAY; + relative = false; + } else if (count == 0) { + // Show "Today" if time and now are on the same day. + unit = UDAT_ABSOLUTE_DAY; + direction = UDAT_DIRECTION_THIS; + relative = false; + } + } else if (minResolution == WEEK_IN_MILLIS) { + count = (int)(duration / WEEK_IN_MILLIS); + unit = UDAT_RELATIVE_WEEKS; + } else { + // The duration is longer than a week and minResolution is not + // WEEK_IN_MILLIS. Return the absolute date instead of relative time. + return DateIntervalFormat.formatDateRange(locale, tz, time, time, flags); + } + + if (relative) { + synchronized (CACHED_FORMATTERS) { + return formatWithRelativeUnit(getFormatter(locale.toString(), style, + capitalizationContext), + count, direction, unit); + } + } else { + capitalizationContext = UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE; + synchronized (CACHED_FORMATTERS) { + return formatWithAbsoluteUnit(getFormatter(locale.toString(), style, + capitalizationContext), + direction, unit); + } + } + } + + /** + * 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 icu4c'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. + */ + public static String getRelativeDateTimeString(Locale locale, TimeZone tz, long time, + long now, long minResolution, long transitionResolution, int flags) { + + if (locale == null) { + throw new NullPointerException("locale == null"); + } + if (tz == null) { + throw new NullPointerException("tz == null"); + } + + // Get the time clause first. + String timeClause = DateIntervalFormat.formatDateRange(locale, tz, time, time, + FORMAT_SHOW_TIME); + + long duration = Math.abs(now - time); + // It doesn't make much sense to have results like: "1 week ago, 10:50 AM". + if (transitionResolution > WEEK_IN_MILLIS) { + transitionResolution = WEEK_IN_MILLIS; + } + // Use UDAT_STYLE_SHORT or UDAT_STYLE_LONG. + int style; + if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) { + style = UDAT_STYLE_SHORT; + } else { + style = UDAT_STYLE_LONG; + } + + // icu4c also has other options available to control the capitalization. We + // are currently using the _NONE option only. + int capitalizationContext = UDISPCTX_CAPITALIZATION_NONE; + + Calendar timeCalendar = new GregorianCalendar(false); + timeCalendar.setTimeZone(tz); + timeCalendar.setTimeInMillis(time); + Calendar nowCalendar = new GregorianCalendar(false); + nowCalendar.setTimeZone(tz); + nowCalendar.setTimeInMillis(now); + + int days = Math.abs(DateIntervalFormat.dayDistance(timeCalendar, nowCalendar)); + + // Now get the date clause, either in relative format or the actual date. + String dateClause; + if (duration < transitionResolution) { + // This is to fix bug 5252772. If there is any date difference, we should + // promote the minResolution to DAY_IN_MILLIS so that it can display the + // date instead of "x hours/minutes ago, [time]". + if (days > 0 && minResolution < DAY_IN_MILLIS) { + minResolution = DAY_IN_MILLIS; + } + dateClause = getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags); + } else { + // We always use fixed flags to format the date clause. User-supplied + // flags are ignored. + if (days == 0) { + // Same day + flags = FORMAT_SHOW_TIME; + } else if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) { + // Different years + flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE; + } else { + // Default + flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; + } + + dateClause = DateIntervalFormat.formatDateRange(locale, tz, time, time, flags); + } + + // Combine the two clauses, such as '5 days ago, 10:50 AM'. + synchronized (CACHED_FORMATTERS) { + return combineDateAndTime(getFormatter(locale.toString(), style, capitalizationContext), + dateClause, timeClause); + } + } + + /** + * 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 long getFormatter(String localeName, int style, int capitalizationContext) { + String key = localeName + "\t" + style + "\t" + capitalizationContext; + Long formatter = CACHED_FORMATTERS.get(key); + if (formatter == null) { + formatter = createRelativeDateTimeFormatter(localeName, style, capitalizationContext); + CACHED_FORMATTERS.put(key, formatter); + } + return formatter; + } + + private static native long createRelativeDateTimeFormatter(String localeName, int style, int capitalizationContext); + private static native void destroyRelativeDateTimeFormatter(long address); + private static native String formatWithRelativeUnit(long address, int quantity, int direction, int unit); + private static native String formatWithAbsoluteUnit(long address, int direction, int unit); + private static native String combineDateAndTime(long address, String relativeDateString, String timeString); +} diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp index 6a2c939..d8c9d5c 100644 --- a/luni/src/main/native/Register.cpp +++ b/luni/src/main/native/Register.cpp @@ -67,6 +67,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) { REGISTER(register_libcore_icu_NativeIDN); REGISTER(register_libcore_icu_NativeNormalizer); REGISTER(register_libcore_icu_NativePluralRules); + REGISTER(register_libcore_icu_RelativeDateTimeFormatter); REGISTER(register_libcore_icu_TimeZoneNames); REGISTER(register_libcore_icu_Transliterator); REGISTER(register_libcore_io_AsynchronousCloseMonitor); diff --git a/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp b/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp new file mode 100644 index 0000000..bba2b0e --- /dev/null +++ b/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 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. + */ + +#define LOG_TAG "RelativeDateTimeFormatter" + +#include "IcuUtilities.h" +#include "JniConstants.h" +#include "ScopedIcuLocale.h" +#include "ScopedJavaUnicodeString.h" +#include "cutils/log.h" +#include "unicode/reldatefmt.h" + +static jlong RelativeDateTimeFormatter_createRelativeDateTimeFormatter(JNIEnv* env, jclass, + jstring javaLocaleName, jint style, jint capitalizationContext) { + ScopedIcuLocale icuLocale(env, javaLocaleName); + if (!icuLocale.valid()) { + return 0; + } + + UErrorCode status = U_ZERO_ERROR; + RelativeDateTimeFormatter* formatter = new RelativeDateTimeFormatter( + icuLocale.locale(), nullptr, static_cast<UDateRelativeDateTimeFormatterStyle>(style), + static_cast<UDisplayContext>(capitalizationContext), status); + if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::RelativeDateTimeFormatter", status)) { + return 0; + } + + return reinterpret_cast<uintptr_t>(formatter); +} + +static void RelativeDateTimeFormatter_destroyRelativeDateTimeFormatter(JNIEnv*, jclass, + jlong formatterAddress) { + delete reinterpret_cast<RelativeDateTimeFormatter*>(static_cast<uintptr_t>(formatterAddress)); +} + +static jstring RelativeDateTimeFormatter_formatWithRelativeUnit(JNIEnv* env, jclass, + jlong formatterAddress, jint quantity, jint direction, jint unit) { + RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress)); + UnicodeString s; + UErrorCode status = U_ZERO_ERROR; + // RelativeDateTimeFormatter::format() takes a double-type quantity. + formatter->format(static_cast<double>(quantity), static_cast<UDateDirection>(direction), + static_cast<UDateRelativeUnit>(unit), s, status); + if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::format", status)) { + return nullptr; + } + + return env->NewString(s.getBuffer(), s.length()); +} + +static jstring RelativeDateTimeFormatter_formatWithAbsoluteUnit(JNIEnv* env, jclass, + jlong formatterAddress, jint direction, jint unit) { + RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress)); + UnicodeString s; + UErrorCode status = U_ZERO_ERROR; + formatter->format(static_cast<UDateDirection>(direction), static_cast<UDateAbsoluteUnit>(unit), s, status); + if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::format", status)) { + return nullptr; + } + + return env->NewString(s.getBuffer(), s.length()); +} + +static jstring RelativeDateTimeFormatter_combineDateAndTime(JNIEnv* env, jclass, + jlong formatterAddress, jstring relativeDateString0, jstring timeString0) { + RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress)); + ScopedJavaUnicodeString relativeDateString(env, relativeDateString0); + if (!relativeDateString.valid()) { + return 0; + } + + ScopedJavaUnicodeString timeString(env, timeString0); + if (!timeString.valid()) { + return 0; + } + UnicodeString s; + UErrorCode status = U_ZERO_ERROR; + formatter->combineDateAndTime(relativeDateString.unicodeString(), timeString.unicodeString(), s, status); + if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::combineDateAndTime", status)) { + return nullptr; + } + + return env->NewString(s.getBuffer(), s.length()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(RelativeDateTimeFormatter, createRelativeDateTimeFormatter, "(Ljava/lang/String;II)J"), + NATIVE_METHOD(RelativeDateTimeFormatter, destroyRelativeDateTimeFormatter, "(J)V"), + NATIVE_METHOD(RelativeDateTimeFormatter, formatWithRelativeUnit, "(JIII)Ljava/lang/String;"), + NATIVE_METHOD(RelativeDateTimeFormatter, formatWithAbsoluteUnit, "(JII)Ljava/lang/String;"), + NATIVE_METHOD(RelativeDateTimeFormatter, combineDateAndTime, "(JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), +}; + +void register_libcore_icu_RelativeDateTimeFormatter(JNIEnv* env) { + jniRegisterNativeMethods(env, "libcore/icu/RelativeDateTimeFormatter", gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk index ebd0f59..da866a6 100644 --- a/luni/src/main/native/sub.mk +++ b/luni/src/main/native/sub.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES := \ libcore_icu_NativeIDN.cpp \ libcore_icu_NativeNormalizer.cpp \ libcore_icu_NativePluralRules.cpp \ + libcore_icu_RelativeDateTimeFormatter.cpp \ libcore_icu_TimeZoneNames.cpp \ libcore_icu_Transliterator.cpp \ libcore_io_AsynchronousCloseMonitor.cpp \ |