Fix account list for group editing (for master)

- Don't use contact writable account list to populate the
account picker when creating new groups, instead check for
isGroupMembershipEditable() on the account type

- Repeat the same for determining whether the "new group"
button should appear in the PeopleActivity or not

Bug: 5360120
Change-Id: Ifd2fd1cf4ea7bf02d4dcba3e9c023bd7041fab83
Original-Id: Ia1ec62eff3fe4fd5b495548218c58f238ff49788
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 4de62b6..9a3f2ef 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts;
 
+import com.android.contacts.model.AccountType;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.test.NeededForTesting;
@@ -172,11 +173,15 @@
         return detector.detectCountry().getCountryIso();
     }
 
-    public static boolean areAccountsAvailable(Context context) {
+    public static boolean areContactWritableAccountsAvailable(Context context) {
         final List<AccountWithDataSet> accounts =
                 AccountTypeManager.getInstance(context).getAccounts(true /* writeable */);
         return !accounts.isEmpty();
     }
 
-
+    public static boolean areGroupWritableAccountsAvailable(Context context) {
+        final List<AccountWithDataSet> accounts =
+                AccountTypeManager.getInstance(context).getGroupWritableAccounts();
+        return !accounts.isEmpty();
+    }
 }
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index d63ea6a..3e2a893 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.contacts.activities;
 
-import android.accounts.Account;
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
@@ -34,6 +33,7 @@
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 
 import java.util.List;
 
@@ -97,7 +97,8 @@
             button.setOnClickListener(mAddAccountClickListener);
 
             final ListView accountListView = (ListView) findViewById(R.id.account_list);
-            mAccountListAdapter = new AccountsListAdapter(this, true);
+            mAccountListAdapter = new AccountsListAdapter(this,
+                    AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
             accountListView.setAdapter(mAccountListAdapter);
             accountListView.setOnItemClickListener(mAccountListItemClickListener);
         } else if (numAccounts == 1) {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 3430109..b088b89 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -56,6 +56,7 @@
 import com.android.contacts.util.AccountPromptUtils;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.PhoneCapabilityTester;
@@ -200,10 +201,13 @@
         return mProviderStatus == ProviderStatus.STATUS_NORMAL;
     }
 
