OSDN Git Service

Migrate ChooseAccountActivity to DashboardFragment
authorEmily Chuang <emilychuang@android.com>
Thu, 12 Apr 2018 02:12:28 +0000 (10:12 +0800)
committerFan Zhang <zhfan@google.com>
Sat, 28 Apr 2018 05:17:23 +0000 (22:17 -0700)
- 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

12 files changed:
AndroidManifest.xml
res/xml/add_account_settings.xml
src/com/android/settings/accounts/ChooseAccountFragment.java [new file with mode: 0644]
src/com/android/settings/accounts/ChooseAccountPreferenceController.java [moved from src/com/android/settings/accounts/ChooseAccountActivity.java with 54% similarity]
src/com/android/settings/accounts/EnterpriseDisclosurePreferenceController.java [new file with mode: 0644]
src/com/android/settings/accounts/ProviderEntry.java [new file with mode: 0644]
src/com/android/settings/core/gateway/SettingsGateway.java
tests/robotests/assets/grandfather_not_implementing_indexable
tests/robotests/src/com/android/settings/accounts/ChooseAccountPreferenceControllerTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/accounts/EnterpriseDisclosurePreferenceControllerTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccountManager.java
tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtils.java

index 69416a7..5a0f181 100644 (file)
             android:label="@string/header_add_an_account"
             android:configChanges="orientation|keyboardHidden|screenSize">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.accounts.ChooseAccountActivity" />
+                       android:value="com.android.settings.accounts.ChooseAccountFragment" />
         </activity>
 
         <activity android:name=".CryptKeeper"
index 0ec46b7..6b36f8d 100644 (file)
@@ -14,6 +14,9 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-</PreferenceScreen>
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:key="add_account_screen"
+    android:title="@string/header_add_an_account"
+    settings:controller="com.android.settings.accounts.ChooseAccountPreferenceController" />
diff --git a/src/com/android/settings/accounts/ChooseAccountFragment.java b/src/com/android/settings/accounts/ChooseAccountFragment.java
new file mode 100644 (file)
index 0000000..98b9ee6
--- /dev/null
@@ -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<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        return buildControllers(context);
+    }
+
+    private static List<AbstractPreferenceController> buildControllers(Context context) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new EnterpriseDisclosurePreferenceController(context));
+        return controllers;
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    final ArrayList<SearchIndexableResource> result = new ArrayList<>();
+
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.add_account_settings;
+                    result.add(sir);
+                    return result;
+                }
+
+                @Override
+                public List<AbstractPreferenceController> createPreferenceControllers(
+                        Context context) {
+                    return buildControllers(context);
+                }
+            };
+}
@@ -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<ProviderEntry> mProviderList;
+    private final Map<String, AuthenticatorDescription> mTypeToAuthDescription;
 
     private String[] mAuthorities;
-    private PreferenceGroup mAddAccountGroup;
-    private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>();
-    public HashSet<String> mAccountTypesFilter;
+    private Set<String> mAccountTypesFilter;
     private AuthenticatorDescription[] mAuthDescs;
-    private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
-    private Map<String, AuthenticatorDescription> mTypeToAuthDescription
-            = new HashMap<String, AuthenticatorDescription>();
+    private Map<String, List<String>> 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<ProviderEntry> {
-        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<String>();
-            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<String> accountAuths = getAuthoritiesForAccountType(accountType);
+            final List<String> 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<String> getAuthoritiesForAccountType(String type) {
+    private List<String> 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<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
+                List<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
                 if (authorities == null) {
-                    authorities = new ArrayList<String>();
+                    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 (file)
index 0000000..e8a444f
--- /dev/null
@@ -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 (file)
index 0000000..cf55e14
--- /dev/null
@@ -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<ProviderEntry> {
+    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
index be9b722..ced158e 100644 (file)
@@ -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(),
index 8523b2f..57b75e1 100644 (file)
@@ -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 (file)
index 0000000..67e01f8
--- /dev/null
@@ -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 (file)
index 0000000..4ad0982
--- /dev/null
@@ -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);
+    }
+}
index 2bccccc..03aabb5 100644 (file)
@@ -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<String, AuthenticatorDescription> 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();
     }
 }
index c53f771..30e6401 100644 (file)
@@ -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;
     }
 }