blob: 1464ab387a7e0647126b12cc9118568d1103fcac [file] [log] [blame]
Paul Westbrookaa3e7a52012-01-04 17:55:39 -08001/**
2 * Copyright (c) 2011, Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andy Huang30e2c242012-01-06 18:14:30 -080017package com.android.mail.providers;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080018
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080019import com.android.mail.providers.Account;
Paul Westbrookbb303e72012-03-01 15:34:24 -080020import com.android.mail.providers.protos.boot.AccountReceiver;
Paul Westbrookf464a542012-03-01 11:37:01 -080021
Paul Westbrookbb303e72012-03-01 15:34:24 -080022import android.content.Intent;
23import android.content.Loader;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080024import android.content.ContentProvider;
Paul Westbrookbfe41fe2012-02-09 15:20:13 -080025import android.content.ContentResolver;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080026import android.content.ContentValues;
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080027import android.content.Context;
Paul Westbrookbb303e72012-03-01 15:34:24 -080028import android.content.CursorLoader;
29import android.content.Loader.OnLoadCompleteListener;
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080030import android.content.SharedPreferences;
Paul Westbrookbb303e72012-03-01 15:34:24 -080031import com.android.mail.utils.LogUtils;
32
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080033import android.database.Cursor;
34import android.database.MatrixCursor;
35import android.net.Uri;
36import android.provider.BaseColumns;
37import android.text.TextUtils;
38
Paul Westbrookbb303e72012-03-01 15:34:24 -080039import com.google.common.collect.ImmutableSet;
Marc Blank9ace18a2012-02-21 16:34:07 -080040import com.google.common.collect.Maps;
Paul Westbrook5301c172012-03-01 13:23:57 -080041import com.google.common.collect.Sets;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080042
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080043import java.lang.IllegalStateException;
Paul Westbrook72a9d382012-03-09 04:54:06 -080044import java.lang.StringBuilder;
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080045import java.util.Collections;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080046import java.util.Map;
Paul Westbrook5301c172012-03-01 13:23:57 -080047import java.util.Set;
Paul Westbrook72a9d382012-03-09 04:54:06 -080048import java.util.regex.Pattern;
49
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080050
51
52/**
53 * The Account Cache provider allows email providers to register "accounts" and the UI has a single
54 * place to query for the list of accounts.
55 *
56 * During development this will allow new account types to be added, and allow them to be shown in
57 * the application. For example, the mock accounts can be enabled/disabled.
58 * In the future, once other processes can add new accounts, this could allow other "mail"
59 * applications have their content appear within the application
60 */
Paul Westbrookbb303e72012-03-01 15:34:24 -080061public abstract class AccountCacheProvider extends ContentProvider
62 implements OnLoadCompleteListener<Cursor>{
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080063
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080064 private static final String SHARED_PREFERENCES_NAME = "AccountCacheProvider";
65 private static final String ACCOUNT_LIST_KEY = "accountList";
66
Paul Westbrookf464a542012-03-01 11:37:01 -080067 private final static String LOG_TAG = new LogUtils().getLogTag();
68
Paul Westbrook72a9d382012-03-09 04:54:06 -080069 private final Map<Uri, AccountCacheEntry> mAccountCache = Maps.newHashMap();
Paul Westbrook5301c172012-03-01 13:23:57 -080070
Paul Westbrookbb303e72012-03-01 15:34:24 -080071 private final Map<Uri, CursorLoader> mCursorLoaderMap = Maps.newHashMap();
72
Paul Westbrookbfe41fe2012-02-09 15:20:13 -080073 private ContentResolver mResolver;
Paul Westbrook77177b12012-02-07 15:23:42 -080074 private static String sAuthority;
Paul Westbrookbfe41fe2012-02-09 15:20:13 -080075 private static AccountCacheProvider sInstance;
Paul Westbrook77177b12012-02-07 15:23:42 -080076
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080077 private SharedPreferences mSharedPrefs;
78
Paul Westbrook77177b12012-02-07 15:23:42 -080079 /**
80 * Allows the implmenting provider to specify the authority that should be used.
81 */
82 protected abstract String getAuthority();
83
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080084 public static Uri getAccountsUri() {
Paul Westbrook77177b12012-02-07 15:23:42 -080085 return Uri.parse("content://" + sAuthority + "/");
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080086 }
87
Paul Westbrookdb8f2f92012-03-02 11:43:38 -080088 public static AccountCacheProvider getInstance() {
89 return sInstance;
90 }
91
Paul Westbrookaa3e7a52012-01-04 17:55:39 -080092 @Override
93 public boolean onCreate() {
Paul Westbrookbfe41fe2012-02-09 15:20:13 -080094 sInstance = this;
Paul Westbrook77177b12012-02-07 15:23:42 -080095 sAuthority = getAuthority();
Paul Westbrookbfe41fe2012-02-09 15:20:13 -080096 mResolver = getContext().getContentResolver();
Paul Westbrookbb303e72012-03-01 15:34:24 -080097
98 final Intent intent = new Intent(AccountReceiver.ACTION_PROVIDER_CREATED);
99 getContext().sendBroadcast(intent);
100
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800101 // Load the previously saved account list
102 loadCachedAccountList();
103
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800104 return true;
105 }
106
107 @Override
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800108 public void shutdown() {
109 sInstance = null;
Paul Westbrookbb303e72012-03-01 15:34:24 -0800110
111 for (CursorLoader loader : mCursorLoaderMap.values()) {
112 loader.stopLoading();
113 }
114 mCursorLoaderMap.clear();
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800115 }
116
117 @Override
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800118 public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
119 String sortOrder) {
120 // This content provider currently only supports one query (to return the list of accounts).
121 // No reason to check the uri. Currently only checking the projections
122
123 // Validates and returns the projection that should be used.
Paul Westbrook583a1142012-01-05 15:49:26 -0800124 final String[] resultProjection = UIProviderValidator.validateAccountProjection(projection);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800125 final MatrixCursor cursor = new MatrixCursor(resultProjection);
126
Paul Westbrookbb303e72012-03-01 15:34:24 -0800127 // Make a copy of the account cache
128
Paul Westbrook72a9d382012-03-09 04:54:06 -0800129 final Set<AccountCacheEntry> accountList;
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800130 synchronized (mAccountCache) {
131 accountList = ImmutableSet.copyOf(mAccountCache.values());
Paul Westbrookbb303e72012-03-01 15:34:24 -0800132 }
Paul Westbrook72a9d382012-03-09 04:54:06 -0800133 for (AccountCacheEntry accountEntry : accountList) {
134 final Account account = accountEntry.mAccount;
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800135 final MatrixCursor.RowBuilder builder = cursor.newRow();
136
137 for (String column : resultProjection) {
138 if (TextUtils.equals(column, BaseColumns._ID)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800139 // TODO(pwestbro): remove this as it isn't used.
140 builder.add(Integer.valueOf(0));
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800141 } else if (TextUtils.equals(column, UIProvider.AccountColumns.NAME)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800142 builder.add(account.name);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800143 } else if (TextUtils.equals(column, UIProvider.AccountColumns.PROVIDER_VERSION)) {
144 // TODO fix this
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800145 builder.add(Integer.valueOf(account.providerVersion));
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800146 } else if (TextUtils.equals(column, UIProvider.AccountColumns.URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800147 builder.add(account.uri);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800148 } else if (TextUtils.equals(column, UIProvider.AccountColumns.CAPABILITIES)) {
Vikram Aggarwal27e85f22012-03-05 16:29:17 -0800149 builder.add(Integer.valueOf(account.capabilities));
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800150 } else if (TextUtils.equals(column, UIProvider.AccountColumns.FOLDER_LIST_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800151 builder.add(account.folderListUri);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800152 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEARCH_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800153 builder.add(account.searchUri);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800154 } else if (TextUtils.equals(column,
155 UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800156 builder.add(account.accountFromAddressesUri);
Mindy Pereira33fe9082012-01-09 16:24:30 -0800157 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SAVE_DRAFT_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800158 builder.add(account.saveDraftUri);
Mindy Pereira7ed1c112012-01-18 10:59:25 -0800159 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEND_MAIL_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800160 builder.add(account.sendMessageUri);
Marc Blankb31ab5a2012-02-01 12:28:29 -0800161 } else if (TextUtils.equals(column,
162 UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800163 builder.add(account.expungeMessageUri);
Marc Blankb31ab5a2012-02-01 12:28:29 -0800164 } else if (TextUtils.equals(column, UIProvider.AccountColumns.UNDO_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800165 builder.add(account.undoUri);
Paul Westbrook2861b6a2012-02-15 15:25:34 -0800166 } else if (TextUtils.equals(column,
167 UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800168 builder.add(account.settingsIntentUri);
Paul Westbrook94e440d2012-02-24 11:03:47 -0800169 } else if (TextUtils.equals(column,
Paul Westbrook63eef792012-02-27 14:01:06 -0800170 UIProvider.AccountColumns.SETTINGS_QUERY_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800171 builder.add(account.settingsQueryUri);
Paul Westbrook63eef792012-02-27 14:01:06 -0800172 } else if (TextUtils.equals(column,
Paul Westbrook94e440d2012-02-24 11:03:47 -0800173 UIProvider.AccountColumns.HELP_INTENT_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800174 builder.add(account.helpIntentUri);
Marc Blank9ace18a2012-02-21 16:34:07 -0800175 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SYNC_STATUS)) {
Vikram Aggarwal27e85f22012-03-05 16:29:17 -0800176 builder.add(Integer.valueOf(account.syncStatus));
Mindy Pereira23755e22012-02-27 13:58:04 -0800177 } else if (TextUtils.equals(column, UIProvider.AccountColumns.COMPOSE_URI)) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800178 builder.add(account.composeIntentUri);
Mindy Pereira898cd382012-03-06 08:42:47 -0800179 } else if (TextUtils.equals(column, UIProvider.AccountColumns.MIME_TYPE)) {
180 builder.add(account.mimeType);
Vikram Aggarwal27e85f22012-03-05 16:29:17 -0800181 } else if (TextUtils.equals(column,
182 UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI)) {
183 builder.add(account.recentFolderListUri);
Marc Blankb31ab5a2012-02-01 12:28:29 -0800184 } else {
185 throw new IllegalStateException("Column not found: " + column);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800186 }
187 }
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800188 }
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800189
190 cursor.setNotificationUri(mResolver, getAccountsUri());
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800191 return cursor;
192 }
193
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800194 @Override
195 public Uri insert(Uri url, ContentValues values) {
196 return url;
197 }
198
199 @Override
200 public int update(Uri url, ContentValues values, String selection,
201 String[] selectionArgs) {
202 return 0;
203 }
204
205 @Override
206 public int delete(Uri url, String selection, String[] selectionArgs) {
207 return 0;
208 }
209
210 @Override
211 public String getType(Uri uri) {
212 return null;
213 }
214
Paul Westbrookf464a542012-03-01 11:37:01 -0800215 /**
Paul Westbrookbb303e72012-03-01 15:34:24 -0800216 * Asynchronously ads all of the accounts that are specified by the result set returned by
Paul Westbrookf464a542012-03-01 11:37:01 -0800217 * {@link ContentProvider#query()} for the specified uri. The content provider handling the
218 * query needs to handle the {@link UIProvider.ACCOUNTS_PROJECTION}
Paul Westbrookbb303e72012-03-01 15:34:24 -0800219 * Any changes to the underlying provider will automatically be reflected.
Paul Westbrookf464a542012-03-01 11:37:01 -0800220 * @param resolver
221 * @param accountsQueryUri
222 */
Paul Westbrookbb303e72012-03-01 15:34:24 -0800223 public static void addAccountsForUriAsync(Uri accountsQueryUri) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800224 getInstance().startAccountsLoader(accountsQueryUri);
Paul Westbrookbb303e72012-03-01 15:34:24 -0800225 }
226
227 private synchronized void startAccountsLoader(Uri accountsQueryUri) {
Paul Westbrookbb303e72012-03-01 15:34:24 -0800228 final CursorLoader accountsCursorLoader = new CursorLoader(getContext(), accountsQueryUri,
229 UIProvider.ACCOUNTS_PROJECTION, null, null, null);
230
231 // Listen for the results
232 accountsCursorLoader.registerListener(accountsQueryUri.hashCode(), this);
233 accountsCursorLoader.startLoading();
234
235 // If there is a previous loader for the given uri, stop it
236 final CursorLoader oldLoader = mCursorLoaderMap.get(accountsQueryUri);
237 if (oldLoader != null) {
238 oldLoader.stopLoading();
Paul Westbrookf464a542012-03-01 11:37:01 -0800239 }
Paul Westbrookbb303e72012-03-01 15:34:24 -0800240 mCursorLoaderMap.put(accountsQueryUri, accountsCursorLoader);
Paul Westbrookf464a542012-03-01 11:37:01 -0800241 }
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800242
Paul Westbrook72a9d382012-03-09 04:54:06 -0800243 public static void addAccount(Account account, Uri accountsQueryUri) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800244 final AccountCacheProvider provider = getInstance();
245 if (provider == null) {
246 throw new IllegalStateException("AccountCacheProvider not intialized");
247 }
Paul Westbrook72a9d382012-03-09 04:54:06 -0800248 provider.addAccountImpl(account, accountsQueryUri);
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800249 }
250
Paul Westbrook72a9d382012-03-09 04:54:06 -0800251 private void addAccountImpl(Account account, Uri accountsQueryUri) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800252 synchronized (mAccountCache) {
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800253 if (account != null) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800254 LogUtils.v(LOG_TAG, "adding account %s", account);
Paul Westbrook72a9d382012-03-09 04:54:06 -0800255 mAccountCache.put(account.uri, new AccountCacheEntry(account, accountsQueryUri));
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800256 }
257 }
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800258 // Explicitly calling this out of the synchronized block in case any of the observers get
259 // called synchronously.
260 broadcastAccountChange();
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800261
262 // Cache the updated account list
263 cacheAccountList();
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800264 }
265
Paul Westbrook5301c172012-03-01 13:23:57 -0800266 public static void removeAccount(Uri accountUri) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800267 final AccountCacheProvider provider = getInstance();
268 if (provider == null) {
269 throw new IllegalStateException("AccountCacheProvider not intialized");
Paul Westbrook5301c172012-03-01 13:23:57 -0800270 }
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800271 provider.removeAccounts(Collections.singleton(accountUri));
Paul Westbrook5301c172012-03-01 13:23:57 -0800272 }
273
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800274 private void removeAccounts(Set<Uri> uris) {
275 synchronized (mAccountCache) {
Paul Westbrook5301c172012-03-01 13:23:57 -0800276 for (Uri accountUri : uris) {
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800277 mAccountCache.remove(accountUri);
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800278 }
279 }
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800280
281 // Explicitly calling this out of the synchronized block in case any of the observers get
282 // called synchronously.
283 broadcastAccountChange();
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800284
285 // Cache the updated account list
286 cacheAccountList();
Paul Westbrookbfe41fe2012-02-09 15:20:13 -0800287 }
288
289 private static void broadcastAccountChange() {
290 final AccountCacheProvider provider = sInstance;
291
292 if (provider != null) {
293 provider.mResolver.notifyChange(getAccountsUri(), null);
294 }
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800295 }
296
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800297 private void loadCachedAccountList() {
298 final SharedPreferences preference = getPreferences();
299
300 final Set<String> accountsStringSet = preference.getStringSet(ACCOUNT_LIST_KEY, null);
301
302 if (accountsStringSet != null) {
303 for (String serializedAccount : accountsStringSet) {
Paul Westbrookf5477e52012-03-07 14:04:49 -0800304 try {
Paul Westbrook72a9d382012-03-09 04:54:06 -0800305 final AccountCacheEntry accountEntry = new AccountCacheEntry(serializedAccount);
306 addAccount(accountEntry.mAccount, accountEntry.mAccountsQueryUri);
Paul Westbrookf5477e52012-03-07 14:04:49 -0800307 } catch (IllegalArgumentException e) {
308 // Unable to create account object, skip to next
Paul Westbrook72a9d382012-03-09 04:54:06 -0800309 LogUtils.e(LOG_TAG, e,
Paul Westbrookf5477e52012-03-07 14:04:49 -0800310 "Unable to create account object from serialized string'%s'",
311 serializedAccount);
312 }
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800313 }
314 }
315 }
316
317 private void cacheAccountList() {
318 final SharedPreferences preference = getPreferences();
319
Paul Westbrook72a9d382012-03-09 04:54:06 -0800320 final Set<AccountCacheEntry> accountList;
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800321 synchronized (mAccountCache) {
322 accountList = ImmutableSet.copyOf(mAccountCache.values());
323 }
324
325 final Set<String> serializedAccounts = Sets.newHashSet();
Paul Westbrook72a9d382012-03-09 04:54:06 -0800326 for (AccountCacheEntry accountEntry : accountList) {
327 serializedAccounts.add(accountEntry.serialize());
Paul Westbrookdb8f2f92012-03-02 11:43:38 -0800328 }
329
330 final SharedPreferences.Editor editor = getPreferences().edit();
331 editor.putStringSet(ACCOUNT_LIST_KEY, serializedAccounts);
332 editor.apply();
333 }
334
335 private SharedPreferences getPreferences() {
336 if (mSharedPrefs == null) {
337 mSharedPrefs = getContext().getSharedPreferences(
338 SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
339 }
340 return mSharedPrefs;
341 }
342
Paul Westbrookbb303e72012-03-01 15:34:24 -0800343 @Override
344 public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
345 if (data == null) {
346 LogUtils.d(LOG_TAG, "null account cursor returned");
347 return;
348 }
349
Paul Westbrooke3e84292012-03-05 16:19:30 -0800350 LogUtils.d(LOG_TAG, "Cursor with %d accounts returned", data.getCount());
Paul Westbrookbb303e72012-03-01 15:34:24 -0800351 final CursorLoader cursorLoader = (CursorLoader)loader;
352 final Uri accountsQueryUri = cursorLoader.getUri();
Paul Westbrook72a9d382012-03-09 04:54:06 -0800353
354 final Set<AccountCacheEntry> accountList;
355 synchronized (mAccountCache) {
356 accountList = ImmutableSet.copyOf(mAccountCache.values());
357 }
358
359 // Build a set of the account uris that had been associated with that query
360 final Set<Uri> previousQueryUriMap = Sets.newHashSet();
361 for (AccountCacheEntry entry : accountList) {
362 if (accountsQueryUri.equals(entry.mAccountsQueryUri)) {
363 previousQueryUriMap.add(entry.mAccount.uri);
364 }
365 }
Paul Westbrookbb303e72012-03-01 15:34:24 -0800366
367 final Set<Uri> newQueryUriMap = Sets.newHashSet();
368 while (data.moveToNext()) {
369 final Account account = new Account(data);
370 final Uri accountUri = account.uri;
371 newQueryUriMap.add(accountUri);
Paul Westbrook72a9d382012-03-09 04:54:06 -0800372 addAccount(account, accountsQueryUri);
Paul Westbrookbb303e72012-03-01 15:34:24 -0800373 }
374
375 if (previousQueryUriMap != null) {
376 // Remove all of the accounts that are in the new result set
377 previousQueryUriMap.removeAll(newQueryUriMap);
378
379 // For all of the entries that had been in the previous result set, and are not
380 // in the new result set, remove them from the cache
381 if (previousQueryUriMap.size() > 0) {
382 removeAccounts(previousQueryUriMap);
383 }
384 }
385 }
Paul Westbrook72a9d382012-03-09 04:54:06 -0800386
387 /**
388 * Object that allows the Account Cache provider to associate the account with the content
389 * provider uri that originated that account.
390 */
391 private static class AccountCacheEntry {
392 final Account mAccount;
393 final Uri mAccountsQueryUri;
394
395 private static final String ACCOUNT_ENTRY_COMPONENT_SEPARATOR = "^**^";
396 private static final Pattern ACCOUNT_ENTRY_COMPONENT_SEPARATOR_PATTERN =
397 Pattern.compile("\\^\\*\\*\\^");
398
399 private static final int NUMBER_MEMBERS = 2;
400
401 public AccountCacheEntry(Account account, Uri accountQueryUri) {
402 mAccount = account;
403 mAccountsQueryUri = accountQueryUri;
404 }
405
406 /**
407 * Return a serialized String for this AccountCacheEntry.
408 */
409 public synchronized String serialize() {
410 StringBuilder out = new StringBuilder();
411 out.append(mAccount.serialize()).append(ACCOUNT_ENTRY_COMPONENT_SEPARATOR);
412 final String accountQueryUri =
413 mAccountsQueryUri != null ? mAccountsQueryUri.toString() : "";
414 out.append(accountQueryUri);
415 return out.toString();
416 }
417
Vikram Aggarwal6dde1782012-03-12 17:16:48 -0700418 /**
419 * Create an account cache object from a serialized string previously stored away.
420 * If the serializedString does not parse as a valid account, we throw an
421 * {@link IllegalArgumentException}. The caller is responsible for checking this and
422 * ignoring the newly created object if the exception is thrown.
423 * @param serializedString
424 */
425 public AccountCacheEntry(String serializedString) throws IllegalArgumentException {
Paul Westbrook72a9d382012-03-09 04:54:06 -0800426 String[] cacheEntryMembers = TextUtils.split(serializedString,
427 ACCOUNT_ENTRY_COMPONENT_SEPARATOR_PATTERN);
428 if (cacheEntryMembers.length != NUMBER_MEMBERS) {
429 throw new IllegalArgumentException("AccountCacheEntry de-serializing failed. "
430 + "Wrong number of members detected. "
431 + cacheEntryMembers.length + " detected");
432 }
Vikram Aggarwal6dde1782012-03-12 17:16:48 -0700433 mAccount = Account.newinstance(cacheEntryMembers[0]);
434 if (mAccount == null) {
435 throw new IllegalArgumentException("AccountCacheEntry de-serializing failed. "
436 + "Account object couldn not be created by the serialized string"
437 + serializedString);
438 }
Paul Westbrook72a9d382012-03-09 04:54:06 -0800439 mAccountsQueryUri = !TextUtils.isEmpty(cacheEntryMembers[1]) ?
440 Uri.parse(cacheEntryMembers[1]) : null;
441 }
Paul Westbrook72a9d382012-03-09 04:54:06 -0800442 }
Paul Westbrookaa3e7a52012-01-04 17:55:39 -0800443}