-    private boolean areAccountsAvailable() {
-        return ContactsUtils.areAccountsAvailable(this);
+    private boolean areContactWritableAccountsAvailable() {
+        return ContactsUtils.areContactWritableAccountsAvailable(this);
     }
 
+    private boolean areGroupWritableAccountsAvailable() {
+        return ContactsUtils.areGroupWritableAccountsAvailable(this);
+    }
 
     /**
      * Initialize fragments that are (or may not be) in the layout.
@@ -604,7 +608,7 @@
             invalidateOptionsMenu();
             showEmptyStateForTab(tab);
             if (tab == TabState.GROUPS) {
-                mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+                mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
             }
             return;
         }
@@ -625,7 +629,7 @@
                 mFavoritesView.setVisibility(View.GONE);
                 mBrowserView.setVisibility(View.VISIBLE);
                 mDetailsView.setVisibility(View.VISIBLE);
-                mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+                mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
                 break;
             case ALL:
                 mFavoritesView.setVisibility(View.GONE);
@@ -686,7 +690,7 @@
                     break;
                 case GROUPS:
                     mContactsUnavailableFragment.setMessageText(R.string.noGroups,
-                            areAccountsAvailable() ? -1 : R.string.noAccounts);
+                            areGroupWritableAccountsAvailable() ? -1 : R.string.noAccounts);
                     break;
                 case ALL:
                     mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1);
@@ -712,7 +716,7 @@
                 mActionBarAdapter.setCurrentTab(selectedTab, false);
                 showEmptyStateForTab(selectedTab);
                 if (selectedTab == TabState.GROUPS) {
-                    mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+                    mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
                 }
                 invalidateOptionsMenu();
             }
@@ -922,7 +926,8 @@
             // If there are no accounts on the device and we should show the "no account" prompt
             // (based on {@link SharedPreferences}), then launch the account setup activity so the
             // user can sign-in or create an account.
-            if (!areAccountsAvailable() && AccountPromptUtils.shouldShowAccountPrompt(this)) {
+            if (!areContactWritableAccountsAvailable() &&
+                    AccountPromptUtils.shouldShowAccountPrompt(this)) {
                 AccountPromptUtils.launchAccountPrompt(this);
                 return;
             }
@@ -1304,7 +1309,7 @@
                     break;
                 case GROUPS:
                     // Do not display the "new group" button if no accounts are available
-                    if (areAccountsAvailable()) {
+                    if (areGroupWritableAccountsAvailable()) {
                         addGroupMenu.setVisible(true);
                     } else {
                         addGroupMenu.setVisible(false);
@@ -1410,7 +1415,8 @@
         popup.setAnchorView(mAddGroupImageView);
         // Create a list adapter with all writeable accounts (assume that the writeable accounts all
         // allow group creation).
-        final AccountsListAdapter adapter = new AccountsListAdapter(this, true);
+        final AccountsListAdapter adapter = new AccountsListAdapter(this,
+                AccountListFilter.ACCOUNTS_GROUP_WRITABLE);
         popup.setAdapter(adapter);
         popup.setOnItemClickListener(new OnItemClickListener() {
             @Override
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 062c021..b341f83 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -34,6 +34,7 @@
 import com.android.contacts.model.EntityModifier;
 import com.android.contacts.model.GoogleAccountType;
 import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -826,7 +827,8 @@
             public void onClick(View v) {
                 final ListPopupWindow popup = new ListPopupWindow(mContext, null);
                 final AccountsListAdapter adapter =
-                        new AccountsListAdapter(mContext, true, currentAccount);
+                        new AccountsListAdapter(mContext,
+                        AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, currentAccount);
                 popup.setWidth(anchorView.getWidth());
                 popup.setAnchorView(anchorView);
                 popup.setAdapter(adapter);
diff --git a/src/com/android/contacts/editor/SelectAccountDialogFragment.java b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
index 9dbe20a..3a8681a 100644
--- a/src/com/android/contacts/editor/SelectAccountDialogFragment.java
+++ b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
@@ -19,6 +19,7 @@
 import com.android.contacts.R;
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -37,13 +38,18 @@
 public class SelectAccountDialogFragment extends DialogFragment {
     public static final String TAG = "SelectAccountDialogFragment";
 
-    private int mTitleResourceId = R.string.dialog_new_contact_account;
+    // TODO: This dialog is used in the context of group editing by default, but should be generic
+    // to work for contact editing as well. Save/restore the resource ID and account list filter
+    // that are passed in as parameters on device rotation. Bug: 5369853
+    private int mTitleResourceId = R.string.dialog_new_group_account;
+    private AccountListFilter mAccountListFilter = AccountListFilter.ACCOUNTS_GROUP_WRITABLE;
 
     public SelectAccountDialogFragment() {
     }
 
-    public SelectAccountDialogFragment(int titleResourceId) {
+    public SelectAccountDialogFragment(int titleResourceId, AccountListFilter accountListFilter) {
         mTitleResourceId = titleResourceId;
+        mAccountListFilter = accountListFilter;
     }
 
     @Override
@@ -51,7 +57,7 @@
         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
 
         final AccountsListAdapter accountAdapter = new AccountsListAdapter(builder.getContext(),
-                true);
+                mAccountListFilter);
 
         final DialogInterface.OnClickListener clickListener =
                 new DialogInterface.OnClickListener() {
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index 82539e8..79bdd09 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -144,7 +144,7 @@
                 startActivity(intent);
             }
         });
-        setAddAccountsVisibility(!ContactsUtils.areAccountsAvailable(mContext));
+        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
 
         return mRootView;
     }
@@ -214,7 +214,7 @@
 
     private void bindGroupList() {
         mEmptyView.setText(R.string.noGroups);
-        setAddAccountsVisibility(!ContactsUtils.areAccountsAvailable(mContext));
+        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
         if (mGroupListCursor == null) {
             return;
         }
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 99e6b48..1d1237e 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -28,6 +28,7 @@
 import com.android.contacts.model.AccountType;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.AccountWithDataSet;
+import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 import com.android.internal.util.Objects;
 
 import android.accounts.Account;
@@ -332,7 +333,7 @@
 
         mStatus = Status.SELECTING_ACCOUNT;
         final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment(
-                R.string.dialog_new_group_account);
+                R.string.dialog_new_group_account, AccountListFilter.ACCOUNTS_GROUP_WRITABLE);
         dialog.setTargetFragment(this, 0);
         dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
     }
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index dc2fb0d..5443196 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -85,7 +85,17 @@
         return new AccountTypeManagerImpl(context);
     }
 
-    public abstract List<AccountWithDataSet> getAccounts(boolean writableOnly);
+    /**
+     * Returns the list of all accounts (if contactWritableOnly is false) or just the list of
+     * contact writable accounts (if contactWritableOnly is true).
+     */
+    // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
+    public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly);
+
+    /**
+     * Returns the list of accounts that are group writable.
+     */
+    public abstract List<AccountWithDataSet> getGroupWritableAccounts();
 
     public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
 
@@ -130,7 +140,8 @@
     private AccountType mFallbackAccountType;
 
     private List<AccountWithDataSet> mAccounts = Lists.newArrayList();
-    private List<AccountWithDataSet> mWritableAccounts = Lists.newArrayList();
+    private List<AccountWithDataSet> mContactWritableAccounts = Lists.newArrayList();
+    private List<AccountWithDataSet> mGroupWritableAccounts = Lists.newArrayList();
     private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = Maps.newHashMap();
     private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes =
             Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>());
@@ -288,16 +299,18 @@
         final long startTimeWall = SystemClock.elapsedRealtime();
 
         // Account types, keyed off the account type and data set concatenation.
-        Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet = Maps.newHashMap();
+        final Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet =
+                Maps.newHashMap();
 
         // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}.  Since there can
         // be multiple account types (with different data sets) for the same type of account, each
         // type string may have multiple AccountType entries.
