Add setter for accounts in AccountsListAdapter

This allows the accounts to be set after the adapter has been created
which simplifies usage in cases where the accounts are loaded with a
loader.

Test: manually verify that account list is correct for default account
preference, import from .vcf and create label.

Bug 33627801

Change-Id: I5a33ad2746cff41d0251cead9a33dc53e5bf822d
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index f0bc37e..fc7639a 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -70,7 +70,6 @@
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.util.AccountFilterUtil;
-import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 import com.android.contacts.util.ImplicitIntentsUtil;
 import com.android.contacts.util.MaterialColorMapUtils;
 import com.android.contacts.util.SharedPreferenceUtil;
@@ -693,7 +692,7 @@
             return;
         }
         SelectAccountDialogFragment.show(getFragmentManager(), R.string.dialog_new_group_account,
-                AccountListFilter.ACCOUNTS_GROUP_WRITABLE, /* extraArgs */ null,
+                AccountTypeManager.AccountFilter.GROUPS_WRITABLE, /* extraArgs */ null,
                 TAG_SELECT_ACCOUNT_DIALOG);
     }
 
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index 2c3c2c8..2717cab 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -37,7 +37,6 @@
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.model.account.AccountsLoader;
 import com.android.contacts.util.AccountsListAdapter;
-import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 import com.android.contacts.util.ImplicitIntentsUtil;
 
 import java.util.List;
@@ -142,8 +141,7 @@
             button.setOnClickListener(mAddAccountClickListener);
 
             final ListView accountListView = (ListView) view.findViewById(R.id.account_list);
