From b279b1c0251870a1bb385c8c93ed9ba1792a2790 Mon Sep 17 00:00:00 2001 From: Emily Chuang Date: Thu, 12 Apr 2018 10:12:28 +0800 Subject: [PATCH] Migrate ChooseAccountActivity to DashboardFragment - Build a controller to generate/manage a list of preferences. - Move some logics to the controller and add tests. - Rename to ChooseAccountFragment. Bug: 73899467 Test: make RunSettingsRoboTests -j atest UniquePreferenceTest SettingsGatewayTest Change-Id: Id2906c4b922ef159d08c803b976671264c54665f --- AndroidManifest.xml | 2 +- res/xml/add_account_settings.xml | 9 +- .../settings/accounts/ChooseAccountFragment.java | 107 ++++++++++ ...java => ChooseAccountPreferenceController.java} | 215 +++++++++------------ .../EnterpriseDisclosurePreferenceController.java | 79 ++++++++ .../android/settings/accounts/ProviderEntry.java | 47 +++++ .../settings/core/gateway/SettingsGateway.java | 4 +- .../assets/grandfather_not_implementing_indexable | 1 - .../ChooseAccountPreferenceControllerTest.java | 205 ++++++++++++++++++++ ...terpriseDisclosurePreferenceControllerTest.java | 99 ++++++++++ .../testutils/shadow/ShadowAccountManager.java | 18 +- .../shadow/ShadowRestrictedLockUtils.java | 75 +++++-- 12 files changed, 710 insertions(+), 151 deletions(-) create mode 100644 src/com/android/settings/accounts/ChooseAccountFragment.java rename src/com/android/settings/accounts/{ChooseAccountActivity.java => ChooseAccountPreferenceController.java} (54%) create mode 100644 src/com/android/settings/accounts/EnterpriseDisclosurePreferenceController.java create mode 100644 src/com/android/settings/accounts/ProviderEntry.java create mode 100644 tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceControllerTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 69416a7223..5a0f18192f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2341,7 +2341,7 @@ android:label="@string/header_add_an_account" android:configChanges="orientation|keyboardHidden|screenSize"> + android:value="com.android.settings.accounts.ChooseAccountFragment" /> - - - + diff --git a/src/com/android/settings/accounts/ChooseAccountFragment.java b/src/com/android/settings/accounts/ChooseAccountFragment.java new file mode 100644 index 0000000000..98b9ee6c5e --- /dev/null +++ b/src/com/android/settings/accounts/ChooseAccountFragment.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 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.accounts; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.SearchIndexableResource; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Activity asking a user to select an account to be set up. + */ +@SearchIndexable +public class ChooseAccountFragment extends DashboardFragment { + + private static final String TAG = "ChooseAccountFragment"; + + @Override + public int getMetricsCategory() { + return MetricsEvent.ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + final String[] authorities = getIntent().getStringArrayExtra( + AccountPreferenceBase.AUTHORITIES_FILTER_KEY); + final String[] accountTypesFilter = getIntent().getStringArrayExtra( + AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); + final UserManager userManager = UserManager.get(getContext()); + final UserHandle userHandle = Utils.getSecureTargetUser(getActivity().getActivityToken(), + userManager, null /* arguments */, getIntent().getExtras()); + + use(ChooseAccountPreferenceController.class).initialize(authorities, accountTypesFilter, + userHandle, getActivity()); + use(EnterpriseDisclosurePreferenceController.class).setFooterPreferenceMixin( + mFooterPreferenceMixin); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.add_account_settings; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected List createPreferenceControllers(Context context) { + return buildControllers(context); + } + + private static List buildControllers(Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new EnterpriseDisclosurePreferenceController(context)); + return controllers; + } + + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex(Context context, + boolean enabled) { + final ArrayList result = new ArrayList<>(); + + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.add_account_settings; + result.add(sir); + return result; + } + + @Override + public List createPreferenceControllers( + Context context) { + return buildControllers(context); + } + }; +} diff --git a/src/com/android/settings/accounts/ChooseAccountActivity.java b/src/com/android/settings/accounts/ChooseAccountPreferenceController.java similarity index 54% rename from src/com/android/settings/accounts/ChooseAccountActivity.java rename to src/com/android/settings/accounts/ChooseAccountPreferenceController.java index 35f51afa80..ded204bde1 100644 --- a/src/com/android/settings/accounts/ChooseAccountActivity.java +++ b/src/com/android/settings/accounts/ChooseAccountPreferenceController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2018 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. @@ -30,24 +30,11 @@ import android.content.SyncAdapterType; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.os.UserHandle; -import android.os.UserManager; -import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; import android.util.Log; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.CharSequences; -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.Utils; -import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; -import com.android.settings.overlay.FeatureFactory; +import com.android.settings.core.BasePreferenceController; import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; -import com.android.settingslib.widget.FooterPreference; -import com.android.settingslib.widget.FooterPreferenceMixin; import com.google.android.collect.Maps; @@ -55,81 +42,79 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; /** - * Activity asking a user to select an account to be set up. - * * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for * which the action needs to be performed is different to the one the Settings App will run in. */ -public class ChooseAccountActivity extends SettingsPreferenceFragment { +public class ChooseAccountPreferenceController extends BasePreferenceController { - private static final String TAG = "ChooseAccountActivity"; + private static final String TAG = "ChooseAccountPrefCtrler"; - private EnterprisePrivacyFeatureProvider mFeatureProvider; - private FooterPreference mEnterpriseDisclosurePreference = null; + private final List mProviderList; + private final Map mTypeToAuthDescription; private String[] mAuthorities; - private PreferenceGroup mAddAccountGroup; - private final ArrayList mProviderList = new ArrayList(); - public HashSet mAccountTypesFilter; + private Set mAccountTypesFilter; private AuthenticatorDescription[] mAuthDescs; - private HashMap> mAccountTypeToAuthorities = null; - private Map mTypeToAuthDescription - = new HashMap(); + private Map> mAccountTypeToAuthorities; // The UserHandle of the user we are choosing an account for private UserHandle mUserHandle; - private UserManager mUm; + private Activity mActivity; + private PreferenceScreen mScreen; - private static class ProviderEntry implements Comparable { - private final CharSequence name; - private final String type; - ProviderEntry(CharSequence providerName, String accountType) { - name = providerName; - type = accountType; - } + public ChooseAccountPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); - public int compareTo(ProviderEntry another) { - if (name == null) { - return -1; - } - if (another.name == null) { - return +1; + mProviderList = new ArrayList<>(); + mTypeToAuthDescription = new HashMap<>(); + } + + public void initialize(String[] authorities, String[] accountTypesFilter, UserHandle userHandle, + Activity activity) { + mActivity = activity; + mAuthorities = authorities; + mUserHandle = userHandle; + + if (accountTypesFilter != null) { + mAccountTypesFilter = new HashSet<>(); + for (String accountType : accountTypesFilter) { + mAccountTypesFilter.add(accountType); } - return CharSequences.compareToIgnoreCase(name, another.name); } } @Override - public int getMetricsCategory() { - return MetricsEvent.ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY; + public int getAvailabilityStatus() { + return AVAILABLE; } @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mScreen = screen; + updateAuthDescriptions(); + } - final Activity activity = getActivity(); - mFeatureProvider = FeatureFactory.getFactory(activity) - .getEnterprisePrivacyFeatureProvider(activity); + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!(preference instanceof ProviderPreference)) { + return false; + } - addPreferencesFromResource(R.xml.add_account_settings); - mAuthorities = getIntent().getStringArrayExtra( - AccountPreferenceBase.AUTHORITIES_FILTER_KEY); - String[] accountTypesFilter = getIntent().getStringArrayExtra( - AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); - if (accountTypesFilter != null) { - mAccountTypesFilter = new HashSet(); - for (String accountType : accountTypesFilter) { - mAccountTypesFilter.add(accountType); - } + ProviderPreference pref = (ProviderPreference) preference; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Attempting to add account of type " + pref.getAccountType()); } - mAddAccountGroup = getPreferenceScreen(); - mUm = UserManager.get(getContext()); - mUserHandle = Utils.getSecureTargetUser(getActivity().getActivityToken(), mUm, - null /* arguments */, getIntent().getExtras()); - updateAuthDescriptions(); + finishWithAccountType(pref.getAccountType()); + return true; } /** @@ -137,7 +122,7 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated(). */ private void updateAuthDescriptions() { - mAuthDescs = AccountManager.get(getContext()).getAuthenticatorTypesAsUser( + mAuthDescs = AccountManager.get(mContext).getAuthenticatorTypesAsUser( mUserHandle.getIdentifier()); for (int i = 0; i < mAuthDescs.length; i++) { mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); @@ -148,12 +133,12 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { private void onAuthDescriptionsUpdated() { // Create list of providers to show on preference screen for (int i = 0; i < mAuthDescs.length; i++) { - String accountType = mAuthDescs[i].type; - CharSequence providerName = getLabelForType(accountType); + final String accountType = mAuthDescs[i].type; + final CharSequence providerName = getLabelForType(accountType); // Skip preferences for authorities not specified. If no authorities specified, // then include them all. - ArrayList accountAuths = getAuthoritiesForAccountType(accountType); + final List accountAuths = getAuthoritiesForAccountType(accountType); boolean addAccountPref = true; if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) { addAccountPref = false; @@ -169,38 +154,39 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { addAccountPref = false; } if (addAccountPref) { - mProviderList.add(new ProviderEntry(providerName, accountType)); + mProviderList.add( + new ProviderEntry(providerName, accountType)); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Skipped pref " + providerName + ": has no authority we need"); } } } - - final Context context = getPreferenceScreen().getContext(); + final Context context = mScreen.getContext(); if (mProviderList.size() == 1) { // There's only one provider that matches. If it is disabled by admin show the // support dialog otherwise run it. - EnforcedAdmin admin = RestrictedLockUtils.checkIfAccountManagementDisabled( - context, mProviderList.get(0).type, mUserHandle.getIdentifier()); + final RestrictedLockUtils.EnforcedAdmin admin = + RestrictedLockUtils.checkIfAccountManagementDisabled( + context, mProviderList.get(0).getType(), mUserHandle.getIdentifier()); if (admin != null) { - setResult(RESULT_CANCELED, RestrictedLockUtils.getShowAdminSupportDetailsIntent( - context, admin)); - finish(); + mActivity.setResult(RESULT_CANCELED, + RestrictedLockUtils.getShowAdminSupportDetailsIntent( + context, admin)); + mActivity.finish(); } else { - finishWithAccountType(mProviderList.get(0).type); + finishWithAccountType(mProviderList.get(0).getType()); } } else if (mProviderList.size() > 0) { Collections.sort(mProviderList); - mAddAccountGroup.removeAll(); for (ProviderEntry pref : mProviderList) { - Drawable drawable = getDrawableForType(pref.type); - ProviderPreference p = new ProviderPreference(getPreferenceScreen().getContext(), - pref.type, drawable, pref.name); + final Drawable drawable = getDrawableForType(pref.getType()); + final ProviderPreference p = new ProviderPreference(context, + pref.getType(), drawable, pref.getName()); + p.setKey(pref.getType().toString()); p.checkAccountManagementAndSetDisabled(mUserHandle.getIdentifier()); - mAddAccountGroup.addPreference(p); + mScreen.addPreference(p); } - addEnterpriseDisclosure(); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { final StringBuilder auths = new StringBuilder(); @@ -210,38 +196,25 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { } Log.v(TAG, "No providers found for authorities: " + auths); } - setResult(RESULT_CANCELED); - finish(); + mActivity.setResult(RESULT_CANCELED); + mActivity.finish(); } } - private void addEnterpriseDisclosure() { - final CharSequence disclosure = mFeatureProvider.getDeviceOwnerDisclosure(); - if (disclosure == null) { - return; - } - if (mEnterpriseDisclosurePreference == null) { - mEnterpriseDisclosurePreference = mFooterPreferenceMixin.createFooterPreference(); - mEnterpriseDisclosurePreference.setSelectable(false); - } - mEnterpriseDisclosurePreference.setTitle(disclosure); - mAddAccountGroup.addPreference(mEnterpriseDisclosurePreference); - } - - public ArrayList getAuthoritiesForAccountType(String type) { + private List getAuthoritiesForAccountType(String type) { if (mAccountTypeToAuthorities == null) { mAccountTypeToAuthorities = Maps.newHashMap(); - SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( + final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( mUserHandle.getIdentifier()); for (int i = 0, n = syncAdapters.length; i < n; i++) { final SyncAdapterType sa = syncAdapters[i]; - ArrayList authorities = mAccountTypeToAuthorities.get(sa.accountType); + List authorities = mAccountTypeToAuthorities.get(sa.accountType); if (authorities == null) { - authorities = new ArrayList(); + authorities = new ArrayList<>(); mAccountTypeToAuthorities.put(sa.accountType, authorities); } if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "added authority " + sa.authority + " to accountType " + Log.v(TAG, "added authority " + sa.authority + " to accountType " + sa.accountType); } authorities.add(sa.authority); @@ -252,18 +225,20 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { /** * Gets an icon associated with a particular account type. If none found, return null. + * * @param accountType the type of account * @return a drawable for the icon or a default icon returned by * {@link PackageManager#getDefaultActivityIcon} if one cannot be found. */ - protected Drawable getDrawableForType(final String accountType) { + @VisibleForTesting + Drawable getDrawableForType(final String accountType) { Drawable icon = null; if (mTypeToAuthDescription.containsKey(accountType)) { try { - AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); - Context authContext = getActivity() + final AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); + final Context authContext = mActivity .createPackageContextAsUser(desc.packageName, 0, mUserHandle); - icon = getPackageManager().getUserBadgedIcon( + icon = mContext.getPackageManager().getUserBadgedIcon( authContext.getDrawable(desc.iconId), mUserHandle); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "No icon name for account type " + accountType); @@ -274,21 +249,23 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { if (icon != null) { return icon; } else { - return getPackageManager().getDefaultActivityIcon(); + return mContext.getPackageManager().getDefaultActivityIcon(); } } /** * Gets the label associated with a particular account type. If none found, return null. + * * @param accountType the type of account * @return a CharSequence for the label or null if one cannot be found. */ - protected CharSequence getLabelForType(final String accountType) { + @VisibleForTesting + CharSequence getLabelForType(final String accountType) { CharSequence label = null; if (mTypeToAuthDescription.containsKey(accountType)) { try { - AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); - Context authContext = getActivity() + final AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); + final Context authContext = mActivity .createPackageContextAsUser(desc.packageName, 0, mUserHandle); label = authContext.getResources().getText(desc.labelId); } catch (PackageManager.NameNotFoundException e) { @@ -300,23 +277,11 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment { return label; } - @Override - public boolean onPreferenceTreeClick(Preference preference) { - if (preference instanceof ProviderPreference) { - ProviderPreference pref = (ProviderPreference) preference; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Attempting to add account of type " + pref.getAccountType()); - } - finishWithAccountType(pref.getAccountType()); - } - return true; - } - private void finishWithAccountType(String accountType) { Intent intent = new Intent(); intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType); intent.putExtra(EXTRA_USER, mUserHandle); - setResult(RESULT_OK, intent); - finish(); + mActivity.setResult(RESULT_OK, intent); + mActivity.finish(); } } diff --git a/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceController.java b/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceController.java new file mode 100644 index 0000000000..e8a444fdfb --- /dev/null +++ b/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceController.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 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.accounts; + +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.widget.FooterPreference; +import com.android.settingslib.widget.FooterPreferenceMixin; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +public class EnterpriseDisclosurePreferenceController extends BasePreferenceController { + + private final EnterprisePrivacyFeatureProvider mFeatureProvider; + private FooterPreferenceMixin mFooterPreferenceMixin; + private PreferenceScreen mScreen; + + public EnterpriseDisclosurePreferenceController(Context context) { + // Preference key doesn't matter as we are creating the preference in code. + super(context, "add_account_enterprise_disclosure_footer"); + + mFeatureProvider = FeatureFactory.getFactory(mContext) + .getEnterprisePrivacyFeatureProvider(mContext); + } + + public void setFooterPreferenceMixin(FooterPreferenceMixin footerPreferenceMixin) { + mFooterPreferenceMixin = footerPreferenceMixin; + } + + @Override + public int getAvailabilityStatus() { + if (getDisclosure() == null) { + return DISABLED_UNSUPPORTED; + } + return AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mScreen = screen; + addEnterpriseDisclosure(); + } + + @VisibleForTesting + CharSequence getDisclosure() { + return mFeatureProvider.getDeviceOwnerDisclosure(); + } + + private void addEnterpriseDisclosure() { + final CharSequence disclosure = getDisclosure(); + if (disclosure == null) { + return; + } + final FooterPreference enterpriseDisclosurePreference = + mFooterPreferenceMixin.createFooterPreference(); + enterpriseDisclosurePreference.setSelectable(false); + enterpriseDisclosurePreference.setTitle(disclosure); + mScreen.addPreference(enterpriseDisclosurePreference); + } +} diff --git a/src/com/android/settings/accounts/ProviderEntry.java b/src/com/android/settings/accounts/ProviderEntry.java new file mode 100644 index 0000000000..cf55e1423f --- /dev/null +++ b/src/com/android/settings/accounts/ProviderEntry.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 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.accounts; + +import com.android.internal.util.CharSequences; + +public class ProviderEntry implements Comparable { + private final CharSequence name; + private final String type; + + ProviderEntry(CharSequence providerName, String accountType) { + name = providerName; + type = accountType; + } + + public int compareTo(ProviderEntry another) { + if (name == null) { + return -1; + } + if (another.name == null) { + return +1; + } + return CharSequences.compareToIgnoreCase(name, another.name); + } + + public CharSequence getName() { + return name; + } + + public String getType() { + return type; + } +} \ No newline at end of file diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index be9b722a5a..ced158eb3e 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -31,7 +31,7 @@ import com.android.settings.accessibility.CaptionPropertiesFragment; import com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment; import com.android.settings.accounts.AccountDashboardFragment; import com.android.settings.accounts.AccountSyncSettings; -import com.android.settings.accounts.ChooseAccountActivity; +import com.android.settings.accounts.ChooseAccountFragment; import com.android.settings.accounts.ManagedProfileSettings; import com.android.settings.applications.AppAndNotificationDashboardFragment; import com.android.settings.applications.DefaultAppSettings; @@ -236,7 +236,7 @@ public class SettingsGateway { PictureInPictureSettings.class.getName(), PictureInPictureDetails.class.getName(), ManagedProfileSettings.class.getName(), - ChooseAccountActivity.class.getName(), + ChooseAccountFragment.class.getName(), IccLockSettings.class.getName(), TestingSettings.class.getName(), WifiAPITest.class.getName(), diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable index 8523b2f1ab..57b75e1eb8 100644 --- a/tests/robotests/assets/grandfather_not_implementing_indexable +++ b/tests/robotests/assets/grandfather_not_implementing_indexable @@ -17,7 +17,6 @@ com.android.settings.applications.ManageDomainUrls com.android.settings.applications.appinfo.WriteSettingsDetails com.android.settings.applications.ProcessStatsSummary com.android.settings.users.RestrictedProfileSettings -com.android.settings.accounts.ChooseAccountActivity com.android.settings.accessibility.ToggleAutoclickPreferenceFragment com.android.settings.applications.AppLaunchSettings com.android.settings.applications.ProcessStatsUi diff --git a/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java new file mode 100644 index 0000000000..67e01f8817 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 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.accounts; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.accounts.AuthenticatorDescription; +import android.app.Activity; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.SyncAdapterType; +import android.graphics.drawable.ColorDrawable; +import android.os.UserHandle; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowAccountManager; +import com.android.settings.testutils.shadow.ShadowContentResolver; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, + ShadowRestrictedLockUtils.class}) +public class ChooseAccountPreferenceControllerTest { + + private Context mContext; + private ChooseAccountPreferenceController mController; + private Activity mActivity; + private PreferenceManager mPreferenceManager; + private PreferenceScreen mPreferenceScreen; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = spy(new ChooseAccountPreferenceController(mContext, "controller_key")); + mActivity = Robolectric.setupActivity(Activity.class); + mPreferenceManager = new PreferenceManager(mContext); + mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); + } + + @After + public void tearDown() { + ShadowContentResolver.reset(); + ShadowAccountManager.resetAuthenticator(); + ShadowRestrictedLockUtils.clearDisabledTypes(); + } + + @Test + public void getAvailabilityStatus_byDefault_shouldBeShown() { + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void handlePreferenceTreeClick_notProviderPreference_shouldReturnFalse() { + final Preference preference = new Preference(mContext); + assertThat(mController.handlePreferenceTreeClick(preference)).isFalse(); + } + + @Test + public void handlePreferenceTreeClick_isProviderPreference_shouldFinishFragment() { + final ProviderPreference providerPreference = new ProviderPreference(mContext, + "account_type", null /* icon */, "provider_name"); + + mController.initialize(null, null, null, mActivity); + mController.handlePreferenceTreeClick(providerPreference); + + assertThat(mActivity.isFinishing()).isTrue(); + } + + @Test + public void updateAuthDescriptions_oneProvider_shouldNotAddPreference() { + final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", + "com.android.settings", + R.string.header_add_an_account, 0, 0, 0, false); + ShadowAccountManager.addAuthenticator(authDesc); + + final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, + "com.acct1" /* accountType */, false /* userVisible */, + true /* supportsUploading */)}; + ShadowContentResolver.setSyncAdapterTypes(syncAdapters); + + ShadowRestrictedLockUtils.setHasSystemFeature(true); + ShadowRestrictedLockUtils.setDevicePolicyManager(mock(DevicePolicyManager.class)); + ShadowRestrictedLockUtils.setDisabledTypes(new String[] {"test_type"}); + + doReturn("label").when(mController).getLabelForType(anyString()); + + mController.initialize(null, null, new UserHandle(3), mActivity); + mController.displayPreference(mPreferenceScreen); + + assertThat(mActivity.isFinishing()).isTrue(); + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void updateAuthDescriptions_oneAdminDisabledProvider_shouldNotAddPreference() { + final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", + "com.android.settings", + R.string.header_add_an_account, 0, 0, 0, false); + ShadowAccountManager.addAuthenticator(authDesc); + + final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, + "com.acct1" /* accountType */, false /* userVisible */, + true /* supportsUploading */)}; + ShadowContentResolver.setSyncAdapterTypes(syncAdapters); + + ShadowRestrictedLockUtils.setHasSystemFeature(true); + ShadowRestrictedLockUtils.setDevicePolicyManager(mock(DevicePolicyManager.class)); + ShadowRestrictedLockUtils.setDisabledTypes(new String[] {"com.acct1"}); + + doReturn("label").when(mController).getLabelForType(anyString()); + + mController.initialize(null, null, new UserHandle(3), mActivity); + mController.displayPreference(mPreferenceScreen); + + assertThat(mActivity.isFinishing()).isTrue(); + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void updateAuthDescriptions_noProvider_shouldNotAddPreference() { + final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", + "com.android.settings", + R.string.header_add_an_account, 0, 0, 0, false); + ShadowAccountManager.addAuthenticator(authDesc); + + final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, + "com.acct1" /* accountType */, false /* userVisible */, + true /* supportsUploading */)}; + ShadowContentResolver.setSyncAdapterTypes(syncAdapters); + + doReturn("label").when(mController).getLabelForType(anyString()); + + mController.initialize(new String[] {"test_authoritiy1"}, null, new UserHandle(3), + mActivity); + mController.displayPreference(mPreferenceScreen); + + assertThat(mActivity.isFinishing()).isTrue(); + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void + updateAuthDescriptions_twoProvider_shouldAddTwoPreference() { + final AuthenticatorDescription authDesc = new AuthenticatorDescription("com.acct1", + "com.android.settings", + R.string.header_add_an_account, 0, 0, 0, false); + final AuthenticatorDescription authDesc2 = new AuthenticatorDescription("com.acct2", + "com.android.settings", + R.string.header_add_an_account, 0, 0, 0, false); + ShadowAccountManager.addAuthenticator(authDesc); + ShadowAccountManager.addAuthenticator(authDesc2); + + final SyncAdapterType[] syncAdapters = {new SyncAdapterType("authority" /* authority */, + "com.acct1" /* accountType */, false /* userVisible */, + true /* supportsUploading */), new SyncAdapterType("authority2" /* authority */, + "com.acct2" /* accountType */, false /* userVisible */, + true /* supportsUploading */)}; + ShadowContentResolver.setSyncAdapterTypes(syncAdapters); + + doReturn("label").when(mController).getLabelForType(anyString()); + doReturn("label2").when(mController).getLabelForType(anyString()); + doReturn(new ColorDrawable()).when(mController).getDrawableForType(anyString()); + doReturn(new ColorDrawable()).when(mController).getDrawableForType(anyString()); + + mController.initialize(null, null, new UserHandle(3), mActivity); + mController.displayPreference(mPreferenceScreen); + + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(2); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceControllerTest.java new file mode 100644 index 0000000000..4ad0982d6a --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceControllerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 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.accounts; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.widget.FooterPreferenceMixin; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; + +@RunWith(SettingsRobolectricTestRunner.class) +public class EnterpriseDisclosurePreferenceControllerTest { + + private static final String TEST_DISCLOSURE = "This is a test disclosure."; + + private ChooseAccountFragment mFragment; + private Context mContext; + private EnterpriseDisclosurePreferenceController mController; + private FooterPreferenceMixin mFooterPreferenceMixin; + private PreferenceManager mPreferenceManager; + private PreferenceScreen mPreferenceScreen; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = spy(new EnterpriseDisclosurePreferenceController(mContext)); + mFragment = spy(new ChooseAccountFragment()); + mFooterPreferenceMixin = new FooterPreferenceMixin(mFragment, mFragment.getLifecycle()); + mPreferenceManager = new PreferenceManager(mContext); + mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); + } + + @Test + public void getAvailabilityStatus_hasDisclosure_shouldBeAvailable() { + doReturn(TEST_DISCLOSURE).when(mController).getDisclosure(); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_noDisclosure_shouldBeDisabled() { + doReturn(null).when(mController).getDisclosure(); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_UNSUPPORTED); + } + + @Test + public void displayPreference_hasDisclosure_shouldSetTitle() { + doReturn(TEST_DISCLOSURE).when(mController).getDisclosure(); + doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); + doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); + + mController.setFooterPreferenceMixin(mFooterPreferenceMixin); + mController.displayPreference(mPreferenceScreen); + + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(1); + assertThat(mPreferenceScreen.getPreference(0).getTitle()).isEqualTo(TEST_DISCLOSURE); + } + + @Test + public void displayPreference_noDisclosure_shouldBeInvisible() { + doReturn(null).when(mController).getDisclosure(); + + mController.displayPreference(mPreferenceScreen); + + assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(0); + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java index 2bccccc74d..03aabb5dbb 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java @@ -22,10 +22,24 @@ import android.accounts.AuthenticatorDescription; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.HashMap; +import java.util.Map; + @Implements(AccountManager.class) -public class ShadowAccountManager { +public class ShadowAccountManager{ + + private static final Map sAuthenticators = new HashMap<>(); + @Implementation public AuthenticatorDescription[] getAuthenticatorTypesAsUser(int userId) { - return null; + return sAuthenticators.values().toArray(new AuthenticatorDescription[sAuthenticators.size()]); + } + + public static void addAuthenticator(AuthenticatorDescription authenticator) { + sAuthenticators.put(authenticator.type, authenticator); + } + + public static void resetAuthenticator() { + sAuthenticators.clear(); } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtils.java index c53f7712bf..30e6401f53 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtils.java @@ -16,6 +16,7 @@ package com.android.settings.testutils.shadow; import android.annotation.UserIdInt; +import android.app.admin.DevicePolicyManager; import android.content.Context; import com.android.internal.util.ArrayUtils; @@ -28,26 +29,31 @@ import org.robolectric.annotation.Resetter; @Implements(RestrictedLockUtils.class) public class ShadowRestrictedLockUtils { - private static boolean isRestricted; - private static String[] restrictedPkgs; - private static boolean adminSupportDetailsIntentLaunched; - private static int keyguardDisabledFeatures; + + private static boolean sIsRestricted; + private static boolean sAdminSupportDetailsIntentLaunched; + private static boolean sHasSystemFeature; + private static String[] sRestrictedPkgs; + private static DevicePolicyManager sDevicePolicyManager; + private static String[] sDisabledTypes; + private static int sKeyguardDisabledFeatures; @Resetter public static void reset() { - isRestricted = false; - restrictedPkgs = null; - adminSupportDetailsIntentLaunched = false; - keyguardDisabledFeatures = 0; + sIsRestricted = false; + sRestrictedPkgs = null; + sAdminSupportDetailsIntentLaunched = false; + sKeyguardDisabledFeatures = 0; + sDisabledTypes = new String[0]; } @Implementation public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, String packageName, int userId) { - if (isRestricted) { + if (sIsRestricted) { return new EnforcedAdmin(); } - if (ArrayUtils.contains(restrictedPkgs, packageName)) { + if (ArrayUtils.contains(sRestrictedPkgs, packageName)) { return new EnforcedAdmin(); } return null; @@ -55,32 +61,67 @@ public class ShadowRestrictedLockUtils { @Implementation public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { - adminSupportDetailsIntentLaunched = true; + sAdminSupportDetailsIntentLaunched = true; + } + + @Implementation + public static EnforcedAdmin checkIfAccountManagementDisabled(Context context, + String accountType, int userId) { + if (accountType == null) { + return null; + } + if (!sHasSystemFeature || sDevicePolicyManager == null) { + return null; + } + boolean isAccountTypeDisabled = false; + if (ArrayUtils.contains(sDisabledTypes, accountType)) { + isAccountTypeDisabled = true; + } + if (!isAccountTypeDisabled) { + return null; + } + return new EnforcedAdmin(); } @Implementation public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context, int features, final @UserIdInt int userId) { - return (keyguardDisabledFeatures & features) == 0 ? null : new EnforcedAdmin(); + return (sKeyguardDisabledFeatures & features) == 0 ? null : new EnforcedAdmin(); } public static boolean hasAdminSupportDetailsIntentLaunched() { - return adminSupportDetailsIntentLaunched; + return sAdminSupportDetailsIntentLaunched; } public static void clearAdminSupportDetailsIntentLaunch() { - adminSupportDetailsIntentLaunched = false; + sAdminSupportDetailsIntentLaunched = false; } public static void setRestricted(boolean restricted) { - isRestricted = restricted; + sIsRestricted = restricted; } public static void setRestrictedPkgs(String... pkgs) { - restrictedPkgs = pkgs; + sRestrictedPkgs = pkgs; + } + + public static void setHasSystemFeature(boolean hasSystemFeature) { + sHasSystemFeature = hasSystemFeature; + } + + public static void setDevicePolicyManager(DevicePolicyManager dpm) { + sDevicePolicyManager = dpm; + } + + public static void setDisabledTypes(String[] disabledTypes) { + sDisabledTypes = disabledTypes; + } + + public static void clearDisabledTypes() { + sDisabledTypes = new String[0]; } public static void setKeyguardDisabledFeatures(int features) { - keyguardDisabledFeatures = features; + sKeyguardDisabledFeatures = features; } } -- 2.11.0