-        Map<String, List<AccountType>> accountTypesByType = Maps.newHashMap();
+        final Map<String, List<AccountType>> accountTypesByType = Maps.newHashMap();
 
-        List<AccountWithDataSet> allAccounts = Lists.newArrayList();
-        List<AccountWithDataSet> writableAccounts = Lists.newArrayList();
-        Set<String> extensionPackages = Sets.newHashSet();
+        final List<AccountWithDataSet> allAccounts = Lists.newArrayList();
+        final List<AccountWithDataSet> contactWritableAccounts = Lists.newArrayList();
+        final List<AccountWithDataSet> groupWritableAccounts = Lists.newArrayList();
+        final Set<String> extensionPackages = Sets.newHashSet();
 
         final AccountManager am = mAccountManager;
         final IContentService cs = ContentResolver.getContentService();
@@ -402,7 +415,10 @@
                                 account.name, account.type, accountType.dataSet);
                         allAccounts.add(accountWithDataSet);
                         if (accountType.areContactsWritable()) {
-                            writableAccounts.add(accountWithDataSet);
+                            contactWritableAccounts.add(accountWithDataSet);
+                        }
+                        if (accountType.isGroupMembershipEditable()) {
+                            groupWritableAccounts.add(accountWithDataSet);
                         }
                     }
                 }
@@ -410,14 +426,16 @@
         }
 
         Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
-        Collections.sort(writableAccounts, ACCOUNT_COMPARATOR);
+        Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR);
+        Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR);
 
         timings.addSplit("Loaded accounts");
 
         synchronized (this) {
             mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
             mAccounts = allAccounts;
-            mWritableAccounts = writableAccounts;
+            mContactWritableAccounts = contactWritableAccounts;
+            mGroupWritableAccounts = groupWritableAccounts;
             mInvitableAccountTypes = findInvitableAccountTypes(
                     mContext, allAccounts, accountTypesByTypeAndDataSet);
         }
@@ -467,12 +485,20 @@
     }
 
     /**
-     * Return list of all known, writable {@link AccountWithDataSet}'s.
+     * Return list of all known, contact writable {@link AccountWithDataSet}'s.
      */
     @Override
-    public List<AccountWithDataSet> getAccounts(boolean writableOnly) {
+    public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) {
         ensureAccountsLoaded();
-        return writableOnly ? mWritableAccounts : mAccounts;
+        return contactWritableOnly ? mContactWritableAccounts : mAccounts;
+    }
+
+    /**
+     * Return the list of all known, group writable {@link AccountWithDataSet}'s.
+     */
+    public List<AccountWithDataSet> getGroupWritableAccounts() {
+        ensureAccountsLoaded();
+        return mGroupWritableAccounts;
     }
 
     /**
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index fc48e72..058cf84 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -42,20 +42,28 @@
     private final AccountTypeManager mAccountTypes;
     private final Context mContext;
 
-    public AccountsListAdapter(Context context, boolean writableOnly) {
-        this(context, writableOnly, null);
+    /**
+     * Filters that affect the list of accounts that is displayed by this adapter.
+     */
+    public enum AccountListFilter {
+        ALL_ACCOUNTS,                   // All read-only and writable accounts
+        ACCOUNTS_CONTACT_WRITABLE,      // Only where the account type is contact writable
+        ACCOUNTS_GROUP_WRITABLE         // Only accounts where the account type is group writable
+    }
+
+    public AccountsListAdapter(Context context, AccountListFilter accountListFilter) {
+        this(context, accountListFilter, null);
     }
 
     /**
      * @param currentAccount the Account currently selected by the user, which should come
      * first in the list. Can be null.
      */
-    public AccountsListAdapter(Context context, boolean writableOnly,
+    public AccountsListAdapter(Context context, AccountListFilter accountListFilter,
             AccountWithDataSet currentAccount) {
         mContext = context;
         mAccountTypes = AccountTypeManager.getInstance(context);
-        // We don't want possible side-effect toward AccountTypeManager
-        mAccounts = new ArrayList<AccountWithDataSet>(mAccountTypes.getAccounts(writableOnly));
+        mAccounts = getAccounts(accountListFilter);
         if (currentAccount != null
                 && !mAccounts.isEmpty()
                 && !mAccounts.get(0).equals(currentAccount)
@@ -65,6 +73,14 @@
         mInflater = LayoutInflater.from(context);
     }
 
+    private List<AccountWithDataSet> getAccounts(AccountListFilter accountListFilter) {
+        if (accountListFilter == AccountListFilter.ACCOUNTS_GROUP_WRITABLE) {
+            return new ArrayList<AccountWithDataSet>(mAccountTypes.getGroupWritableAccounts());
+        }
+        return new ArrayList<AccountWithDataSet>(mAccountTypes.getAccounts(
+                accountListFilter == AccountListFilter.ACCOUNTS_CONTACT_WRITABLE));
+    }
+
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         final View resultView = convertView != null ? convertView