-            mAccountListAdapter = new AccountsListAdapter(this,
-                    AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
+            mAccountListAdapter = new AccountsListAdapter(this, accounts);
             accountListView.setAdapter(mAccountListAdapter);
             accountListView.setOnItemClickListener(mAccountListItemClickListener);
         } else if (numAccounts == 1 && !accounts.get(0).getAccount().isNullAccount()) {
diff --git a/src/com/android/contacts/editor/SelectAccountDialogFragment.java b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
index fe3d9d6..88e92b7 100644
--- a/src/com/android/contacts/editor/SelectAccountDialogFragment.java
+++ b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
@@ -27,43 +27,51 @@
 import android.widget.TextView;
 
 import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.account.AccountInfo;
 import com.android.contacts.model.account.AccountWithDataSet;
+import com.android.contacts.model.account.AccountsLoader;
 import com.android.contacts.util.AccountsListAdapter;
-import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Shows a dialog asking the user which account to chose.
  *
  * The result is passed to {@code targetFragment} passed to {@link #show}.
  */
-public final class SelectAccountDialogFragment extends DialogFragment {
+public final class SelectAccountDialogFragment extends DialogFragment
+        implements AccountsLoader.AccountsListener {
     public static final String TAG = "SelectAccountDialogFragment";
 
     private static final String KEY_TITLE_RES_ID = "title_res_id";
     private static final String KEY_LIST_FILTER = "list_filter";
     private static final String KEY_EXTRA_ARGS = "extra_args";
 
+    private AccountsListAdapter mAccountsAdapter;
+    private AccountTypeManager.AccountFilter mFilter;
+
     /**
      * Show the dialog.
      *
      * @param fragmentManager {@link FragmentManager}.
      * @param titleResourceId resource ID to use as the title.
-     * @param accountListFilter account filter.
      * @param extraArgs Extra arguments, which will later be passed to
      *     {@link Listener#onAccountChosen}.  {@code null} will be converted to
      *     {@link Bundle#EMPTY}.
      */
     public static void show(FragmentManager fragmentManager, int titleResourceId,
-            AccountListFilter accountListFilter, Bundle extraArgs) {
-        show(fragmentManager, titleResourceId, accountListFilter, extraArgs, /* tag */ null);
+            AccountTypeManager.AccountFilter filter, Bundle extraArgs) {
+        show(fragmentManager, titleResourceId, filter, extraArgs, /* tag */ null);
     }
 
     public static void show(FragmentManager fragmentManager, int titleResourceId,
-            AccountListFilter accountListFilter, Bundle extraArgs, String tag) {
+            AccountTypeManager.AccountFilter filter, Bundle extraArgs, String tag) {
         final Bundle args = new Bundle();
         args.putInt(KEY_TITLE_RES_ID, titleResourceId);
-        args.putSerializable(KEY_LIST_FILTER, accountListFilter);
         args.putBundle(KEY_EXTRA_ARGS, (extraArgs == null) ? Bundle.EMPTY : extraArgs);
+        args.putSerializable(KEY_LIST_FILTER, filter);
 
         final SelectAccountDialogFragment instance = new SelectAccountDialogFragment();
         instance.setArguments(args);
@@ -71,14 +79,22 @@
     }
 
     @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Bundle args = getArguments();
+        mFilter = (AccountTypeManager.AccountFilter) args.getSerializable(KEY_LIST_FILTER);
+        if (mFilter == null) {
+            mFilter = AccountTypeManager.AccountFilter.ALL;
+        }
+    }
+
+    @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
         final Bundle args = getArguments();
 
-        final AccountListFilter filter = (AccountListFilter) args.getSerializable(KEY_LIST_FILTER);
-        final AccountsListAdapter accountAdapter = new AccountsListAdapter(builder.getContext(),
-                filter);
-        accountAdapter.setCustomLayout(R.layout.account_selector_list_item_condensed);
+        mAccountsAdapter = new AccountsListAdapter(builder.getContext());
+        mAccountsAdapter.setCustomLayout(R.layout.account_selector_list_item_condensed);
 
         final DialogInterface.OnClickListener clickListener =
                 new DialogInterface.OnClickListener() {
@@ -86,19 +102,25 @@
             public void onClick(DialogInterface dialog, int which) {
                 dialog.dismiss();
 
-                onAccountSelected(accountAdapter.getItem(which));
+                onAccountSelected(mAccountsAdapter.getItem(which));
             }
         };
 
         final TextView title = (TextView) View.inflate(getActivity(), R.layout.dialog_title, null);
         title.setText(args.getInt(KEY_TITLE_RES_ID));
         builder.setCustomTitle(title);
-        builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
+        builder.setSingleChoiceItems(mAccountsAdapter, 0, clickListener);
         final AlertDialog result = builder.create();
         return result;
     }
 
     @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        AccountsLoader.loadAccounts(this, 0, mFilter);
+    }
+
+    @Override
     public void onCancel(DialogInterface dialog) {
         super.onCancel(dialog);
         final Listener listener = getListener();
@@ -126,6 +148,13 @@
         return listener;
     }
 
+    @Override
+    public void onAccountsLoaded(List<AccountInfo> accounts) {
+        Preconditions.checkNotNull(mAccountsAdapter,
+                "Accounts adapter should have been initialized");
+        mAccountsAdapter.setAccounts(accounts, null);
+    }
+
     public interface Listener {
         void onAccountChosen(AccountWithDataSet account, Bundle extraArgs);
         void onAccountSelectorCancelled();
diff --git a/src/com/android/contacts/interactions/ImportDialogFragment.java b/src/com/android/contacts/interactions/ImportDialogFragment.java
index 5bf4fe4..367ae2f 100644
--- a/src/com/android/contacts/interactions/ImportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportDialogFragment.java
@@ -44,12 +44,9 @@
 import com.android.contacts.model.SimCard;
 import com.android.contacts.model.SimContact;
 import com.android.contacts.model.account.AccountInfo;
-import com.android.contacts.model.account.AccountType;
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.util.AccountSelectionUtil;
-import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 
 import java.util.List;
 import java.util.concurrent.Future;
@@ -274,7 +271,7 @@
             args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
             SelectAccountDialogFragment.show(
                     getFragmentManager(), R.string.dialog_new_contact_account,
-                    AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, args);
+                    AccountTypeManager.AccountFilter.CONTACTS_WRITABLE, args);
         } else {
             AccountSelectionUtil.doImport(getActivity(), resId,
                     (size == 1 ? accountList.get(0) : null),
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index cbdccd5..b3f05c5 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -83,6 +83,27 @@
     public static final String BROADCAST_ACCOUNTS_CHANGED = AccountTypeManager.class.getName() +
             ".AccountsChanged";
 
+    public enum AccountFilter implements Predicate<AccountInfo> {
+        ALL {
+            @Override
+            public boolean apply(@Nullable AccountInfo input) {
+                return input != null;
+            }
+        },
+        CONTACTS_WRITABLE {
+            @Override
+            public boolean apply(@Nullable AccountInfo input) {
+                return input != null && input.getType().areContactsWritable();
+            }
+        },
+        GROUPS_WRITABLE {
+            @Override
+            public boolean apply(@Nullable AccountInfo input) {
+                return input != null && input.getType().isGroupMembershipEditable();
+            }
+        };
+    }
+
     /**
      * Requests the singleton instance of {@link AccountTypeManager} with data bound from
      * the available authenticators. This method can safely be called from the UI thread.
@@ -298,43 +319,12 @@
         return canGetAccounts && canReadContacts;
     }
 
-    public static Predicate<AccountInfo> nonNullAccountFilter() {
-        return new Predicate<AccountInfo>() {
-            @Override
-            public boolean apply(AccountInfo info) {
-                AccountWithDataSet account = info != null ? info.getAccount() : null;
-                return account != null && !account.isNullAccount();
-            }
-        };
-
-    }
-
     public static Predicate<AccountInfo> writableFilter() {
-        return new Predicate<AccountInfo>() {
-            @Override
-            public boolean apply(AccountInfo account) {
-                return account.getType().areContactsWritable();
-            }
-        };
+        return AccountFilter.CONTACTS_WRITABLE;
     }
 
     public static Predicate<AccountInfo> groupWritableFilter() {
-        return new Predicate<AccountInfo>() {
-            @Override
-            public boolean apply(@Nullable AccountInfo account) {
-                return account.getType().isGroupMembershipEditable();
-            }
-        };
-    }
-
-    public static Predicate<AccountInfo> onlyNonEmptyExtensionFilter(Context context) {
-        final Context appContext = context.getApplicationContext();
-        return new Predicate<AccountInfo>() {
-            @Override
-            public boolean apply(@Nullable AccountInfo input) {
-                return !input.getType().isExtension() || input.getAccount().hasData(appContext);
-            }
-        };
+        return AccountFilter.GROUPS_WRITABLE;
     }
 }
 
diff --git a/src/com/android/contacts/model/account/AccountsLoader.java b/src/com/android/contacts/model/account/AccountsLoader.java
index 260c44b..e3b4336 100644
--- a/src/com/android/contacts/model/account/AccountsLoader.java
+++ b/src/com/android/contacts/model/account/AccountsLoader.java
@@ -71,6 +71,8 @@
      * <p>This is a convenience method to reduce the
      * boilerplate needed when implementing {@link android.app.LoaderManager.LoaderCallbacks}
      * in the simple case that the fragment wants to just load the accounts directly</p>
+     * <p>Note that changing the filter between invocations in the same component will not work
+     * properly because the loader is cached.</p>
      */
     public static <FragmentType extends Fragment & AccountsListener> void loadAccounts(
             final FragmentType fragment, int loaderId, final Predicate<AccountInfo> filter) {
@@ -78,6 +80,9 @@
                 fragment.getActivity(), fragment.getLoaderManager(), loaderId, filter, fragment);
     }
 
+    /**
+     * Same as {@link #loadAccounts(Fragment, int, Predicate)} for an Activity
+     */
     public static <ActivityType extends Activity & AccountsListener> void loadAccounts(
             final ActivityType activity, int id, final Predicate<AccountInfo> filter) {
         loadAccounts(activity, activity.getLoaderManager(), id, filter, activity);
diff --git a/src/com/android/contacts/preference/DefaultAccountPreference.java b/src/com/android/contacts/preference/DefaultAccountPreference.java
index 72ba74d..efff5a0 100644
--- a/src/com/android/contacts/preference/DefaultAccountPreference.java
+++ b/src/com/android/contacts/preference/DefaultAccountPreference.java
@@ -24,14 +24,17 @@
 import android.view.View;
 
 import com.android.contacts.model.AccountTypeManager;
-import com.android.contacts.model.account.AccountDisplayInfoFactory;
+import com.android.contacts.model.account.AccountInfo;
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.util.AccountsListAdapter;
 
+import java.util.List;
+
 public class DefaultAccountPreference extends DialogPreference {
     private ContactsPreferences mPreferences;
     private AccountsListAdapter mListAdapter;
     private AccountTypeManager mAccountTypeManager;
+    private List<AccountInfo> mAccounts;
     private int mChosenIndex = -1;
 
     public DefaultAccountPreference(Context context) {
@@ -44,6 +47,13 @@
         prepare();
     }
 
+    public void setAccounts(List<AccountInfo> accounts) {
+        mAccounts = accounts;
+        if (mListAdapter != null) {
+            mListAdapter.setAccounts(accounts, null);
+        }
+    }
+
     @Override
     protected View onCreateDialogView() {
         prepare();
@@ -52,8 +62,10 @@
 
     private void prepare() {
         mPreferences = new ContactsPreferences(getContext());
-        mListAdapter = new AccountsListAdapter(getContext(),
-                AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
+        mListAdapter = new AccountsListAdapter(getContext());
+        if (mAccounts != null) {
+            mListAdapter.setAccounts(mAccounts, null);
+        }
         mAccountTypeManager = AccountTypeManager.getInstance(getContext());
     }
 
diff --git a/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
index a9ec250..e9c3a01 100644
--- a/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
+++ b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
@@ -297,6 +297,10 @@
         if (accounts.isEmpty()) {
             getPreferenceScreen().removePreference(findPreference(KEY_DEFAULT_ACCOUNT));
             getPreferenceScreen().removePreference(findPreference(KEY_CUSTOM_CONTACTS_FILTER));
+        } else {
+            final DefaultAccountPreference preference =
+                    (DefaultAccountPreference) findPreference(KEY_DEFAULT_ACCOUNT);
+            preference.setAccounts(accounts);
         }
     }
 
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index 005bb8d..2bcc68b 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -30,6 +30,7 @@
 import com.android.contacts.model.account.AccountWithDataSet;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -37,50 +38,11 @@
  */
 public final class AccountsListAdapter extends BaseAdapter {
     private final LayoutInflater mInflater;
-    private final List<AccountInfo> mAccounts;
-    private final Context mContext;
+    private List<AccountInfo> mAccounts;
     private int mCustomLayout = -1;
 
-    public enum AccountListFilter {
-        ALL_ACCOUNTS {
-            @Override
-            public List<AccountWithDataSet> getSourceAccounts(Context context) {
-                return AccountTypeManager.getInstance(context).getAccounts(false);
-            }
-        },
-        ACCOUNTS_CONTACT_WRITABLE {
-            @Override
-            public List<AccountWithDataSet> getSourceAccounts(Context context) {
-                return AccountTypeManager.getInstance(context).getAccounts(true);
-            }
-        },
-        ACCOUNTS_GROUP_WRITABLE {
-            @Override
-            public List<AccountWithDataSet> getSourceAccounts(Context context) {
-                return AccountTypeManager.getInstance(context).getGroupWritableAccounts();
-            }
-        };
-
-        private List<AccountInfo> getAccounts(Context context) {
-            final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
-            final List<AccountInfo> result = new ArrayList<>();
-            final List<AccountWithDataSet> sourceAccounts = getSourceAccounts(context);
-            for (AccountWithDataSet account : sourceAccounts) {
-                result.add(accountTypeManager.getAccountInfoForAccount(account));
-            }
-            return result;
-        }
-
-        public abstract List<AccountWithDataSet> getSourceAccounts(Context context);
-    }
-
-    public AccountsListAdapter(Context context, AccountListFilter filter) {
-        this(context, filter.getAccounts(context), null);
-    }
-
-    public AccountsListAdapter(Context context, AccountListFilter filter,
-            AccountWithDataSet currentAccount) {
-        this(context, filter.getAccounts(context), currentAccount);
+    public AccountsListAdapter(Context context) {
+        this(context, Collections.<AccountInfo>emptyList(), null);
     }
 
     public AccountsListAdapter(Context context, List<AccountInfo> accounts) {
@@ -93,19 +55,28 @@
      */
     public AccountsListAdapter(Context context, List<AccountInfo> accounts,
             AccountWithDataSet currentAccount) {
-        mContext = context;
-
-        final AccountInfo currentInfo = AccountInfo.getAccount(accounts, currentAccount);
-        if (currentInfo != null
-                && !accounts.isEmpty()
-                && !accounts.get(0).sameAccount(currentAccount)
-                && accounts.remove(currentInfo)) {
-            accounts.add(0, currentInfo);
-        }
-
         mInflater = LayoutInflater.from(context);
 
-        mAccounts = accounts;
+        mAccounts = new ArrayList<>(accounts.size());
+        setAccounts(accounts, currentAccount);
+    }
+
+    public void setAccounts(List<AccountInfo> accounts, AccountWithDataSet currentAccount) {
+        // If it's not empty use the previous "current" account (the first one in the list)
+        final AccountInfo currentInfo = mAccounts.isEmpty()
+                ? AccountInfo.getAccount(accounts, currentAccount)
+                : AccountInfo.getAccount(accounts, mAccounts.get(0).getAccount());
+
+        mAccounts.clear();
+        mAccounts.addAll(accounts);
+
+        if (currentInfo != null
+                && !mAccounts.isEmpty()
+                && !mAccounts.get(0).sameAccount(currentAccount)
+                && mAccounts.remove(currentInfo)) {
+            mAccounts.add(0, currentInfo);
+        }
+        notifyDataSetChanged();
     }
 
     public void setCustomLayout(int customLayout) {