Merge changes I3bfc9e37,Ib3907fbe,Ib673838d,I423def04,Ifde6c5f3, ... into ub-contactsdialer-i-dev
* changes:
Fix test flakiness caused by not cleaning up test accounts
Fix issue with account existence check
Add convenience methods for account loading
Use AccountsLoader in settings fragment
Replace several usages for of AccountTypeManager.getAccounts
Split AccountTypeManager.contains method
diff --git a/src/com/android/contacts/SimImportFragment.java b/src/com/android/contacts/SimImportFragment.java
index a8db4ee..9393e1a 100644
--- a/src/com/android/contacts/SimImportFragment.java
+++ b/src/com/android/contacts/SimImportFragment.java
@@ -126,9 +126,9 @@
if (savedInstanceState != null) {
mAccountHeaderPresenter.onRestoreInstanceState(savedInstanceState);
} else {
- final AccountWithDataSet currentDefaultAccount = AccountWithDataSet
- .getDefaultOrBestFallback(mPreferences, mAccountTypeManager);
- mAccountHeaderPresenter.setCurrentAccount(currentDefaultAccount);
+ // Default may be null in which case the first account in the list will be selected
+ // after they are loaded.
+ mAccountHeaderPresenter.setCurrentAccount(mPreferences.getDefaultAccount());
}
mAccountHeaderPresenter.setObserver(new AccountHeaderPresenter.Observer() {
@Override
@@ -173,6 +173,9 @@
private void rememberSelectionsForCurrentAccount() {
final AccountWithDataSet current = mAdapter.getAccount();
+ if (current == null) {
+ return;
+ }
final long[] ids = mListView.getCheckedItemIds();
Arrays.sort(ids);
mPerAccountCheckedIds.put(current, ids);
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index 3531125..2ca51f6 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -36,6 +36,7 @@
import com.android.contacts.editor.ContactEditorUtils;
import com.android.contacts.model.AccountTypeManager;
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.model.account.AccountsLoader;
import com.android.contacts.util.AccountsListAdapter;
@@ -54,7 +55,7 @@
* account for this contact.
*/
public class ContactEditorAccountsChangedActivity extends Activity
- implements LoaderManager.LoaderCallbacks<List<AccountInfo>> {
+ implements AccountsLoader.AccountsListener {
private static final String TAG = ContactEditorAccountsChangedActivity.class.getSimpleName();
@@ -102,7 +103,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEditorUtils = ContactEditorUtils.create(this);
- getLoaderManager().initLoader(0, null, this);
+ AccountsLoader.loadAccounts(this, 0, AccountTypeManager.writableFilter());
}
@Override
@@ -236,16 +237,7 @@
}
@Override
- public Loader<List<AccountInfo>> onCreateLoader(int id, Bundle args) {
- return new AccountsLoader(this, AccountTypeManager.writableFilter());
- }
-
- @Override
- public void onLoadFinished(Loader<List<AccountInfo>> loader, List<AccountInfo> data) {
- updateDisplayedAccounts(data);
- }
-
- @Override
- public void onLoaderReset(Loader<List<AccountInfo>> loader) {
+ public void onAccountsLoaded(List<AccountInfo> accounts) {
+ updateDisplayedAccounts(accounts);
}
}
diff --git a/src/com/android/contacts/editor/AccountHeaderPresenter.java b/src/com/android/contacts/editor/AccountHeaderPresenter.java
index e7e8e50..4e4f35a 100644
--- a/src/com/android/contacts/editor/AccountHeaderPresenter.java
+++ b/src/com/android/contacts/editor/AccountHeaderPresenter.java
@@ -99,8 +99,9 @@
public void setAccounts(List<AccountInfo> accounts) {
mAccounts = accounts;
- // If the current account was removed just switch to the next one in the list.
- if (mCurrentAccount != null && !AccountInfo.contains(mAccounts, mCurrentAccount)) {
+ // If the current account hasn't been set or it has been removed just use the first
+ // account.
+ if (mCurrentAccount == null || !AccountInfo.contains(mAccounts, mCurrentAccount)) {
mCurrentAccount = mAccounts.isEmpty() ? null : accounts.get(0).getAccount();
mObserver.onChange(this);
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ede4a37..489cfd1 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -111,7 +111,8 @@
JoinContactConfirmationDialogFragment.Listener,
AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener,
CancelEditDialogFragment.Listener,
- RawContactEditorView.Listener, PhotoEditorView.Listener {
+ RawContactEditorView.Listener, PhotoEditorView.Listener,
+ AccountsLoader.AccountsListener {
static final String TAG = "ContactEditor";
@@ -435,52 +436,6 @@
}
};
- protected LoaderManager.LoaderCallbacks<List<AccountInfo>> mAccountsLoaderListener =
- new LoaderManager.LoaderCallbacks<List<AccountInfo>>() {
- @Override
- public Loader<List<AccountInfo>> onCreateLoader(int id, Bundle args) {
- return new AccountsLoader(getActivity(), AccountTypeManager.writableFilter());
- }
-
- @Override
- public void onLoadFinished(
- Loader<List<AccountInfo>> loader, List<AccountInfo> data) {
- mWritableAccounts = data;
- // The user may need to select a new account to save to
- if (mAccountWithDataSet == null && mHasNewContact) {
- selectAccountAndCreateContact();
- }
-
- final RawContactEditorView view = getContent();
- if (view == null) {
- return;
- }
- view.setAccounts(data);
- if (mAccountWithDataSet == null && view.getCurrentRawContactDelta() == null) {
- return;
- }
-
- final AccountWithDataSet account = mAccountWithDataSet != null
- ? mAccountWithDataSet
- : view.getCurrentRawContactDelta().getAccountWithDataSet();
-
- // The current account was removed
- if (!AccountInfo.contains(data, account) && !data.isEmpty()) {
- if (isReadyToBindEditors()) {
- onRebindEditorsForNewContact(getContent().getCurrentRawContactDelta(),
- account, data.get(0).getAccount());
- } else {
- mAccountWithDataSet = data.get(0).getAccount();
- }
- }
- }
-
- @Override
- public void onLoaderReset(Loader<List<AccountInfo>> loader) {
- }
- };
-
-
private long mPhotoRawContactId;
private Bundle mUpdatedPhotos = new Bundle();
@@ -611,7 +566,7 @@
}
if (mHasNewContact) {
- getLoaderManager().initLoader(LOADER_ACCOUNTS, null, mAccountsLoaderListener);
+ AccountsLoader.loadAccounts(this, LOADER_ACCOUNTS, AccountTypeManager.writableFilter());
}
}
@@ -720,6 +675,38 @@
}
}
+ @Override
+ public void onAccountsLoaded(List<AccountInfo> data) {
+ mWritableAccounts = data;
+ // The user may need to select a new account to save to
+ if (mAccountWithDataSet == null && mHasNewContact) {
+ selectAccountAndCreateContact();
+ }
+
+ final RawContactEditorView view = getContent();
+ if (view == null) {
+ return;
+ }
+ view.setAccounts(data);
+ if (mAccountWithDataSet == null && view.getCurrentRawContactDelta() == null) {
+ return;
+ }
+
+ final AccountWithDataSet account = mAccountWithDataSet != null
+ ? mAccountWithDataSet
+ : view.getCurrentRawContactDelta().getAccountWithDataSet();
+
+ // The current account was removed
+ if (!AccountInfo.contains(data, account) && !data.isEmpty()) {
+ if (isReadyToBindEditors()) {
+ onRebindEditorsForNewContact(getContent().getCurrentRawContactDelta(),
+ account, data.get(0).getAccount());
+ } else {
+ mAccountWithDataSet = data.get(0).getAccount();
+ }
+ }
+ }
+
//
// Options menu
//
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
index d894371..5a9c9fd 100644
--- a/src/com/android/contacts/editor/PickRawContactDialogFragment.java
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -53,7 +53,6 @@
private final LayoutInflater mInflater;
private final Context mContext;
private final RawContactsMetadata mRawContactsMetadata;
- private final AccountDisplayInfoFactory mAccountDisplayInfoFactory;
private final AccountTypeManager mAccountTypeManager;
private final ContactsPreferences mPreferences;
@@ -61,7 +60,6 @@
RawContactsMetadata rawContactsMetadata) {
mContext = context;
mInflater = LayoutInflater.from(context);
- mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(context);
mAccountTypeManager = AccountTypeManager.getInstance(context);
mPreferences = new ContactsPreferences(context);
mRawContactsMetadata = rawContactsMetadata;
diff --git a/src/com/android/contacts/interactions/ImportDialogFragment.java b/src/com/android/contacts/interactions/ImportDialogFragment.java
index 7f5ce4e..5bf4fe4 100644
--- a/src/com/android/contacts/interactions/ImportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportDialogFragment.java
@@ -43,11 +43,16 @@
import com.android.contacts.model.AccountTypeManager;
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;
/**
* An dialog invoked to import/export contacts.
@@ -65,6 +70,8 @@
private boolean mSimOnly = false;
private SimContactDao mSimDao;
+ private Future<List<AccountInfo>> mAccountsFuture;
+
/** Preferred way to show this dialog */
public static void show(FragmentManager fragmentManager) {
final ImportDialogFragment fragment = new ImportDialogFragment();
@@ -100,6 +107,15 @@
}
@Override
+ public void onResume() {
+ super.onResume();
+
+ // Start loading the accounts. This is done in onResume in case they were refreshed.
+ mAccountsFuture = AccountTypeManager.getInstance(getActivity()).filterAccountsAsync(
+ AccountTypeManager.writableFilter());
+ }
+
+ @Override
public Context getContext() {
return getActivity();
}
@@ -240,12 +256,16 @@
* Handle "import from SD".
*/
private void handleImportRequest(int resId, int subscriptionId) {
+ // Get the accounts. Because this only happens after a user action this should pretty
+ // much never block since it will usually be at least several seconds before the user
+ // interacts with the view
+ final List<AccountWithDataSet> accountList = AccountInfo.extractAccounts(
+ Futures.getUnchecked(mAccountsFuture));
+
// There are three possibilities:
// - more than one accounts -> ask the user
// - just one account -> use the account without asking the user
// - no account -> use phone-local storage without asking the user
- final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
- final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
final int size = accountList.size();
if (size > 1) {
// Send over to the account selector
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
index 4245be4..32e4b9c 100644
--- a/src/com/android/contacts/list/ContactListFilter.java
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -392,12 +392,16 @@
}
}
+ public boolean isSyncable() {
+ return isGoogleAccountType() && filterType == FILTER_TYPE_ACCOUNT;
+ }
+
/**
* Returns true if this ContactListFilter contains at least one Google account.
* (see {@link #isGoogleAccountType)
*/
public boolean isSyncable(List<AccountWithDataSet> accounts) {
- if (isGoogleAccountType() && filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ if (isSyncable()) {
return true;
}
// Since we don't know which group is selected until the actual contacts loading, we
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
index 3a4596d..df81832 100644
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -194,6 +194,6 @@
final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
final AccountWithDataSet filterAccount = new AccountWithDataSet(
mFilter.accountName, mFilter.accountType, mFilter.dataSet);
- return accountTypeManager.contains(filterAccount, /* contactWritableOnly */ false);
+ return accountTypeManager.exists(filterAccount);
}
}
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 855a530..bf3ee81 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -70,6 +70,7 @@
import com.android.contacts.logging.Logger;
import com.android.contacts.logging.ScreenEvent;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.account.AccountInfo;
import com.android.contacts.model.account.AccountWithDataSet;
import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contacts.util.AccountFilterUtil;
@@ -78,9 +79,11 @@
import com.android.contacts.util.SyncUtil;
import com.android.contactsbind.FeatureHighlightHelper;
import com.android.contactsbind.experiments.Flags;
+import com.google.common.util.concurrent.Futures;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.Future;
/**
* Fragment containing a contact list used for browsing (as compared to
@@ -152,6 +155,8 @@
private ContactsRequest mContactsRequest;
private ContactListFilterController mContactListFilterController;
+ private Future<List<AccountInfo>> mWritableAccountsFuture;
+
private final ActionBarAdapter.Listener mActionBarListener = new ActionBarAdapter.Listener() {
@Override
public void onAction(int action) {
@@ -320,12 +325,13 @@
// TODO(samchen) : Check ContactListFilter.FILTER_TYPE_CUSTOM
if (ContactListFilter.FILTER_TYPE_DEFAULT == filter.filterType
|| ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS == filter.filterType) {
- final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(getContext())
- .getAccounts(/* contactsWritableOnly */ true);
- final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
+ final List<AccountInfo> syncableAccounts =
+ AccountTypeManager.getInstance(getContext()).getWritableGoogleAccounts();
if (syncableAccounts != null && syncableAccounts.size() > 0) {
- for (Account account : syncableAccounts) {
+ for (AccountInfo info : syncableAccounts) {
+ // Won't be null because Google accounts have a non-null name and type.
+ final Account account = info.getAccount().getAccountOrNull();
if (SyncUtil.isSyncStatusPendingOrActive(account)
|| SyncUtil.isUnsyncableGoogleAccount(account)) {
return false;
@@ -508,9 +514,11 @@
public void onEnableAutoSync(ContactListFilter filter) {
// Turn on auto-sync
ContentResolver.setMasterSyncAutomatically(true);
+
+ // This should be OK (won't block) because this only happens after a user action
+ final List<AccountInfo> accountInfos = Futures.getUnchecked(mWritableAccountsFuture);
// Also enable Contacts sync
- final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(
- getContext()).getAccounts(/* contactsWritableOnly */ true);
+ final List<AccountWithDataSet> accounts = AccountInfo.extractAccounts(accountInfos);
final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
if (syncableAccounts != null && syncableAccounts.size() > 0) {
for (Account account : syncableAccounts) {
@@ -578,8 +586,8 @@
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(
- getContext()).getAccounts(/* contactsWritableOnly */ true);
+ final List<AccountWithDataSet> accounts = AccountInfo.extractAccounts(
+ Futures.getUnchecked(mWritableAccountsFuture));
final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
if (syncableAccounts != null && syncableAccounts.size() > 0) {
for (Account account : syncableAccounts) {
@@ -729,6 +737,9 @@
mActionBarAdapter.setListener(mActionBarListener);
mDisableOptionItemSelected = false;
maybeHideCheckBoxes();
+
+ mWritableAccountsFuture = AccountTypeManager.getInstance(getContext()).filterAccountsAsync(
+ AccountTypeManager.writableFilter());
}
private void maybeHideCheckBoxes() {
@@ -843,9 +854,9 @@
if (filter != null && !mActionBarAdapter.isSearchMode()
&& !mActionBarAdapter.isSelectionMode()) {
- final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(getContext())
- .getAccounts(/* contactsWritableOnly */ true);
- if (filter.isSyncable(accounts)) {
+ if (filter.isSyncable()
+ || (filter.shouldShowSyncState()
+ && SyncUtil.hasSyncableAccount(AccountTypeManager.getInstance(getContext())))) {
swipeRefreshLayout.setEnabled(true);
}
}
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 1d3a5fa..cbdccd5 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -206,7 +206,8 @@
* Returns true if there are real accounts (not "local" account) in the list of accounts.
*/
public boolean hasNonLocalAccount() {
- final List<AccountWithDataSet> allAccounts = getAccounts(/* contactWritableOnly */ false);
+ final List<AccountWithDataSet> allAccounts =
+ AccountInfo.extractAccounts(Futures.getUnchecked(getAccountsAsync()));
if (allAccounts == null || allAccounts.size() == 0) {
return false;
}
@@ -267,16 +268,22 @@
}
/**
- * @param contactWritableOnly if true, it only returns ones that support writing contacts.
- * @return true when this instance contains the given account.
+ * Returns whether the specified account still exists
*/
- public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) {
- for (AccountWithDataSet account_2 : getAccounts(contactWritableOnly)) {
- if (account.equals(account_2)) {
- return true;
- }
- }
- return false;
+ public boolean exists(AccountWithDataSet account) {
+ final List<AccountWithDataSet> accounts =
+ AccountInfo.extractAccounts(Futures.getUnchecked(getAccountsAsync()));
+ return accounts.contains(account);
+ }
+
+ /**
+ * Returns whether the specified account is writable
+ *
+ * <p>This checks that the account still exists and that
+ * {@link AccountType#areContactsWritable()} is true</p>
+ */
+ public boolean isWritable(AccountWithDataSet account) {
+ return exists(account) && getAccountInfoForAccount(account).getType().areContactsWritable();
}
public boolean hasGoogleAccount() {
@@ -650,10 +657,25 @@
return result;
}
+ /**
+ * Returns true if there are real accounts (not "local" account) in the list of accounts.
+ *
+ * <p>This is overriden for performance since the default implementation blocks until all
+ * accounts are loaded
+ * </p>
+ */
@Override
public boolean hasNonLocalAccount() {
final Account[] accounts = mAccountManager.getAccounts();
- return accounts != null && accounts.length > 0;
+ if (accounts == null) {
+ return false;
+ }
+ for (Account account : accounts) {
+ if (mTypeProvider.supportsContactsSyncing(account.type)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -685,6 +707,26 @@
}
/**
+ * Returns whether the account still exists on the device
+ *
+ * <p>This is overridden for performance. The default implementation loads all accounts then
+ * searches through them for specified. This implementation will only load the types for the
+ * specified AccountType (it may still require blocking on IO in some cases but it shouldn't
+ * be as bad as blocking for all accounts).
+ * </p>
+ */
+ @Override
+ public boolean exists(AccountWithDataSet account) {
+ final Account[] accounts = mAccountManager.getAccountsByType(account.type);
+ for (Account existingAccount : accounts) {
+ if (existingAccount.name.equals(account.name)) {
+ return mTypeProvider.getTypeForAccount(account) != null;
+ }
+ }
+ return false;
+ }
+
+ /**
* Return {@link AccountType} for the given account type and data set.
*/
@Override
diff --git a/src/com/android/contacts/model/account/AccountDisplayInfoFactory.java b/src/com/android/contacts/model/account/AccountDisplayInfoFactory.java
index 759eede..ac1ad35 100644
--- a/src/com/android/contacts/model/account/AccountDisplayInfoFactory.java
+++ b/src/com/android/contacts/model/account/AccountDisplayInfoFactory.java
@@ -89,18 +89,6 @@
return new AccountDisplayInfoFactory(context, accounts);
}
- public static AccountDisplayInfoFactory forAllAccounts(Context context) {
- final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
- final List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(false);
- return new AccountDisplayInfoFactory(context, accounts);
- }
-
- public static AccountDisplayInfoFactory forWritableAccounts(Context context) {
- final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
- final List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(true);
- return new AccountDisplayInfoFactory(context, accounts);
- }
-
private boolean shouldUseTypeLabelForName(AccountWithDataSet account) {
final int type = mDeviceAccountTypeFactory.classifyAccount(account.type);
return (type == DeviceLocalAccountTypeFactory.TYPE_SIM && mSimAccountCount == 1)
diff --git a/src/com/android/contacts/model/account/AccountInfo.java b/src/com/android/contacts/model/account/AccountInfo.java
index 2161edb..b07204e 100644
--- a/src/com/android/contacts/model/account/AccountInfo.java
+++ b/src/com/android/contacts/model/account/AccountInfo.java
@@ -76,6 +76,10 @@
return mDisplayInfo.isDeviceAccount();
}
+ public boolean hasGoogleAccountType() {
+ return mDisplayInfo.hasGoogleAccountType();
+ }
+
public boolean sameAccount(AccountInfo other) {
return sameAccount(other.getAccount());
}
diff --git a/src/com/android/contacts/model/account/AccountTypeProvider.java b/src/com/android/contacts/model/account/AccountTypeProvider.java
index e5d0448..e30ae29 100644
--- a/src/com/android/contacts/model/account/AccountTypeProvider.java
+++ b/src/com/android/contacts/model/account/AccountTypeProvider.java
@@ -15,6 +15,8 @@
*/
package com.android.contacts.model.account;
+import static com.android.contacts.util.DeviceLocalAccountTypeFactory.Util.isLocalAccountType;
+
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.content.ContentResolver;
@@ -38,8 +40,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import static com.android.contacts.util.DeviceLocalAccountTypeFactory.Util.isLocalAccountType;
-
/**
* Provides access to {@link AccountType}s with contact data
*
@@ -150,6 +150,10 @@
return true;
}
+ public boolean supportsContactsSyncing(String accountType) {
+ return mAuthTypes.containsKey(accountType);
+ }
+
private List<AccountType> loadTypes(String type) {
final AuthenticatorDescription auth = mAuthTypes.get(type);
if (auth == null) {
diff --git a/src/com/android/contacts/model/account/AccountWithDataSet.java b/src/com/android/contacts/model/account/AccountWithDataSet.java
index 0f36918..a163139 100644
--- a/src/com/android/contacts/model/account/AccountWithDataSet.java
+++ b/src/com/android/contacts/model/account/AccountWithDataSet.java
@@ -254,30 +254,5 @@
return ret;
}
-
- public static AccountWithDataSet getDefaultOrBestFallback(ContactsPreferences preferences,
- AccountTypeManager accountTypeManager) {
- if (preferences.isDefaultAccountSet()) {
- final AccountWithDataSet account = preferences.getDefaultAccount();
- if (accountTypeManager.contains(account, true)) {
- return account;
- }
- }
- final List<AccountWithDataSet> accounts = accountTypeManager
- .getAccounts(/* writableOnly */ true);
-
- if (accounts.isEmpty()) {
- return AccountWithDataSet.getNullAccount();
- }
-
- // Return the first google account
- for (AccountWithDataSet account : accounts) {
- if (GoogleAccountType.ACCOUNT_TYPE.equals(account) && account.dataSet == null) {
- return account;
- }
- }
- // Arbitrarily return the first writable account
- return accounts.get(0);
- }
}
diff --git a/src/com/android/contacts/model/account/AccountsLoader.java b/src/com/android/contacts/model/account/AccountsLoader.java
index 78f309b..260c44b 100644
--- a/src/com/android/contacts/model/account/AccountsLoader.java
+++ b/src/com/android/contacts/model/account/AccountsLoader.java
@@ -15,8 +15,13 @@
*/
package com.android.contacts.model.account;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.Loader;
+import android.os.Bundle;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.util.concurrent.ListenableFutureLoader;
@@ -55,4 +60,47 @@
AccountInfo.extractAccounts(next));
}
+
+ public interface AccountsListener {
+ void onAccountsLoaded(List<AccountInfo> accounts);
+ }
+
+ /**
+ * Loads the accounts into the target fragment using {@link LoaderManager}
+ *
+ * <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>
+ */
+ public static <FragmentType extends Fragment & AccountsListener> void loadAccounts(
+ final FragmentType fragment, int loaderId, final Predicate<AccountInfo> filter) {
+ loadAccounts(
+ fragment.getActivity(), fragment.getLoaderManager(), loaderId, filter, fragment);
+ }
+
+ public static <ActivityType extends Activity & AccountsListener> void loadAccounts(
+ final ActivityType activity, int id, final Predicate<AccountInfo> filter) {
+ loadAccounts(activity, activity.getLoaderManager(), id, filter, activity);
+ }
+
+ private static void loadAccounts(final Context context, LoaderManager loaderManager, int id,
+ final Predicate<AccountInfo> filter, final AccountsListener listener) {
+ loaderManager.initLoader(id, null,
+ new LoaderManager.LoaderCallbacks<List<AccountInfo>>() {
+ @Override
+ public Loader<List<AccountInfo>> onCreateLoader(int id, Bundle args) {
+ return new AccountsLoader(context, filter);
+ }
+
+ @Override
+ public void onLoadFinished(
+ Loader<List<AccountInfo>> loader, List<AccountInfo> data) {
+ listener.onAccountsLoaded(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<AccountInfo>> loader) {
+ }
+ });
+ }
}
diff --git a/src/com/android/contacts/preference/DefaultAccountPreference.java b/src/com/android/contacts/preference/DefaultAccountPreference.java
index fc23e13..72ba74d 100644
--- a/src/com/android/contacts/preference/DefaultAccountPreference.java
+++ b/src/com/android/contacts/preference/DefaultAccountPreference.java
@@ -31,7 +31,6 @@
public class DefaultAccountPreference extends DialogPreference {
private ContactsPreferences mPreferences;
private AccountsListAdapter mListAdapter;
- private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
private AccountTypeManager mAccountTypeManager;
private int mChosenIndex = -1;
@@ -56,7 +55,6 @@
mListAdapter = new AccountsListAdapter(getContext(),
AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
mAccountTypeManager = AccountTypeManager.getInstance(getContext());
- mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
}
@Override
@@ -68,10 +66,10 @@
public CharSequence getSummary() {
final AccountWithDataSet defaultAccount = mPreferences.getDefaultAccount();
if (defaultAccount == null ||
- !mAccountTypeManager.getAccounts(/* writable */ true).contains(defaultAccount)) {
+ !mAccountTypeManager.exists(defaultAccount)) {
return null;
} else {
- return mAccountDisplayInfoFactory.getAccountDisplayInfo(defaultAccount).getNameLabel();
+ return mAccountTypeManager.getAccountInfoForAccount(defaultAccount).getNameLabel();
}
}
diff --git a/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
index 0d34f68..a9ec250 100644
--- a/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
+++ b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
@@ -54,7 +54,8 @@
import com.android.contacts.list.ContactListFilterController;
import com.android.contacts.logging.ScreenEvent.ScreenType;
import com.android.contacts.model.AccountTypeManager;
-import com.android.contacts.model.account.AccountWithDataSet;
+import com.android.contacts.model.account.AccountInfo;
+import com.android.contacts.model.account.AccountsLoader;
import com.android.contacts.util.AccountFilterUtil;
import com.android.contacts.util.ImplicitIntentsUtil;
import com.android.contactsbind.HelpUtils;
@@ -65,7 +66,7 @@
* This fragment shows the preferences for "display options"
*/
public class DisplayOptionsPreferenceFragment extends PreferenceFragment
- implements Preference.OnPreferenceClickListener {
+ implements Preference.OnPreferenceClickListener, AccountsLoader.AccountsListener {
private static final int REQUEST_CODE_CUSTOM_CONTACTS_FILTER = 0;
@@ -84,6 +85,7 @@
private static final String KEY_SORT_ORDER = "sortOrder";
private static final int LOADER_PROFILE = 0;
+ private static final int LOADER_ACCOUNTS = 1;
/**
* Callbacks for hosts of the {@link DisplayOptionsPreferenceFragment}.
@@ -241,7 +243,8 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- getLoaderManager().restartLoader(LOADER_PROFILE, null, mProfileLoaderListener);
+ getLoaderManager().initLoader(LOADER_PROFILE, null, mProfileLoaderListener);
+ AccountsLoader.loadAccounts(this, LOADER_ACCOUNTS, AccountTypeManager.writableFilter());
}
@Override
@@ -275,15 +278,6 @@
getPreferenceScreen().removePreference(findPreference(KEY_DISPLAY_ORDER));
}
- // Remove the default account and custom view settings there aren't any writable accounts
- final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(getContext());
- final List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(
- /* contactWritableOnly */ true);
- if (accounts.isEmpty()) {
- getPreferenceScreen().removePreference(findPreference(KEY_DEFAULT_ACCOUNT));
- getPreferenceScreen().removePreference(findPreference(KEY_CUSTOM_CONTACTS_FILTER));
- }
-
final boolean isPhone = TelephonyManagerCompat.isVoiceCapable(
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE));
final boolean showBlockedNumbers = isPhone && ContactsUtils.FLAG_N_FEATURE
@@ -298,6 +292,15 @@
}
@Override
+ public void onAccountsLoaded(List<AccountInfo> accounts) {
+ // Hide accounts preferences if no writable accounts exist
+ if (accounts.isEmpty()) {
+ getPreferenceScreen().removePreference(findPreference(KEY_DEFAULT_ACCOUNT));
+ getPreferenceScreen().removePreference(findPreference(KEY_CUSTOM_CONTACTS_FILTER));
+ }
+ }
+
+ @Override
public Context getContext() {
return getActivity();
}
diff --git a/src/com/android/contacts/util/AccountFilterUtil.java b/src/com/android/contacts/util/AccountFilterUtil.java
index a7824f4..54c16e2 100644
--- a/src/com/android/contacts/util/AccountFilterUtil.java
+++ b/src/com/android/contacts/util/AccountFilterUtil.java
@@ -230,13 +230,16 @@
}
private static String getActionBarTitleForAccount(Context context, ContactListFilter filter) {
- final AccountDisplayInfoFactory factory =
- AccountDisplayInfoFactory.forAllAccounts(context);
- final AccountDisplayInfo account = factory.getAccountDisplayInfoFor(filter);
- if (account.hasGoogleAccountType()) {
+ final AccountInfo info = AccountTypeManager.getInstance(context)
+ .getAccountInfoForAccount(filter.toAccountWithDataSet());
+ if (info == null) {
+ return context.getString(R.string.contactsList);
+ }
+
+ if (info.hasGoogleAccountType()) {
return context.getString(R.string.title_from_google);
}
- return account.withFormattedName(context, R.string.title_from_other_accounts)
- .getNameLabel().toString();
+ return context.getString(R.string.title_from_other_accounts,
+ info.getNameLabel().toString());
}
}
diff --git a/src/com/android/contacts/util/SyncUtil.java b/src/com/android/contacts/util/SyncUtil.java
index ce10937..c5feb39 100644
--- a/src/com/android/contacts/util/SyncUtil.java
+++ b/src/com/android/contacts/util/SyncUtil.java
@@ -22,6 +22,7 @@
import android.net.NetworkInfo;
import android.provider.ContactsContract;
+import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.account.AccountWithDataSet;
import com.android.contacts.model.account.GoogleAccountType;
@@ -71,6 +72,10 @@
return ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0;
}
+ public static final boolean hasSyncableAccount(AccountTypeManager accountTypeManager) {
+ return !accountTypeManager.getWritableGoogleAccounts().isEmpty();
+ }
+
public static boolean isAlertVisible(Context context, Account account, int reason) {
if (reason == SYNC_SETTING_GLOBAL_SYNC_OFF) {
return (SharedPreferenceUtil.getNumOfDismissesForAutoSyncOff(context) == 0);
diff --git a/tests/src/com/android/contacts/activities/SimImportActivityTest.java b/tests/src/com/android/contacts/activities/SimImportActivityTest.java
index 64b86e2..46b5f40 100644
--- a/tests/src/com/android/contacts/activities/SimImportActivityTest.java
+++ b/tests/src/com/android/contacts/activities/SimImportActivityTest.java
@@ -68,6 +68,7 @@
import com.google.common.util.concurrent.SettableFuture;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -120,6 +121,12 @@
}
}
+ @AfterClass
+ public static void tearDownClass() {
+ AccountsTestHelper.removeAccountsWithPrefix(
+ InstrumentationRegistry.getTargetContext(), "SimImportActivity");
+ }
+
@Test
public void shouldDisplaySimContacts() {
mDao.addSim(someSimCard(),
@@ -214,7 +221,7 @@
AccountTypeManager.setInstanceForTest(null);
final AccountWithDataSet targetAccount = mAccountHelper.addTestAccount(
- mAccountHelper.generateAccountName("SimImportActivity_target_"));
+ mAccountHelper.generateAccountName("SimImportActivity0_targetAccount_"));
final MockContentProvider iccProvider = new MockContentProvider();
iccProvider.expect(MockContentProvider.Query.forAnyUri())
@@ -252,7 +259,7 @@
assertTrue(mDevice.wait(Until.hasObject(By.desc("Show more")), TIMEOUT));
mDevice.findObject(By.desc("Show more")).clickAndWait(Until.newWindow(), TIMEOUT);
- mDevice.findObject(By.textStartsWith("SimImportActivity_target_")).click();
+ mDevice.findObject(By.textContains("_targetAccount_")).click();
assertTrue(mDevice.wait(Until.hasObject(By.text("Skip Two")), TIMEOUT));
diff --git a/tests/src/com/android/contacts/tests/AccountsTestHelper.java b/tests/src/com/android/contacts/tests/AccountsTestHelper.java
index be0a985..2b2c16e 100644
--- a/tests/src/com/android/contacts/tests/AccountsTestHelper.java
+++ b/tests/src/com/android/contacts/tests/AccountsTestHelper.java
@@ -45,7 +45,6 @@
private final ContentResolver mResolver;
private List<Account> mAddedAccounts;
- private Account mTestAccount;
public AccountsTestHelper() {
// Use context instead of target context because the test package has the permissions needed
@@ -61,9 +60,9 @@
}
public void addTestAccount(AccountWithDataSet account) {
- mTestAccount = new Account(account.name, TEST_ACCOUNT_TYPE);
- assertTrue(mAccountManager.addAccountExplicitly(mTestAccount, null, null));
- mAddedAccounts.add(mTestAccount);
+ Account newAccount = new Account(account.name, account.type);
+ assertTrue(mAccountManager.addAccountExplicitly(newAccount, null, null));
+ mAddedAccounts.add(newAccount);
}
public AccountWithDataSet addTestAccount() {
@@ -104,11 +103,6 @@
return accounts.contains(new Account(name, TEST_ACCOUNT_TYPE));
}
- public void removeContactsForAccount() {
- removeContactsForAccount(
- new AccountWithDataSet(mTestAccount.name, mTestAccount.type, null));
- }
-
public void removeContactsForAccount(AccountWithDataSet account) {
mResolver.delete(RawContacts.CONTENT_URI,
RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?",
@@ -117,8 +111,6 @@
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
public void cleanup() {
- assertNotNull(mTestAccount);
-
// Note that we don't need to cleanup up the contact data associated with the account.
// CP2 will eventually do that automatically so as long as we're using unique account
// names we should be safe. Note that cleanup is not done synchronously when the account
@@ -129,11 +121,19 @@
mAccountManager.removeAccountExplicitly(account);
}
mAddedAccounts.clear();
-
- mTestAccount = null;
}
- private AccountWithDataSet convertTestAccount() {
- return new AccountWithDataSet(mTestAccount.name, mTestAccount.type, null);
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
+ public static void removeAccountsWithPrefix(Context context, String prefix) {
+ final AccountManager accountManager =
+ (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
+ final Account[] accounts = accountManager.getAccountsByType(TEST_ACCOUNT_TYPE);
+ for (Account account : accounts) {
+ if (account.name.startsWith(prefix)) {
+ accountManager.removeAccountExplicitly(account);
+ }
+ }
+
+
}
}