summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roozbeh Pournader <roozbeh@google.com> 2017-06-20 15:57:30 -0700
committer Roozbeh Pournader <roozbeh@google.com> 2017-06-20 16:50:01 -0700
commit2ee9075462c53db73b6bb59b1f3d9174f9542bbd (patch)
tree0f618efc3c731dfa4ba336e0dfee76d2e3735e02
parent2739fb9e279312266e0e944fd52ff56398c0fcb5 (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.java60
-rw-r--r--core/res/res/values/strings.xml42
-rw-r--r--core/res/res/values/symbols.xml12
-rw-r--r--core/tests/coretests/src/android/text/format/FormatterTest.java94
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">&lt;Untitled&gt;</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,