1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.contacts.model;
17
18import android.accounts.Account;
19import android.accounts.AccountManager;
20import android.content.Context;
21
22import com.android.contacts.Experiments;
23import com.android.contacts.model.account.AccountWithDataSet;
24import com.android.contacts.model.account.GoogleAccountType;
25import com.android.contactsbind.ObjectFactory;
26import com.android.contactsbind.experiments.Flags;
27
28import java.util.Collections;
29import java.util.HashSet;
30import java.util.List;
31import java.util.Set;
32
33/**
34 * Attempts to detect accounts for device contacts
35 */
36public abstract class DeviceLocalAccountLocator {
37
38    /**
39     * Returns a list of device local accounts
40     */
41    public abstract List<AccountWithDataSet> getDeviceLocalAccounts();
42
43    // This works on Nexus and AOSP because the local device account is the null account but most
44    // OEMs have a special account name and type for their device account.
45    public static final DeviceLocalAccountLocator NULL_ONLY = new DeviceLocalAccountLocator() {
46        @Override
47        public List<AccountWithDataSet> getDeviceLocalAccounts() {
48            return Collections.singletonList(AccountWithDataSet.getNullAccount());
49        }
50    };
51
52    public static DeviceLocalAccountLocator create(Context context,
53            Set<String> knownAccountTypes) {
54        if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
55            return new Cp2DeviceLocalAccountLocator(context.getContentResolver(),
56                    ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownAccountTypes);
57        }
58        return NULL_ONLY;
59    }
60
61    public static DeviceLocalAccountLocator create(Context context) {
62        final AccountManager accountManager =
63                (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
64        final Set<String> knownTypes = new HashSet<>();
65        for (Account account : accountManager.getAccounts()) {
66            knownTypes.add(account.type);
67        }
68        if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
69            return new Cp2DeviceLocalAccountLocator(context.getContentResolver(),
70                    ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownTypes);
71        } else {
72            return new NexusDeviceAccountLocator(accountManager);
73        }
74    }
75
76    /**
77     * On Nexus the "device" account uses "null" values for the account name and type columns
78     *
79     * <p>However, the focus sync adapter automatically migrates contacts from this null
80     * account to a Google account if one exists. Hence, the device account should be returned
81     * only when there is no Google Account added
82     * </p>
83     */
84    public static class NexusDeviceAccountLocator extends DeviceLocalAccountLocator {
85
86        private final AccountManager mAccountManager;
87
88        public NexusDeviceAccountLocator(AccountManager accountManager) {
89            mAccountManager = accountManager;
90        }
91
92        @Override
93        public List<AccountWithDataSet> getDeviceLocalAccounts() {
94            @SuppressWarnings("MissingPermission")
95            final Account[] accounts = mAccountManager
96                    .getAccountsByType(GoogleAccountType.ACCOUNT_TYPE);
97
98            if (accounts.length > 0) {
99                return Collections.emptyList();
100            } else {
101                return Collections.singletonList(AccountWithDataSet.getNullAccount());
102            }
103        }
104    }
105}
106