diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 8c90156d159d5..ad3b4b6d63436 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -20,11 +20,7 @@ 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.text.UnicodeSet; -import android.icu.text.UnicodeSetSpanner; import android.icu.util.Measure; import android.icu.util.MeasureUnit; import android.net.NetworkUtils; @@ -32,8 +28,6 @@ import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; -import java.lang.reflect.Constructor; -import java.math.BigDecimal; import java.util.Locale; /** @@ -42,8 +36,6 @@ import java.util.Locale; */ public final class Formatter { - /** {@hide} */ - public static final int FLAG_DEFAULT = 0; /** {@hide} */ public static final int FLAG_SHORTER = 1 << 0; /** {@hide} */ @@ -66,9 +58,7 @@ public final class Formatter { return context.getResources().getConfiguration().getLocales().get(0); } - /** - * Wraps the source string in bidi formatting characters in RTL locales. - */ + /* Wraps the source string in bidi formatting characters in RTL locales */ private static String bidiWrap(@NonNull Context context, String source) { final Locale locale = localeFromContext(context); if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) { @@ -97,7 +87,12 @@ public final class Formatter { * @return formatted string with the number */ public static String formatFileSize(@Nullable Context context, long sizeBytes) { - return formatFileSize(context, sizeBytes, FLAG_DEFAULT); + if (context == null) { + return ""; + } + final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0); + return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, + res.value, res.units)); } /** @@ -105,207 +100,88 @@ public final class Formatter { * (showing fewer digits of precision). */ public static String formatShortFileSize(@Nullable Context context, long sizeBytes) { - return formatFileSize(context, sizeBytes, FLAG_SHORTER); - } - - private static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) { if (context == null) { return ""; } - final RoundedBytesResult res = RoundedBytesResult.roundBytes(sizeBytes, flags); - return bidiWrap(context, formatRoundedBytesResult(context, res)); - } - - private static String getSuffixOverride(@NonNull Resources res, MeasureUnit unit) { - if (unit == MeasureUnit.BYTE) { - return res.getString(com.android.internal.R.string.byteShort); - } else { // unit == PETABYTE - return res.getString(com.android.internal.R.string.petabyteShort); - } - } - - 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 deleteFirstFromString(String source, String toDelete) { - final int location = source.indexOf(toDelete); - if (location == -1) { - return source; - } else { - return source.substring(0, location) - + source.substring(location + toDelete.length(), source.length()); - } - } - - 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 final UnicodeSetSpanner SPACES_AND_CONTROLS = - new UnicodeSetSpanner(new UnicodeSet("[[:Zs:][:Cf:]]").freeze()); - - private static String formatRoundedBytesResult( - @NonNull Context context, @NonNull RoundedBytesResult input) { - final Locale locale = localeFromContext(context); - final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits); - if (input.units == MeasureUnit.BYTE || input.units == PETABYTE) { - // ICU spells out "byte" instead of "B", and can't format petabytes yet. - final String formattedNumber = numberFormatter.format(input.value); - return context.getString(com.android.internal.R.string.fileSizeSuffix, - formattedNumber, getSuffixOverride(context.getResources(), input.units)); - } else { - return formatMeasureShort(locale, numberFormatter, input.value, input.units); - } + final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER); + return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, + res.value, res.units)); } /** {@hide} */ public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { - final RoundedBytesResult rounded = RoundedBytesResult.roundBytes(sizeBytes, flags); - final Locale locale = res.getConfiguration().getLocales().get(0); - final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits); - final String formattedNumber = numberFormatter.format(rounded.value); - final String units; - if (rounded.units == MeasureUnit.BYTE || rounded.units == PETABYTE) { - // ICU spells out "byte" instead of "B", and can't format petabytes yet. - units = getSuffixOverride(res, rounded.units); - } else { - // Since ICU does not give us access to the pattern, we need to extract the unit string - // from ICU, which we do by taking out the formatted number out of the formatted string - // and trimming the result of spaces and controls. - final String formattedMeasure = formatMeasureShort( - locale, numberFormatter, rounded.value, rounded.units); - final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber); - units = SPACES_AND_CONTROLS.trim(numberRemoved).toString(); + final boolean isNegative = (sizeBytes < 0); + float result = isNegative ? -sizeBytes : sizeBytes; + int suffix = com.android.internal.R.string.byteShort; + long mult = 1; + if (result > 900) { + suffix = com.android.internal.R.string.kilobyteShort; + mult = 1000; + result = result / 1000; } - return new BytesResult(formattedNumber, units, rounded.roundedBytes); - } - - /** - * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way. - */ - private static final MeasureUnit PETABYTE = createPetaByte(); - - /** - * Create a petabyte MeasureUnit without registering it with ICU. - * ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so - * is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as - * an available type and thus leaks it to code that doesn't expect or support it. - *
This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This
- * instance is only to be used in this class.
- */
- private static MeasureUnit createPetaByte() {
- try {
- Constructor