Merge "Add default IME to Privacy Settings page"
diff --git a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
index 801a20b..65959b4 100644
--- a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
@@ -76,7 +76,7 @@
             }
         }
         mAccountSynController.init(mAccount, userHandle);
-        mRemoveAccountController.setAccount(mAccount);
+        mRemoveAccountController.init(mAccount, userHandle);
     }
 
     @Override
diff --git a/src/com/android/settings/accounts/RemoveAccountPreferenceController.java b/src/com/android/settings/accounts/RemoveAccountPreferenceController.java
index c8dbe4c..f331144 100644
--- a/src/com/android/settings/accounts/RemoveAccountPreferenceController.java
+++ b/src/com/android/settings/accounts/RemoveAccountPreferenceController.java
@@ -27,8 +27,10 @@
 import android.app.Fragment;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Process;
+import android.os.UserHandle;
 import android.support.v7.preference.PreferenceScreen;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -49,6 +51,7 @@
 
     private Account mAccount;
     private Fragment mParentFragment;
+    private UserHandle mUserHandle;
 
     public RemoveAccountPreferenceController(Context context, Fragment parent) {
         super(context);
@@ -76,11 +79,12 @@
 
     @Override
     public void onClick(View v) {
-        ConfirmRemoveAccountDialog.show(mParentFragment, mAccount);
+        ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle);
     }
 
-    public void setAccount(Account account) {
+    public void init(Account account, UserHandle userHandle) {
         mAccount = account;
+        mUserHandle = userHandle;
     }
 
     /**
@@ -88,27 +92,37 @@
      */
     public static class ConfirmRemoveAccountDialog extends InstrumentedDialogFragment implements
             DialogInterface.OnClickListener {
-        private static final String SAVE_ACCOUNT = "account";
+        private static final String KEY_ACCOUNT = "account";
         private static final String REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount";
         private Account mAccount;
+        private UserHandle mUserHandle;
 
-        public static ConfirmRemoveAccountDialog show(Fragment parent, Account account) {
+        public static ConfirmRemoveAccountDialog show(
+                Fragment parent, Account account, UserHandle userHandle) {
             if (!parent.isAdded()) {
                 return null;
             }
             final ConfirmRemoveAccountDialog dialog = new ConfirmRemoveAccountDialog();
-            dialog.mAccount = account;
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(KEY_ACCOUNT, account);
+            bundle.putParcelable(Intent.EXTRA_USER, userHandle);
+            dialog.setArguments(bundle);
             dialog.setTargetFragment(parent, 0);
             dialog.show(parent.getFragmentManager(), REMOVE_ACCOUNT_DIALOG);
             return dialog;
         }
 
         @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            final Bundle arguments = getArguments();
+            mAccount = arguments.getParcelable(KEY_ACCOUNT);
+            mUserHandle = arguments.getParcelable(Intent.EXTRA_USER);
+        }
+
+        @Override
         public Dialog onCreateDialog(Bundle savedInstanceState) {
             final Context context = getActivity();
-            if (savedInstanceState != null) {
-                mAccount = (Account) savedInstanceState.getParcelable(SAVE_ACCOUNT);
-            }
             return new AlertDialog.Builder(context)
                 .setTitle(R.string.really_remove_account_title)
                 .setMessage(R.string.really_remove_account_message)
@@ -118,12 +132,6 @@
         }
 
         @Override
-        public void onSaveInstanceState(Bundle outState) {
-            super.onSaveInstanceState(outState);
-            outState.putParcelable(SAVE_ACCOUNT, mAccount);
-        }
-
-        @Override
         public int getMetricsCategory() {
             return MetricsProto.MetricsEvent.DIALOG_ACCOUNT_SYNC_REMOVE;
         }
@@ -159,7 +167,7 @@
                                 activity.finish();
                             }
                         }
-                    }, null, Process.myUserHandle());
+                    }, null, mUserHandle);
         }
     }
 
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index b4d7526..32c8d32 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -615,28 +615,6 @@
         }
     };
 
