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
This commit is contained in:
@@ -4164,27 +4164,6 @@
|
|||||||
<!-- Text for checkbox that pops up when an app requests permission to bind a widget [CHAR LIMIT=NONE] -->
|
<!-- Text for checkbox that pops up when an app requests permission to bind a widget [CHAR LIMIT=NONE] -->
|
||||||
<string name="allow_bind_app_widget_activity_always_allow_bind">Always allow <xliff:g id="widget_host_name">%1$s</xliff:g> to create widgets and access their data</string>
|
<string name="allow_bind_app_widget_activity_always_allow_bind">Always allow <xliff:g id="widget_host_name">%1$s</xliff:g> to create widgets and access their data</string>
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "d days, h hours, m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_days"><xliff:g id="days">%1$d</xliff:g>d <xliff:g id="hours">%2$d</xliff:g>h <xliff:g id="minutes">%3$d</xliff:g>m <xliff:g id="seconds">%4$d</xliff:g>s</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "h hours, m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_hours"><xliff:g id="hours">%1$d</xliff:g>h <xliff:g id="minutes">%2$d</xliff:g>m <xliff:g id="seconds">%3$d</xliff:g>s</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_minutes"><xliff:g id="minutes">%1$d</xliff:g>m <xliff:g id="seconds">%2$d</xliff:g>s</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_seconds"><xliff:g id="seconds">%1$d</xliff:g>s</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "d days, h hours, m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_days_no_seconds"><xliff:g id="days">%1$d</xliff:g>d <xliff:g id="hours">%2$d</xliff:g>h <xliff:g id="minutes">%3$d</xliff:g>m</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "h hours, m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_hours_no_seconds"><xliff:g id="hours">%1$d</xliff:g>h <xliff:g id="minutes">%2$d</xliff:g>m</string>
|
|
||||||
|
|
||||||
<!-- Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
|
|
||||||
<string name="battery_history_minutes_no_seconds"><xliff:g id="minutes">%1$d</xliff:g>m</string>
|
|
||||||
|
|
||||||
<!-- XXX remove? Strings used for displaying usage statistics -->
|
<!-- XXX remove? Strings used for displaying usage statistics -->
|
||||||
<string name="usage_stats_label">Usage statistics</string>
|
<string name="usage_stats_label">Usage statistics</string>
|
||||||
|
|
||||||
|
@@ -51,6 +51,9 @@ import android.database.Cursor;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
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.ConnectivityManager;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
@@ -821,33 +824,36 @@ public final class Utils extends com.android.settingslib.Utils {
|
|||||||
minutes = seconds / SECONDS_PER_MINUTE;
|
minutes = seconds / SECONDS_PER_MINUTE;
|
||||||
seconds -= minutes * 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"
|
final ArrayList<Measure> measureList = new ArrayList(4);
|
||||||
TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
|
if (days > 0) {
|
||||||
.setUnit("minute").build();
|
measureList.add(new Measure(days, MeasureUnit.DAY));
|
||||||
sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
}
|
||||||
}
|
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;
|
return sb;
|
||||||
|
@@ -94,8 +94,8 @@ public class UtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
|
public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
|
||||||
final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
|
final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
|
||||||
final String expectedTime = "5m 0s";
|
final String expectedTime = "5m 30s";
|
||||||
|
|
||||||
assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo(
|
assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo(
|
||||||
expectedTime);
|
expectedTime);
|
||||||
@@ -103,8 +103,8 @@ public class UtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
|
public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
|
||||||
final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
|
final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
|
||||||
final String expectedTime = "5m";
|
final String expectedTime = "6m";
|
||||||
|
|
||||||
assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
|
assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
|
||||||
expectedTime);
|
expectedTime);
|
||||||
@@ -120,6 +120,33 @@ public class UtilsTest {
|
|||||||
expectedTime);
|
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
|
@Test
|
||||||
public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
|
public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
|
||||||
final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
|
final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
|
||||||
|
Reference in New Issue
Block a user