1aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook/** 2aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * Copyright (c) 2011, Google Inc. 3aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * 4aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * Licensed under the Apache License, Version 2.0 (the "License"); 5aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * you may not use this file except in compliance with the License. 6aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * You may obtain a copy of the License at 7aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * 8aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * http://www.apache.org/licenses/LICENSE-2.0 9aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * 10aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * Unless required by applicable law or agreed to in writing, software 11aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * distributed under the License is distributed on an "AS IS" BASIS, 12aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * See the License for the specific language governing permissions and 14aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * limitations under the License. 15aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook */ 16aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 1730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail.providers; 18aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 19a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blankimport android.app.Activity; 20aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport android.content.ContentProvider; 21c95485c4c63e3667d9a9d3bde0f8e57da6e02f17Marc Blankimport android.content.ContentProviderClient; 22bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrookimport android.content.ContentResolver; 23aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport android.content.ContentValues; 24db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrookimport android.content.Context; 25bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrookimport android.content.CursorLoader; 26b287833c2645e2502792821e54d7226cfad59955Marc Blankimport android.content.Intent; 27b287833c2645e2502792821e54d7226cfad59955Marc Blankimport android.content.Loader; 28bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrookimport android.content.Loader.OnLoadCompleteListener; 29db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrookimport android.content.SharedPreferences; 3045402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrookimport android.content.res.Resources; 31aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport android.database.Cursor; 32aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport android.database.MatrixCursor; 33aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport android.net.Uri; 342388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrookimport android.os.Bundle; 35aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 3645402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrookimport com.android.mail.R; 37b287833c2645e2502792821e54d7226cfad59955Marc Blankimport com.android.mail.providers.UIProvider.AccountCursorExtraKeys; 38b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 39b287833c2645e2502792821e54d7226cfad59955Marc Blankimport com.android.mail.utils.LogUtils; 40b287833c2645e2502792821e54d7226cfad59955Marc Blankimport com.android.mail.utils.MatrixCursorWithExtra; 41bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huangimport com.google.common.collect.ImmutableList; 429ace18a77e1c61700291f6116bb8162c8fce1fadMarc Blankimport com.google.common.collect.Maps; 435301c17703e01eccd31f91027373093791929997Paul Westbrookimport com.google.common.collect.Sets; 44aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 45bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huangimport org.json.JSONArray; 46bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huangimport org.json.JSONException; 47bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huangimport org.json.JSONObject; 48bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 49bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huangimport java.util.LinkedHashMap; 50c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrookimport java.util.List; 51aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrookimport java.util.Map; 525301c17703e01eccd31f91027373093791929997Paul Westbrookimport java.util.Set; 5372a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 54aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 55aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook/** 566006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal * The Mail App provider allows email providers to register "accounts" and the UI has a single 57aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * place to query for the list of accounts. 58aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * 59aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * During development this will allow new account types to be added, and allow them to be shown in 60aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * the application. For example, the mock accounts can be enabled/disabled. 61aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * In the future, once other processes can add new accounts, this could allow other "mail" 62aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook * applications have their content appear within the application 63aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook */ 64c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrookpublic abstract class MailAppProvider extends ContentProvider 65bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook implements OnLoadCompleteListener<Cursor>{ 66aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 67c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrook private static final String SHARED_PREFERENCES_NAME = "MailAppProvider"; 68db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook private static final String ACCOUNT_LIST_KEY = "accountList"; 690d647354209f4bf57cffa35f008e09853977b189Andy Huang private static final String LAST_VIEWED_ACCOUNT_KEY = "lastViewedAccount"; 7047d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira private static final String LAST_SENT_FROM_ACCOUNT_KEY = "lastSendFromAccount"; 71db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 722388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook /** 732388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * Extra used in the result from the activity launched by the intent specified 742388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * by {@link #getNoAccountsIntent} to return the list of accounts. The data 752388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * specified by this extra key should be a ParcelableArray. 762388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook */ 772388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook public static final String ADD_ACCOUNT_RESULT_ACCOUNTS_EXTRA = "addAccountResultAccounts"; 782388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 79b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private final static String LOG_TAG = LogTag.getLogTag(); 80f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook 81bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang private final LinkedHashMap<Uri, AccountCacheEntry> mAccountCache = 82bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang new LinkedHashMap<Uri, AccountCacheEntry>(); 835301c17703e01eccd31f91027373093791929997Paul Westbrook 84bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook private final Map<Uri, CursorLoader> mCursorLoaderMap = Maps.newHashMap(); 85bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 86bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook private ContentResolver mResolver; 8777177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook private static String sAuthority; 88c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrook private static MailAppProvider sInstance; 8977177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook 902388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook private volatile boolean mAccountsFullyLoaded = false; 912388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 92db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook private SharedPreferences mSharedPrefs; 93db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 9477177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook /** 956a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * Allows the implementing provider to specify the authority for this provider. Email and Gmail 966a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * must specify different authorities. 9777177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook */ 9877177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook protected abstract String getAuthority(); 9977177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook 1002388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook /** 1016a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * Authority for the suggestions provider. Email and Gmail must specify different authorities, 1026a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * much like the implementation of {@link #getAuthority()}. 1036a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * @return the suggestion authority associated with this provider. 1046a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal */ 1056a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal public abstract String getSuggestionAuthority(); 1066a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal 1076a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal /** 1086a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal * Allows the implementing provider to specify an intent that should be used in a call to 1092388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * {@link Context#startActivityForResult(android.content.Intent)} when the account provider 1102388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * doesn't return any accounts. 1112388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * 1122388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * The result from the {@link Activity} activity should include the list of accounts in 1132388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * the returned intent, in the 1142388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 11570f130ee9be956e72bc0686e80c3fc4c92807451Vikram Aggarwal * @return Intent or null, if the provider doesn't specify a behavior when no accounts are 1162388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * specified. 1172388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook */ 1182388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook protected abstract Intent getNoAccountsIntent(Context context); 1192388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 1202388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook /** 121828f46144e79c7bb0856f43dc237e4faef3623deVikram Aggarwal * The cursor returned from a call to {@link android.content.ContentResolver#query()} with this 1222388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * uri will return a cursor that with columns that are a subset of the columns specified 1232388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * in {@link UIProvider.ConversationColumns} 1242388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * The cursor returned by this query can return a {@link android.os.Bundle} 1252388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * from a call to {@link android.database.Cursor#getExtras()}. This Bundle may have 1262388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * values with keys listed in {@link AccountCursorExtraKeys} 1272388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook */ 128aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public static Uri getAccountsUri() { 12977177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook return Uri.parse("content://" + sAuthority + "/"); 130aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 131aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 132c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrook public static MailAppProvider getInstance() { 133db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook return sInstance; 134db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 135db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 1366a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal /** Default constructor */ 1376006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal protected MailAppProvider() { 1386a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal } 1396a62146d0af3bf33ff472e4fbfad64f6c582dd8eVikram Aggarwal 140aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 141aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public boolean onCreate() { 14277177b171c483d485bdbff0178564394e8f57d0fPaul Westbrook sAuthority = getAuthority(); 14345402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook sInstance = this; 144bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook mResolver = getContext().getContentResolver(); 145bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 146db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook // Load the previously saved account list 147db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook loadCachedAccountList(); 148db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 14945402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook final Resources res = getContext().getResources(); 15045402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook // Load the uris for the account list 15145402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook final String[] accountQueryUris = res.getStringArray(R.array.account_providers); 152f9dbd206d1dd37e8ff2e3e72759710f5e6fb45aePaul Westbrook 15345402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook for (String accountQueryUri : accountQueryUris) { 15445402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook final Uri uri = Uri.parse(accountQueryUri); 15545402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook addAccountsForUriAsync(uri); 156f9dbd206d1dd37e8ff2e3e72759710f5e6fb45aePaul Westbrook } 157f9dbd206d1dd37e8ff2e3e72759710f5e6fb45aePaul Westbrook 158aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return true; 159aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 160aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 161aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 162bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook public void shutdown() { 16345402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook sInstance = null; 164bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 165bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook for (CursorLoader loader : mCursorLoaderMap.values()) { 166bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook loader.stopLoading(); 167bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 168bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook mCursorLoaderMap.clear(); 169bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook } 170bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook 171bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook @Override 172aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, 173aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook String sortOrder) { 174aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook // This content provider currently only supports one query (to return the list of accounts). 175aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook // No reason to check the uri. Currently only checking the projections 176aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 177aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook // Validates and returns the projection that should be used. 178583a114f7b72ef1b2ebe8af53870ccb089afe530Paul Westbrook final String[] resultProjection = UIProviderValidator.validateAccountProjection(projection); 1792388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook final Bundle extras = new Bundle(); 1802388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook extras.putInt(AccountCursorExtraKeys.ACCOUNTS_LOADED, mAccountsFullyLoaded ? 1 : 0); 181aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 182bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook // Make a copy of the account cache 183bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final List<AccountCacheEntry> accountList; 184db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook synchronized (mAccountCache) { 185bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang accountList = ImmutableList.copyOf(mAccountCache.values()); 186bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 1872388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 1882388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook final MatrixCursor cursor = 1892388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook new MatrixCursorWithExtra(resultProjection, accountList.size(), extras); 1902388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 19172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook for (AccountCacheEntry accountEntry : accountList) { 19272a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook final Account account = accountEntry.mAccount; 193aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook final MatrixCursor.RowBuilder builder = cursor.newRow(); 194a4836711cf7909575bfb25720232c22c201efd06Tony Mantler final Map<String, Object> accountValues = account.getValueMap(); 195aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 196a9a05d566650f31f09191eda0ba61af843bd4cd8Vikram Aggarwal for (final String columnName : resultProjection) { 197d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (accountValues.containsKey(columnName)) { 198d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy builder.add(accountValues.get(columnName)); 199d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 200d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy throw new IllegalStateException("Unexpected column: " + columnName); 201aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 202aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 203aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 204bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook 205bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook cursor.setNotificationUri(mResolver, getAccountsUri()); 206aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return cursor; 207aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 208aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 209aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 210aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public Uri insert(Uri url, ContentValues values) { 211aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return url; 212aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 213aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 214aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 215aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public int update(Uri url, ContentValues values, String selection, 216aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook String[] selectionArgs) { 217aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return 0; 218aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 219aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 220aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 221aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public int delete(Uri url, String selection, String[] selectionArgs) { 222aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return 0; 223aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 224aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 225aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook @Override 226aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook public String getType(Uri uri) { 227aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook return null; 228aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 229aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 230f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook /** 23155124f6fbc230042714d6eec119f0573e2878246Mindy Pereira * Asynchronously adds all of the accounts that are specified by the result set returned by 232f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook * {@link ContentProvider#query()} for the specified uri. The content provider handling the 233f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook * query needs to handle the {@link UIProvider.ACCOUNTS_PROJECTION} 234bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook * Any changes to the underlying provider will automatically be reflected. 235f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook * @param accountsQueryUri 236f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook */ 23745402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook private void addAccountsForUriAsync(Uri accountsQueryUri) { 23845402ff1f55eb83fc9add99f83649a259b5c51a2Paul Westbrook startAccountsLoader(accountsQueryUri); 239bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 240f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook 2412388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook /** 2422388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * Returns the intent that should be used in a call to 2432388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * {@link Context#startActivity(android.content.Intent)} when the account provider doesn't 2442388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * return any accounts 2452388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * @return Intent or null, if the provider doesn't specify a behavior when no acccounts are 2462388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook * specified. 2472388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook */ 2482388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook public static Intent getNoAccountIntent(Context context) { 2492388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook return getInstance().getNoAccountsIntent(context); 2502388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook } 2512388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook 252bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook private synchronized void startAccountsLoader(Uri accountsQueryUri) { 253bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final CursorLoader accountsCursorLoader = new CursorLoader(getContext(), accountsQueryUri, 254bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook UIProvider.ACCOUNTS_PROJECTION, null, null, null); 2555301c17703e01eccd31f91027373093791929997Paul Westbrook 256bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook // Listen for the results 257bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook accountsCursorLoader.registerListener(accountsQueryUri.hashCode(), this); 258bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook accountsCursorLoader.startLoading(); 2595301c17703e01eccd31f91027373093791929997Paul Westbrook 260bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook // If there is a previous loader for the given uri, stop it 261bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final CursorLoader oldLoader = mCursorLoaderMap.get(accountsQueryUri); 262bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook if (oldLoader != null) { 263bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook oldLoader.stopLoading(); 2645301c17703e01eccd31f91027373093791929997Paul Westbrook } 265bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook mCursorLoaderMap.put(accountsQueryUri, accountsCursorLoader); 266f464a541c5571836aaf5bca346c40c26e8e3c16dPaul Westbrook } 267aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 268e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook private void addAccountImpl(Account account, Uri accountsQueryUri, boolean notify) { 269bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang addAccountImpl(account.uri, new AccountCacheEntry(account, accountsQueryUri)); 270c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook 271bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook // Explicitly calling this out of the synchronized block in case any of the observers get 272bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook // called synchronously. 273e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook if (notify) { 274e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook broadcastAccountChange(); 275e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook } 276aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 277aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 278bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang private void addAccountImpl(Uri key, AccountCacheEntry accountEntry) { 279db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook synchronized (mAccountCache) { 280bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang LogUtils.v(LOG_TAG, "adding account %s", accountEntry.mAccount); 281bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // LinkedHashMap will not change the iteration order when re-inserting a key 282bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccountCache.put(key, accountEntry); 283e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook } 284bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook } 285bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook 286bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook private static void broadcastAccountChange() { 287c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrook final MailAppProvider provider = sInstance; 288bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook 289bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook if (provider != null) { 290bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook provider.mResolver.notifyChange(getAccountsUri(), null); 291bfe41fe4632795af332cd003b105ece7d222a28bPaul Westbrook } 292aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook } 293aa3e7a53861d62b8ca3858c83d6cb07ae4617cf9Paul Westbrook 2940d647354209f4bf57cffa35f008e09853977b189Andy Huang /** 2950d647354209f4bf57cffa35f008e09853977b189Andy Huang * Returns the {@link Account#uri} (in String form) of the last viewed account. 2960d647354209f4bf57cffa35f008e09853977b189Andy Huang */ 2970d647354209f4bf57cffa35f008e09853977b189Andy Huang public String getLastViewedAccount() { 2980d647354209f4bf57cffa35f008e09853977b189Andy Huang return getPreferences().getString(LAST_VIEWED_ACCOUNT_KEY, null); 2990d647354209f4bf57cffa35f008e09853977b189Andy Huang } 3000d647354209f4bf57cffa35f008e09853977b189Andy Huang 3010d647354209f4bf57cffa35f008e09853977b189Andy Huang /** 3020d647354209f4bf57cffa35f008e09853977b189Andy Huang * Persists the {@link Account#uri} (in String form) of the last viewed account. 3030d647354209f4bf57cffa35f008e09853977b189Andy Huang */ 3040d647354209f4bf57cffa35f008e09853977b189Andy Huang public void setLastViewedAccount(String accountUriStr) { 3050d647354209f4bf57cffa35f008e09853977b189Andy Huang final SharedPreferences.Editor editor = getPreferences().edit(); 3060d647354209f4bf57cffa35f008e09853977b189Andy Huang editor.putString(LAST_VIEWED_ACCOUNT_KEY, accountUriStr); 3070d647354209f4bf57cffa35f008e09853977b189Andy Huang editor.apply(); 3080d647354209f4bf57cffa35f008e09853977b189Andy Huang } 3090d647354209f4bf57cffa35f008e09853977b189Andy Huang 31047d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira /** 31147d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira * Returns the {@link Account#uri} (in String form) of the last account the 31247d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira * user compose a message from. 31347d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira */ 31447d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira public String getLastSentFromAccount() { 31547d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira return getPreferences().getString(LAST_SENT_FROM_ACCOUNT_KEY, null); 31647d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira } 31747d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira 31847d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira /** 31947d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira * Persists the {@link Account#uri} (in String form) of the last account the 32047d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira * user compose a message from. 32147d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira */ 32247d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira public void setLastSentFromAccount(String accountUriStr) { 32347d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira final SharedPreferences.Editor editor = getPreferences().edit(); 32447d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira editor.putString(LAST_SENT_FROM_ACCOUNT_KEY, accountUriStr); 32547d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira editor.apply(); 32647d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira } 32747d0e65536c55d7aad8c902aef31efb41fd05cf2Mindy Pereira 328db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook private void loadCachedAccountList() { 329bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang JSONArray accounts = null; 330bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang try { 331bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final String accountsJson = getPreferences().getString(ACCOUNT_LIST_KEY, null); 332bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (accountsJson != null) { 333bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang accounts = new JSONArray(accountsJson); 334db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 335bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } catch (Exception e) { 336bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang LogUtils.e(LOG_TAG, e, "ignoring unparsable accounts cache"); 337bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 338bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 339bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (accounts == null) { 340bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang return; 341bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 342bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 343bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang for (int i = 0; i < accounts.length(); i++) { 344bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang try { 345bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final AccountCacheEntry accountEntry = new AccountCacheEntry( 346bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang accounts.getJSONObject(i)); 347bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 348bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (accountEntry.mAccount.settings == null) { 349d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang LogUtils.e(LOG_TAG, "Dropping account that doesn't specify settings"); 350bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang continue; 351d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang } 352bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 353bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang Account account = accountEntry.mAccount; 354bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang ContentProviderClient client = 355bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mResolver.acquireContentProviderClient(account.uri); 356bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (client != null) { 357bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang client.release(); 358bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang addAccountImpl(account.uri, accountEntry); 359bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } else { 360bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang LogUtils.e(LOG_TAG, "Dropping account without provider: %s", 361bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang account.name); 362bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 363bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 364bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } catch (Exception e) { 365bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // Unable to create account object, skip to next 366bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang LogUtils.e(LOG_TAG, e, 367bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang "Unable to create account object from serialized form"); 368d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang } 369db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 370bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang broadcastAccountChange(); 371db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 372db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 373db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook private void cacheAccountList() { 374bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final List<AccountCacheEntry> accountList; 375bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 376db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook synchronized (mAccountCache) { 377bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang accountList = ImmutableList.copyOf(mAccountCache.values()); 378db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 379db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 380bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final JSONArray arr = new JSONArray(); 38172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook for (AccountCacheEntry accountEntry : accountList) { 382bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang arr.put(accountEntry.toJSONObject()); 383db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 384db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 385db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook final SharedPreferences.Editor editor = getPreferences().edit(); 386bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang editor.putString(ACCOUNT_LIST_KEY, arr.toString()); 387db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook editor.apply(); 388db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 389db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 390db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook private SharedPreferences getPreferences() { 391db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook if (mSharedPrefs == null) { 392db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook mSharedPrefs = getContext().getSharedPreferences( 393db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); 394db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 395db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook return mSharedPrefs; 396db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook } 397db8f2f9ad5dcb1d52c399520129d89b1ca38f423Paul Westbrook 398a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank static public Account getAccountFromAccountUri(Uri accountUri) { 399a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank MailAppProvider provider = getInstance(); 400a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank if (provider != null && provider.mAccountsFullyLoaded) { 401a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank synchronized(provider.mAccountCache) { 402a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank AccountCacheEntry entry = provider.mAccountCache.get(accountUri); 403a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank if (entry != null) { 404a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank return entry.mAccount; 405a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank } 406a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank } 407a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank } 408a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank return null; 409a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank } 410a6b671dd9f5ba358a05888b3ab3bf1c5cb5cf493Marc Blank 411bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook @Override 412bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook public void onLoadComplete(Loader<Cursor> loader, Cursor data) { 413bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook if (data == null) { 414bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook LogUtils.d(LOG_TAG, "null account cursor returned"); 415bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook return; 416bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 417bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 418e3e8429f5665e5cfe3901962ffbae204a5c38147Paul Westbrook LogUtils.d(LOG_TAG, "Cursor with %d accounts returned", data.getCount()); 419bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final CursorLoader cursorLoader = (CursorLoader)loader; 420bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final Uri accountsQueryUri = cursorLoader.getUri(); 42172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 422bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // preserve ordering on partial updates 423bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // also preserve ordering on complete updates for any that existed previously 424bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 425bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 426bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final List<AccountCacheEntry> accountList; 42772a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook synchronized (mAccountCache) { 428bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang accountList = ImmutableList.copyOf(mAccountCache.values()); 42972a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook } 43072a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 43172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook // Build a set of the account uris that had been associated with that query 432c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook final Set<Uri> previousQueryUriSet = Sets.newHashSet(); 43372a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook for (AccountCacheEntry entry : accountList) { 43472a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook if (accountsQueryUri.equals(entry.mAccountsQueryUri)) { 435c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook previousQueryUriSet.add(entry.mAccount.uri); 436c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook } 43772a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook } 438bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 439e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook // Update the internal state of this provider if the returned result set 440e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook // represents all accounts 441e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook // TODO: determine what should happen with a heterogeneous set of accounts 442e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook final Bundle extra = data.getExtras(); 443e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook mAccountsFullyLoaded = extra.getInt(AccountCursorExtraKeys.ACCOUNTS_LOADED) != 0; 444e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook 445bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final Set<Uri> newQueryUriMap = Sets.newHashSet(); 446c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook 447c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook // We are relying on the fact that all accounts are added in the order specified in the 448c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook // cursor. Initially assume that we insert these items to at the end of the list 449bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook while (data.moveToNext()) { 450bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final Account account = new Account(data); 451bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook final Uri accountUri = account.uri; 452bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook newQueryUriMap.add(accountUri); 453bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // preserve existing order if already present and this is a partial update, 454bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // otherwise add to the end 455bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // 456bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // N.B. this ordering policy means the order in which providers respond will affect 457bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // the order of accounts. 458bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (mAccountsFullyLoaded) { 459d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang synchronized (mAccountCache) { 460bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // removing the existing item will prevent LinkedHashMap from preserving the 461bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // original insertion order 462bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccountCache.remove(accountUri); 463d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang } 464d0705dcd413e763d4ba1ee1acc3c67afe05a08d4Andy Huang } 465bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang addAccountImpl(account, accountsQueryUri, false /* don't notify */); 466bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 46768b6927dbf5678c9b3728e52782a2f1eef55466eVikram Aggarwal // Remove all of the accounts that are in the new result set 468c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook previousQueryUriSet.removeAll(newQueryUriMap); 469bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook 47068b6927dbf5678c9b3728e52782a2f1eef55466eVikram Aggarwal // For all of the entries that had been in the previous result set, and are not 47168b6927dbf5678c9b3728e52782a2f1eef55466eVikram Aggarwal // in the new result set, remove them from the cache 472c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook if (previousQueryUriSet.size() > 0 && mAccountsFullyLoaded) { 473bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang synchronized (mAccountCache) { 474bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang for (Uri accountUri : previousQueryUriSet) { 475bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang LogUtils.d(LOG_TAG, "Removing account %s", accountUri); 476bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccountCache.remove(accountUri); 477bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 478bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 479bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 480e95c7ded028607899d9596685c7cec21c1274d8ePaul Westbrook broadcastAccountChange(); 481bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 482bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // Cache the updated account list 483bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang cacheAccountList(); 484bb303e7a71ae6e54d58bb1d5d254dc58317c6382Paul Westbrook } 48572a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 48672a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook /** 48772a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook * Object that allows the Account Cache provider to associate the account with the content 48872a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook * provider uri that originated that account. 48972a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook */ 490bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang private static class AccountCacheEntry { 49172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook final Account mAccount; 49272a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook final Uri mAccountsQueryUri; 49372a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 494bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang private static final String KEY_ACCOUNT = "acct"; 495bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang private static final String KEY_QUERY_URI = "queryUri"; 49672a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 497bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang public AccountCacheEntry(Account account, Uri accountQueryUri) { 49872a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook mAccount = account; 49972a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook mAccountsQueryUri = accountQueryUri; 50072a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook } 50172a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook 502bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang public AccountCacheEntry(JSONObject o) throws JSONException { 503bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccount = Account.newinstance(o.getString(KEY_ACCOUNT)); 5046dde178687e85aaab9b3f8e9c124c3ab99b77d5aVikram Aggarwal if (mAccount == null) { 5056dde178687e85aaab9b3f8e9c124c3ab99b77d5aVikram Aggarwal throw new IllegalArgumentException("AccountCacheEntry de-serializing failed. " 506bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang + "Account object could not be created from the JSONObject: " 507bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang + o); 5086dde178687e85aaab9b3f8e9c124c3ab99b77d5aVikram Aggarwal } 5090a88d81a9f6d6997ff62c669824f1f7e85206ca3Andy Huang if (mAccount.settings == Settings.EMPTY_SETTINGS) { 5100a88d81a9f6d6997ff62c669824f1f7e85206ca3Andy Huang throw new IllegalArgumentException("AccountCacheEntry de-serializing failed. " 511bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang + "Settings could not be created from the JSONObject: " + o); 512bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 513bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang final String uriStr = o.optString(KEY_QUERY_URI, null); 514bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang if (uriStr != null) { 515bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccountsQueryUri = Uri.parse(uriStr); 516bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } else { 517bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang mAccountsQueryUri = null; 5180a88d81a9f6d6997ff62c669824f1f7e85206ca3Andy Huang } 519c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook } 520c597cb55f02fe41c9fb180d1b66311e1a06523e1Paul Westbrook 521bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang public JSONObject toJSONObject() { 522bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang try { 523bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang return new JSONObject() 524bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang .put(KEY_ACCOUNT, mAccount.serialize()) 525bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang .putOpt(KEY_QUERY_URI, mAccountsQueryUri); 526bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } catch (JSONException e) { 527bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang // shouldn't happen 528bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang throw new IllegalArgumentException(e); 529bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang } 53072a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook } 531bc14a6f8da73a563c27dd99dc433f8c301dfe8edAndy Huang 53272a9d380a0effb191c4ef8e32899ea62ebf52f55Paul Westbrook } 5332388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook} 534