Merge "Update app icon for ManageDomainUrls setting"
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d7064e6..cab3139 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -48,7 +48,6 @@
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -62,7 +61,6 @@
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
 import android.os.Bundle;
@@ -97,7 +95,6 @@
 import android.text.style.TtsSpan;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -108,13 +105,10 @@
 
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.UserIcons;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settings.wrapper.FingerprintManagerWrapper;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -131,11 +125,6 @@
     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
 
     /**
-     * The opacity level of a disabled icon.
-     */
-    public static final float DISABLED_ALPHA = 0.4f;
-
-    /**
      * Color spectrum to use to indicate badness.  0 is completely transparent (no data),
      * 1 is most bad (red), the last value is least bad (green).
      */
@@ -152,8 +141,6 @@
 
     public static final String OS_PKG = "os";
 
-    private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
-
     /**
      * Finds a matching activity for a preference's intent. If a matching
      * activity is not found, it will remove the preference.
@@ -344,46 +331,6 @@
         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
     }
 
-    /* Used by UserSettings as well. Call this on a non-ui thread. */
-    public static void copyMeProfilePhoto(Context context, UserInfo user) {
-        Uri contactUri = Profile.CONTENT_URI;
-
-        int userId = user != null ? user.id : UserHandle.myUserId();
-
-        InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
-                    context.getContentResolver(),
-                    contactUri, true);
-        // If there's no profile photo, assign a default avatar
-        if (avatarDataStream == null) {
-            assignDefaultPhoto(context, userId);
-            return;
-        }
-
-        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
-        um.setUserIcon(userId, icon);
-        try {
-            avatarDataStream.close();
-        } catch (IOException ioe) { }
-    }
-
-    /**
-     * Assign the default photo to user with {@paramref userId}
-     * @param context used to get the {@link UserManager}
-     * @param userId  used to get the icon bitmap
-     * @return true if assign photo successfully, false if failed
-     */
-    public static boolean assignDefaultPhoto(Context context, int userId) {
-        if (context == null) {
-            return false;
-        }
-        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
-        um.setUserIcon(userId, bitmap);
-
-        return true;
-    }
-
     public static String getMeProfileName(Context context, boolean full) {
         if (full) {
             return getProfileDisplayName(context);
@@ -965,23 +912,6 @@
         return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
     }
 
-    /**
-     * Returns a default user icon (as a {@link Bitmap}) for the given user.
-     *
-     * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
-     * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
-     */
-    public static Bitmap getDefaultUserIconAsBitmap(int userId) {
-        Bitmap bitmap = null;
-        // Try finding the corresponding bitmap in the dark bitmap cache
-        bitmap = sDarkDefaultUserBitmapCache.get(userId);
-        if (bitmap == null) {
-            bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
-            // Save it to cache
-            sDarkDefaultUserBitmapCache.put(userId, bitmap);
-        }
-        return bitmap;
-    }
 
     public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
         // Get list of preferred activities
@@ -998,7 +928,7 @@
         List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
 
         ArraySet<String> result = new ArraySet<>();
-        if (iviList.size() > 0) {
+        if (iviList != null && iviList.size() > 0) {
             for (IntentFilterVerificationInfo ivi : iviList) {
                 for (String host : ivi.getDomains()) {
                     result.add(host);
diff --git a/src/com/android/settings/applications/ManageDomainUrls.java b/src/com/android/settings/applications/ManageDomainUrls.java
index 53cad4a..93416ad 100644
--- a/src/com/android/settings/applications/ManageDomainUrls.java
+++ b/src/com/android/settings/applications/ManageDomainUrls.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.support.annotation.VisibleForTesting;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
@@ -37,6 +38,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
+import com.android.settings.widget.AppPreference;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 
@@ -175,7 +177,7 @@
             String key = entry.info.packageName + "|" + entry.info.uid;
             DomainAppPreference preference = (DomainAppPreference) getCachedPreference(key);
             if (preference == null) {
-                preference = new DomainAppPreference(getPrefContext(), entry);
+                preference = new DomainAppPreference(getPrefContext(), mApplicationsState, entry);
                 preference.setKey(key);
                 preference.setOnPreferenceClickListener(this);
                 group.addPreference(preference);
@@ -225,12 +227,16 @@
         return false;
     }
 
-    private class DomainAppPreference extends Preference {
+    @VisibleForTesting
+    static class DomainAppPreference extends AppPreference {
         private final AppEntry mEntry;
         private final PackageManager mPm;
+        private final ApplicationsState mApplicationsState;
 
-        public DomainAppPreference(final Context context, AppEntry entry) {
+        public DomainAppPreference(final Context context, ApplicationsState applicationsState,
+                AppEntry entry) {
             super(context);
+            mApplicationsState = applicationsState;
             mPm = context.getPackageManager();
             mEntry = entry;
             mEntry.ensureLabel(getContext());
diff --git a/src/com/android/settings/users/ProfileUpdateReceiver.java b/src/com/android/settings/users/ProfileUpdateReceiver.java
index d532089..80fa10a 100644
--- a/src/com/android/settings/users/ProfileUpdateReceiver.java
+++ b/src/com/android/settings/users/ProfileUpdateReceiver.java
@@ -38,13 +38,13 @@
         // Profile changed, lets get the photo and write to user manager
         new Thread() {
             public void run() {
-                Utils.copyMeProfilePhoto(context, null);
+                UserSettings.copyMeProfilePhoto(context, null);
                 copyProfileName(context);
             }
         }.start();
     }
 
-    static void copyProfileName(Context context) {
+    private static void copyProfileName(Context context) {
         SharedPreferences prefs = context.getSharedPreferences("profile", Context.MODE_PRIVATE);
         if (prefs.contains(KEY_PROFILE_NAME_COPIED_ONCE)) {
             return;
@@ -55,7 +55,8 @@
         String profileName = Utils.getMeProfileName(context, false /* partial name */);
         if (profileName != null && profileName.length() > 0) {
             um.setUserName(userId, profileName);
-            // Flag that we've written the profile one time at least. No need to do it in the future.
+            // Flag that we've written the profile one time at least. No need to do it in the
+            // future.
             prefs.edit().putBoolean(KEY_PROFILE_NAME_COPIED_ONCE, true).commit();
         }
     }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 642f289..906c9d4 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -30,7 +30,9 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,7 +40,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.ContactsContract;
 import android.provider.Settings.Global;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.support.v7.preference.PreferenceGroup;
@@ -53,6 +58,7 @@
 import android.widget.SimpleAdapter;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.UserIcons;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
@@ -68,6 +74,8 @@
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -134,7 +142,8 @@
     private boolean mShouldUpdateUserList = true;
     private final Object mUserLock = new Object();
     private UserManager mUserManager;
-    private SparseArray<Bitmap> mUserIcons = new SparseArray<Bitmap>();
+    private SparseArray<Bitmap> mUserIcons = new SparseArray<>();
+    private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
 
     private EditUserInfoController mEditUserInfoController =
             new EditUserInfoController();
@@ -324,7 +333,7 @@
                 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
                 if (user.iconPath == null || user.iconPath.equals("")) {
                     // Assign profile photo.
-                    Utils.copyMeProfilePhoto(getActivity(), user);
+                    copyMeProfilePhoto(getActivity(), user);
                 }
                 return user.name;
             }
@@ -397,7 +406,7 @@
 
     private UserInfo createRestrictedProfile() {
         UserInfo newUserInfo = mUserManager.createRestrictedProfile(mAddingUserName);
-        if (newUserInfo != null && !Utils.assignDefaultPhoto(getActivity(), newUserInfo.id)) {
+        if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
             return null;
         }
         return newUserInfo;
@@ -405,7 +414,7 @@
 
     private UserInfo createTrustedUser() {
         UserInfo newUserInfo = mUserManager.createUser(mAddingUserName, 0);
-        if (newUserInfo != null && !Utils.assignDefaultPhoto(getActivity(), newUserInfo.id)) {
+        if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
             return null;
         }
         return newUserInfo;
@@ -914,7 +923,7 @@
                 for (int userId : values[0]) {
                     Bitmap bitmap = mUserManager.getUserIcon(userId);
                     if (bitmap == null) {
-                        bitmap = Utils.getDefaultUserIconAsBitmap(userId);
+                        bitmap = getDefaultUserIconAsBitmap(userId);
                     }
                     mUserIcons.append(userId, bitmap);
                 }
@@ -925,7 +934,7 @@
 
     private Drawable getEncircledDefaultIcon() {
         if (mDefaultIconDrawable == null) {
-            mDefaultIconDrawable = encircle(Utils.getDefaultUserIconAsBitmap(UserHandle.USER_NULL));
+            mDefaultIconDrawable = encircle(getDefaultUserIconAsBitmap(UserHandle.USER_NULL));
         }
         return mDefaultIconDrawable;
     }
@@ -1025,6 +1034,65 @@
         mMePreference.setTitle(label);
     }
 
+    /**
+     * Returns a default user icon (as a {@link Bitmap}) for the given user.
+     *
+     * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
+     * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
+     */
+    private static Bitmap getDefaultUserIconAsBitmap(int userId) {
+        Bitmap bitmap = null;
+        // Try finding the corresponding bitmap in the dark bitmap cache
+        bitmap = sDarkDefaultUserBitmapCache.get(userId);
+        if (bitmap == null) {
+            bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
+            // Save it to cache
+            sDarkDefaultUserBitmapCache.put(userId, bitmap);
+        }
+        return bitmap;
+    }
+
+    /**
+     * Assign the default photo to user with {@paramref userId}
+     * @param context used to get the {@link UserManager}
+     * @param userId  used to get the icon bitmap
+     * @return true if assign photo successfully, false if failed
+     */
+    @VisibleForTesting
+    static boolean assignDefaultPhoto(Context context, int userId) {
+        if (context == null) {
+            return false;
+        }
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
+        um.setUserIcon(userId, bitmap);
+
+        return true;
+    }
+
+    @WorkerThread
+    static void copyMeProfilePhoto(Context context, UserInfo user) {
+        Uri contactUri = ContactsContract.Profile.CONTENT_URI;
+
+        int userId = user != null ? user.id : UserHandle.myUserId();
+
+        InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
+                context.getContentResolver(),
+                contactUri, true);
+        // If there's no profile photo, assign a default avatar
+        if (avatarDataStream == null) {
+            assignDefaultPhoto(context, userId);
+            return;
+        }
+
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
+        um.setUserIcon(userId, icon);
+        try {
+            avatarDataStream.close();
+        } catch (IOException ioe) { }
+    }
+
     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
 
         private final Context mContext;
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index ca02c1d..f813457 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -47,9 +47,8 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class UtilsTest {
 
-    private static final String TIME_DESCRIPTION = "1 day 20 hours 30 minutes";
     private static final String PACKAGE_NAME = "com.android.app";
-    private Context mContext;
+
     @Mock
     private WifiManager wifiManager;
     @Mock
@@ -60,6 +59,7 @@
     private DevicePolicyManagerWrapper mDevicePolicyManager;
     @Mock
     private UserManager mUserManager;
+    private Context mContext;
 
     @Before
     public void setUp() {
@@ -100,12 +100,6 @@
     }
 
     @Test
-    public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() {
-        // Should not crash here
-        assertThat(Utils.assignDefaultPhoto(null, 0)).isFalse();
-    }
-
-    @Test
     public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "5m 30s";
diff --git a/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java b/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
index 583a004..35d1194 100644
--- a/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
@@ -24,11 +24,10 @@
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.view.LayoutInflater;
-import android.view.View;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,19 +36,17 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class LayoutPreferenceTest {
 
     private Context mContext;
     private LayoutPreference mPreference;
-    private View mRootView;
     private PreferenceViewHolder mHolder;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
         mPreference = new LayoutPreference(mContext, R.layout.two_action_buttons);
-        mRootView = mPreference.mRootView;
         mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
                 .inflate(R.layout.layout_preference_frame, null, false));
     }
diff --git a/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java b/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java
new file mode 100644
index 0000000..3e89647
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.applications;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ManageDomainUrlsTest {
+
+    @Mock
+    private ApplicationsState.AppEntry mAppEntry;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void domainAppPreferenceShouldUseAppPreferenceLayout() {
+        mAppEntry.info = new ApplicationInfo();
+        mAppEntry.info.packageName = "com.android.settings.test";
+        final ManageDomainUrls.DomainAppPreference pref =
+                new ManageDomainUrls.DomainAppPreference(mContext, null, mAppEntry);
+
+        assertThat(pref.getLayoutResource()).isEqualTo(R.layout.preference_app);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index a578364..56f3949 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -16,27 +16,28 @@
 
 package com.android.settings.users;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.Activity;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
 import org.robolectric.Robolectric;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -70,4 +71,9 @@
             mActivity.getString(R.string.users_summary, name));
     }
 
+    @Test
+    public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() {
+        // Should not crash here
+        assertThat(UserSettings.assignDefaultPhoto(null, 0)).isFalse();
+    }
 }