Merge "Add user icon to the other user preferences." into oc-dev
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 4c8fb2d..d04ae8d 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -73,7 +73,6 @@
 import android.provider.ContactsContract.Profile;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.Settings;
-import android.service.persistentdata.PersistentDataBlockManager;
 import android.support.annotation.StringRes;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
diff --git a/src/com/android/settings/applications/UserManagerWrapper.java b/src/com/android/settings/applications/UserManagerWrapper.java
index daefb84..5b4ed2a 100644
--- a/src/com/android/settings/applications/UserManagerWrapper.java
+++ b/src/com/android/settings/applications/UserManagerWrapper.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications;
 
 import android.content.pm.UserInfo;
+
 import java.util.List;
 
 /**
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 75c0e75..602e65f 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -47,6 +48,7 @@
 import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
 import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
 import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController;
+import com.android.settings.deviceinfo.storage.UserIconLoader;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settingslib.applications.StorageStatsSource;
@@ -61,6 +63,7 @@
     implements LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
     private static final String TAG = "StorageDashboardFrag";
     private static final int STORAGE_JOB_ID = 0;
+    private static final int ICON_JOB_ID = 1;
     private static final int OPTIONS_MENU_MIGRATE_DATA = 100;
 
     private VolumeInfo mVolume;
@@ -71,34 +74,6 @@
     private List<PreferenceController> mSecondaryUsers;
 
     @Override
-    public void onResume() {
-        super.onResume();
-        getLoaderManager().initLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
-    }
-
-    @Override
-    public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
-            Bundle args) {
-        Context context = getContext();
-        return new StorageAsyncLoader(context,
-                new UserManagerWrapperImpl(context.getSystemService(UserManager.class)),
-                mVolume.fsUuid,
-                new StorageStatsSource(context),
-                new PackageManagerWrapperImpl(context.getPackageManager()));
-    }
-
-    @Override
-    public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
-            SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
-        mPreferenceController.onLoadFinished(data.get(UserHandle.myUserId()));
-        updateSecondaryUserControllers(mSecondaryUsers, data);
-    }
-
-    @Override
-    public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
-    }
-
-    @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
@@ -140,6 +115,13 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        getLoaderManager().initLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
+        getLoaderManager().initLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsProto.MetricsEvent.SETTINGS_STORAGE_CATEGORY;
     }
@@ -227,4 +209,55 @@
                 }
 
             };
+
+    @Override
+    public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
+            Bundle args) {
+        Context context = getContext();
+        return new StorageAsyncLoader(context,
+                new UserManagerWrapperImpl(context.getSystemService(UserManager.class)),
+                mVolume.fsUuid,
+                new StorageStatsSource(context),
+                new PackageManagerWrapperImpl(context.getPackageManager()));
+    }
+
+    @Override
+    public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
+            SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
+        mPreferenceController.onLoadFinished(data.get(UserHandle.myUserId()));
+        updateSecondaryUserControllers(mSecondaryUsers, data);
+    }
+
+    @Override
+    public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
+    }
+
+    /**
+     * IconLoaderCallbacks exists because StorageDashboardFragment already implements
+     * LoaderCallbacks for a different type.
+     */
+    public final class IconLoaderCallbacks
+            implements LoaderManager.LoaderCallbacks<SparseArray<Drawable>> {
+        @Override
+        public Loader<SparseArray<Drawable>> onCreateLoader(int id, Bundle args) {
+            return new UserIconLoader(
+                    getContext(),
+                    () -> UserIconLoader.loadUserIconsWithContext(getContext()));
+        }
+
+        @Override
+        public void onLoadFinished(
+                Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
+            mSecondaryUsers
+                    .stream()
+                    .filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
+                    .forEach(
+                            controller ->
+                                    ((UserIconLoader.UserIconHandler) controller)
+                                            .handleUserIcons(data));
+        }
+
+        @Override
+        public void onLoaderReset(Loader<SparseArray<Drawable>> loader) {}
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
index b3e89c6..62e946d 100644
--- a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
+++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -37,8 +38,8 @@
  * SecondaryUserController controls the preferences on the Storage screen which had to do with
  * secondary users.
  */
-public class SecondaryUserController extends PreferenceController implements
-        StorageAsyncLoader.ResultHandler {
+public class SecondaryUserController extends PreferenceController
+        implements StorageAsyncLoader.ResultHandler, UserIconLoader.UserIconHandler {
     // PreferenceGroupKey to try to add our preference onto.
     private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
     private static final String PREFERENCE_KEY_BASE = "pref_user_";
@@ -69,8 +70,9 @@
             }
 
             if (info == null || Utils.isProfileOf(primaryUser, info)) {
-                controllers.add(new UserProfileController(context, info,
-                        USER_PROFILE_INSERTION_LOCATION));
+                controllers.add(
+                        new UserProfileController(
+                                context, info, userManager, USER_PROFILE_INSERTION_LOCATION));
                 continue;
             }
 
@@ -109,8 +111,6 @@
                 mStoragePreference.setStorageSize(mSize, mTotalSizeBytes);
             }
 
-            // TODO(b/36252572): Set the user icon appropriately here.
-
             group.setVisible(true);
             group.addPreference(mStoragePreference);
         }
