16c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng/*
26c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * Copyright (C) 2009 The Android Open Source Project
36c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng *
46c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
56c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * you may not use this file except in compliance with the License.
66c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * You may obtain a copy of the License at
76c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng *
86c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
96c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng *
106c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * Unless required by applicable law or agreed to in writing, software
116c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
126c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * See the License for the specific language governing permissions and
146c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * limitations under the License.
156c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng */
166c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
1769c182afb0e6d82a341a28b4317aa703af768906Gary Maipackage com.android.contacts.model;
186c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
196c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.accounts.Account;
20413589f0b9279e06d8913761cdcf03c8fef7ffd8Ihab Awadimport android.accounts.AccountManager;
216c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.accounts.OnAccountsUpdateListener;
226c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.BroadcastReceiver;
236c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.ContentResolver;
246c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.Context;
256c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.Intent;
266c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.IntentFilter;
2756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wangimport android.content.SharedPreferences;
286c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.SyncStatusObserver;
296c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.content.pm.PackageManager;
30fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerottimport android.database.ContentObserver;
316c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.net.Uri;
326c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.os.Handler;
336c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.os.Looper;
346c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.provider.ContactsContract;
35fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerottimport android.support.v4.content.ContextCompat;
36e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerottimport android.support.v4.content.LocalBroadcastManager;
376c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.text.TextUtils;
386c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport android.util.Log;
396c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
400a49afa2ad697307cc04ef4cb86570574fa720f2Gary Maiimport com.android.contacts.Experiments;
4156c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wangimport com.android.contacts.R;
4269c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.list.ContactListFilterController;
4375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerottimport com.android.contacts.model.account.AccountInfo;
4469c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.AccountType;
4504be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.android.contacts.model.account.AccountTypeProvider;
4669c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.AccountTypeWithDataSet;
4769c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.AccountWithDataSet;
4869c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.FallbackAccountType;
4969c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.GoogleAccountType;
5069c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.dataitem.DataKind;
5104be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.android.contacts.util.concurrent.ContactsExecutors;
52511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerottimport com.android.contactsbind.experiments.Flags;
5375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerottimport com.google.common.base.Preconditions;
5404be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.google.common.base.Function;
552a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerottimport com.google.common.base.Objects;
5667a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerottimport com.google.common.base.Predicate;
5767a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerottimport com.google.common.collect.Collections2;
5804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.google.common.util.concurrent.FutureCallback;
5904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.google.common.util.concurrent.Futures;
6004be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.google.common.util.concurrent.ListenableFuture;
6104be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport com.google.common.util.concurrent.ListeningExecutorService;
626c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
63ac33359ec62ff26c57da4328c2fa7b0f091b4e13Gary Maiimport java.util.ArrayList;
646c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport java.util.Collections;
656c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengimport java.util.List;
6604be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport java.util.concurrent.Callable;
6704be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerottimport java.util.concurrent.Executor;
686c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6967a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerottimport javax.annotation.Nullable;
7067a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerott
716c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng/**
726c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * Singleton holder for all parsed {@link AccountType} available on the
736c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng * system, typically filled through {@link PackageManager} queries.
746c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng */
756c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengpublic abstract class AccountTypeManager {
766c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    static final String TAG = "AccountTypeManager";
776c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
786c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    private static final Object mInitializationLock = new Object();
796c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    private static AccountTypeManager mAccountTypeManager;
806c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
81e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    public static final String BROADCAST_ACCOUNTS_CHANGED = AccountTypeManager.class.getName() +
82e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott            ".AccountsChanged";
83e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
84ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott    public enum AccountFilter implements Predicate<AccountInfo> {
85ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        ALL {
86ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            @Override
87ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            public boolean apply(@Nullable AccountInfo input) {
88ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott                return input != null;
89ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            }
90ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        },
91ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        CONTACTS_WRITABLE {
92ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            @Override
93ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            public boolean apply(@Nullable AccountInfo input) {
94ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott                return input != null && input.getType().areContactsWritable();
95ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            }
96ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        },
97ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        GROUPS_WRITABLE {
98ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            @Override
99ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            public boolean apply(@Nullable AccountInfo input) {
100ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott                return input != null && input.getType().isGroupMembershipEditable();
101ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott            }
102ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        };
103ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott    }
104ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott
1056c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
1066c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Requests the singleton instance of {@link AccountTypeManager} with data bound from
1076c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * the available authenticators. This method can safely be called from the UI thread.
1086c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
1096c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public static AccountTypeManager getInstance(Context context) {
110fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        if (!hasRequiredPermissions(context)) {
111fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott            // Hopefully any component that depends on the values returned by this class
112fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott            // will be restarted if the permissions change.
113fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott            return EMPTY;
114fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        }
1156c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        synchronized (mInitializationLock) {
1166c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            if (mAccountTypeManager == null) {
1176c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng                context = context.getApplicationContext();
11804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott                mAccountTypeManager = new AccountTypeManagerImpl(context);
1196c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            }
1206c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
1216c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        return mAccountTypeManager;
1226c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
1236c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
1246c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
1256c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Set the instance of account type manager.  This is only for and should only be used by unit
1266c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * tests.  While having this method is not ideal, it's simpler than the alternative of
1276c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * holding this as a service in the ContactsApplication context class.
1286c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     *
1296c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * @param mockManager The mock AccountTypeManager.
1306c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
1316c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public static void setInstanceForTest(AccountTypeManager mockManager) {
1326c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        synchronized (mInitializationLock) {
1336c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            mAccountTypeManager = mockManager;
1346c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
1356c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
1366c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
137fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott    private static final AccountTypeManager EMPTY = new AccountTypeManager() {
13804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott
139fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        @Override
14075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        public ListenableFuture<List<AccountInfo>> getAccountsAsync() {
14175895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            return Futures.immediateFuture(Collections.<AccountInfo>emptyList());
14267a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerott        }
14367a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerott
14467a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerott        @Override
14575895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        public ListenableFuture<List<AccountInfo>> filterAccountsAsync(
14675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                Predicate<AccountInfo> filter) {
14775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            return Futures.immediateFuture(Collections.<AccountInfo>emptyList());
14804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        }
14904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott
15004be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        @Override
15175895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        public AccountInfo getAccountInfoForAccount(AccountWithDataSet account) {
15275895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            return null;
153e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        }
154e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
155e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        @Override
15656c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        public Account getDefaultGoogleAccount() {
15756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang            return null;
15856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        }
15956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
16056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        @Override
161fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
162fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott            return null;
163fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        }
164fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott    };
165fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott
1666c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
1676c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Returns the list of all accounts (if contactWritableOnly is false) or just the list of
1686c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * contact writable accounts (if contactWritableOnly is true).
169107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     *
170107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * <p>TODO(mhagerott) delete this method. It's left in place to prevent build breakages when
171107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * this change is automerged. Usages of this method in downstream branches should be
172107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * replaced with an asynchronous account loading pattern</p>
173107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     */
174107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott    public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) {
175107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott        return contactWritableOnly
176107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott                ? blockForWritableAccounts()
177107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott                : AccountInfo.extractAccounts(Futures.getUnchecked(getAccountsAsync()));
178107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott    }
179107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott
180107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott    /**
181107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * Returns all contact writable accounts
182107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     *
183107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * <p>In general this method should be avoided. It exists to support some legacy usages of
184107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * accounts in infrequently used features where refactoring to asynchronous loading is
185107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * not justified. The chance that this will actually block is pretty low if the app has been
186107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott     * launched previously</p>
1876c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
188107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott    public List<AccountWithDataSet> blockForWritableAccounts() {
189107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott        return AccountInfo.extractAccounts(
190107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott                Futures.getUnchecked(filterAccountsAsync(AccountFilter.CONTACTS_WRITABLE)));
191107cd7a225eccad56dfe12ca6d32908e651b27acMarcus Hagerott    }
1926c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
193aa0e6ffbb780316a4e6e96e64085afb6e8679867Wenyi Wang    /**
19404be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott     * Loads accounts in background and returns future that will complete with list of all accounts
19504be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott     */
19675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public abstract ListenableFuture<List<AccountInfo>> getAccountsAsync();
19775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott
19875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    /**
19975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott     * Loads accounts and applies the fitler returning only for which the predicate is true
20075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott     */
20175895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public abstract ListenableFuture<List<AccountInfo>> filterAccountsAsync(
20275895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            Predicate<AccountInfo> filter);
20304be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott
20475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public abstract AccountInfo getAccountInfoForAccount(AccountWithDataSet account);
205e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
20604be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    /**
20756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang     * Returns the default google account.
20856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang     */
20956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    public abstract Account getDefaultGoogleAccount();
21056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
211396aab726710c557188ace4e961064a65900524aMarcus Hagerott    /**
212396aab726710c557188ace4e961064a65900524aMarcus Hagerott     * Returns the Google Accounts.
213396aab726710c557188ace4e961064a65900524aMarcus Hagerott     *
214396aab726710c557188ace4e961064a65900524aMarcus Hagerott     * <p>This method exists in addition to filterAccountsByTypeAsync because it should be safe
215396aab726710c557188ace4e961064a65900524aMarcus Hagerott     * to call synchronously.
216396aab726710c557188ace4e961064a65900524aMarcus Hagerott     * </p>
217396aab726710c557188ace4e961064a65900524aMarcus Hagerott     */
21875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public List<AccountInfo> getWritableGoogleAccounts() {
219396aab726710c557188ace4e961064a65900524aMarcus Hagerott        // This implementation may block and should be overridden by the Impl class
22075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        return Futures.getUnchecked(filterAccountsAsync(new Predicate<AccountInfo>() {
221396aab726710c557188ace4e961064a65900524aMarcus Hagerott            @Override
22275895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            public boolean apply(@Nullable AccountInfo input) {
22375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                return  input.getType().areContactsWritable() &&
22475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                        GoogleAccountType.ACCOUNT_TYPE.equals(input.getType().accountType);
225396aab726710c557188ace4e961064a65900524aMarcus Hagerott            }
226396aab726710c557188ace4e961064a65900524aMarcus Hagerott        }));
227396aab726710c557188ace4e961064a65900524aMarcus Hagerott    }
228396aab726710c557188ace4e961064a65900524aMarcus Hagerott
229396aab726710c557188ace4e961064a65900524aMarcus Hagerott    /**
230396aab726710c557188ace4e961064a65900524aMarcus Hagerott     * Returns true if there are real accounts (not "local" account) in the list of accounts.
231396aab726710c557188ace4e961064a65900524aMarcus Hagerott     */
232396aab726710c557188ace4e961064a65900524aMarcus Hagerott    public boolean hasNonLocalAccount() {
233c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        final List<AccountWithDataSet> allAccounts =
234c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott                AccountInfo.extractAccounts(Futures.getUnchecked(getAccountsAsync()));
235396aab726710c557188ace4e961064a65900524aMarcus Hagerott        if (allAccounts == null || allAccounts.size() == 0) {
236396aab726710c557188ace4e961064a65900524aMarcus Hagerott            return false;
237396aab726710c557188ace4e961064a65900524aMarcus Hagerott        }
238396aab726710c557188ace4e961064a65900524aMarcus Hagerott        if (allAccounts.size() > 1) {
239396aab726710c557188ace4e961064a65900524aMarcus Hagerott            return true;
240396aab726710c557188ace4e961064a65900524aMarcus Hagerott        }
241396aab726710c557188ace4e961064a65900524aMarcus Hagerott        return !allAccounts.get(0).isNullAccount();
242396aab726710c557188ace4e961064a65900524aMarcus Hagerott    }
243396aab726710c557188ace4e961064a65900524aMarcus Hagerott
24456c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    static Account getDefaultGoogleAccount(AccountManager accountManager,
24556c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang            SharedPreferences prefs, String defaultAccountKey) {
24656c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        // Get all the google accounts on the device
24756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        final Account[] accounts = accountManager.getAccountsByType(
24856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                GoogleAccountType.ACCOUNT_TYPE);
24956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        if (accounts == null || accounts.length == 0) {
25056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang            return null;
25156c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        }
25256c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
25356c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        // Get the default account from preferences
25456c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        final String defaultAccount = prefs.getString(defaultAccountKey, null);
25556c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        final AccountWithDataSet accountWithDataSet = defaultAccount == null ? null :
25656c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                AccountWithDataSet.unstringify(defaultAccount);
25756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
25856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        // Look for an account matching the one from preferences
25956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        if (accountWithDataSet != null) {
26056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang            for (int i = 0; i < accounts.length; i++) {
26156c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                if (TextUtils.equals(accountWithDataSet.name, accounts[i].name)
26256c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                        && TextUtils.equals(accountWithDataSet.type, accounts[i].type)) {
26356c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                    return accounts[i];
26456c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                }
26556c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang            }
26656c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        }
26756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
26856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        // Just return the first one
26956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        return accounts[0];
27056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    }
27156c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
2726c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
2736c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
2746c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public final AccountType getAccountType(String accountType, String dataSet) {
2756c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
2766c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
2776c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
2786c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
2792ae200dc7b2995f6758887cce8dedf563b3f3aa3Jay Shrauner        if (account != null) {
2802ae200dc7b2995f6758887cce8dedf563b3f3aa3Jay Shrauner            return getAccountType(account.getAccountTypeWithDataSet());
2812ae200dc7b2995f6758887cce8dedf563b3f3aa3Jay Shrauner        }
2822ae200dc7b2995f6758887cce8dedf563b3f3aa3Jay Shrauner        return getAccountType(null, null);
2836c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
2846c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
2856c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
2866c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Find the best {@link DataKind} matching the requested
2876c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
2886c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * If no direct match found, we try searching {@link FallbackAccountType}.
2896c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
2906c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public DataKind getKindOrFallback(AccountType type, String mimeType) {
2916c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        return type == null ? null : type.getKindForMimetype(mimeType);
2926c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
2936c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
2946c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
295c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * Returns whether the specified account still exists
2966c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
297c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    public boolean exists(AccountWithDataSet account) {
298c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        final List<AccountWithDataSet> accounts =
299c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott                AccountInfo.extractAccounts(Futures.getUnchecked(getAccountsAsync()));
300c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        return accounts.contains(account);
301c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    }
302c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott
303c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    /**
304c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * Returns whether the specified account is writable
305c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     *
306c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * <p>This checks that the account still exists and that
307c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * {@link AccountType#areContactsWritable()} is true</p>
308c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     */
309c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    public boolean isWritable(AccountWithDataSet account) {
310c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        return exists(account) && getAccountInfoForAccount(account).getType().areContactsWritable();
3116c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
312fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott
313596eb7e7cefd97fddc738c0600c24b08cbba827fMarcus Hagerott    public boolean hasGoogleAccount() {
314596eb7e7cefd97fddc738c0600c24b08cbba827fMarcus Hagerott        return getDefaultGoogleAccount() != null;
315596eb7e7cefd97fddc738c0600c24b08cbba827fMarcus Hagerott    }
316596eb7e7cefd97fddc738c0600c24b08cbba827fMarcus Hagerott
317fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott    private static boolean hasRequiredPermissions(Context context) {
318fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        final boolean canGetAccounts = ContextCompat.checkSelfPermission(context,
319fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott                android.Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED;
320fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        final boolean canReadContacts = ContextCompat.checkSelfPermission(context,
321fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott                android.Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED;
322fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott        return canGetAccounts && canReadContacts;
323fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott    }
324fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott
32575895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public static Predicate<AccountInfo> writableFilter() {
326ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        return AccountFilter.CONTACTS_WRITABLE;
327e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    }
328e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
32975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public static Predicate<AccountInfo> groupWritableFilter() {
330ee3d3a503553de2699285d98bc6fe7d34699a8b0Marcus Hagerott        return AccountFilter.GROUPS_WRITABLE;
331e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    }
332aa0e6ffbb780316a4e6e96e64085afb6e8679867Wenyi Wang}
333aa0e6ffbb780316a4e6e96e64085afb6e8679867Wenyi Wang
3346c712f45c80cef426a86fa14668a935c488d0ee4Chiao Chengclass AccountTypeManagerImpl extends AccountTypeManager
3356c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        implements OnAccountsUpdateListener, SyncStatusObserver {
3366c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3372a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final Context mContext;
3382a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final AccountManager mAccountManager;
3392a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final DeviceLocalAccountLocator mLocalAccountLocator;
3402a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final Executor mMainThreadExecutor;
3412a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final ListeningExecutorService mExecutor;
342e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    private AccountTypeProvider mTypeProvider;
3436c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3442a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final AccountType mFallbackAccountType;
3456c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
346c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    private ListenableFuture<List<AccountWithDataSet>> mLocalAccountsFuture;
347c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    private ListenableFuture<AccountTypeProvider> mAccountTypesFuture;
348c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott
3492a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private List<AccountWithDataSet> mLocalAccounts = new ArrayList<>();
3502a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private List<AccountWithDataSet> mAccountManagerAccounts = new ArrayList<>();
3516c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3526c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
3536c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3542a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private final Function<AccountTypeProvider, List<AccountWithDataSet>> mAccountsExtractor =
3552a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            new Function<AccountTypeProvider, List<AccountWithDataSet>>() {
3562a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                @Nullable
3572a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                @Override
3582a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                public List<AccountWithDataSet> apply(@Nullable AccountTypeProvider typeProvider) {
3592a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                    return getAccountsWithDataSets(mAccountManager.getAccounts(), typeProvider);
3602a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                }
3612a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            };
3622a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
3632a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
3646c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
3656c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        @Override
3666c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        public void onReceive(Context context, Intent intent) {
3672a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            // Don't use reloadAccountTypesIfNeeded when packages change in case a contacts.xml
3682a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            // was updated.
36904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            reloadAccountTypes();
3706c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
3716c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    };
3726c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3736c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
3746c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Internal constructor that only performs initial parsing.
3756c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
37604be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    public AccountTypeManagerImpl(Context context) {
3776c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        mContext = context;
378c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        mLocalAccountLocator = DeviceLocalAccountLocator.create(context);
379e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        mTypeProvider = new AccountTypeProvider(context);
3806c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        mFallbackAccountType = new FallbackAccountType(context);
3816c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
382413589f0b9279e06d8913761cdcf03c8fef7ffd8Ihab Awad        mAccountManager = AccountManager.get(mContext);
3836c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
38404be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        mExecutor = ContactsExecutors.getDefaultThreadPoolExecutor();
38504be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        mMainThreadExecutor = ContactsExecutors.newHandlerExecutor(mMainThreadHandler);
3866c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3876c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        // Request updates when packages or accounts change
3886c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
3896c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
3906c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
3916c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        filter.addDataScheme("package");
3926c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        mContext.registerReceiver(mBroadcastReceiver, filter);
3936c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        IntentFilter sdFilter = new IntentFilter();
3946c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
3956c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
3966c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
3976c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
3986c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        // Request updates when locale is changed so that the order of each field will
3996c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        // be able to be changed on the locale change.
4006c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
4016c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        mContext.registerReceiver(mBroadcastReceiver, filter);
4026c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
40304be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        mAccountManager.addOnAccountsUpdatedListener(this, mMainThreadHandler, false);
4046c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
4056c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
4066c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
407ffcb31f6ece6257d0bc50aa572de0d4b93c82e97Walter Jang        if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
408511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott            // Observe changes to RAW_CONTACTS so that we will update the list of "Device" accounts
409511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott            // if a new device contact is added.
410511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott            mContext.getContentResolver().registerContentObserver(
411511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                    ContactsContract.RawContacts.CONTENT_URI, /* notifyDescendents */ true,
41204be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott                    new ContentObserver(mMainThreadHandler) {
413511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        @Override
414511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        public boolean deliverSelfNotifications() {
415511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                            return true;
416511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        }
417511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott
418511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        @Override
419511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        public void onChange(boolean selfChange) {
420c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott                            reloadLocalAccounts();
421511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        }
422511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott
423511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        @Override
424511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        public void onChange(boolean selfChange, Uri uri) {
425c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott                            reloadLocalAccounts();
426511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                        }
427511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott                    });
428511504f977e83a60049bc051ee7e6064ca2b0264Marcus Hagerott        }
429e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        loadAccountTypes();
4306c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
4316c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
4326c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    @Override
4336c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public void onStatusChanged(int which) {
4342a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        reloadAccountTypesIfNeeded();
4356c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
4366c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
4379a679e742e9e738d659f6f8a566c1db7a252f77fMarcus Hagerott    /* This notification will arrive on the UI thread */
4386c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public void onAccountsUpdated(Account[] accounts) {
4394fabad6a2949f028f575c1cbde6b01368cd78d60Marcus Hagerott        reloadLocalAccounts();
4402a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        maybeNotifyAccountsUpdated(mAccountManagerAccounts,
4412a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                getAccountsWithDataSets(accounts, mTypeProvider));
4422a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    }
4432a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
4442a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private void maybeNotifyAccountsUpdated(List<AccountWithDataSet> current,
4452a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            List<AccountWithDataSet> update) {
4462a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        if (Objects.equal(current, update)) {
4472a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            return;
4482a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        }
4492a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        current.clear();
4502a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        current.addAll(update);
4512a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        notifyAccountsChanged();
4526c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
4536c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
4542a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private void notifyAccountsChanged() {
45504be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        ContactListFilterController.getInstance(mContext).checkFilterValidity(true);
456e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        LocalBroadcastManager.getInstance(mContext).sendBroadcast(
457e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott                new Intent(BROADCAST_ACCOUNTS_CHANGED));
4586c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
4596c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
46004be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    private synchronized void startLoadingIfNeeded() {
461c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        if (mTypeProvider == null && mAccountTypesFuture == null) {
4622a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            reloadAccountTypesIfNeeded();
4636c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
464c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        if (mLocalAccountsFuture == null) {
465c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott            reloadLocalAccounts();
466c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        }
46704be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    }
4686c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
4692a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private synchronized void loadAccountTypes() {
470e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        mTypeProvider = new AccountTypeProvider(mContext);
4716c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
472c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        mAccountTypesFuture = mExecutor.submit(new Callable<AccountTypeProvider>() {
47304be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            @Override
47404be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            public AccountTypeProvider call() throws Exception {
4752a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                // This will request the AccountType for each Account forcing them to be loaded
4762a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                getAccountsWithDataSets(mAccountManager.getAccounts(), mTypeProvider);
477e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott                return mTypeProvider;
478fac695a36b227f5591eac064ded8e8dad3398400Marcus Hagerott            }
47904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        });
480e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    }
481e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
4822a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private FutureCallback<List<AccountWithDataSet>> newAccountsUpdatedCallback(
4832a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            final List<AccountWithDataSet> currentAccounts) {
4842a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        return new FutureCallback<List<AccountWithDataSet>>() {
4852a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            @Override
4862a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            public void onSuccess(List<AccountWithDataSet> result) {
4872a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                maybeNotifyAccountsUpdated(currentAccounts, result);
4882a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            }
4892a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
4902a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            @Override
4912a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            public void onFailure(Throwable t) {
4922a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            }
4932a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        };
4942a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    }
4952a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
4962a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private synchronized void reloadAccountTypesIfNeeded() {
4972a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        if (mTypeProvider == null || mTypeProvider.shouldUpdate(
4982a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                mAccountManager.getAuthenticatorTypes(), ContentResolver.getSyncAdapterTypes())) {
4992a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            reloadAccountTypes();
5002a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        }
5012a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    }
5022a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
503c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    private synchronized void reloadAccountTypes() {
504e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott        loadAccountTypes();
5052a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        Futures.addCallback(
5062a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                Futures.transform(mAccountTypesFuture, mAccountsExtractor),
5072a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                newAccountsUpdatedCallback(mAccountManagerAccounts),
5082a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                mMainThreadExecutor);
509c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    }
5106c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
511c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    private synchronized void loadLocalAccounts() {
512c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        mLocalAccountsFuture = mExecutor.submit(new Callable<List<AccountWithDataSet>>() {
51304be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            @Override
514c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott            public List<AccountWithDataSet> call() throws Exception {
515c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott                return mLocalAccountLocator.getDeviceLocalAccounts();
5166c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            }
517c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        });
518c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott    }
519c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott
5202a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private synchronized void reloadLocalAccounts() {
521c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        loadLocalAccounts();
5222a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        Futures.addCallback(mLocalAccountsFuture, newAccountsUpdatedCallback(mLocalAccounts),
5232a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                mMainThreadExecutor);
5246c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
5256c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
52667a0639c912ce5bed209d791f0bf8d09853bdbbdMarcus Hagerott    @Override
52775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public ListenableFuture<List<AccountInfo>> getAccountsAsync() {
52875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        return getAllAccountsAsyncInternal();
52904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    }
53004be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott
5312a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private synchronized ListenableFuture<List<AccountInfo>> getAllAccountsAsyncInternal() {
53204be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        startLoadingIfNeeded();
53375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        final AccountTypeProvider typeProvider = mTypeProvider;
5342a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        final ListenableFuture<List<List<AccountWithDataSet>>> all =
5352a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                Futures.nonCancellationPropagating(
5362a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                        Futures.successfulAsList(
5372a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                                Futures.transform(mAccountTypesFuture, mAccountsExtractor),
5382a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                                mLocalAccountsFuture));
539c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott
5402a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        return Futures.transform(all, new Function<List<List<AccountWithDataSet>>,
54175895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                List<AccountInfo>>() {
5422a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            @Nullable
543c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott            @Override
5442a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            public List<AccountInfo> apply(@Nullable List<List<AccountWithDataSet>> input) {
54575895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                // input.get(0) contains accounts from AccountManager
54675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                // input.get(1) contains device local accounts
54775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                Preconditions.checkArgument(input.size() == 2,
54875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                        "List should have exactly 2 elements");
54975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott
5502a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                final List<AccountInfo> result = new ArrayList<>();
5512a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                for (AccountWithDataSet account : input.get(0)) {
5522a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                    result.add(
5532a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                            typeProvider.getTypeForAccount(account).wrapAccount(mContext, account));
5542a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                }
5552a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott
5562a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                for (AccountWithDataSet account : input.get(1)) {
5572a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                    result.add(
5582a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                            typeProvider.getTypeForAccount(account).wrapAccount(mContext, account));
559c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott                }
56075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                AccountInfo.sortAccounts(null, result);
561c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott                return result;
562c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott            }
563c2093f312f71e730e1cc4daaa305782fda394215Marcus Hagerott        });
564e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott    }
565e7a71cbbe8929a9d8a7ca0a3b5668646d49a611aMarcus Hagerott
56675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    @Override
56775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public ListenableFuture<List<AccountInfo>> filterAccountsAsync(
56875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            final Predicate<AccountInfo> filter) {
56975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        return Futures.transform(getAllAccountsAsyncInternal(), new Function<List<AccountInfo>,
57075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                List<AccountInfo>>() {
57175895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            @Override
57275895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            public List<AccountInfo> apply(List<AccountInfo> input) {
57375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                return new ArrayList<>(Collections2.filter(input, filter));
57475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            }
57575895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        }, mExecutor);
57675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    }
57775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott
57875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    @Override
57975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public AccountInfo getAccountInfoForAccount(AccountWithDataSet account) {
5804bd50d69d2b8934dbd4cbd634ad83807d9b02735Marcus Hagerott        if (account == null) {
5814bd50d69d2b8934dbd4cbd634ad83807d9b02735Marcus Hagerott            return null;
5824bd50d69d2b8934dbd4cbd634ad83807d9b02735Marcus Hagerott        }
5834fbcd1943f493524e96db648e2a6957689fcc83dMarcus Hagerott        AccountType type = mTypeProvider.getTypeForAccount(account);
58475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        if (type == null) {
5854fbcd1943f493524e96db648e2a6957689fcc83dMarcus Hagerott            type = mFallbackAccountType;
58675895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        }
58775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        return type.wrapAccount(mContext, account);
58875895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    }
58975895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott
5902a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott    private List<AccountWithDataSet> getAccountsWithDataSets(Account[] accounts,
5912a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott            AccountTypeProvider typeProvider) {
5922a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott        List<AccountWithDataSet> result = new ArrayList<>();
59304be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        for (Account account : accounts) {
59475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            final List<AccountType> types = typeProvider.getAccountTypes(account.type);
59504be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            for (AccountType type : types) {
5962a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                result.add(new AccountWithDataSet(
5972a4673a93880c43d9dda547e7bdc5ee35a1b025cMarcus Hagerott                        account.name, account.type, type.dataSet));
59804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott            }
59904be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        }
60004be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott        return result;
60104be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott    }
60204be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott
6036c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
60456c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang     * Returns the default google account specified in preferences, the first google account
60556c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang     * if it is not specified in preferences or is no longer on the device, and null otherwise.
60656c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang     */
60756c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    @Override
60856c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    public Account getDefaultGoogleAccount() {
60956c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        final SharedPreferences sharedPreferences =
61056c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                mContext.getSharedPreferences(mContext.getPackageName(), Context.MODE_PRIVATE);
61156c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang        final String defaultAccountKey =
61256c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang                mContext.getResources().getString(R.string.contact_editor_default_account_key);
613396aab726710c557188ace4e961064a65900524aMarcus Hagerott        return getDefaultGoogleAccount(mAccountManager, sharedPreferences, defaultAccountKey);
614396aab726710c557188ace4e961064a65900524aMarcus Hagerott    }
615396aab726710c557188ace4e961064a65900524aMarcus Hagerott
616396aab726710c557188ace4e961064a65900524aMarcus Hagerott    @Override
61775895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott    public List<AccountInfo> getWritableGoogleAccounts() {
618396aab726710c557188ace4e961064a65900524aMarcus Hagerott        final Account[] googleAccounts =
619396aab726710c557188ace4e961064a65900524aMarcus Hagerott                mAccountManager.getAccountsByType(GoogleAccountType.ACCOUNT_TYPE);
62075895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott        final List<AccountInfo> result = new ArrayList<>();
621396aab726710c557188ace4e961064a65900524aMarcus Hagerott        for (Account account : googleAccounts) {
62275895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
62375895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott                    account.name, account.type, null);
62475895e73379aa26a3d4135c772af4ecb8a79b4c9Marcus Hagerott            final AccountType type = mTypeProvider.getTypeForAccount(accountWithDataSet);
625032560017404fd778dfb8a72674eb5e9fd35332fNicholas Sauer            if (type != null) {
626032560017404fd778dfb8a72674eb5e9fd35332fNicholas Sauer                // Accounts with a dataSet (e.g. Google plus accounts) are not writable.
627032560017404fd778dfb8a72674eb5e9fd35332fNicholas Sauer                result.add(type.wrapAccount(mContext, accountWithDataSet));
628032560017404fd778dfb8a72674eb5e9fd35332fNicholas Sauer            }
629396aab726710c557188ace4e961064a65900524aMarcus Hagerott        }
630396aab726710c557188ace4e961064a65900524aMarcus Hagerott        return result;
631396aab726710c557188ace4e961064a65900524aMarcus Hagerott    }
632396aab726710c557188ace4e961064a65900524aMarcus Hagerott
633c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    /**
634c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * Returns true if there are real accounts (not "local" account) in the list of accounts.
635c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     *
636c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * <p>This is overriden for performance since the default implementation blocks until all
637c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * accounts are loaded
638c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * </p>
639c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     */
640396aab726710c557188ace4e961064a65900524aMarcus Hagerott    @Override
641396aab726710c557188ace4e961064a65900524aMarcus Hagerott    public boolean hasNonLocalAccount() {
642396aab726710c557188ace4e961064a65900524aMarcus Hagerott        final Account[] accounts = mAccountManager.getAccounts();
6437778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott        if (accounts == null) {
6447778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott            return false;
6457778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott        }
6467778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott        for (Account account : accounts) {
6477778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott            if (mTypeProvider.supportsContactsSyncing(account.type)) {
6487778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott                return true;
6497778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott            }
6507778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott        }
6517778080136f70e4f5ac90a3bb8281addf5898894Marcus Hagerott        return false;
65256c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    }
65356c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang
65456c8a0cce9ea98e0f088e74474a57968a02810abWenyi Wang    /**
6556c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Find the best {@link DataKind} matching the requested
6566c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
6576c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * If no direct match found, we try searching {@link FallbackAccountType}.
6586c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
6596c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    @Override
6606c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public DataKind getKindOrFallback(AccountType type, String mimeType) {
6616c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        DataKind kind = null;
6626c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6636c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        // Try finding account type and kind matching request
6646c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        if (type != null) {
6656c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            kind = type.getKindForMimetype(mimeType);
6666c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
6676c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6686c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        if (kind == null) {
6696c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            // Nothing found, so try fallback as last resort
6706c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng            kind = mFallbackAccountType.getKindForMimetype(mimeType);
6716c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
6726c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6736c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        if (kind == null) {
6745df737617966dc1a3e3150443a30eddb3a1ac057Chiao Cheng            if (Log.isLoggable(TAG, Log.DEBUG)) {
6755df737617966dc1a3e3150443a30eddb3a1ac057Chiao Cheng                Log.d(TAG, "Unknown type=" + type + ", mime=" + mimeType);
6765df737617966dc1a3e3150443a30eddb3a1ac057Chiao Cheng            }
6776c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        }
6786c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6796c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng        return kind;
6806c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
6816c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng
6826c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    /**
683c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * Returns whether the account still exists on the device
684c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     *
685c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * <p>This is overridden for performance. The default implementation loads all accounts then
686c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * searches through them for specified. This implementation will only load the types for the
687c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * specified AccountType (it may still require blocking on IO in some cases but it shouldn't
688c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * be as bad as blocking for all accounts).
689c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     * </p>
690c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott     */
691c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    @Override
692c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    public boolean exists(AccountWithDataSet account) {
693c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        final Account[] accounts = mAccountManager.getAccountsByType(account.type);
694c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        for (Account existingAccount : accounts) {
695c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott            if (existingAccount.name.equals(account.name)) {
696c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott                return mTypeProvider.getTypeForAccount(account) != null;
697c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott            }
698c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        }
699c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott        return false;
700c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    }
701c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott
702c34812341069afae7ddd253fc45b233706b739d9Marcus Hagerott    /**
7036c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     * Return {@link AccountType} for the given account type and data set.
7046c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng     */
7056c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    @Override
7066c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
7074fbcd1943f493524e96db648e2a6957689fcc83dMarcus Hagerott        final AccountType type = mTypeProvider.getType(
70804be88c2bec8d0a0cc9e1f4f0af3b3a2ff8ac4e7Marcus Hagerott                accountTypeWithDataSet.accountType, accountTypeWithDataSet.dataSet);
7094fbcd1943f493524e96db648e2a6957689fcc83dMarcus Hagerott        return type != null ? type : mFallbackAccountType;
7106c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng    }
7116c712f45c80cef426a86fa14668a935c488d0ee4Chiao Cheng}
712