1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.utils; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.text.BidiFormatter; 23 import android.text.format.Formatter; 24 25 /** 26 * Utility class to aid in formatting file sizes always with the same unit. This is modified from 27 * android.text.format.Formatter to fit this purpose. 28 */ 29 public final class FileSizeFormatter { 30 public static final long KILOBYTE_IN_BYTES = 1000; 31 public static final long MEGABYTE_IN_BYTES = KILOBYTE_IN_BYTES * 1000; 32 public static final long GIGABYTE_IN_BYTES = MEGABYTE_IN_BYTES * 1000; 33 34 /** 35 * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc. 36 * 37 * <p>As of O, the prefixes are used in their standard meanings in the SI system, so kB = 1000 38 * bytes, MB = 1,000,000 bytes, etc. 39 * 40 * <p class="note">In {@link android.os.Build.VERSION_CODES#N} and earlier, powers of 1024 are 41 * used instead, with KB = 1024 bytes, MB = 1,048,576 bytes, etc. 42 * 43 * <p>If the context has a right-to-left locale, the returned string is wrapped in bidi 44 * formatting characters to make sure it's displayed correctly if inserted inside a 45 * right-to-left string. (This is useful in cases where the unit strings, like "MB", are 46 * left-to-right, but the locale is right-to-left.) 47 * 48 * @param context Context to use to load the localized units 49 * @param sizeBytes size value to be formatted, in bytes 50 * @param suffix String id for the unit suffix. 51 * @param mult Amount of bytes in the unit. * @return formatted string with the number 52 */ formatFileSize( @ullable Context context, long sizeBytes, int suffix, long mult)53 public static String formatFileSize( 54 @Nullable Context context, long sizeBytes, int suffix, long mult) { 55 if (context == null) { 56 return ""; 57 } 58 final Formatter.BytesResult res = 59 formatBytes(context.getResources(), sizeBytes, suffix, mult); 60 return BidiFormatter.getInstance() 61 .unicodeWrap(context.getString(getFileSizeSuffix(context), res.value, res.units)); 62 } 63 getFileSizeSuffix(Context context)64 private static int getFileSizeSuffix(Context context) { 65 final Resources res = context.getResources(); 66 return res.getIdentifier("fileSizeSuffix", "string", "android"); 67 } 68 69 /** 70 * A simplified version of the SettingsLib file size formatter. The primary difference is that 71 * this version always assumes it is doing a "short file size" and allows for a suffix to be 72 * provided. 73 * 74 * @param res Resources to fetch strings with. 75 * @param sizeBytes File size in bytes to format. 76 * @param suffix String id for the unit suffix. 77 * @param mult Amount of bytes in the unit. 78 */ formatBytes( Resources res, long sizeBytes, int suffix, long mult)79 private static Formatter.BytesResult formatBytes( 80 Resources res, long sizeBytes, int suffix, long mult) { 81 final boolean isNegative = (sizeBytes < 0); 82 float result = isNegative ? -sizeBytes : sizeBytes; 83 result = result / mult; 84 // Note we calculate the rounded long by ourselves, but still let String.format() 85 // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to 86 // floating point errors. 87 final int roundFactor; 88 final String roundFormat; 89 if (mult == 1) { 90 roundFactor = 1; 91 roundFormat = "%.0f"; 92 } else if (result < 1) { 93 roundFactor = 100; 94 roundFormat = "%.2f"; 95 } else if (result < 10) { 96 roundFactor = 10; 97 roundFormat = "%.1f"; 98 } else { // 10 <= result < 100 99 roundFactor = 1; 100 roundFormat = "%.0f"; 101 } 102 103 if (isNegative) { 104 result = -result; 105 } 106 final String roundedString = String.format(roundFormat, result); 107 108 // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so 109 // it's okay (for now)... 110 final long roundedBytes = (((long) Math.round(result * roundFactor)) * mult / roundFactor); 111 112 final String units = res.getString(suffix); 113 114 return new Formatter.BytesResult(roundedString, units, roundedBytes); 115 } 116 } 117