diff --git a/res/layout/data_usage_bytes_editor.xml b/res/layout/data_usage_bytes_editor.xml index 2878c3e4560..af2d59b823f 100644 --- a/res/layout/data_usage_bytes_editor.xml +++ b/res/layout/data_usage_bytes_editor.xml @@ -37,7 +37,6 @@ android:id="@+id/size_spinner" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:entries="@array/bytes_picker_sizes" /> + android:layout_gravity="center_vertical" /> diff --git a/res/values/strings.xml b/res/values/strings.xml index 72827039dc7..3d17894781f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8331,11 +8331,6 @@ If device is locked, prevent typing replies or other text in notifications - - @*android:string/megabyteShort - @*android:string/gigabyteShort - - Default spell checker diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java index 1025ad7e7dc..ea0bcf064cf 100644 --- a/src/com/android/settings/datausage/BillingCycleSettings.java +++ b/src/com/android/settings/datausage/BillingCycleSettings.java @@ -21,6 +21,8 @@ import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; +import android.icu.text.MeasureFormat; +import android.icu.util.MeasureUnit; import android.net.NetworkPolicy; import android.net.NetworkTemplate; import android.os.Bundle; @@ -31,6 +33,7 @@ import android.text.format.Time; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.NumberPicker; import android.widget.Spinner; @@ -248,6 +251,17 @@ public class BillingCycleSettings extends DataUsageBase implements : editor.getPolicyWarningBytes(template); final long limitDisabled = isLimit ? LIMIT_DISABLED : WARNING_DISABLED; + final MeasureFormat formatter = MeasureFormat.getInstance( + getContext().getResources().getConfiguration().locale, + MeasureFormat.FormatWidth.SHORT); + final String[] unitNames = new String[] { + formatter.getUnitDisplayName(MeasureUnit.MEGABYTE), + formatter.getUnitDisplayName(MeasureUnit.GIGABYTE) + }; + final ArrayAdapter adapter = new ArrayAdapter( + getContext(), android.R.layout.simple_spinner_item, unitNames); + type.setAdapter(adapter); + if (bytes > 1.5f * GB_IN_BYTES) { final String bytesText = formatText(bytes / (float) GB_IN_BYTES); bytesPicker.setText(bytesText); diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java index 3dcf935aba7..d0114e3d3b2 100644 --- a/src/com/android/settings/deviceinfo/StorageItemPreference.java +++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java @@ -18,6 +18,7 @@ package com.android.settings.deviceinfo; import android.content.Context; import android.content.res.Resources; +import android.icu.util.MeasureUnit; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceViewHolder; import android.util.AttributeSet; @@ -51,7 +52,7 @@ public class StorageItemPreference extends Preference { FileSizeFormatter.formatFileSize( getContext(), size, - getGigabyteSuffix(getContext().getResources()), + MeasureUnit.GIGABYTE, FileSizeFormatter.GIGABYTE_IN_BYTES)); if (total == 0) { mProgressPercent = 0; @@ -75,8 +76,4 @@ public class StorageItemPreference extends Preference { updateProgressBar(); super.onBindViewHolder(view); } - - private static int getGigabyteSuffix(Resources res) { - return res.getIdentifier("gigabyteShort", "string", "android"); - } } diff --git a/src/com/android/settings/utils/FileSizeFormatter.java b/src/com/android/settings/utils/FileSizeFormatter.java index e56388af543..c0d360f9d86 100644 --- a/src/com/android/settings/utils/FileSizeFormatter.java +++ b/src/com/android/settings/utils/FileSizeFormatter.java @@ -16,11 +16,22 @@ package com.android.settings.utils; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; +import android.icu.text.DecimalFormat; +import android.icu.text.MeasureFormat; +import android.icu.text.NumberFormat; +import android.icu.util.Measure; +import android.icu.util.MeasureUnit; import android.text.BidiFormatter; +import android.text.TextUtils; import android.text.format.Formatter; +import android.view.View; + +import java.math.BigDecimal; +import java.util.Locale; /** * Utility class to aid in formatting file sizes always with the same unit. This is modified from @@ -31,6 +42,61 @@ public final class FileSizeFormatter { public static final long MEGABYTE_IN_BYTES = KILOBYTE_IN_BYTES * 1000; public static final long GIGABYTE_IN_BYTES = MEGABYTE_IN_BYTES * 1000; + private static class RoundedBytesResult { + public final float value; + public final MeasureUnit units; + public final int fractionDigits; + public final long roundedBytes; + + public RoundedBytesResult( + float value, MeasureUnit units, int fractionDigits, long roundedBytes) { + this.value = value; + this.units = units; + this.fractionDigits = fractionDigits; + this.roundedBytes = roundedBytes; + } + } + + private static Locale localeFromContext(@NonNull Context context) { + return context.getResources().getConfiguration().locale; + } + + private static String bidiWrap(@NonNull Context context, String source) { + final Locale locale = localeFromContext(context); + if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) { + return BidiFormatter.getInstance(true /* RTL*/).unicodeWrap(source); + } else { + return source; + } + } + + private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) { + final NumberFormat numberFormatter = NumberFormat.getInstance(locale); + numberFormatter.setMinimumFractionDigits(fractionDigits); + numberFormatter.setMaximumFractionDigits(fractionDigits); + numberFormatter.setGroupingUsed(false); + if (numberFormatter instanceof DecimalFormat) { + // We do this only for DecimalFormat, since in the general NumberFormat case, calling + // setRoundingMode may throw an exception. + numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP); + } + return numberFormatter; + } + + private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter, + float value, MeasureUnit units) { + final MeasureFormat measureFormatter = MeasureFormat.getInstance( + locale, MeasureFormat.FormatWidth.SHORT, numberFormatter); + return measureFormatter.format(new Measure(value, units)); + } + + private static String formatRoundedBytesResult( + @NonNull Context context, @NonNull RoundedBytesResult input) { + final Locale locale = localeFromContext(context); + final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits); + return formatMeasureShort(locale, numberFormatter, input.value, input.units); + } + /** * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc. * @@ -47,23 +113,17 @@ public final class FileSizeFormatter { * * @param context Context to use to load the localized units * @param sizeBytes size value to be formatted, in bytes - * @param suffix String id for the unit suffix. - * @param mult Amount of bytes in the unit. * @return formatted string with the number + * @param unit The unit used for formatting. + * @param mult Amount of bytes in the unit. + * @return formatted string with the number */ public static String formatFileSize( - @Nullable Context context, long sizeBytes, int suffix, long mult) { + @Nullable Context context, long sizeBytes, MeasureUnit unit, long mult) { if (context == null) { return ""; } - final Formatter.BytesResult res = - formatBytes(context.getResources(), sizeBytes, suffix, mult); - return BidiFormatter.getInstance() - .unicodeWrap(context.getString(getFileSizeSuffix(context), res.value, res.units)); - } - - private static int getFileSizeSuffix(Context context) { - final Resources res = context.getResources(); - return res.getIdentifier("fileSizeSuffix", "string", "android"); + final RoundedBytesResult res = formatBytes(sizeBytes, unit, mult); + return bidiWrap(context, formatRoundedBytesResult(context, res)); } /** @@ -76,8 +136,8 @@ public final class FileSizeFormatter { * @param suffix String id for the unit suffix. * @param mult Amount of bytes in the unit. */ - private static Formatter.BytesResult formatBytes( - Resources res, long sizeBytes, int suffix, long mult) { + private static RoundedBytesResult formatBytes( + long sizeBytes, MeasureUnit unit, long mult) { final boolean isNegative = (sizeBytes < 0); float result = isNegative ? -sizeBytes : sizeBytes; result = result / mult; @@ -85,32 +145,29 @@ public final class FileSizeFormatter { // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to // floating point errors. final int roundFactor; - final String roundFormat; + final int roundDigits; if (mult == 1) { roundFactor = 1; - roundFormat = "%.0f"; + roundDigits = 0; } else if (result < 1) { roundFactor = 100; - roundFormat = "%.2f"; + roundDigits = 2; } else if (result < 10) { roundFactor = 10; - roundFormat = "%.1f"; + roundDigits = 1; } else { // 10 <= result < 100 roundFactor = 1; - roundFormat = "%.0f"; + roundDigits = 0; } if (isNegative) { result = -result; } - final String roundedString = String.format(roundFormat, result); // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so // it's okay (for now)... final long roundedBytes = (((long) Math.round(result * roundFactor)) * mult / roundFactor); - final String units = res.getString(suffix); - - return new Formatter.BytesResult(roundedString, units, roundedBytes); + return new RoundedBytesResult(result, unit, roundDigits, roundedBytes); } } diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java index a154c03ab4d..5b34c7d5a07 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java @@ -55,7 +55,7 @@ public class StorageItemPreferenceTest { @Test public void testAfterLoad() { mPreference.setStorageSize(MEGABYTE_IN_BYTES * 10, MEGABYTE_IN_BYTES * 100); - assertThat(((String) mPreference.getSummary())).isEqualTo("0.01GB"); + assertThat(((String) mPreference.getSummary())).isEqualTo("0.01 GB"); } @Test diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java index fcda085f88e..a871c1963f3 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java @@ -104,7 +104,7 @@ public class SecondaryUserControllerTest { verify(mGroup).addPreference(argumentCaptor.capture()); Preference preference = argumentCaptor.getValue(); - assertThat(preference.getSummary()).isEqualTo("0.01GB"); + assertThat(preference.getSummary()).isEqualTo("0.01 GB"); } @Test @@ -177,7 +177,7 @@ public class SecondaryUserControllerTest { verify(mGroup).addPreference(argumentCaptor.capture()); Preference preference = argumentCaptor.getValue(); - assertThat(preference.getSummary()).isEqualTo("0.03GB"); + assertThat(preference.getSummary()).isEqualTo("0.03 GB"); } @Test diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java index 0d6a4d7988d..159944033dc 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java @@ -84,8 +84,6 @@ public class StorageItemPreferenceControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - SettingsShadowResources.overrideResource("android:string/fileSizeSuffix", "%1$s %2$s"); - SettingsShadowResources.overrideResource("android:string/gigabyteShort", "GB"); mContext = spy(RuntimeEnvironment.application.getApplicationContext()); FakeFeatureFactory.setupForTest(mContext); mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); @@ -287,12 +285,12 @@ public class StorageItemPreferenceControllerTest { results.put(0, result); mController.onLoadFinished(results, 0); - assertThat(audio.getSummary().toString()).isEqualTo("0.14GB"); - assertThat(image.getSummary().toString()).isEqualTo("0.35GB"); - assertThat(games.getSummary().toString()).isEqualTo("0.08GB"); - assertThat(movies.getSummary().toString()).isEqualTo("0.16GB"); - assertThat(apps.getSummary().toString()).isEqualTo("0.09GB"); - assertThat(files.getSummary().toString()).isEqualTo("0.05GB"); + assertThat(audio.getSummary().toString()).isEqualTo("0.14 GB"); + assertThat(image.getSummary().toString()).isEqualTo("0.35 GB"); + assertThat(games.getSummary().toString()).isEqualTo("0.08 GB"); + assertThat(movies.getSummary().toString()).isEqualTo("0.16 GB"); + assertThat(apps.getSummary().toString()).isEqualTo("0.09 GB"); + assertThat(files.getSummary().toString()).isEqualTo("0.05 GB"); } @Test diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java index db7c9f76371..a2e57b95e96 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java @@ -122,7 +122,7 @@ public class UserProfileControllerTest { verify(mScreen).addPreference(argumentCaptor.capture()); Preference preference = argumentCaptor.getValue(); - assertThat(preference.getSummary()).isEqualTo("0.10GB"); + assertThat(preference.getSummary()).isEqualTo("0.10 GB"); } @Test diff --git a/tests/unit/README b/tests/unit/README index 5184b0724df..2544ea5b6af 100644 --- a/tests/unit/README +++ b/tests/unit/README @@ -1,8 +1,8 @@ To build the tests you can use the following command at the root of your android source tree -$ make SettingsUnitTests +$ make -j SettingsUnitTests The test apk then needs to be installed onto your test device via for example -$ adb install -r out/target/product/shamu/data/app/SettingsUnitTests/SettingsUnitTests.apk +$ adb install -r ${ANDROID_PRODUCT_OUT}/data/app/SettingsUnitTests/SettingsUnitTests.apk To run all tests: $ adb shell am instrument -w com.android.settings.tests.unit/android.support.test.runner.AndroidJUnitRunner diff --git a/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java b/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java index c5b050a5a7e..41b236c73d6 100644 --- a/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java +++ b/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java @@ -22,6 +22,7 @@ import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES; import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.icu.util.MeasureUnit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -46,7 +47,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, 0 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("0.00 GB"); } @@ -57,7 +58,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, MEGABYTE_IN_BYTES * 11 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("0.01 GB"); } @@ -68,7 +69,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, MEGABYTE_IN_BYTES * 155 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("0.16 GB"); } @@ -79,7 +80,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, MEGABYTE_IN_BYTES * 1551 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("1.6 GB"); } @@ -91,7 +92,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, GIGABYTE_IN_BYTES * 15 + MEGABYTE_IN_BYTES * 50 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("15 GB"); } @@ -102,7 +103,7 @@ public class FileSizeFormatterTest { FileSizeFormatter.formatFileSize( mContext, MEGABYTE_IN_BYTES * -155 /* size */, - com.android.internal.R.string.gigabyteShort, + MeasureUnit.GIGABYTE, GIGABYTE_IN_BYTES)) .isEqualTo("-0.16 GB"); }