Use loader for accounts in editor account chooser
The list of accounts on the dialog now updates when they are added or
removed.
Test: manually verify that list of accounts updates when adding or
removing accounts
Bug 33627801
Change-Id: I3c92c80f0a93a865050b115b5b3d931eea80b2af
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index 50e11f3..3531125 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -18,8 +18,10 @@
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.LoaderManager;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.Loader;
import android.os.Bundle;
import android.provider.ContactsContract.Intents;
import android.view.View;
@@ -33,10 +35,13 @@
import com.android.contacts.R;
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.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 com.google.common.util.concurrent.Futures;
import java.util.List;
@@ -48,7 +53,8 @@
* the new contact in. If the activity result doesn't contain intent data, then there is no
* account for this contact.
*/
-public class ContactEditorAccountsChangedActivity extends Activity {
+public class ContactEditorAccountsChangedActivity extends Activity
+ implements LoaderManager.LoaderCallbacks<List<AccountInfo>> {
private static final String TAG = ContactEditorAccountsChangedActivity.class.getSimpleName();
@@ -95,10 +101,30 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
mEditorUtils = ContactEditorUtils.create(this);
- final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this).
- getAccounts(true);
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == SUBACTIVITY_ADD_NEW_ACCOUNT) {
+ // If the user canceled the account setup process, then keep this activity visible to
+ // the user.
+ if (resultCode != RESULT_OK) {
+ return;
+ }
+ // Subactivity was successful, so pass the result back and finish the activity.
+ AccountWithDataSet account = mEditorUtils.getCreatedAccount(resultCode, data);
+ if (account == null) {
+ setResult(resultCode);
+ finish();
+ return;
+ }
+ saveAccountAndReturnResult(account);
+ }
+ }
+
+ private void updateDisplayedAccounts(List<AccountInfo> accounts) {
final int numAccounts = accounts.size();
if (numAccounts < 0) {
throw new IllegalStateException("Cannot have a negative number of accounts");
@@ -123,7 +149,7 @@
AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
accountListView.setAdapter(mAccountListAdapter);
accountListView.setOnItemClickListener(mAccountListItemClickListener);
- } else if (numAccounts == 1 && !accounts.get(0).isNullAccount()) {
+ } else if (numAccounts == 1 && !accounts.get(0).getAccount().isNullAccount()) {
// If the user has 1 writable account we will just show the user a message with 2
// possible action buttons.
view = View.inflate(this,
@@ -133,9 +159,9 @@
final Button leftButton = (Button) view.findViewById(R.id.left_button);
final Button rightButton = (Button) view.findViewById(R.id.right_button);
- final AccountWithDataSet account = accounts.get(0);
+ final AccountInfo accountInfo = accounts.get(0);
textView.setText(getString(R.string.contact_editor_prompt_one_account,
- account.name));
+ accountInfo.getNameLabel()));
// This button allows the user to add a new account to the device and return to
// this app afterwards.
@@ -148,7 +174,7 @@
rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- saveAccountAndReturnResult(account);
+ saveAccountAndReturnResult(accountInfo.getAccount());
}
});
} else {
@@ -183,6 +209,9 @@
rightButton.setOnClickListener(mAddAccountClickListener);
}
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
mDialog = new AlertDialog.Builder(this)
.setView(view)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@@ -195,25 +224,6 @@
mDialog.show();
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == SUBACTIVITY_ADD_NEW_ACCOUNT) {
- // If the user canceled the account setup process, then keep this activity visible to
- // the user.
- if (resultCode != RESULT_OK) {
- return;
- }
- // Subactivity was successful, so pass the result back and finish the activity.
- AccountWithDataSet account = mEditorUtils.getCreatedAccount(resultCode, data);
- if (account == null) {
- setResult(resultCode);
- finish();
- return;
- }
- saveAccountAndReturnResult(account);
- }
- }
-
private void saveAccountAndReturnResult(AccountWithDataSet account) {
// Save this as the default account
mEditorUtils.saveDefaultAccount(account);
@@ -224,4 +234,18 @@
setResult(RESULT_OK, intent);
finish();
}
+
+ @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) {
+ }
}
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 93ddd04..961fa63 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -429,7 +429,7 @@
reloadAccountTypes();
}
- /* This notification will arrive on the background thread */
+ /* This notification will arrive on the UI thread */
public void onAccountsUpdated(Account[] accounts) {
onAccountsUpdatedInternal();
}
diff --git a/src/com/android/contacts/model/account/AccountsLoader.java b/src/com/android/contacts/model/account/AccountsLoader.java
new file mode 100644
index 0000000..78f309b
--- /dev/null
+++ b/src/com/android/contacts/model/account/AccountsLoader.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 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.contacts.model.account;
+
+import android.content.Context;
+import android.content.IntentFilter;
+
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.concurrent.ListenableFutureLoader;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.List;
+
+/**
+ * Loads the accounts from AccountTypeManager
+ */
+public class AccountsLoader extends ListenableFutureLoader<List<AccountInfo>> {
+ private final AccountTypeManager mAccountTypeManager;
+ private final Predicate<AccountInfo> mFilter;
+
+ public AccountsLoader(Context context) {
+ this(context, Predicates.<AccountInfo>alwaysTrue());
+ }
+
+ public AccountsLoader(Context context, Predicate<AccountInfo> filter) {
+ super(context, new IntentFilter(AccountTypeManager.BROADCAST_ACCOUNTS_CHANGED));
+ mAccountTypeManager = AccountTypeManager.getInstance(context);
+ mFilter = filter;
+ }
+
+ @Override
+ protected ListenableFuture<List<AccountInfo>> loadData() {
+ return mAccountTypeManager.filterAccountsAsync(mFilter);
+ }
+
+ @Override
+ protected boolean isSameData(List<AccountInfo> previous, List<AccountInfo> next) {
+ return Objects.equal(AccountInfo.extractAccounts(previous),
+ AccountInfo.extractAccounts(next));
+ }
+
+}
diff --git a/src/com/android/contacts/util/concurrent/ListenableFutureLoader.java b/src/com/android/contacts/util/concurrent/ListenableFutureLoader.java
index 8c90d87..441ca68 100644
--- a/src/com/android/contacts/util/concurrent/ListenableFutureLoader.java
+++ b/src/com/android/contacts/util/concurrent/ListenableFutureLoader.java
@@ -94,8 +94,10 @@
Futures.addCallback(mFuture, new FutureCallback<D>() {
@Override
public void onSuccess(D result) {
+ if (mLoadedData == null || !isSameData(mLoadedData, result)) {
+ deliverResult(result);
+ }
mLoadedData = result;
- deliverResult(mLoadedData);
commitContentChanged();
}
@@ -130,6 +132,21 @@
protected abstract ListenableFuture<D> loadData();
+ /**
+ * Returns whether the newly loaded data is the same as the cached value
+ *
+ * <p>This allows subclasses to suppress delivering results when the data hasn't
+ * actually changed. By default it will always return false.
+ * </p>
+ */
+ protected boolean isSameData(D previousData, D newData) {
+ return false;
+ }
+
+ public final D getLoadedData() {
+ return mLoadedData;
+ }
+
public class ForceLoadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {