12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/* 22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project 32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License. 62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at 72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and 142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License. 152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model; 182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 193f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.accounts.Account; 205f4af705cef8c914d5875983900e5cf5a5524b68Evan Millarimport android.accounts.AccountManager; 215f4af705cef8c914d5875983900e5cf5a5524b68Evan Millarimport android.accounts.AuthenticatorDescription; 228e55e0daa3967dbfdada4a42bec51a0435bc3384Fred Quintanaimport android.accounts.OnAccountsUpdateListener; 23ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkeyimport android.content.BroadcastReceiver; 243f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.content.ContentResolver; 25802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context; 263f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.content.IContentService; 27ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkeyimport android.content.Intent; 28ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkeyimport android.content.IntentFilter; 293f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.content.SyncAdapterType; 30b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport android.content.SyncStatusObserver; 312ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.pm.PackageManager; 3208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuanimport android.content.pm.ResolveInfo; 3308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuanimport android.net.Uri; 3408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuanimport android.os.AsyncTask; 35b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport android.os.Handler; 36b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport android.os.HandlerThread; 37a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawaimport android.os.Looper; 38b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport android.os.Message; 393f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.os.RemoteException; 40b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikovimport android.os.SystemClock; 413f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.provider.ContactsContract; 429aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onukiimport android.text.TextUtils; 433f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkeyimport android.util.Log; 44e598332967106e3db63b73c701f21902d169efefMakoto Onukiimport android.util.TimingLogger; 452ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 46e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactsUtils; 47e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.list.ContactListFilterController; 48851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType; 49851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountTypeWithDataSet; 50851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountWithDataSet; 51851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.ExchangeAccountType; 52851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.ExternalAccountType; 53851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.FallbackAccountType; 54851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.GoogleAccountType; 55851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.DataKind; 56e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.Constants; 57e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.internal.util.Objects; 58e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.annotations.VisibleForTesting; 59e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Lists; 60e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Maps; 61e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Sets; 62e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng 639aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onukiimport java.util.Collection; 64b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport java.util.Collections; 65b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikovimport java.util.Comparator; 662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.HashMap; 672b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport java.util.List; 689aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onukiimport java.util.Map; 692b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport java.util.Set; 7056c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikovimport java.util.concurrent.CountDownLatch; 7108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuanimport java.util.concurrent.atomic.AtomicBoolean; 722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 732ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/** 744597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * Singleton holder for all parsed {@link AccountType} available on the 752ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * system, typically filled through {@link PackageManager} queries. 762ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 776f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikovpublic abstract class AccountTypeManager { 78179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov static final String TAG = "AccountTypeManager"; 796f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 806f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov public static final String ACCOUNT_TYPE_SERVICE = "contactAccountTypes"; 816f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 826f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov /** 836f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov * Requests the singleton instance of {@link AccountTypeManager} with data bound from 846f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov * the available authenticators. This method can safely be called from the UI thread. 856f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov */ 866f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov public static AccountTypeManager getInstance(Context context) { 873572293500a7406813e61e6fedd63ca606debc19Jeff Hamilton context = context.getApplicationContext(); 88179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov AccountTypeManager service = 89179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov (AccountTypeManager) context.getSystemService(ACCOUNT_TYPE_SERVICE); 90179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov if (service == null) { 91179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov service = createAccountTypeManager(context); 92179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov Log.e(TAG, "No account type service in context: " + context); 93179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov } 94179c9960e50019608d91661cfbcbb3cc8bc48093Dmitri Plotnikov return service; 956f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov } 966f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 976f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov public static synchronized AccountTypeManager createAccountTypeManager(Context context) { 986f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov return new AccountTypeManagerImpl(context); 996f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov } 1006f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 1016f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki /** 1026f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki * Returns the list of all accounts (if contactWritableOnly is false) or just the list of 1036f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki * contact writable accounts (if contactWritableOnly is true). 1046f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki */ 1056f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts() 1066f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly); 1076f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki 1086f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki /** 1096f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki * Returns the list of accounts that are group writable. 1106f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki */ 1116f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki public abstract List<AccountWithDataSet> getGroupWritableAccounts(); 1126f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 1133ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet); 1143ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki 1153ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki public final AccountType getAccountType(String accountType, String dataSet) { 1163ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet)); 1173ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki } 1183ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki 1193ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki public final AccountType getAccountTypeForAccount(AccountWithDataSet account) { 1203ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki return getAccountType(account.getAccountTypeWithDataSet()); 1213ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki } 1226f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 1236f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov /** 1246ad227f990265254864a04d3289292ca42330c71Makoto Onuki * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s 1256ad227f990265254864a04d3289292ca42330c71Makoto Onuki * which support the "invite" feature and have one or more account. 12608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * 12708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * This is a filtered down and more "usable" list compared to 12808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * {@link #getAllInvitableAccountTypes}, where usable is defined as: 12908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (1) making sure that the app that contributed the account type is not disabled 13008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (in order to avoid presenting the user with an option that does nothing), and 13108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (2) that there is at least one raw contact with that account type in the database 13208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (assuming that the user probably doesn't use that account type). 13308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * 13408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Warning: Don't use on the UI thread because this can scan the database. 1359aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki */ 13608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan public abstract Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes(); 1379aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki 1389aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki /** 1396f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov * Find the best {@link DataKind} matching the requested 1402b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}. 1412b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * If no direct match found, we try searching {@link FallbackAccountType}. 1426f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov */ 1432b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro public DataKind getKindOrFallback(String accountType, String dataSet, String mimeType) { 1442b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = getAccountType(accountType, dataSet); 1456f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov return type == null ? null : type.getKindForMimetype(mimeType); 1466f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov } 147558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki 148a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa /** 149558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki * Returns all registered {@link AccountType}s, including extension ones. 150558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki * 151558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki * @param contactWritableOnly if true, it only returns ones that support writing contacts. 152558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki */ 153558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki public abstract List<AccountType> getAccountTypes(boolean contactWritableOnly); 154a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa 155a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa /** 156a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa * @param contactWritableOnly if true, it only returns ones that support writing contacts. 157a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa * @return true when this instance contains the given account. 158a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa */ 159a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) { 160a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa for (AccountWithDataSet account_2 : getAccounts(false)) { 161a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa if (account.equals(account_2)) { 162a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa return true; 163a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa } 164a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa } 165a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa return false; 166a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa } 1676f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov} 1686f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 1696f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikovclass AccountTypeManagerImpl extends AccountTypeManager 170b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov implements OnAccountsUpdateListener, SyncStatusObserver { 1712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 17208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private static final Map<AccountTypeWithDataSet, AccountType> 17308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP = 17408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>()); 17508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 17608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 17708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * A sample contact URI used to test whether any activities will respond to an 17808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * invitable intent with the given URI as the intent data. This doesn't need to be 17908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * specific to a real contact because an app that intercepts the intent should probably do so 18008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * for all types of contact URIs. 18108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 18208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private static final Uri SAMPLE_CONTACT_URI = ContactsContract.Contacts.getLookupUri( 18308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 1, "xxx"); 18408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 1851e349d136d0162ce82a2d51b8a3724972e20bd7cDmitri Plotnikov private Context mContext; 18699b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey private AccountManager mAccountManager; 1875f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar 1881dc28bef7d5353310359c3711b6cdac390d8a2e0Dmitri Plotnikov private AccountType mFallbackAccountType; 189ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 1902b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro private List<AccountWithDataSet> mAccounts = Lists.newArrayList(); 1916f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki private List<AccountWithDataSet> mContactWritableAccounts = Lists.newArrayList(); 1926f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki private List<AccountWithDataSet> mGroupWritableAccounts = Lists.newArrayList(); 1936ad227f990265254864a04d3289292ca42330c71Makoto Onuki private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = Maps.newHashMap(); 1946ad227f990265254864a04d3289292ca42330c71Makoto Onuki private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes = 19508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP; 19608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 19708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private final InvitableAccountTypeCache mInvitableAccountTypeCache; 19808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 19908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 20008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * The boolean value is equal to true if the {@link InvitableAccountTypeCache} has been 20108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * initialized. False otherwise. 20208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 20308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private final AtomicBoolean mInvitablesCacheIsInitialized = new AtomicBoolean(false); 20408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 20508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 20608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * The boolean value is equal to true if the {@link FindInvitablesTask} is still executing. 20708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * False otherwise. 20808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 20908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private final AtomicBoolean mInvitablesTaskIsRunning = new AtomicBoolean(false); 2103f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey 21156c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov private static final int MESSAGE_LOAD_DATA = 0; 21256c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1; 213b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 214b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov private HandlerThread mListenerThread; 215b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov private Handler mListenerHandler; 216b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 217a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); 218a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa private final Runnable mCheckFilterValidityRunnable = new Runnable () { 219a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa @Override 220a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa public void run() { 221be79b8f05f45c4cddeea9106399521529ec27caeKatherine Kuan ContactListFilterController.getInstance(mContext).checkFilterValidity(true); 222a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa } 223a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa }; 224a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa 2256f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 2266f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 2276f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov @Override 2286f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov public void onReceive(Context context, Intent intent) { 2296f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent); 2306f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov mListenerHandler.sendMessage(msg); 2316f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov } 2326f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 2336f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov }; 2346f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov 23556c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov /* A latch that ensures that asynchronous initialization completes before data is used */ 236b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1); 23756c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov 238b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov private static final Comparator<Account> ACCOUNT_COMPARATOR = new Comparator<Account>() { 239b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov @Override 2402b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro public int compare(Account a, Account b) { 2412b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String aDataSet = null; 2422b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String bDataSet = null; 2432b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (a instanceof AccountWithDataSet) { 2442b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro aDataSet = ((AccountWithDataSet) a).dataSet; 2452b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 2462b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (b instanceof AccountWithDataSet) { 2472b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro bDataSet = ((AccountWithDataSet) b).dataSet; 2482b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 2492b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 2502b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (Objects.equal(a.name, b.name) && Objects.equal(a.type, b.type) 2512b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro && Objects.equal(aDataSet, bDataSet)) { 2522b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return 0; 2532b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } else if (b.name == null || b.type == null) { 2542b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return -1; 2552b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } else if (a.name == null || a.type == null) { 2562b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return 1; 2572b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } else { 2582b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro int diff = a.name.compareTo(b.name); 2592b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (diff != 0) { 2602b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return diff; 2612b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 2622b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro diff = a.type.compareTo(b.type); 2632b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (diff != 0) { 2642b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return diff; 2652b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 2662b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 2672b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Accounts without data sets get sorted before those that have them. 2682b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (aDataSet != null) { 2692b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return bDataSet == null ? 1 : aDataSet.compareTo(bDataSet); 2702b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } else { 2712b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro return -1; 2722b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 273b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 274b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 275b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov }; 2765f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar 2775f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar /** 2787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * Internal constructor that only performs initial parsing. 2792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2806f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov public AccountTypeManagerImpl(Context context) { 2811e349d136d0162ce82a2d51b8a3724972e20bd7cDmitri Plotnikov mContext = context; 2821dc28bef7d5353310359c3711b6cdac390d8a2e0Dmitri Plotnikov mFallbackAccountType = new FallbackAccountType(context); 2831dc28bef7d5353310359c3711b6cdac390d8a2e0Dmitri Plotnikov 284b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov mAccountManager = AccountManager.get(mContext); 285ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 286b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov mListenerThread = new HandlerThread("AccountChangeListener"); 287b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov mListenerThread.start(); 288b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov mListenerHandler = new Handler(mListenerThread.getLooper()) { 289b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov @Override 290b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov public void handleMessage(Message msg) { 291b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov switch (msg.what) { 29256c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov case MESSAGE_LOAD_DATA: 29356c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov loadAccountsInBackground(); 29456c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov break; 295b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov case MESSAGE_PROCESS_BROADCAST_INTENT: 296b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov processBroadcastIntent((Intent) msg.obj); 297b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov break; 298b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 299b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 300b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov }; 301b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 30208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitableAccountTypeCache = new InvitableAccountTypeCache(); 30308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 30499b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey // Request updates when packages or accounts change 305f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 30699b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 30799b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 30899b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey filter.addDataScheme("package"); 3096f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov mContext.registerReceiver(mBroadcastReceiver, filter); 310e23c9e1380c1489901c95d34c6974d1848120416Suchi Amalapurapu IntentFilter sdFilter = new IntentFilter(); 311e23c9e1380c1489901c95d34c6974d1848120416Suchi Amalapurapu sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 312e23c9e1380c1489901c95d34c6974d1848120416Suchi Amalapurapu sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 3136f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov mContext.registerReceiver(mBroadcastReceiver, sdFilter); 31499b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey 315f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa // Request updates when locale is changed so that the order of each field will 316f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa // be able to be changed on the locale change. 317f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); 3186f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov mContext.registerReceiver(mBroadcastReceiver, filter); 319f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa 320b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false); 321b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 322b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this); 32356c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov 32456c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA); 3252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 3262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 327b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov @Override 328b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov public void onStatusChanged(int which) { 329b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA); 330b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 331b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 332b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov public void processBroadcastIntent(Intent intent) { 333b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA); 334f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa } 335f4ac04628763d8df783768b86d5e7b162a9a3d38Daisuke Miyakawa 336b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov /* This notification will arrive on the background thread */ 33799b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey public void onAccountsUpdated(Account[] accounts) { 33899b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey // Refresh to catch any changed accounts 33956c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov loadAccountsInBackground(); 34056c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } 34156c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov 34256c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov /** 34356c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov * Returns instantly if accounts and account types have already been loaded. 34456c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov * Otherwise waits for the background thread to complete the loading. 34556c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov */ 34656c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov void ensureAccountsLoaded() { 34756c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov CountDownLatch latch = mInitializationLatch; 34856c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov if (latch == null) { 34956c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov return; 35056c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } 35156c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov while (true) { 35256c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov try { 35356c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov latch.await(); 35456c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov return; 35556c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } catch (InterruptedException e) { 35656c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov Thread.currentThread().interrupt(); 35756c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } 35856c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } 35999b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey } 36099b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey 3612ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 3622b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Loads account list and corresponding account types (potentially with data sets). Always 3632b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * called on a background thread. 3642ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 36556c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov protected void loadAccountsInBackground() { 36649627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 36749627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground start"); 36849627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki } 369e598332967106e3db63b73c701f21902d169efefMakoto Onuki TimingLogger timings = new TimingLogger(TAG, "loadAccountsInBackground"); 370e598332967106e3db63b73c701f21902d169efefMakoto Onuki final long startTime = SystemClock.currentThreadTimeMillis(); 371e598332967106e3db63b73c701f21902d169efefMakoto Onuki final long startTimeWall = SystemClock.elapsedRealtime(); 372b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov 3732b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Account types, keyed off the account type and data set concatenation. 3746f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet = 3756f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki Maps.newHashMap(); 3762b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 3772b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can 3782b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // be multiple account types (with different data sets) for the same type of account, each 3792b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // type string may have multiple AccountType entries. 3806f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final Map<String, List<AccountType>> accountTypesByType = Maps.newHashMap(); 3812b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 3826f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final List<AccountWithDataSet> allAccounts = Lists.newArrayList(); 3836f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final List<AccountWithDataSet> contactWritableAccounts = Lists.newArrayList(); 3846f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final List<AccountWithDataSet> groupWritableAccounts = Lists.newArrayList(); 3856f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki final Set<String> extensionPackages = Sets.newHashSet(); 3862ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 38799b1db73e9db33aa76883a7f60feafbc38e2860aJeff Sharkey final AccountManager am = mAccountManager; 3883f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey final IContentService cs = ContentResolver.getContentService(); 3892ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 3903f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey try { 3913f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey final SyncAdapterType[] syncs = cs.getSyncAdapterTypes(); 3923f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey final AuthenticatorDescription[] auths = am.getAuthenticatorTypes(); 3932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 3942b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // First process sync adapters to find any that provide contact data. 3953f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey for (SyncAdapterType sync : syncs) { 396b5f40208255bf1d326e70716d5916e463bec397eJeff Hamilton if (!ContactsContract.AUTHORITY.equals(sync.authority)) { 3973f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey // Skip sync adapters that don't provide contact data. 3983f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey continue; 3993f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey } 4002ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 4013f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey // Look for the formatting details provided by each sync 4023f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey // adapter, using the authenticator to find general resources. 40369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final String type = sync.accountType; 40469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final AuthenticatorDescription auth = findAuthenticator(auths, type); 40532d64e590d4f9302d204d9a96960ca5739807500Nicklas Shamlo if (auth == null) { 40669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it."); 40732d64e590d4f9302d204d9a96960ca5739807500Nicklas Shamlo continue; 40832d64e590d4f9302d204d9a96960ca5739807500Nicklas Shamlo } 409e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 41069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov AccountType accountType; 41169f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) { 412b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov accountType = new GoogleAccountType(mContext, auth.packageName); 413de8a66404acd36544e3559404f6542473224aa4aMarc Blank } else if (ExchangeAccountType.isExchangeType(type)) { 414de8a66404acd36544e3559404f6542473224aa4aMarc Blank accountType = new ExchangeAccountType(mContext, auth.packageName, type); 415ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey } else { 416ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey // TODO: use syncadapter package instead, since it provides resources 417b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov Log.d(TAG, "Registering external account type=" + type 418ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey + ", packageName=" + auth.packageName); 419b5e4419f4ba06d630331669a08613936889f34dfMakoto Onuki accountType = new ExternalAccountType(mContext, auth.packageName, false); 42086ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki } 42186ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki if (!accountType.isInitialized()) { 42286ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki if (accountType.isEmbedded()) { 42386ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki throw new IllegalStateException("Problem initializing embedded type " 42486ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki + accountType.getClass().getCanonicalName()); 42586ccb6ce2ff6078b06add93aab489951ef83bc72Makoto Onuki } else { 4265226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro // Skip external account types that couldn't be initialized. 4275226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro continue; 4285226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro } 429ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey } 430ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 43169f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov accountType.accountType = auth.type; 43269f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov accountType.titleRes = auth.labelId; 43369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov accountType.iconRes = auth.iconId; 4342ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 4352b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType); 4362b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 4372b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Check to see if the account type knows of any other non-sync-adapter packages 4382b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // that may provide other data sets of contact data. 4392b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro extensionPackages.addAll(accountType.getExtensionPackageNames()); 4402b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 4412b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 4422b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // If any extension packages were specified, process them as well. 4432b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (!extensionPackages.isEmpty()) { 4442b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages"); 4452b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro for (String extensionPackage : extensionPackages) { 4462b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ExternalAccountType accountType = 447b5e4419f4ba06d630331669a08613936889f34dfMakoto Onuki new ExternalAccountType(mContext, extensionPackage, true); 4485226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro if (!accountType.isInitialized()) { 4495226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro // Skip external account types that couldn't be initialized. 4505226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro continue; 4515226abf2ee19f53b9d9bf63109138aff79c7a222Dave Santoro } 4528545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki if (!accountType.hasContactsMetadata()) { 4538545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki Log.w(TAG, "Skipping extension package " + extensionPackage + " because" 4548545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki + " it doesn't have the CONTACTS_STRUCTURE metadata"); 4558545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki continue; 4568545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki } 4578545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki if (TextUtils.isEmpty(accountType.accountType)) { 4588545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki Log.w(TAG, "Skipping extension package " + extensionPackage + " because" 4598545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki + " the CONTACTS_STRUCTURE metadata doesn't have the accountType" 4608545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki + " attribute"); 4618545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki continue; 4628545ec4d78e5bdaec87b0ae59c448acc96856b4cMakoto Onuki } 4632b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.d(TAG, "Registering extension package account type=" 4642b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro + accountType.accountType + ", dataSet=" + accountType.dataSet 4652b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro + ", packageName=" + extensionPackage); 4662b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 4672b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType); 4682b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 4693f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey } 4703f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey } catch (RemoteException e) { 4713f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey Log.w(TAG, "Problem loading accounts: " + e.toString()); 4722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 473e598332967106e3db63b73c701f21902d169efefMakoto Onuki timings.addSplit("Loaded account types"); 474b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 4752b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Map in accounts to associate the account names with each account type entry. 476b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov Account[] accounts = mAccountManager.getAccounts(); 477b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov for (Account account : accounts) { 478b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov boolean syncable = false; 479b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov try { 4802b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro syncable = cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0; 481b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } catch (RemoteException e) { 482b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov Log.e(TAG, "Cannot obtain sync flag for account: " + account, e); 483b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 484b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 485b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov if (syncable) { 4862b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro List<AccountType> accountTypes = accountTypesByType.get(account.type); 4872b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (accountTypes != null) { 4882b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Add an account-with-data-set entry for each account type that is 4892b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // authenticated by this account. 4902b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro for (AccountType accountType : accountTypes) { 4912b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro AccountWithDataSet accountWithDataSet = new AccountWithDataSet( 4922b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro account.name, account.type, accountType.dataSet); 4932b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro allAccounts.add(accountWithDataSet); 49496e87fd6e808e9d853263ffeb5104f3253a18639Daniel Lehmann if (accountType.areContactsWritable()) { 4956f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki contactWritableAccounts.add(accountWithDataSet); 4966f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki } 4976f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki if (accountType.isGroupMembershipEditable()) { 4986f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki groupWritableAccounts.add(accountWithDataSet); 4992b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 500b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 501b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 502b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 503b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov } 504b5cd5959f0f0c5aa8ce50d3e872c0dcec12af9d4Dmitri Plotnikov 505b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov Collections.sort(allAccounts, ACCOUNT_COMPARATOR); 5066f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR); 5076f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR); 508169ff2ab666eb0cf5414b3a5eb8333ec4ddc669aDmitri Plotnikov 509e598332967106e3db63b73c701f21902d169efefMakoto Onuki timings.addSplit("Loaded accounts"); 510b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov 511b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov synchronized (this) { 5122b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro mAccountTypesWithDataSets = accountTypesByTypeAndDataSet; 513b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov mAccounts = allAccounts; 5146f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki mContactWritableAccounts = contactWritableAccounts; 5156f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki mGroupWritableAccounts = groupWritableAccounts; 51608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitableAccountTypes = findAllInvitableAccountTypes( 5172b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro mContext, allAccounts, accountTypesByTypeAndDataSet); 518b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov } 519b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov 520e598332967106e3db63b73c701f21902d169efefMakoto Onuki timings.dumpToLog(); 521e598332967106e3db63b73c701f21902d169efefMakoto Onuki final long endTimeWall = SystemClock.elapsedRealtime(); 522e598332967106e3db63b73c701f21902d169efefMakoto Onuki final long endTime = SystemClock.currentThreadTimeMillis(); 523e598332967106e3db63b73c701f21902d169efefMakoto Onuki 5242b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.i(TAG, "Loaded meta-data for " + mAccountTypesWithDataSets.size() + " account types, " 525e598332967106e3db63b73c701f21902d169efefMakoto Onuki + mAccounts.size() + " accounts in " + (endTimeWall - startTimeWall) + "ms(wall) " 526e598332967106e3db63b73c701f21902d169efefMakoto Onuki + (endTime - startTime) + "ms(cpu)"); 527b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov 52856c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov if (mInitializationLatch != null) { 52956c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov mInitializationLatch.countDown(); 53056c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov mInitializationLatch = null; 53156c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov } 53249627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 53349627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground finish"); 53449627cc3f606085d001397ebba93dcb52bf67a5cMakoto Onuki } 535a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa 536a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa // Check filter validity since filter may become obsolete after account update. It must be 537a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa // done from UI thread. 538a012aec6f22dd6a37c518c895db45b173e186ef4Daisuke Miyakawa mMainThreadHandler.post(mCheckFilterValidityRunnable); 5392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 5402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 5412b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro // Bookkeeping method for tracking the known account types in the given maps. 5422b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro private void addAccountType(AccountType accountType, 5436ad227f990265254864a04d3289292ca42330c71Makoto Onuki Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet, 5442b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Map<String, List<AccountType>> accountTypesByType) { 5452b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType); 5462b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType); 5472b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (accountsForType == null) { 5482b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro accountsForType = Lists.newArrayList(); 5492b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 5502b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro accountsForType.add(accountType); 5512b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro accountTypesByType.put(accountType.accountType, accountsForType); 5522b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro } 5532b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro 5542ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 5553f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey * Find a specific {@link AuthenticatorDescription} in the provided list 5563f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey * that matches the given account type. 5575f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar */ 5583f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey protected static AuthenticatorDescription findAuthenticator(AuthenticatorDescription[] auths, 5593f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey String accountType) { 5603f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey for (AuthenticatorDescription auth : auths) { 5613f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey if (accountType.equals(auth.type)) { 5623f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey return auth; 563802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey } 5642ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 56532d64e590d4f9302d204d9a96960ca5739807500Nicklas Shamlo return null; 5662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 567aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 5682ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 5696f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki * Return list of all known, contact writable {@link AccountWithDataSet}'s. 5702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 5716f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov @Override 5726f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) { 5736f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki ensureAccountsLoaded(); 5746f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki return contactWritableOnly ? mContactWritableAccounts : mAccounts; 5756f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki } 5766f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki 5776f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki /** 5786f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki * Return the list of all known, group writable {@link AccountWithDataSet}'s. 5796f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki */ 5806f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki public List<AccountWithDataSet> getGroupWritableAccounts() { 58156c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov ensureAccountsLoaded(); 5826f74c0f3313cbb08ee8a8fbb79bfefc5b03fe215Makoto Onuki return mGroupWritableAccounts; 5832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 584d4c41d5c553745fb1126e01334319f05f0808eb3Jeff Sharkey 585ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey /** 586ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey * Find the best {@link DataKind} matching the requested 5872b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}. 5882b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * If no direct match found, we try searching {@link FallbackAccountType}. 589ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey */ 5906f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov @Override 5912b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro public DataKind getKindOrFallback(String accountType, String dataSet, String mimeType) { 59256c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov ensureAccountsLoaded(); 593ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey DataKind kind = null; 594ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 59569f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov // Try finding account type and kind matching request 5962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = mAccountTypesWithDataSets.get( 5976ad227f990265254864a04d3289292ca42330c71Makoto Onuki AccountTypeWithDataSet.get(accountType, dataSet)); 59869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov if (type != null) { 59969f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov kind = type.getKindForMimetype(mimeType); 600ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey } 601ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 602ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey if (kind == null) { 603ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey // Nothing found, so try fallback as last resort 60469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov kind = mFallbackAccountType.getKindForMimetype(mimeType); 605ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey } 606ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 607ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey if (kind == null) { 608ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey Log.w(TAG, "Unknown type=" + accountType + ", mime=" + mimeType); 609d4c41d5c553745fb1126e01334319f05f0808eb3Jeff Sharkey } 610ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey 611ab066931efd4b6408b5f57026b421eb4a7934a39Jeff Sharkey return kind; 612d4c41d5c553745fb1126e01334319f05f0808eb3Jeff Sharkey } 6135f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar 6145f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar /** 6152b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro * Return {@link AccountType} for the given account type and data set. 6165f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar */ 6176f667b55687bf9193323802e8f3234f0ab254388Dmitri Plotnikov @Override 6183ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) { 61956c55827a83eef9ebafbb68beb3656c2506c1e0fDmitri Plotnikov ensureAccountsLoaded(); 620b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov synchronized (this) { 6213ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet); 622b77be6d2ad06edfd8751f55043e4aa9fd9f36015Dmitri Plotnikov return type != null ? type : mFallbackAccountType; 6233f0b7b87cd41b5a9cd631b6fcf29ea5025905e18Jeff Sharkey } 6245f4af705cef8c914d5875983900e5cf5a5524b68Evan Millar } 6259aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki 62608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 62708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s 62808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * which support the "invite" feature and have one or more account. This is an unfiltered 62908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * list. See {@link #getUsableInvitableAccountTypes()}. 63008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 63108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private Map<AccountTypeWithDataSet, AccountType> getAllInvitableAccountTypes() { 63208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan ensureAccountsLoaded(); 6339aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki return mInvitableAccountTypes; 6349aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki } 6359aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki 63608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan @Override 63708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan public Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes() { 63808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan ensureAccountsLoaded(); 63908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // Since this method is not thread-safe, it's possible for multiple threads to encounter 64008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // the situation where (1) the cache has not been initialized yet or 64108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // (2) an async task to refresh the account type list in the cache has already been 64208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // started. Hence we use {@link AtomicBoolean}s and return cached values immediately 64308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // while we compute the actual result in the background. We use this approach instead of 64408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // using "synchronized" because computing the account type list involves a DB read, and 64508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // can potentially cause a deadlock situation if this method is called from code which 64608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // holds the DB lock. The trade-off of potentially having an incorrect list of invitable 64708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // account types for a short period of time seems more manageable than enforcing the 64808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // context in which this method is called. 64908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 65008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // Computing the list of usable invitable account types is done on the fly as requested. 65108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // If this method has never been called before, then block until the list has been computed. 65208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (!mInvitablesCacheIsInitialized.get()) { 65308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitableAccountTypeCache.setCachedValue(findUsableInvitableAccountTypes(mContext)); 65408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitablesCacheIsInitialized.set(true); 65508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } else { 65608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // Otherwise, there is a value in the cache. If the value has expired and 65708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // an async task has not already been started by another thread, then kick off a new 65808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // async task to compute the list. 65908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (mInvitableAccountTypeCache.isExpired() && 66008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitablesTaskIsRunning.compareAndSet(false, true)) { 66108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan new FindInvitablesTask().execute(); 66208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 66308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 66408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 66508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return mInvitableAccountTypeCache.getCachedValue(); 66608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 66708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 6689aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki /** 6699aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki * Return all {@link AccountType}s with at least one account which supports "invite", i.e. 6709aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki * its {@link AccountType#getInviteContactActivityClassName()} is not empty. 6719aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki */ 6729aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki @VisibleForTesting 67308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan static Map<AccountTypeWithDataSet, AccountType> findAllInvitableAccountTypes(Context context, 6742b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Collection<AccountWithDataSet> accounts, 6756ad227f990265254864a04d3289292ca42330c71Makoto Onuki Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) { 6766ad227f990265254864a04d3289292ca42330c71Makoto Onuki HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(); 6772b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro for (AccountWithDataSet account : accounts) { 6783ae114e72617f2faea281d82f7f4ee026d8c5674Makoto Onuki AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet(); 6796ad227f990265254864a04d3289292ca42330c71Makoto Onuki AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet); 6809aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki if (type == null) continue; // just in case 6812b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (result.containsKey(accountTypeWithDataSet)) continue; 6829aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki 6839aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki if (Log.isLoggable(TAG, Log.DEBUG)) { 6842b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.d(TAG, "Type " + accountTypeWithDataSet 685d124d99339a6a3a7dc0bfa016263988120ea2a4cMakoto Onuki + " inviteClass=" + type.getInviteContactActivityClassName()); 6869aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki } 6879aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) { 6882b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro result.put(accountTypeWithDataSet, type); 6899aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki } 6909aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki } 6919aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki return Collections.unmodifiableMap(result); 6929aa9e846dec6c2958be6ce120e138e484fdba330Makoto Onuki } 693558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki 69408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 69508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Return all usable {@link AccountType}s that support the "invite" feature from the 69608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * list of all potential invitable account types (retrieved from 69708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * {@link #getAllInvitableAccountTypes}). A usable invitable account type means: 69808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (1) there is at least 1 raw contact in the database with that account type, and 69908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * (2) the app contributing the account type is not disabled. 70008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * 70108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Warning: Don't use on the UI thread because this can scan the database. 70208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 70308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private Map<AccountTypeWithDataSet, AccountType> findUsableInvitableAccountTypes( 70408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan Context context) { 70508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan Map<AccountTypeWithDataSet, AccountType> allInvitables = getAllInvitableAccountTypes(); 70608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (allInvitables.isEmpty()) { 70708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP; 70808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 70908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 71008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan final HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(); 71108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan result.putAll(allInvitables); 71208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 71308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan final PackageManager packageManager = context.getPackageManager(); 71408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan for (AccountTypeWithDataSet accountTypeWithDataSet : allInvitables.keySet()) { 71508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan AccountType accountType = allInvitables.get(accountTypeWithDataSet); 71608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 71708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // Make sure that account types don't come from apps that are disabled. 71808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan Intent invitableIntent = ContactsUtils.getInvitableIntent(accountType, 71908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan SAMPLE_CONTACT_URI); 72008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (invitableIntent == null) { 72108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan result.remove(accountTypeWithDataSet); 72208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan continue; 72308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 72408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan ResolveInfo resolveInfo = packageManager.resolveActivity(invitableIntent, 72508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan PackageManager.MATCH_DEFAULT_ONLY); 72608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (resolveInfo == null) { 72708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // If we can't find an activity to start for this intent, then there's no point in 72808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // showing this option to the user. 72908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan result.remove(accountTypeWithDataSet); 73008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan continue; 73108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 73208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 73308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // Make sure that there is at least 1 raw contact with this account type. This check 73408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan // is non-trivial and should not be done on the UI thread. 73508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan if (!accountTypeWithDataSet.hasData(context)) { 73608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan result.remove(accountTypeWithDataSet); 73708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 73808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 73908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 74008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return Collections.unmodifiableMap(result); 74108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 74208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 743558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki @Override 744558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki public List<AccountType> getAccountTypes(boolean contactWritableOnly) { 74508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan ensureAccountsLoaded(); 746558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki final List<AccountType> accountTypes = Lists.newArrayList(); 747558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki synchronized (this) { 748558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki for (AccountType type : mAccountTypesWithDataSets.values()) { 749558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki if (!contactWritableOnly || type.areContactsWritable()) { 750558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki accountTypes.add(type); 751558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki } 752558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki } 753558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki } 754558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki return accountTypes; 755558669dab4109afebd19eade1f95a396215fb44dMakoto Onuki } 75608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 75708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 75808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Background task to find all usable {@link AccountType}s that support the "invite" feature 75908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * from the list of all potential invitable account types. Once the work is completed, 76008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * the list of account types is stored in the {@link AccountTypeManager}'s 76108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * {@link InvitableAccountTypeCache}. 76208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 76308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private class FindInvitablesTask extends AsyncTask<Void, Void, 76408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan Map<AccountTypeWithDataSet, AccountType>> { 76508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 76608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan @Override 76708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan protected Map<AccountTypeWithDataSet, AccountType> doInBackground(Void... params) { 76808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return findUsableInvitableAccountTypes(mContext); 76908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 77008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 77108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan @Override 77208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan protected void onPostExecute(Map<AccountTypeWithDataSet, AccountType> accountTypes) { 77308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitableAccountTypeCache.setCachedValue(accountTypes); 77408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitablesTaskIsRunning.set(false); 77508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 77608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 77708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 77808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 77908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * This cache holds a list of invitable {@link AccountTypeWithDataSet}s, in the form of a 78008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * {@link Map<AccountTypeWithDataSet, AccountType>}. Note that the cached value is valid only 78108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * for {@link #TIME_TO_LIVE} milliseconds. 78208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 78308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private static final class InvitableAccountTypeCache { 78408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 78508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 78608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * The cached {@link #mInvitableAccountTypes} list expires after this number of milliseconds 78708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * has elapsed. 78808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 78908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private static final long TIME_TO_LIVE = 60000; 79008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 79108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes; 79208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 79308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan private long mTimeLastSet; 79408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 79508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 79608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Returns true if the data in this cache is stale and needs to be refreshed. Returns false 79708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * otherwise. 79808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 79908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan public boolean isExpired() { 80008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return SystemClock.elapsedRealtime() - mTimeLastSet > TIME_TO_LIVE; 80108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 80208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 80308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan /** 80408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * Returns the cached value. Note that the caller is responsible for checking 80508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan * {@link #isExpired()} to ensure that the value is not stale. 80608bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan */ 80708bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan public Map<AccountTypeWithDataSet, AccountType> getCachedValue() { 80808bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan return mInvitableAccountTypes; 80908bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 81008bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan 81108bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan public void setCachedValue(Map<AccountTypeWithDataSet, AccountType> map) { 81208bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mInvitableAccountTypes = map; 81308bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan mTimeLastSet = SystemClock.elapsedRealtime(); 81408bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 81508bcf715d5ea7f07ce18a282d9850ac70552ca9dKatherine Kuan } 8162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey} 817