diff options
| author | 2017-06-20 15:57:30 -0700 | |
|---|---|---|
| committer | 2017-06-20 16:50:01 -0700 | |
| commit | 2ee9075462c53db73b6bb59b1f3d9174f9542bbd (patch) | |
| tree | 0f618efc3c731dfa4ba336e0dfee76d2e3735e02 | |
| parent | 2739fb9e279312266e0e944fd52ff56398c0fcb5 (diff) | |
Switch formatElapsedTime to use ICU's MeasureFormat
Previously, localizable strings were used instead, causing various
difficulties and inconsistencies. The formatting code also
hard-coded the pluralization rules for English and used it for all
locales.
Now we use ICU's MeasureFormat. There are some small formatting
differences with the previous code, but they are not important,
especially since the old strings were not necessarily well-thought.
Bug: 36994779
Test: adb shell am instrument -w -e package android.text com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Change-Id: Ie8cb119a76e89e2ee971353761fbd54e0d9a556c
| -rw-r--r-- | core/java/android/text/format/Formatter.java | 60 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 42 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 12 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/format/FormatterTest.java | 94 |
4 files changed, 123 insertions, 85 deletions
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index b67ac9840d60..e5bc32bb4f0a 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -20,6 +20,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; +import android.icu.text.MeasureFormat; +import android.icu.util.Measure; +import android.icu.util.MeasureUnit; import android.net.NetworkUtils; import android.text.BidiFormatter; import android.text.TextUtils; @@ -51,9 +54,13 @@ public final class Formatter { } } + private static Locale localeFromContext(@NonNull Context context) { + return context.getResources().getConfiguration().getLocales().get(0); + } + /* Wraps the source string in bidi formatting characters in RTL locales */ private static String bidiWrap(@NonNull Context context, String source) { - final Locale locale = context.getResources().getConfiguration().locale; + final Locale locale = localeFromContext(context); if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) { return BidiFormatter.getInstance(true /* RTL*/).unicodeWrap(source); } else { @@ -197,7 +204,7 @@ public final class Formatter { /** * Returns elapsed time for the given millis, in the following format: - * 1 day 5 hrs; will include at most two units, can go down to seconds precision. + * 1 day, 5 hr; will include at most two units, can go down to seconds precision. * @param context the application context * @param millis the elapsed time in milli seconds * @return the formatted elapsed time @@ -221,44 +228,38 @@ public final class Formatter { } int seconds = (int)secondsLong; + final Locale locale = localeFromContext(context); + final MeasureFormat measureFormat = MeasureFormat.getInstance( + locale, MeasureFormat.FormatWidth.SHORT); if (days >= 2) { days += (hours+12)/24; - return context.getString(com.android.internal.R.string.durationDays, days); + return measureFormat.format(new Measure(days, MeasureUnit.DAY)); } else if (days > 0) { - if (hours == 1) { - return context.getString(com.android.internal.R.string.durationDayHour, days, hours); - } - return context.getString(com.android.internal.R.string.durationDayHours, days, hours); + return measureFormat.formatMeasures( + new Measure(days, MeasureUnit.DAY), + new Measure(hours, MeasureUnit.HOUR)); } else if (hours >= 2) { hours += (minutes+30)/60; - return context.getString(com.android.internal.R.string.durationHours, hours); + return measureFormat.format(new Measure(hours, MeasureUnit.HOUR)); } else if (hours > 0) { - if (minutes == 1) { - return context.getString(com.android.internal.R.string.durationHourMinute, hours, - minutes); - } - return context.getString(com.android.internal.R.string.durationHourMinutes, hours, - minutes); + return measureFormat.formatMeasures( + new Measure(hours, MeasureUnit.HOUR), + new Measure(minutes, MeasureUnit.MINUTE)); } else if (minutes >= 2) { minutes += (seconds+30)/60; - return context.getString(com.android.internal.R.string.durationMinutes, minutes); + return measureFormat.format(new Measure(minutes, MeasureUnit.MINUTE)); } else if (minutes > 0) { - if (seconds == 1) { - return context.getString(com.android.internal.R.string.durationMinuteSecond, minutes, - seconds); - } - return context.getString(com.android.internal.R.string.durationMinuteSeconds, minutes, - seconds); - } else if (seconds == 1) { - return context.getString(com.android.internal.R.string.durationSecond, seconds); + return measureFormat.formatMeasures( + new Measure(minutes, MeasureUnit.MINUTE), + new Measure(seconds, MeasureUnit.SECOND)); } else { - return context.getString(com.android.internal.R.string.durationSeconds, seconds); + return measureFormat.format(new Measure(seconds, MeasureUnit.SECOND)); } } /** * Returns elapsed time for the given millis, in the following format: - * 1 day 5 hrs; will include at most two units, can go down to minutes precision. + * 1 day, 5 hr; will include at most two units, can go down to minutes precision. * @param context the application context * @param millis the elapsed time in milli seconds * @return the formatted elapsed time @@ -267,10 +268,11 @@ public final class Formatter { public static String formatShortElapsedTimeRoundingUpToMinutes(Context context, long millis) { long minutesRoundedUp = (millis + MILLIS_PER_MINUTE - 1) / MILLIS_PER_MINUTE; - if (minutesRoundedUp == 0) { - return context.getString(com.android.internal.R.string.durationMinutes, 0); - } else if (minutesRoundedUp == 1) { - return context.getString(com.android.internal.R.string.durationMinute, 1); + if (minutesRoundedUp == 0 || minutesRoundedUp == 1) { + final Locale locale = localeFromContext(context); + final MeasureFormat measureFormat = MeasureFormat.getInstance( + locale, MeasureFormat.FormatWidth.SHORT); + return measureFormat.format(new Measure(minutesRoundedUp, MeasureUnit.MINUTE)); } return formatShortElapsedTime(context, minutesRoundedUp * MILLIS_PER_MINUTE); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f48e037c8e47..f643f516f606 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -38,48 +38,6 @@ the placeholders. --> <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string> - <!-- [CHAR_LIMIT=10] Suffix added to signify duration in days --> - <string name="durationDays"><xliff:g id="days">%1$d</xliff:g> days</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with hours --> - <string name="durationDayHours"><xliff:g id="days">%1$d</xliff:g> day - <xliff:g id="hours">%2$d</xliff:g> hrs</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with one hours --> - <string name="durationDayHour"><xliff:g id="days">%1$d</xliff:g> day - <xliff:g id="hours">%2$d</xliff:g> hr</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration in hours --> - <string name="durationHours"><xliff:g id="hours">%1$d</xliff:g> hrs</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with minutes --> - <string name="durationHourMinutes"><xliff:g id="hours">%1$d</xliff:g> hr - <xliff:g id="minutes">%2$d</xliff:g> mins</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with one minute --> - <string name="durationHourMinute"><xliff:g id="hours">%1$d</xliff:g> hr - <xliff:g id="minutes">%2$d</xliff:g> min</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration in minutes --> - <string name="durationMinutes"><xliff:g id="minutes">%1$d</xliff:g> mins</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute --> - <string name="durationMinute"><xliff:g id="minutes">%1$d</xliff:g> min</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with seconds --> - <string name="durationMinuteSeconds"><xliff:g id="minutes">%1$d</xliff:g> min - <xliff:g id="seconds">%2$d</xliff:g> secs</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with one second --> - <string name="durationMinuteSecond"><xliff:g id="minutes">%1$d</xliff:g> min - <xliff:g id="seconds">%2$d</xliff:g> sec</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration in seconds --> - <string name="durationSeconds"><xliff:g id="seconds">%1$d</xliff:g> secs</string> - - <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one second --> - <string name="durationSecond"><xliff:g id="seconds">%1$d</xliff:g> sec</string> - <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> <string name="untitled"><Untitled></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7cee6d698c81..caa579cc8795 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -655,18 +655,6 @@ <java-symbol type="string" name="display_manager_overlay_display_secure_suffix" /> <java-symbol type="string" name="display_manager_overlay_display_title" /> <java-symbol type="string" name="double_tap_toast" /> - <java-symbol type="string" name="durationDays" /> - <java-symbol type="string" name="durationDayHours" /> - <java-symbol type="string" name="durationDayHour" /> - <java-symbol type="string" name="durationHours" /> - <java-symbol type="string" name="durationHourMinutes" /> - <java-symbol type="string" name="durationHourMinute" /> - <java-symbol type="string" name="durationMinutes" /> - <java-symbol type="string" name="durationMinute" /> - <java-symbol type="string" name="durationMinuteSeconds" /> - <java-symbol type="string" name="durationMinuteSecond" /> - <java-symbol type="string" name="durationSeconds" /> - <java-symbol type="string" name="durationSecond" /> <java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" /> <java-symbol type="string" name="elapsed_time_short_format_mm_ss" /> <java-symbol type="string" name="emailTypeCustom" /> diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index a4ce911cf929..ff75c29cc112 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -26,12 +26,13 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.format.Formatter.BytesResult; -import java.util.Locale; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Locale; + @SmallTest @RunWith(AndroidJUnit4.class) public class FormatterTest { @@ -54,7 +55,7 @@ public class FormatterTest { @Test public void testFormatBytes() { - setLocale(Locale.ENGLISH); + setLocale(Locale.US); checkFormatBytes(0, true, "0", 0); checkFormatBytes(0, false, "0", 0); @@ -99,6 +100,95 @@ public class FormatterTest { checkFormatBytes(9123000, false, "9,12", 9120000); } + private static final long SECOND = 1000; + private static final long MINUTE = 60 * SECOND; + private static final long HOUR = 60 * MINUTE; + private static final long DAY = 24 * HOUR; + + @Test + public void testFormatShortElapsedTime() { + setLocale(Locale.US); + assertEquals("3 days", Formatter.formatShortElapsedTime(mContext, 2 * DAY + 12 * HOUR)); + assertEquals("2 days", Formatter.formatShortElapsedTime(mContext, 2 * DAY + 11 * HOUR)); + assertEquals("2 days", Formatter.formatShortElapsedTime(mContext, 2 * DAY)); + assertEquals("1 day, 23 hr", + Formatter.formatShortElapsedTime(mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE)); + assertEquals("1 day, 0 hr", + Formatter.formatShortElapsedTime(mContext, 1 * DAY + 59 * MINUTE)); + assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTime(mContext, 1 * DAY)); + assertEquals("24 hr", Formatter.formatShortElapsedTime(mContext, 23 * HOUR + 30 * MINUTE)); + assertEquals("3 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR + 30 * MINUTE)); + assertEquals("2 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR)); + assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTime(mContext, 1 * HOUR)); + assertEquals("60 min", + Formatter.formatShortElapsedTime(mContext, 59 * MINUTE + 30 * SECOND)); + assertEquals("59 min", + Formatter.formatShortElapsedTime(mContext, 59 * MINUTE)); + assertEquals("3 min", Formatter.formatShortElapsedTime(mContext, 2 * MINUTE + 30 * SECOND)); + assertEquals("2 min", Formatter.formatShortElapsedTime(mContext, 2 * MINUTE)); + assertEquals("1 min, 59 sec", + Formatter.formatShortElapsedTime(mContext, 1 * MINUTE + 59 * SECOND + 999)); + assertEquals("1 min, 0 sec", Formatter.formatShortElapsedTime(mContext, 1 * MINUTE)); + assertEquals("59 sec", Formatter.formatShortElapsedTime(mContext, 59 * SECOND + 999)); + assertEquals("1 sec", Formatter.formatShortElapsedTime(mContext, 1 * SECOND)); + assertEquals("0 sec", Formatter.formatShortElapsedTime(mContext, 1)); + assertEquals("0 sec", Formatter.formatShortElapsedTime(mContext, 0)); + + // Make sure it works on different locales. + setLocale(Locale.FRANCE); + assertEquals("2 j", Formatter.formatShortElapsedTime(mContext, 2 * DAY)); + } + + @Test + public void testFormatShortElapsedTimeRoundingUpToMinutes() { + setLocale(Locale.US); + assertEquals("3 days", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * DAY + 12 * HOUR)); + assertEquals("2 days", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * DAY + 11 * HOUR)); + assertEquals("2 days", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * DAY)); + assertEquals("1 day, 23 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE)); + assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * DAY + 59 * MINUTE)); + assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * DAY)); + assertEquals("24 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 23 * HOUR + 30 * MINUTE)); + assertEquals("3 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * HOUR + 30 * MINUTE)); + assertEquals("2 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * HOUR)); + assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * HOUR)); + assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 59 * MINUTE + 30 * SECOND)); + assertEquals("59 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 59 * MINUTE)); + assertEquals("3 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * MINUTE + 30 * SECOND)); + assertEquals("2 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 2 * MINUTE)); + assertEquals("2 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * MINUTE + 59 * SECOND + 999)); + assertEquals("1 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * MINUTE)); + assertEquals("1 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 59 * SECOND + 999)); + assertEquals("1 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * SECOND)); + assertEquals("1 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1)); + assertEquals("0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 0)); + + // Make sure it works on different locales. + setLocale(new Locale("ru", "RU")); + assertEquals("1 мин", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + mContext, 1 * SECOND)); + } + private void checkFormatBytes(long bytes, boolean useShort, String expectedString, long expectedRounded) { BytesResult r = Formatter.formatBytes(mContext.getResources(), bytes, |