Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 1 | /** |
| 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 Huang | 30e2c24 | 2012-01-06 18:14:30 -0800 | [diff] [blame] | 17 | package com.android.mail.providers; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 18 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 19 | import com.android.mail.providers.Account; |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 20 | import com.android.mail.providers.protos.boot.AccountReceiver; |
Paul Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 21 | |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 22 | import android.content.Intent; |
| 23 | import android.content.Loader; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 24 | import android.content.ContentProvider; |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 25 | import android.content.ContentResolver; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 26 | import android.content.ContentValues; |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 27 | import android.content.Context; |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 28 | import android.content.CursorLoader; |
| 29 | import android.content.Loader.OnLoadCompleteListener; |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 30 | import android.content.SharedPreferences; |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 31 | import com.android.mail.utils.LogUtils; |
| 32 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 33 | import android.database.Cursor; |
| 34 | import android.database.MatrixCursor; |
| 35 | import android.net.Uri; |
| 36 | import android.provider.BaseColumns; |
| 37 | import android.text.TextUtils; |
| 38 | |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 39 | import com.google.common.collect.ImmutableSet; |
Marc Blank | 9ace18a | 2012-02-21 16:34:07 -0800 | [diff] [blame] | 40 | import com.google.common.collect.Maps; |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 41 | import com.google.common.collect.Sets; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 42 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 43 | import java.lang.IllegalStateException; |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 44 | import java.lang.StringBuilder; |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 45 | import java.util.Collections; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 46 | import java.util.Map; |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 47 | import java.util.Set; |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 48 | import java.util.regex.Pattern; |
| 49 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 50 | |
| 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 Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 61 | public abstract class AccountCacheProvider extends ContentProvider |
| 62 | implements OnLoadCompleteListener<Cursor>{ |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 63 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 64 | private static final String SHARED_PREFERENCES_NAME = "AccountCacheProvider"; |
| 65 | private static final String ACCOUNT_LIST_KEY = "accountList"; |
| 66 | |
Paul Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 67 | private final static String LOG_TAG = new LogUtils().getLogTag(); |
| 68 | |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 69 | private final Map<Uri, AccountCacheEntry> mAccountCache = Maps.newHashMap(); |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 70 | |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 71 | private final Map<Uri, CursorLoader> mCursorLoaderMap = Maps.newHashMap(); |
| 72 | |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 73 | private ContentResolver mResolver; |
Paul Westbrook | 77177b1 | 2012-02-07 15:23:42 -0800 | [diff] [blame] | 74 | private static String sAuthority; |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 75 | private static AccountCacheProvider sInstance; |
Paul Westbrook | 77177b1 | 2012-02-07 15:23:42 -0800 | [diff] [blame] | 76 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 77 | private SharedPreferences mSharedPrefs; |
| 78 | |
Paul Westbrook | 77177b1 | 2012-02-07 15:23:42 -0800 | [diff] [blame] | 79 | /** |
| 80 | * Allows the implmenting provider to specify the authority that should be used. |
| 81 | */ |
| 82 | protected abstract String getAuthority(); |
| 83 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 84 | public static Uri getAccountsUri() { |
Paul Westbrook | 77177b1 | 2012-02-07 15:23:42 -0800 | [diff] [blame] | 85 | return Uri.parse("content://" + sAuthority + "/"); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 86 | } |
| 87 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 88 | public static AccountCacheProvider getInstance() { |
| 89 | return sInstance; |
| 90 | } |
| 91 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 92 | @Override |
| 93 | public boolean onCreate() { |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 94 | sInstance = this; |
Paul Westbrook | 77177b1 | 2012-02-07 15:23:42 -0800 | [diff] [blame] | 95 | sAuthority = getAuthority(); |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 96 | mResolver = getContext().getContentResolver(); |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 97 | |
| 98 | final Intent intent = new Intent(AccountReceiver.ACTION_PROVIDER_CREATED); |
| 99 | getContext().sendBroadcast(intent); |
| 100 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 101 | // Load the previously saved account list |
| 102 | loadCachedAccountList(); |
| 103 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 104 | return true; |
| 105 | } |
| 106 | |
| 107 | @Override |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 108 | public void shutdown() { |
| 109 | sInstance = null; |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 110 | |
| 111 | for (CursorLoader loader : mCursorLoaderMap.values()) { |
| 112 | loader.stopLoading(); |
| 113 | } |
| 114 | mCursorLoaderMap.clear(); |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | @Override |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 118 | 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 Westbrook | 583a114 | 2012-01-05 15:49:26 -0800 | [diff] [blame] | 124 | final String[] resultProjection = UIProviderValidator.validateAccountProjection(projection); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 125 | final MatrixCursor cursor = new MatrixCursor(resultProjection); |
| 126 | |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 127 | // Make a copy of the account cache |
| 128 | |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 129 | final Set<AccountCacheEntry> accountList; |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 130 | synchronized (mAccountCache) { |
| 131 | accountList = ImmutableSet.copyOf(mAccountCache.values()); |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 132 | } |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 133 | for (AccountCacheEntry accountEntry : accountList) { |
| 134 | final Account account = accountEntry.mAccount; |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 135 | final MatrixCursor.RowBuilder builder = cursor.newRow(); |
| 136 | |
| 137 | for (String column : resultProjection) { |
| 138 | if (TextUtils.equals(column, BaseColumns._ID)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 139 | // TODO(pwestbro): remove this as it isn't used. |
| 140 | builder.add(Integer.valueOf(0)); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 141 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.NAME)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 142 | builder.add(account.name); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 143 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.PROVIDER_VERSION)) { |
| 144 | // TODO fix this |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 145 | builder.add(Integer.valueOf(account.providerVersion)); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 146 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 147 | builder.add(account.uri); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 148 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.CAPABILITIES)) { |
Vikram Aggarwal | 27e85f2 | 2012-03-05 16:29:17 -0800 | [diff] [blame] | 149 | builder.add(Integer.valueOf(account.capabilities)); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 150 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.FOLDER_LIST_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 151 | builder.add(account.folderListUri); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 152 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEARCH_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 153 | builder.add(account.searchUri); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 154 | } else if (TextUtils.equals(column, |
| 155 | UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 156 | builder.add(account.accountFromAddressesUri); |
Mindy Pereira | 33fe908 | 2012-01-09 16:24:30 -0800 | [diff] [blame] | 157 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.SAVE_DRAFT_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 158 | builder.add(account.saveDraftUri); |
Mindy Pereira | 7ed1c11 | 2012-01-18 10:59:25 -0800 | [diff] [blame] | 159 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEND_MAIL_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 160 | builder.add(account.sendMessageUri); |
Marc Blank | b31ab5a | 2012-02-01 12:28:29 -0800 | [diff] [blame] | 161 | } else if (TextUtils.equals(column, |
| 162 | UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 163 | builder.add(account.expungeMessageUri); |
Marc Blank | b31ab5a | 2012-02-01 12:28:29 -0800 | [diff] [blame] | 164 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.UNDO_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 165 | builder.add(account.undoUri); |
Paul Westbrook | 2861b6a | 2012-02-15 15:25:34 -0800 | [diff] [blame] | 166 | } else if (TextUtils.equals(column, |
| 167 | UIProvider.AccountColumns.SETTINGS_INTENT_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 168 | builder.add(account.settingsIntentUri); |
Paul Westbrook | 94e440d | 2012-02-24 11:03:47 -0800 | [diff] [blame] | 169 | } else if (TextUtils.equals(column, |
Paul Westbrook | 63eef79 | 2012-02-27 14:01:06 -0800 | [diff] [blame] | 170 | UIProvider.AccountColumns.SETTINGS_QUERY_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 171 | builder.add(account.settingsQueryUri); |
Paul Westbrook | 63eef79 | 2012-02-27 14:01:06 -0800 | [diff] [blame] | 172 | } else if (TextUtils.equals(column, |
Paul Westbrook | 94e440d | 2012-02-24 11:03:47 -0800 | [diff] [blame] | 173 | UIProvider.AccountColumns.HELP_INTENT_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 174 | builder.add(account.helpIntentUri); |
Marc Blank | 9ace18a | 2012-02-21 16:34:07 -0800 | [diff] [blame] | 175 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.SYNC_STATUS)) { |
Vikram Aggarwal | 27e85f2 | 2012-03-05 16:29:17 -0800 | [diff] [blame] | 176 | builder.add(Integer.valueOf(account.syncStatus)); |
Mindy Pereira | 23755e2 | 2012-02-27 13:58:04 -0800 | [diff] [blame] | 177 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.COMPOSE_URI)) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 178 | builder.add(account.composeIntentUri); |
Mindy Pereira | 898cd38 | 2012-03-06 08:42:47 -0800 | [diff] [blame] | 179 | } else if (TextUtils.equals(column, UIProvider.AccountColumns.MIME_TYPE)) { |
| 180 | builder.add(account.mimeType); |
Vikram Aggarwal | 27e85f2 | 2012-03-05 16:29:17 -0800 | [diff] [blame] | 181 | } else if (TextUtils.equals(column, |
| 182 | UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI)) { |
| 183 | builder.add(account.recentFolderListUri); |
Marc Blank | b31ab5a | 2012-02-01 12:28:29 -0800 | [diff] [blame] | 184 | } else { |
| 185 | throw new IllegalStateException("Column not found: " + column); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 186 | } |
| 187 | } |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 188 | } |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 189 | |
| 190 | cursor.setNotificationUri(mResolver, getAccountsUri()); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 191 | return cursor; |
| 192 | } |
| 193 | |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 194 | @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 Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 215 | /** |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 216 | * Asynchronously ads all of the accounts that are specified by the result set returned by |
Paul Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 217 | * {@link ContentProvider#query()} for the specified uri. The content provider handling the |
| 218 | * query needs to handle the {@link UIProvider.ACCOUNTS_PROJECTION} |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 219 | * Any changes to the underlying provider will automatically be reflected. |
Paul Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 220 | * @param resolver |
| 221 | * @param accountsQueryUri |
| 222 | */ |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 223 | public static void addAccountsForUriAsync(Uri accountsQueryUri) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 224 | getInstance().startAccountsLoader(accountsQueryUri); |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | private synchronized void startAccountsLoader(Uri accountsQueryUri) { |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 228 | 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 Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 239 | } |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 240 | mCursorLoaderMap.put(accountsQueryUri, accountsCursorLoader); |
Paul Westbrook | f464a54 | 2012-03-01 11:37:01 -0800 | [diff] [blame] | 241 | } |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 242 | |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 243 | public static void addAccount(Account account, Uri accountsQueryUri) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 244 | final AccountCacheProvider provider = getInstance(); |
| 245 | if (provider == null) { |
| 246 | throw new IllegalStateException("AccountCacheProvider not intialized"); |
| 247 | } |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 248 | provider.addAccountImpl(account, accountsQueryUri); |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 249 | } |
| 250 | |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 251 | private void addAccountImpl(Account account, Uri accountsQueryUri) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 252 | synchronized (mAccountCache) { |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 253 | if (account != null) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 254 | LogUtils.v(LOG_TAG, "adding account %s", account); |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 255 | mAccountCache.put(account.uri, new AccountCacheEntry(account, accountsQueryUri)); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 256 | } |
| 257 | } |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 258 | // Explicitly calling this out of the synchronized block in case any of the observers get |
| 259 | // called synchronously. |
| 260 | broadcastAccountChange(); |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 261 | |
| 262 | // Cache the updated account list |
| 263 | cacheAccountList(); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 264 | } |
| 265 | |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 266 | public static void removeAccount(Uri accountUri) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 267 | final AccountCacheProvider provider = getInstance(); |
| 268 | if (provider == null) { |
| 269 | throw new IllegalStateException("AccountCacheProvider not intialized"); |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 270 | } |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 271 | provider.removeAccounts(Collections.singleton(accountUri)); |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 272 | } |
| 273 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 274 | private void removeAccounts(Set<Uri> uris) { |
| 275 | synchronized (mAccountCache) { |
Paul Westbrook | 5301c17 | 2012-03-01 13:23:57 -0800 | [diff] [blame] | 276 | for (Uri accountUri : uris) { |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 277 | mAccountCache.remove(accountUri); |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 278 | } |
| 279 | } |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 280 | |
| 281 | // Explicitly calling this out of the synchronized block in case any of the observers get |
| 282 | // called synchronously. |
| 283 | broadcastAccountChange(); |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 284 | |
| 285 | // Cache the updated account list |
| 286 | cacheAccountList(); |
Paul Westbrook | bfe41fe | 2012-02-09 15:20:13 -0800 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | private static void broadcastAccountChange() { |
| 290 | final AccountCacheProvider provider = sInstance; |
| 291 | |
| 292 | if (provider != null) { |
| 293 | provider.mResolver.notifyChange(getAccountsUri(), null); |
| 294 | } |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 295 | } |
| 296 | |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 297 | 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 Westbrook | f5477e5 | 2012-03-07 14:04:49 -0800 | [diff] [blame] | 304 | try { |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 305 | final AccountCacheEntry accountEntry = new AccountCacheEntry(serializedAccount); |
| 306 | addAccount(accountEntry.mAccount, accountEntry.mAccountsQueryUri); |
Paul Westbrook | f5477e5 | 2012-03-07 14:04:49 -0800 | [diff] [blame] | 307 | } catch (IllegalArgumentException e) { |
| 308 | // Unable to create account object, skip to next |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 309 | LogUtils.e(LOG_TAG, e, |
Paul Westbrook | f5477e5 | 2012-03-07 14:04:49 -0800 | [diff] [blame] | 310 | "Unable to create account object from serialized string'%s'", |
| 311 | serializedAccount); |
| 312 | } |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 313 | } |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | private void cacheAccountList() { |
| 318 | final SharedPreferences preference = getPreferences(); |
| 319 | |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 320 | final Set<AccountCacheEntry> accountList; |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 321 | synchronized (mAccountCache) { |
| 322 | accountList = ImmutableSet.copyOf(mAccountCache.values()); |
| 323 | } |
| 324 | |
| 325 | final Set<String> serializedAccounts = Sets.newHashSet(); |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 326 | for (AccountCacheEntry accountEntry : accountList) { |
| 327 | serializedAccounts.add(accountEntry.serialize()); |
Paul Westbrook | db8f2f9 | 2012-03-02 11:43:38 -0800 | [diff] [blame] | 328 | } |
| 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 Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 343 | @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 Westbrook | e3e8429 | 2012-03-05 16:19:30 -0800 | [diff] [blame] | 350 | LogUtils.d(LOG_TAG, "Cursor with %d accounts returned", data.getCount()); |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 351 | final CursorLoader cursorLoader = (CursorLoader)loader; |
| 352 | final Uri accountsQueryUri = cursorLoader.getUri(); |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 353 | |
| 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 Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 366 | |
| 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 Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 372 | addAccount(account, accountsQueryUri); |
Paul Westbrook | bb303e7 | 2012-03-01 15:34:24 -0800 | [diff] [blame] | 373 | } |
| 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 Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 386 | |
| 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 Aggarwal | 6dde178 | 2012-03-12 17:16:48 -0700 | [diff] [blame^] | 418 | /** |
| 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 Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 426 | 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 Aggarwal | 6dde178 | 2012-03-12 17:16:48 -0700 | [diff] [blame^] | 433 | 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 Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 439 | mAccountsQueryUri = !TextUtils.isEmpty(cacheEntryMembers[1]) ? |
| 440 | Uri.parse(cacheEntryMembers[1]) : null; |
| 441 | } |
Paul Westbrook | 72a9d38 | 2012-03-09 04:54:06 -0800 | [diff] [blame] | 442 | } |
Paul Westbrook | aa3e7a5 | 2012-01-04 17:55:39 -0800 | [diff] [blame] | 443 | } |