From 158722419337435fb4de2f06decd618c1cd9a5eb Mon Sep 17 00:00:00 2001 From: Roozbeh Pournader Date: Tue, 20 Jun 2017 16:46:49 -0700 Subject: [PATCH] Switch Utils.formatElapsedTime to use ICU's MeasureFormat Previously, localizable strings were used instead, causing various difficulties and inconsistencies. Now we use ICU's MeasureFormat. The results for English are almost identical to the previous results (see below), and we also get higher quality and better-vetted results for other locales. Note: This also makes formatted strings shorter by eliminating zeros. For example, what was previously shown as "2d 0h 15m" is now shown as "2d 15m". Bug: 36994779 Bug: 37701311 Test: make -j RunSettingsRoboTests Change-Id: I78fd09e4e7f63f41ef88d3d3fc4ba2be15e1d812 --- res/values/strings.xml | 21 ------- src/com/android/settings/Utils.java | 58 ++++++++++--------- .../src/com/android/settings/UtilsTest.java | 35 +++++++++-- 3 files changed, 63 insertions(+), 51 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index f15759ef49e..670eb7817f6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4164,27 +4164,6 @@ Always allow %1$s to create widgets and access their data - - %1$dd %2$dh %3$dm %4$ds - - - %1$dh %2$dm %3$ds - - - %1$dm %2$ds - - - %1$ds - - - %1$dd %2$dh %3$dm - - - %1$dh %2$dm - - - %1$dm - Usage statistics diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 15e4eb3fbcc..d1deaafd963 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -51,6 +51,9 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.fingerprint.FingerprintManager; +import android.icu.text.MeasureFormat; +import android.icu.util.Measure; +import android.icu.util.MeasureUnit; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Network; @@ -821,33 +824,36 @@ public final class Utils extends com.android.settingslib.Utils { minutes = seconds / SECONDS_PER_MINUTE; seconds -= minutes * SECONDS_PER_MINUTE; } - if (withSeconds) { - if (days > 0) { - sb.append(context.getString(R.string.battery_history_days, - days, hours, minutes, seconds)); - } else if (hours > 0) { - sb.append(context.getString(R.string.battery_history_hours, - hours, minutes, seconds)); - } else if (minutes > 0) { - sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds)); - } else { - sb.append(context.getString(R.string.battery_history_seconds, seconds)); - } - } else { - if (days > 0) { - sb.append(context.getString(R.string.battery_history_days_no_seconds, - days, hours, minutes)); - } else if (hours > 0) { - sb.append(context.getString(R.string.battery_history_hours_no_seconds, - hours, minutes)); - } else { - sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes)); - // Add ttsSpan if it only have minute value, because it will be read as "meters" - TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes) - .setUnit("minute").build(); - sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } + final ArrayList measureList = new ArrayList(4); + if (days > 0) { + measureList.add(new Measure(days, MeasureUnit.DAY)); + } + if (hours > 0) { + measureList.add(new Measure(hours, MeasureUnit.HOUR)); + } + if (minutes > 0) { + measureList.add(new Measure(minutes, MeasureUnit.MINUTE)); + } + if (withSeconds && seconds > 0) { + measureList.add(new Measure(seconds, MeasureUnit.SECOND)); + } + if (measureList.size() == 0) { + // Everything addable was zero, so nothing was added. We add a zero. + measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE)); + } + final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]); + + final Locale locale = context.getResources().getConfiguration().locale; + final MeasureFormat measureFormat = MeasureFormat.getInstance( + locale, MeasureFormat.FormatWidth.NARROW); + sb.append(measureFormat.formatMeasures(measureArray)); + + if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) { + // Add ttsSpan if it only have minute value, because it will be read as "meters" + final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes) + .setUnit("minute").build(); + sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return sb; diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 93ab4063c48..d85464d1e08 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -94,8 +94,8 @@ public class UtilsTest { @Test public void testFormatElapsedTime_WithSeconds_ShowSeconds() { - final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS; - final String expectedTime = "5m 0s"; + final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "5m 30s"; assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo( expectedTime); @@ -103,8 +103,8 @@ public class UtilsTest { @Test public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() { - final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS; - final String expectedTime = "5m"; + final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "6m"; assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo( expectedTime); @@ -120,6 +120,33 @@ public class UtilsTest { expectedTime); } + @Test + public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() { + final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS; + final String expectedTime = "2d 15m"; + + assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatElapsedTime_FormatZero_WithSeconds() { + final double testMillis = 0; + final String expectedTime = "0s"; + + assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatElapsedTime_FormatZero_NoSeconds() { + final double testMillis = 0; + final String expectedTime = "0m"; + + assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo( + expectedTime); + } + @Test public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() { final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;