-    public static CharSequence getSummary(AppEntry appEntry, Context context) {
-        if (appEntry.size == ApplicationsState.SIZE_INVALID
-                || appEntry.size == ApplicationsState.SIZE_UNKNOWN) {
-            return context.getText(R.string.computing_size);
-        } else {
-            CharSequence storageType = context.getString(
-                    (appEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0
-                    ? R.string.storage_type_external
-                    : R.string.storage_type_internal);
-            return context.getString(R.string.storage_summary_format,
-                    getSize(appEntry, context), storageType);
-        }
-    }
-
-    private static CharSequence getSize(AppEntry appEntry, Context context) {
-        long size = appEntry.size;
-        if (size == SIZE_INVALID) {
-            return context.getText(R.string.invalid_size_value);
-        }
-        return Formatter.formatFileSize(context, size);
-    }
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.APPLICATIONS_APP_STORAGE;
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 3427d9e..5a32531 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -16,10 +16,13 @@
 
 package com.android.settings.applications;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
+import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
@@ -103,6 +106,8 @@
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 import com.android.settingslib.applications.PermissionsSummaryHelper;
 import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback;
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
 import com.android.settingslib.net.ChartData;
 import com.android.settingslib.net.ChartDataLoader;
 
@@ -111,8 +116,6 @@
 import java.util.HashSet;
 import java.util.List;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 /**
  * Activity to display application information from Settings. This activity presents
  * extended information associated with a package like code, data, total size, permissions
@@ -123,7 +126,8 @@
  * uninstall the application.
  */
 public class InstalledAppDetails extends AppInfoBase
-        implements View.OnClickListener, OnPreferenceClickListener {
+        implements View.OnClickListener, OnPreferenceClickListener,
+        LoaderManager.LoaderCallbacks<AppStorageStats> {
 
     private static final String LOG_TAG = "InstalledAppDetails";
 
@@ -138,6 +142,7 @@
     private static final int SUB_INFO_FRAGMENT = 1;
 
     private static final int LOADER_CHART_DATA = 2;
+    private static final int LOADER_STORAGE = 3;
 
     private static final int DLG_FORCE_STOP = DLG_BASE + 1;
     private static final int DLG_DISABLE = DLG_BASE + 2;
@@ -191,6 +196,8 @@
     protected ProcStatsData mStatsManager;
     protected ProcStatsPackageEntry mStats;
 
+    private AppStorageStats mLastResult;
+
     private boolean handleDisableable(Button button) {
         boolean disableable = false;
         // Try to prevent the user from bricking their phone
@@ -359,13 +366,14 @@
         if (mFinishing) {
             return;
         }
-        mState.requestSize(mPackageName, mUserId);
         AppItem app = new AppItem(mAppEntry.info.uid);
         app.addUid(mAppEntry.info.uid);
         if (mStatsSession != null) {
-            getLoaderManager().restartLoader(LOADER_CHART_DATA,
+            LoaderManager loaderManager = getLoaderManager();
+            loaderManager.restartLoader(LOADER_CHART_DATA,
                     ChartDataLoader.buildArgs(getTemplate(getContext()), app),
                     mDataCallbacks);
+            loaderManager.restartLoader(LOADER_STORAGE, Bundle.EMPTY, this);
         }
         new BatteryUpdater().execute();
         new MemoryUpdater().execute();
@@ -536,6 +544,23 @@
         }
     }
 
+    @Override
+    public Loader<AppStorageStats> onCreateLoader(int id, Bundle args) {
+        Context context = getContext();
+        return new FetchPackageStorageAsyncLoader(
+                context, new StorageStatsSource(context), mAppEntry.info, UserHandle.of(mUserId));
+    }
+
+    @Override
+    public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
+        mLastResult = result;
+        refreshUi();
+    }
+
+    @Override
+    public void onLoaderReset(Loader<AppStorageStats> loader) {
+    }
+
     // Utility method to set application label and icon.
     private void setAppLabelAndIcon(PackageInfo pkgInfo) {
         final View appSnippet = mHeader.findViewById(R.id.app_snippet);
@@ -638,7 +663,8 @@
 
         // Update the preference summaries.
         Activity context = getActivity();
-        mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context));
+        boolean isExternal = ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+        mStoragePreference.setSummary(getStorageSummary(context, mLastResult, isExternal));
 
         PermissionsSummaryHelper.getPermissionSummary(getContext(),
                 mPackageName, mPermissionCallback);
@@ -707,6 +733,25 @@
         return getString(R.string.computing_size);
     }
 
