Merge "Always use GB as the unit in Storage Settings." into oc-dev
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 763b6d4..6ae6c1a 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -17,14 +17,15 @@
 package com.android.settings.deviceinfo;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
-import android.text.format.Formatter;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ProgressBar;
 
 import com.android.settings.R;
+import com.android.settings.utils.FileSizeFormatter;
 
 public class StorageItemPreference extends Preference {
     public int userHandle;
@@ -44,9 +45,12 @@
     }
 
     public void setStorageSize(long size, long total) {
-        setSummary(size == 0
-                ? String.valueOf(0)
-                : Formatter.formatFileSize(getContext(), size));
+        setSummary(
+                FileSizeFormatter.formatFileSize(
+                        getContext(),
+                        size,
+                        getGigabyteSuffix(getContext().getResources()),
+                        FileSizeFormatter.GIGABYTE_IN_BYTES));
         if (total == 0) {
             mProgressPercent = 0;
         } else {
@@ -75,4 +79,8 @@
         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
new file mode 100644
index 0000000..e56388a
--- /dev/null
+++ b/src/com/android/settings/utils/FileSizeFormatter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.BidiFormatter;
+import android.text.format.Formatter;
+
+/**
+ * Utility class to aid in formatting file sizes always with the same unit. This is modified from
+ * android.text.format.Formatter to fit this purpose.
+ */
+public final class FileSizeFormatter {
+    public static final long KILOBYTE_IN_BYTES = 1000;
+    public static final long MEGABYTE_IN_BYTES = KILOBYTE_IN_BYTES * 1000;
+    public static final long GIGABYTE_IN_BYTES = MEGABYTE_IN_BYTES * 1000;
+
+    /**
+     * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc.
+     *
+     * <p>As of O, the prefixes are used in their standard meanings in the SI system, so kB = 1000
+     * bytes, MB = 1,000,000 bytes, etc.
+     *
+     * <p class="note">In {@link android.os.Build.VERSION_CODES#N} and earlier, powers of 1024 are
+     * used instead, with KB = 1024 bytes, MB = 1,048,576 bytes, etc.
+     *
+     * <p>If the context has a right-to-left locale, the returned string is wrapped in bidi
+     * formatting characters to make sure it's displayed correctly if inserted inside a
+     * right-to-left string. (This is useful in cases where the unit strings, like "MB", are
+     * left-to-right, but the locale is right-to-left.)
+     *
+     * @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
+     */
+    public static String formatFileSize(
+            @Nullable Context context, long sizeBytes, int suffix, 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");
+    }
+
+    /**
+     * A simplified version of the SettingsLib file size formatter. The primary difference is that
+     * this version always assumes it is doing a "short file size" and allows for a suffix to be
+     * provided.
+     *
+     * @param res Resources to fetch strings with.
+     * @param sizeBytes File size in bytes to format.
+     * @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) {
+        final boolean isNegative = (sizeBytes < 0);
+        float result = isNegative ? -sizeBytes : sizeBytes;
+        result = result / mult;
+        // Note we calculate the rounded long by ourselves, but still let String.format()
+        // 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;
+        if (mult == 1) {
+            roundFactor = 1;
+            roundFormat = "%.0f";
+        } else if (result < 1) {
+            roundFactor = 100;
+            roundFormat = "%.2f";
+        } else if (result < 10) {
+            roundFactor = 10;
+            roundFormat = "%.1f";
+        } else { // 10 <= result < 100
+            roundFactor = 1;
+            roundFormat = "%.0f";
+        }
+
+        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);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
index 969719f..6cb8f58 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.settings.deviceinfo;
 
-import static com.android.settings.TestUtils.KILOBYTE;
+import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -54,8 +54,8 @@
 
     @Test
     public void testAfterLoad() {
-        mPreference.setStorageSize(KILOBYTE, KILOBYTE * 10);
-        assertThat(((String) mPreference.getSummary())).isEqualTo("1.00KB");
+        mPreference.setStorageSize(MEGABYTE_IN_BYTES * 10, MEGABYTE_IN_BYTES * 100);
+        assertThat(((String) mPreference.getSummary())).isEqualTo("0.01GB");
     }
 
     @Test
@@ -66,7 +66,7 @@
                 (ProgressBar) holder.itemView.findViewById(android.R.id.progress);
 
         mPreference.onBindViewHolder(holder);
-        mPreference.setStorageSize(KILOBYTE, KILOBYTE * 10);
+        mPreference.setStorageSize(MEGABYTE_IN_BYTES, MEGABYTE_IN_BYTES * 10);
 
         assertThat(progressBar.getProgress()).isEqualTo(10);
     }
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 963e35f..32def69 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.deviceinfo.storage;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
 
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.verify;
@@ -96,13 +97,13 @@
     public void controllerUpdatesSummaryOfNewPreference() throws Exception {
         mPrimaryUser.name = TEST_NAME;
         mController.displayPreference(mScreen);
-        mController.setSize(10L);
+        mController.setSize(MEGABYTE_IN_BYTES * 10);
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
 
         verify(mGroup).addPreference(argumentCaptor.capture());
 
         Preference preference = argumentCaptor.getValue();
-        assertThat(preference.getSummary()).isEqualTo("10.00B");
+        assertThat(preference.getSummary()).isEqualTo("0.01GB");
     }
 
     @Test
@@ -162,7 +163,12 @@
         StorageAsyncLoader.AppsStorageResult userResult =
                 new StorageAsyncLoader.AppsStorageResult();
         SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
-        userResult.externalStats = new StorageStatsSource.ExternalStorageStats(99, 33, 33, 33);
+        userResult.externalStats =
+                new StorageStatsSource.ExternalStorageStats(
+                        MEGABYTE_IN_BYTES * 30,
+                        MEGABYTE_IN_BYTES * 10,
+                        MEGABYTE_IN_BYTES * 10,
+                        MEGABYTE_IN_BYTES * 10);
         result.put(10, userResult);
 
         mController.handleResult(result);
@@ -170,7 +176,7 @@
         verify(mGroup).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
 
-        assertThat(preference.getSummary()).isEqualTo("99.00B");
+        assertThat(preference.getSummary()).isEqualTo("0.03GB");
     }
 
     @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 82e04ec..fb65993 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -15,9 +15,9 @@
  */
 package com.android.settings.deviceinfo.storage;
 
-import static com.android.settings.TestUtils.KILOBYTE;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
@@ -50,9 +51,11 @@
 import com.android.settings.deviceinfo.PrivateVolumeSettings;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.applications.StorageStatsSource;
 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -80,6 +83,8 @@
     @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);
@@ -96,6 +101,11 @@
                 mPreference.getLayoutResource(), new LinearLayout(mContext), false);
     }
 