@@ -161,6 +161,14 @@
         }
     }
 
+    @Override
+    public void handleUserIcons(SparseArray<Drawable> fetchedIcons) {
+        Drawable userIcon = fetchedIcons.get(mUser.id);
+        if (userIcon != null) {
+            mStoragePreference.setIcon(userIcon);
+        }
+    }
+
     private static class NoSecondaryUserController extends PreferenceController {
         public NoSecondaryUserController(Context context) {
             super(context);
diff --git a/src/com/android/settings/deviceinfo/storage/UserIconLoader.java b/src/com/android/settings/deviceinfo/storage/UserIconLoader.java
new file mode 100644
index 0000000..4f00c3c
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/UserIconLoader.java
@@ -0,0 +1,75 @@
+/*
+ * 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.storage;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+import com.android.settings.Utils;
+import com.android.settings.utils.AsyncLoader;
+
+/**
+ * Fetches a user icon as a loader using a given icon loading lambda.
+ */
+public class UserIconLoader extends AsyncLoader<SparseArray<Drawable>> {
+    private FetchUserIconTask mTask;
+
+    /**
+     * Task to load all user icons.
+     */
+    public interface FetchUserIconTask {
+        SparseArray<Drawable> getUserIcons();
+    }
+
+    /**
+     * Handle the output of this task.
+     */
+    public interface UserIconHandler {
+        void handleUserIcons(SparseArray<Drawable> fetchedIcons);
+    }
+
+    public UserIconLoader(Context context, FetchUserIconTask task) {
+        super(context);
+        mTask = Preconditions.checkNotNull(task);
+    }
+
+    @Override
+    public SparseArray<Drawable> loadInBackground() {
+        return mTask.getUserIcons();
+    }
+
+    @Override
+    protected void onDiscardResult(SparseArray<Drawable> result) {}
+
+    /**
+     * Loads the user icons using a given context. This returns a {@link SparseArray} which maps
+     * user ids to their user icons.
+     */
+    public static SparseArray<Drawable> loadUserIconsWithContext(Context context) {
+        SparseArray<Drawable> value = new SparseArray<>();
+        UserManager um = context.getSystemService(UserManager.class);
+        for (UserInfo userInfo : um.getUsers()) {
+            value.put(userInfo.id, Utils.getUserIcon(context, um, userInfo));
+        }
+        return value;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/UserProfileController.java b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
index 0e19740..18fa7b7 100644
--- a/src/com/android/settings/deviceinfo/storage/UserProfileController.java
+++ b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.storage.VolumeInfo;
 import android.support.v7.preference.Preference;
@@ -28,25 +29,27 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.util.Preconditions;
 import com.android.settings.Utils;
+import com.android.settings.applications.UserManagerWrapper;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.deviceinfo.StorageProfileFragment;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
 
-/**
- * Defines a {@link PreferenceController} which handles a single profile of the primary user.
- */
-public class UserProfileController extends PreferenceController implements
-        StorageAsyncLoader.ResultHandler {
+/** Defines a {@link PreferenceController} which handles a single profile of the primary user. */
+public class UserProfileController extends PreferenceController
+        implements StorageAsyncLoader.ResultHandler, UserIconLoader.UserIconHandler {
     private static final String PREFERENCE_KEY_BASE = "pref_profile_";
     private StorageItemPreference mStoragePreference;
+    private UserManagerWrapper mUserManager;
     private UserInfo mUser;
     private long mTotalSizeBytes;
     private final int mPreferenceOrder;
 
-    public UserProfileController(Context context, UserInfo info, int preferenceOrder) {
+    public UserProfileController(
+            Context context, UserInfo info, UserManagerWrapper userManager, int preferenceOrder) {
         super(context);
         mUser = Preconditions.checkNotNull(info);
+        mUserManager = userManager;
         mPreferenceOrder = preferenceOrder;
     }
 
@@ -66,7 +69,6 @@
         mStoragePreference.setOrder(mPreferenceOrder);
         mStoragePreference.setKey(PREFERENCE_KEY_BASE + mUser.id);
         mStoragePreference.setTitle(mUser.name);
-        // TODO(b/36252572): Set user icon here.
         screen.addPreference(mStoragePreference);
     }
 
@@ -110,4 +112,12 @@
     public void setTotalSize(long totalSize) {
         mTotalSizeBytes = totalSize;
     }
+
+    @Override
+    public void handleUserIcons(SparseArray<Drawable> fetchedIcons) {
+        Drawable userIcon = fetchedIcons.get(mUser.id);
+        if (userIcon != null) {
+            mStoragePreference.setIcon(userIcon);
+        }
+    }
 }
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 16bf1ae..c55ba36 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
@@ -19,12 +19,14 @@
 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.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceScreen;
@@ -34,7 +36,9 @@
 import com.android.settings.TestConfig;
 import com.android.settings.applications.UserManagerWrapper;
 import com.android.settings.core.PreferenceController;
+import com.android.settingslib.R;
 import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -187,4 +191,24 @@
         // We should have the NoSecondaryUserController.
         assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse();
     }
+
+    @Test
+    public void iconCallbackChangesPreferenceIcon() throws Exception {
+        SparseArray<Drawable> icons = new SparseArray<>();
+        Bitmap userBitmap =
+                BitmapFactory.decodeResource(
+                        RuntimeEnvironment.application.getResources(), R.drawable.home);
+        UserIconDrawable drawable = new UserIconDrawable(100 /* size */).setIcon(userBitmap).bake();
+        icons.put(10, drawable);
+        mPrimaryUser.name = TEST_NAME;
+        mPrimaryUser.id = 10;
+        mController.displayPreference(mScreen);
+
+        mController.handleUserIcons(icons);
+
+        final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
+        verify(mGroup).addPreference(argumentCaptor.capture());
+        Preference preference = argumentCaptor.getValue();
+        assertThat(preference.getIcon()).isEqualTo(drawable);
+    }
 }
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 ed49da4..0c3fc47 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -26,6 +25,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.util.SparseArray;
@@ -36,7 +38,9 @@
 import com.android.settings.TestConfig;
 import com.android.settings.applications.UserManagerWrapper;
 import com.android.settings.deviceinfo.StorageProfileFragment;
+import com.android.settingslib.R;
 import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,16 +70,15 @@
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
         mPrimaryProfile = new UserInfo();
-        mController = new UserProfileController(mContext, mPrimaryProfile, 0);
+        mController = new UserProfileController(mContext, mPrimaryProfile, mUserManager, 0);
         when(mScreen.getContext()).thenReturn(mContext);
+        mPrimaryProfile.name = TEST_NAME;
+        mPrimaryProfile.id = 10;
+        mController.displayPreference(mScreen);
     }
 
     @Test
     public void controllerAddsPrimaryProfilePreference() throws Exception {
-        mPrimaryProfile.name = TEST_NAME;
-        mPrimaryProfile.id = 10;
-        mController.displayPreference(mScreen);
-
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
         verify(mScreen).addPreference(argumentCaptor.capture());
         Preference preference = argumentCaptor.getValue();
@@ -86,9 +89,6 @@
 
     @Test
     public void tappingProfilePreferenceSendsToStorageProfileFragment() throws Exception {
-        mPrimaryProfile.name = TEST_NAME;
-        mPrimaryProfile.id = 10;
-        mController.displayPreference(mScreen);
 
         final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
         verify(mScreen).addPreference(argumentCaptor.capture());
@@ -105,9 +105,6 @@
 
     @Test
     public void acceptingResultUpdatesPreferenceSize() throws Exception {
-        mPrimaryProfile.name = TEST_NAME;
-        mPrimaryProfile.id = 10;
-        mController.displayPreference(mScreen);
         SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
         StorageAsyncLoader.AppsStorageResult userResult =
                 new StorageAsyncLoader.AppsStorageResult();
@@ -121,4 +118,21 @@
 
         assertThat(preference.getSummary()).isEqualTo("99.00B");
     }
+
+    @Test
+    public void iconCallbackChangesPreferenceIcon() throws Exception {
+        SparseArray<Drawable> icons = new SparseArray<>();
+        Bitmap userBitmap =
+                BitmapFactory.decodeResource(
+                        RuntimeEnvironment.application.getResources(), R.drawable.home);
+        UserIconDrawable drawable = new UserIconDrawable(100 /* size */).setIcon(userBitmap).bake();
+        icons.put(10, drawable);
+
+        mController.handleUserIcons(icons);
+
+        final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
+        verify(mScreen).addPreference(argumentCaptor.capture());
+        Preference preference = argumentCaptor.getValue();
+        assertThat(preference.getIcon()).isEqualTo(drawable);
+    }
 }
\ No newline at end of file