+    @VisibleForTesting
+    static CharSequence getStorageSummary(
+            Context context, AppStorageStats stats, boolean isExternal) {
+        if (stats == null) {
+            return context.getText(R.string.computing_size);
+        } else {
+            CharSequence storageType = context.getString(isExternal
+                    ? R.string.storage_type_external
+                    : R.string.storage_type_internal);
+            return context.getString(R.string.storage_summary_format,
+                    getSize(context, stats), storageType);
+        }
+    }
+
+    private static CharSequence getSize(Context context, AppStorageStats stats) {
+        return Formatter.formatFileSize(context, stats.getTotalBytes());
+    }
+
+
     @Override
     protected AlertDialog createDialog(int id, int errorCode) {
         switch (id) {
diff --git a/src/com/android/settings/deviceinfo/StorageProfileFragment.java b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
index 6ae03da..d6071c7 100644
--- a/src/com/android/settings/deviceinfo/StorageProfileFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
@@ -24,6 +24,7 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.support.annotation.VisibleForTesting;
 import android.util.SparseArray;
 
 import com.android.internal.logging.nano.MetricsProto;
@@ -119,10 +120,26 @@
     @Override
     public void onLoadFinished(Loader<SparseArray<AppsStorageResult>> loader,
             SparseArray<AppsStorageResult> result) {
-        mPreferenceController.onLoadFinished(result.get(mUserId));
+        mPreferenceController.onLoadFinished(scrubAppsFromResult(result.get(mUserId)));
     }
 
     @Override
     public void onLoaderReset(Loader<SparseArray<AppsStorageResult>> loader) {
     }
+
+    @VisibleForTesting
+    void setPreferenceController(StorageItemPreferenceController controller) {
+        mPreferenceController = controller;
+    }
+
+    private AppsStorageResult scrubAppsFromResult(AppsStorageResult result) {
+        if (result == null) {
+            return null;
+        }
+
+        result.gamesSize = 0;
+        result.musicAppsSize = 0;
+        result.otherAppsSize = 0;
+        return result;
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 7487b28..2fa1b18 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -173,6 +173,8 @@
     }
 
     public void onLoadFinished(StorageAsyncLoader.AppsStorageResult data) {
+        // TODO(b/35927909): Figure out how to split out apps which are only installed for work
+        //       profiles in order to attribute those app's code bytes only to that profile.
         mPhotoPreference.setStorageSize(
                 data.externalStats.imageBytes + data.externalStats.videoBytes);
         mAudioPreference.setStorageSize(data.musicAppsSize + data.externalStats.audioBytes);
diff --git a/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java
index 50f3ac6..033465b 100644
--- a/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java
@@ -23,6 +23,7 @@
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.support.v7.preference.PreferenceScreen;
@@ -127,11 +128,13 @@
         when(mFragment.getActivity()).thenReturn(activity);
 
         Account account = new Account("Account11", "com.acct1");
+        UserHandle userHandle = new UserHandle(10);
         RemoveAccountPreferenceController.ConfirmRemoveAccountDialog dialog =
-            RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.show(mFragment, account);
-
+            RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.show(
+                    mFragment, account, userHandle);
+        dialog.onCreate(new Bundle());
         dialog.onClick(null, 0);
         verify(mAccountManager).removeAccountAsUser(eq(account), any(Activity.class),
-            any(AccountManagerCallback.class), any(Handler.class), any(UserHandle.class));
+            any(AccountManagerCallback.class), any(Handler.class), eq(userHandle));
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index 2a703ef..4fe6293 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -16,7 +16,14 @@
 
 package com.android.settings.applications;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.admin.DevicePolicyManager;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.os.UserManager;
@@ -25,6 +32,7 @@
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -32,14 +40,10 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public final class InstalledAppDetailsTest {
@@ -117,4 +121,24 @@
         assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse();
     }
 
+    @Test
+    public void getStorageSummary_shouldWorkForExternal() {
+        Context context = RuntimeEnvironment.application.getApplicationContext();
+        AppStorageStats stats = mock(AppStorageStats.class);
+        when(stats.getTotalBytes()).thenReturn(1L);
+
+        assertThat(InstalledAppDetails.getStorageSummary(context, stats, true))
+                .isEqualTo("1.00B used in External storage");
+    }
+
+    @Test
+    public void getStorageSummary_shouldWorkForInternal() {
+        Context context = RuntimeEnvironment.application.getApplicationContext();
+        AppStorageStats stats = mock(AppStorageStats.class);
+        when(stats.getTotalBytes()).thenReturn(1L);
+
+        assertThat(InstalledAppDetails.getStorageSummary(context, stats, false))
+                .isEqualTo("1.00B used in Internal storage");
+
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java
new file mode 100644
index 0000000..8da2a9c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.util.SparseArray;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
+import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
+import com.android.settingslib.applications.StorageStatsSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class StorageProfileFragmentTest {
+    @Test
+    public void verifyAppSizesAreZeroedOut() {
+        StorageItemPreferenceController controller = mock(StorageItemPreferenceController.class);
+        StorageProfileFragment fragment = new StorageProfileFragment();
+        StorageAsyncLoader.AppsStorageResult result = new StorageAsyncLoader.AppsStorageResult();
+        result.musicAppsSize = 100;
+        result.otherAppsSize = 200;
+        result.gamesSize = 300;
+        result.externalStats = new StorageStatsSource.ExternalStorageStats(6, 1, 2, 3);
+        SparseArray<StorageAsyncLoader.AppsStorageResult> resultsArray = new SparseArray<>();
+        resultsArray.put(0, result);
+        fragment.setPreferenceController(controller);
+
+        fragment.onLoadFinished(null, resultsArray);
+
+        ArgumentCaptor<StorageAsyncLoader.AppsStorageResult> resultCaptor = ArgumentCaptor.forClass(
+                StorageAsyncLoader.AppsStorageResult.class);
+        verify(controller).onLoadFinished(resultCaptor.capture());
+
+        StorageAsyncLoader.AppsStorageResult extractedResult = resultCaptor.getValue();
+        assertThat(extractedResult.musicAppsSize).isEqualTo(0);
+        assertThat(extractedResult.otherAppsSize).isEqualTo(0);
+        assertThat(extractedResult.gamesSize).isEqualTo(0);
+        assertThat(extractedResult.externalStats.audioBytes).isEqualTo(1);
+        assertThat(extractedResult.externalStats.videoBytes).isEqualTo(2);
+        assertThat(extractedResult.externalStats.imageBytes).isEqualTo(3);
+        assertThat(extractedResult.externalStats.totalBytes).isEqualTo(6);
+    }
+}