+    @After
+    public void tearDown() {
+        SettingsShadowResources.reset();
+    }
+
     @Test
     public void testUpdateStateWithInitialState() {
         assertThat(mPreference.getSummary().toString()).isEqualTo(
@@ -243,28 +253,29 @@
                 eq(StorageItemPreferenceController.FILES_KEY))).thenReturn(files);
         mController.displayPreference(screen);
 
-        mController.setUsedSize(KILOBYTE * 200); // There should 87kB attributed.
+        mController.setUsedSize(MEGABYTE_IN_BYTES * 970); // There should 870MB attributed.
         StorageAsyncLoader.AppsStorageResult result = new StorageAsyncLoader.AppsStorageResult();
-        result.gamesSize = KILOBYTE * 8;
-        result.videoAppsSize = KILOBYTE * 16;
-        result.musicAppsSize = KILOBYTE * 4;
-        result.otherAppsSize = KILOBYTE * 9;
-        result.systemSize = KILOBYTE * 10; // This value is ignored and overriden now.
-        result.externalStats = new StorageStatsSource.ExternalStorageStats(
-                KILOBYTE * 50, // total
-                KILOBYTE * 10, // audio
-                KILOBYTE * 15, // video
-                KILOBYTE * 20); // image
+        result.gamesSize = MEGABYTE_IN_BYTES * 80;
+        result.videoAppsSize = MEGABYTE_IN_BYTES * 160;
+        result.musicAppsSize = MEGABYTE_IN_BYTES * 40;
+        result.otherAppsSize = MEGABYTE_IN_BYTES * 90;
+        result.systemSize = MEGABYTE_IN_BYTES * 100; // This value is ignored and overridden now.
+        result.externalStats =
+                new StorageStatsSource.ExternalStorageStats(
+                        MEGABYTE_IN_BYTES * 500, // total
+                        MEGABYTE_IN_BYTES * 100, // audio
+                        MEGABYTE_IN_BYTES * 150, // video
+                        MEGABYTE_IN_BYTES * 200); // image
 
         mController.onLoadFinished(result);
 
-        assertThat(audio.getSummary().toString()).isEqualTo("14.00KB"); // 4KB apps + 10KB files
-        assertThat(image.getSummary().toString()).isEqualTo("35.00KB"); // 15KB video + 20KB images
-        assertThat(games.getSummary().toString()).isEqualTo("8.00KB");
-        assertThat(movies.getSummary().toString()).isEqualTo("16.00KB");
-        assertThat(apps.getSummary().toString()).isEqualTo("9.00KB");
-        assertThat(system.getSummary().toString()).isEqualTo("113KB");
-        assertThat(files.getSummary().toString()).isEqualTo("5.00KB");
+        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(system.getSummary().toString()).isEqualTo("0.10GB");
+        assertThat(files.getSummary().toString()).isEqualTo("0.05GB");
     }
 
     @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 0c3fc47..8b7110d 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.deviceinfo.storage;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
 
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -108,7 +109,12 @@
         SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
         StorageAsyncLoader.AppsStorageResult userResult =
                 new StorageAsyncLoader.AppsStorageResult();
-        userResult.externalStats = new StorageStatsSource.ExternalStorageStats(99, 33, 33, 33);
+        userResult.externalStats =
+                new StorageStatsSource.ExternalStorageStats(
+                        99 * MEGABYTE_IN_BYTES,
+                        33 * MEGABYTE_IN_BYTES,
+                        33 * MEGABYTE_IN_BYTES,
+                        33 * MEGABYTE_IN_BYTES);
         result.put(10, userResult);
 
         mController.handleResult(result);
@@ -116,7 +122,7 @@
         verify(mScreen).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
 
-        assertThat(preference.getSummary()).isEqualTo("99.00B");
+        assertThat(preference.getSummary()).isEqualTo("0.10GB");
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
index 724909d..ba82835 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
@@ -1,5 +1,11 @@
 package com.android.settings.testutils.shadow;
 
+import static android.util.TypedValue.TYPE_REFERENCE;
+
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
+import static org.robolectric.internal.Shadow.directlyOn;
+
 import android.annotation.DimenRes;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -12,6 +18,7 @@
 import android.support.annotation.ColorRes;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
+import android.util.SparseArray;
 import android.util.TypedValue;
 
 import com.android.settings.R;
@@ -20,21 +27,19 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.RealObject;
+import org.robolectric.internal.Shadow;
 import org.robolectric.res.StyleData;
 import org.robolectric.res.StyleResolver;
 import org.robolectric.res.builder.XmlResourceParserImpl;
 import org.robolectric.shadows.ShadowAssetManager;
 import org.robolectric.shadows.ShadowResources;
 import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
 import org.w3c.dom.Node;
 
 import java.util.List;
 import java.util.Map;
 
-import static android.util.TypedValue.TYPE_REFERENCE;
-import static org.robolectric.Shadows.shadowOf;
-import static org.robolectric.internal.Shadow.directlyOn;
-
 /**
  * Shadow Resources and Theme classes to handle resource references that Robolectric shadows cannot
  * handle because they are too new or private.
@@ -45,6 +50,25 @@
     @RealObject
     public Resources realResources;
 
+    private static SparseArray<Object> sResourceOverrides = new SparseArray<>();
+
+    public static void overrideResource(int id, Object value) {
+        sResourceOverrides.put(id, value);
+    }
+
+    public static void overrideResource(String name, Object value) {
+        final Resources res = application.getResources();
+        final int resId = res.getIdentifier(name, null, null);
+        if (resId == 0) {
+            throw new Resources.NotFoundException("Cannot override \"" + name + "\"");
+        }
+        overrideResource(resId, value);
+    }
+
+    public static void reset() {
+        sResourceOverrides.clear();
+    }
+
     @Implementation
     public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
         // Handle requests for private dimension resources,
@@ -93,6 +117,16 @@
         return directlyOn(realResources, Resources.class).getIntArray(id);
     }
 
+    @Implementation
+    public String getString(int id) {
+        final Object override = sResourceOverrides.get(id);
+        if (override instanceof String) {
+            return (String) override;
+        }
+        return Shadow.directlyOn(
+                realResources, Resources.class, "getString", ClassParameter.from(int.class, id));
+    }
+
     @Implements(Theme.class)
     public static class SettingsShadowTheme extends ShadowTheme {
 
diff --git a/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java b/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java
new file mode 100644
index 0000000..c5b050a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/utils/FileSizeFormatterTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils;
+
+import static com.android.settings.utils.FileSizeFormatter.GIGABYTE_IN_BYTES;
+import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FileSizeFormatterTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void formatFileSize_zero() throws Exception {
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                0 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("0.00 GB");
+    }
+
+    @Test
+    public void formatFileSize_smallSize() throws Exception {
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                MEGABYTE_IN_BYTES * 11 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("0.01 GB");
+    }
+
+    @Test
+    public void formatFileSize_lessThanOneSize() throws Exception {
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                MEGABYTE_IN_BYTES * 155 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("0.16 GB");
+    }
+
+    @Test
+    public void formatFileSize_greaterThanOneSize() throws Exception {
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                MEGABYTE_IN_BYTES * 1551 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("1.6 GB");
+    }
+
+    @Test
+    public void formatFileSize_greaterThanTen() throws Exception {
+        // Should round down due to truncation
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                GIGABYTE_IN_BYTES * 15 + MEGABYTE_IN_BYTES * 50 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("15 GB");
+    }
+
+    @Test
+    public void formatFileSize_handlesNegativeFileSizes() throws Exception {
+        assertThat(
+                        FileSizeFormatter.formatFileSize(
+                                mContext,
+                                MEGABYTE_IN_BYTES * -155 /* size */,
+                                com.android.internal.R.string.gigabyteShort,
+                                GIGABYTE_IN_BYTES))
+                .isEqualTo("-0.16 GB");
+    }
+}