diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index b108c62be66..646e5946f41 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -48,7 +48,6 @@ import android.content.res.Resources; 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.icu.util.ULocale; 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.format.DateUtils; 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 android.widget.TabWidget; 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; @@ -130,11 +124,6 @@ public final class Utils extends com.android.settingslib.Utils { */ 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 final class Utils extends com.android.settingslib.Utils { public static final String OS_PKG = "os"; - private static SparseArray sDarkDefaultUserBitmapCache = new SparseArray(); - /** * 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 @@ public final class Utils extends com.android.settingslib.Utils { 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); @@ -936,23 +883,6 @@ public final class Utils extends com.android.settingslib.Utils { 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 @@ -969,7 +899,7 @@ public final class Utils extends com.android.settingslib.Utils { List filters = pm.getAllIntentFilters(packageName); ArraySet 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 53cad4aff4f..93416ad862d 100644 --- a/src/com/android/settings/applications/ManageDomainUrls.java +++ b/src/com/android/settings/applications/ManageDomainUrls.java @@ -23,6 +23,7 @@ import android.os.Bundle; 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.internal.logging.nano.MetricsProto.MetricsEvent; 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 @@ public class ManageDomainUrls extends SettingsPreferenceFragment 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 @@ public class ManageDomainUrls extends SettingsPreferenceFragment 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 d5320894f0c..80fa10aea9d 100644 --- a/src/com/android/settings/users/ProfileUpdateReceiver.java +++ b/src/com/android/settings/users/ProfileUpdateReceiver.java @@ -38,13 +38,13 @@ public class ProfileUpdateReceiver extends BroadcastReceiver { // 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 @@ public class ProfileUpdateReceiver extends BroadcastReceiver { 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 642f289f9cd..906c9d49491 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -30,7 +30,9 @@ import android.content.SharedPreferences; 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.Message; 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.view.View.OnClickListener; 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.RestrictedLockUtils.EnforcedAdmin; 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 @@ public class UserSettings extends SettingsPreferenceFragment private boolean mShouldUpdateUserList = true; private final Object mUserLock = new Object(); private UserManager mUserManager; - private SparseArray mUserIcons = new SparseArray(); + private SparseArray mUserIcons = new SparseArray<>(); + private static SparseArray sDarkDefaultUserBitmapCache = new SparseArray<>(); private EditUserInfoController mEditUserInfoController = new EditUserInfoController(); @@ -324,7 +333,7 @@ public class UserSettings extends SettingsPreferenceFragment 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 @@ public class UserSettings extends SettingsPreferenceFragment 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 @@ public class UserSettings extends SettingsPreferenceFragment 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 @@ public class UserSettings extends SettingsPreferenceFragment 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 @@ public class UserSettings extends SettingsPreferenceFragment 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 @@ public class UserSettings extends SettingsPreferenceFragment 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 ca02c1d3866..f8134578117 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -47,9 +47,8 @@ import java.util.List; @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 @@ public class UtilsTest { private DevicePolicyManagerWrapper mDevicePolicyManager; @Mock private UserManager mUserManager; + private Context mContext; @Before public void setUp() { @@ -99,12 +99,6 @@ public class UtilsTest { assertThat(Utils.getWifiIpAddresses(mContext)).isNull(); } - @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; diff --git a/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java b/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java index 583a0042aa8..35d1194576d 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.content.Context; 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.RuntimeEnvironment; 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 00000000000..3e896470d32 --- /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 a5783647ca6..56f3949b2bb 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 @@ public class UserSettingsTest { mActivity.getString(R.string.users_summary, name)); } + @Test + public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() { + // Should not crash here + assertThat(UserSettings.assignDefaultPhoto(null, 0)).isFalse(); + } }