Reland "Don't use framework strings for formatting file sizes"
Reland http://ag/2529020 because the over-translation issue should be resolved now. Modify various formatters to avoid using framework strings for formatting file sizes. Also update README instructions for running unit tests. Bug: 36994779 Bug: 71580745 Bug: 217592956 Test: no new test failures from RunSettingsRoboTests Test: manual opening the settings page. Change-Id: Ic4689ab1b76622028004d05e69858228bdc441cf
This commit is contained in:
committed by
Victor Chang
parent
1143a4e8dc
commit
ad389346c8
@@ -16,11 +16,20 @@
|
||||
|
||||
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.format.Formatter;
|
||||
import android.text.TextUtils;
|
||||
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 +40,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;
|
||||
|
||||
private 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 +111,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 +134,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 +143,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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user