Better handling of deletions of accounts
Bug: 6141367
Change-Id: If212a91e772a48717ac77326cd6ea1f1ad5ebfa5
diff --git a/src/com/android/mail/providers/AccountCacheProvider.java b/src/com/android/mail/providers/AccountCacheProvider.java
index 67c80d0..62cab37 100644
--- a/src/com/android/mail/providers/AccountCacheProvider.java
+++ b/src/com/android/mail/providers/AccountCacheProvider.java
@@ -41,10 +41,12 @@
import com.google.common.collect.Sets;
import java.lang.IllegalStateException;
-import java.lang.Override;
+import java.lang.StringBuilder;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
+
/**
@@ -64,10 +66,7 @@
private final static String LOG_TAG = new LogUtils().getLogTag();
- private final Map<Uri, Account> mAccountCache = Maps.newHashMap();
-
- // Map from content provider query uri to the set of account uri that resulted from that query
- private final static Map<Uri, Set<Uri>> QUERY_URI_ACCOUNT_URIS_MAP = Maps.newHashMap();
+ private final Map<Uri, AccountCacheEntry> mAccountCache = Maps.newHashMap();
private final Map<Uri, CursorLoader> mCursorLoaderMap = Maps.newHashMap();
@@ -127,11 +126,12 @@
// Make a copy of the account cache
- final Set<Account> accountList;
+ final Set<AccountCacheEntry> accountList;
synchronized (mAccountCache) {
accountList = ImmutableSet.copyOf(mAccountCache.values());
}
- for (Account account : accountList) {
+ for (AccountCacheEntry accountEntry : accountList) {
+ final Account account = accountEntry.mAccount;
final MatrixCursor.RowBuilder builder = cursor.newRow();
for (String column : resultProjection) {
@@ -240,19 +240,19 @@
mCursorLoaderMap.put(accountsQueryUri, accountsCursorLoader);
}
- public static void addAccount(Account account) {
+ public static void addAccount(Account account, Uri accountsQueryUri) {
final AccountCacheProvider provider = getInstance();
if (provider == null) {
throw new IllegalStateException("AccountCacheProvider not intialized");
}
- provider.addAccountImpl(account);
+ provider.addAccountImpl(account, accountsQueryUri);
}
- private void addAccountImpl(Account account) {
+ private void addAccountImpl(Account account, Uri accountsQueryUri) {
synchronized (mAccountCache) {
if (account != null) {
LogUtils.v(LOG_TAG, "adding account %s", account);
- mAccountCache.put(account.uri, account);
+ mAccountCache.put(account.uri, new AccountCacheEntry(account, accountsQueryUri));
}
}
// Explicitly calling this out of the synchronized block in case any of the observers get
@@ -302,11 +302,11 @@
if (accountsStringSet != null) {
for (String serializedAccount : accountsStringSet) {
try {
- final Account account = new Account(serializedAccount);
- addAccount(account);
+ final AccountCacheEntry accountEntry = new AccountCacheEntry(serializedAccount);
+ addAccount(accountEntry.mAccount, accountEntry.mAccountsQueryUri);
} catch (IllegalArgumentException e) {
// Unable to create account object, skip to next
- LogUtils.e(LOG_TAG,
+ LogUtils.e(LOG_TAG, e,
"Unable to create account object from serialized string'%s'",
serializedAccount);
}
@@ -317,14 +317,14 @@
private void cacheAccountList() {
final SharedPreferences preference = getPreferences();
- final Set<Account> accountList;
+ final Set<AccountCacheEntry> accountList;
synchronized (mAccountCache) {
accountList = ImmutableSet.copyOf(mAccountCache.values());
}
final Set<String> serializedAccounts = Sets.newHashSet();
- for (Account account : accountList) {
- serializedAccounts.add(account.serialize());
+ for (AccountCacheEntry accountEntry : accountList) {
+ serializedAccounts.add(accountEntry.serialize());
}
final SharedPreferences.Editor editor = getPreferences().edit();
@@ -340,8 +340,6 @@
return mSharedPrefs;
}
-
-
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
if (data == null) {
@@ -352,23 +350,26 @@
LogUtils.d(LOG_TAG, "Cursor with %d accounts returned", data.getCount());
final CursorLoader cursorLoader = (CursorLoader)loader;
final Uri accountsQueryUri = cursorLoader.getUri();
- // TODO(pwestbro):
- // 1) Keep a cache of Cursors which would allow changes to be observered.
- final Set<Uri> previousQueryUriMap = QUERY_URI_ACCOUNT_URIS_MAP.get(accountsQueryUri);
+
+ final Set<AccountCacheEntry> accountList;
+ synchronized (mAccountCache) {
+ accountList = ImmutableSet.copyOf(mAccountCache.values());
+ }
+
+ // Build a set of the account uris that had been associated with that query
+ final Set<Uri> previousQueryUriMap = Sets.newHashSet();
+ for (AccountCacheEntry entry : accountList) {
+ if (accountsQueryUri.equals(entry.mAccountsQueryUri)) {
+ previousQueryUriMap.add(entry.mAccount.uri);
+ }
+ }
final Set<Uri> newQueryUriMap = Sets.newHashSet();
while (data.moveToNext()) {
final Account account = new Account(data);
final Uri accountUri = account.uri;
newQueryUriMap.add(accountUri);
- addAccount(account);
- }
-
- // Save the new set, or remove the previous entry if it is empty
- if (newQueryUriMap.size() > 0) {
- QUERY_URI_ACCOUNT_URIS_MAP.put(accountsQueryUri, newQueryUriMap);
- } else {
- QUERY_URI_ACCOUNT_URIS_MAP.remove(accountsQueryUri);
+ addAccount(account, accountsQueryUri);
}
if (previousQueryUriMap != null) {
@@ -382,4 +383,50 @@
}
}
}
+
+ /**
+ * Object that allows the Account Cache provider to associate the account with the content
+ * provider uri that originated that account.
+ */
+ private static class AccountCacheEntry {
+ final Account mAccount;
+ final Uri mAccountsQueryUri;
+
+ private static final String ACCOUNT_ENTRY_COMPONENT_SEPARATOR = "^**^";
+ private static final Pattern ACCOUNT_ENTRY_COMPONENT_SEPARATOR_PATTERN =
+ Pattern.compile("\\^\\*\\*\\^");
+
+ private static final int NUMBER_MEMBERS = 2;
+
+ public AccountCacheEntry(Account account, Uri accountQueryUri) {
+ mAccount = account;
+ mAccountsQueryUri = accountQueryUri;
+ }
+
+ /**
+ * Return a serialized String for this AccountCacheEntry.
+ */
+ public synchronized String serialize() {
+ StringBuilder out = new StringBuilder();
+ out.append(mAccount.serialize()).append(ACCOUNT_ENTRY_COMPONENT_SEPARATOR);
+ final String accountQueryUri =
+ mAccountsQueryUri != null ? mAccountsQueryUri.toString() : "";
+ out.append(accountQueryUri);
+ return out.toString();
+ }
+
+ public AccountCacheEntry(String serializedString) {
+ String[] cacheEntryMembers = TextUtils.split(serializedString,
+ ACCOUNT_ENTRY_COMPONENT_SEPARATOR_PATTERN);
+ if (cacheEntryMembers.length != NUMBER_MEMBERS) {
+ throw new IllegalArgumentException("AccountCacheEntry de-serializing failed. "
+ + "Wrong number of members detected. "
+ + cacheEntryMembers.length + " detected");
+ }
+ mAccount = new Account(cacheEntryMembers[0]);
+ mAccountsQueryUri = !TextUtils.isEmpty(cacheEntryMembers[1]) ?
+ Uri.parse(cacheEntryMembers[1]) : null;
+ }
+
+ }
}
\ No newline at end of file
diff --git a/src/com/android/mail/providers/protos/mock/MockUiProvider.java b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
index d02f472..6a9603d 100644
--- a/src/com/android/mail/providers/protos/mock/MockUiProvider.java
+++ b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
@@ -54,6 +54,9 @@
static final String BASE_URI_STRING = "content://" + AUTHORITY;
+ private static final Uri MOCK_ACCOUNTS_URI = Uri.parse("content://" + AUTHORITY + "/accounts");
+
+
public static final int NUM_MOCK_ACCOUNTS = 2;
// A map of query result for uris
@@ -393,7 +396,7 @@
dest.writeParcelable((Uri) accountInfo.get(AccountColumns.RECENT_FOLDER_LIST_URI), 0);
dest.setDataPosition(0);
final Account account = new Account(dest);
- AccountCacheProvider.addAccount(account);
+ AccountCacheProvider.addAccount(account, MOCK_ACCOUNTS_URI);
}
}