diff options
author | Marcus Hagerott <mhagerott@google.com> | 2020-06-10 10:18:42 -0700 |
---|---|---|
committer | Marcus Hagerott <mhagerott@google.com> | 2020-06-10 12:39:45 -0700 |
commit | c23eb65efc13c87aa76cf2a514232dbf5790a4d5 (patch) | |
tree | 2c2551d7cdcae41b316fe81f927c62075729bd42 /src | |
parent | 85cf6e6edafd9d56795f1ed6400b97be8920ab21 (diff) | |
download | Contacts-c23eb65efc13c87aa76cf2a514232dbf5790a4d5.tar.gz |
Use framework API to get local contacts account
Test: built the app and manually verified that the local account
is available when no google account is present but is hidden when a
google account is added and it has not contacts.
Bug: 155985097
Change-Id: I7d15c10ce154aabf6d6ced06459c49ac4a7e5053
Diffstat (limited to 'src')
7 files changed, 40 insertions, 244 deletions
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java index dea134b5f..8f0509bab 100644 --- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java +++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java @@ -141,7 +141,8 @@ public class ContactEditorAccountsChangedActivity extends Activity mAccountListAdapter = new AccountsListAdapter(this, accounts); accountListView.setAdapter(mAccountListAdapter); accountListView.setOnItemClickListener(mAccountListItemClickListener); - } else if (numAccounts == 1 && !accounts.get(0).getAccount().isNullAccount()) { + } else if (numAccounts == 1 + && !accounts.get(0).getAccount().equals(AccountWithDataSet.getLocalAccount(this))) { // If the user has 1 writable account we will just show the user a message with 2 // possible action buttons. view = View.inflate(this, diff --git a/src/com/android/contacts/editor/ContactEditorUtils.java b/src/com/android/contacts/editor/ContactEditorUtils.java index 517c2c70b..0e9b5c90b 100644 --- a/src/com/android/contacts/editor/ContactEditorUtils.java +++ b/src/com/android/contacts/editor/ContactEditorUtils.java @@ -38,9 +38,11 @@ import java.util.List; public class ContactEditorUtils { private static final String TAG = "ContactEditorUtils"; + private final Context mContext; private final ContactsPreferences mContactsPrefs; private ContactEditorUtils(Context context) { + mContext = context; mContactsPrefs = new ContactsPreferences(context); } @@ -122,7 +124,7 @@ public class ContactEditorUtils { public void maybeUpdateDefaultAccount(List<AccountWithDataSet> currentWritableAccounts) { if (currentWritableAccounts.size() == 1) { final AccountWithDataSet onlyAccount = currentWritableAccounts.get(0); - if (!onlyAccount.isNullAccount() + if (!onlyAccount.equals(AccountWithDataSet.getLocalAccount(mContext)) && !onlyAccount.equals(mContactsPrefs.getDefaultAccount())) { mContactsPrefs.setDefaultAccount(onlyAccount); } diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java index 196e67ff9..5a14b09cf 100644 --- a/src/com/android/contacts/model/AccountTypeManager.java +++ b/src/com/android/contacts/model/AccountTypeManager.java @@ -27,17 +27,14 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SyncStatusObserver; import android.content.pm.PackageManager; -import android.database.ContentObserver; -import android.net.Uri; import android.os.Handler; import android.os.Looper; -import android.provider.ContactsContract; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; -import com.android.contacts.Experiments; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import com.android.contacts.R; import com.android.contacts.list.ContactListFilterController; import com.android.contacts.model.account.AccountInfo; @@ -49,10 +46,10 @@ import com.android.contacts.model.account.FallbackAccountType; import com.android.contacts.model.account.GoogleAccountType; import com.android.contacts.model.dataitem.DataKind; import com.android.contacts.util.concurrent.ContactsExecutors; -import com.android.contactsbind.experiments.Flags; -import com.google.common.base.Preconditions; + import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.util.concurrent.FutureCallback; @@ -376,7 +373,7 @@ class AccountTypeManagerImpl extends AccountTypeManager */ public AccountTypeManagerImpl(Context context) { mContext = context; - mLocalAccountLocator = DeviceLocalAccountLocator.create(context); + mLocalAccountLocator = new DeviceLocalAccountLocator(context, AccountManager.get(context)); mTypeProvider = new AccountTypeProvider(context); mFallbackAccountType = new FallbackAccountType(context); @@ -405,26 +402,6 @@ class AccountTypeManagerImpl extends AccountTypeManager ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this); - // Observe changes to RAW_CONTACTS so that we will update the list of "Device" accounts - // if a new device contact is added or removed. - mContext.getContentResolver().registerContentObserver( - ContactsContract.RawContacts.CONTENT_URI, /* notifyDescendents */ true, - new ContentObserver(mMainThreadHandler) { - @Override - public boolean deliverSelfNotifications() { - return true; - } - - @Override - public void onChange(boolean selfChange) { - reloadLocalAccounts(); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - reloadLocalAccounts(); - } - }); loadAccountTypes(); } diff --git a/src/com/android/contacts/model/Cp2DeviceLocalAccountLocator.java b/src/com/android/contacts/model/Cp2DeviceLocalAccountLocator.java deleted file mode 100644 index 32912dc11..000000000 --- a/src/com/android/contacts/model/Cp2DeviceLocalAccountLocator.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.contacts.model; - -import android.accounts.AccountManager; -import android.content.ContentResolver; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract; -import androidx.annotation.VisibleForTesting; - -import com.android.contacts.model.account.AccountWithDataSet; -import com.android.contacts.util.DeviceLocalAccountTypeFactory; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Attempts to create accounts for "Device" contacts by querying - * CP2 for records with {@link android.provider.ContactsContract.RawContacts#ACCOUNT_TYPE} columns - * that do not exist for any account returned by {@link AccountManager#getAccounts()} - * - * This class should be used from a background thread since it does DB queries - */ -public class Cp2DeviceLocalAccountLocator extends DeviceLocalAccountLocator { - - // Note this class is assuming ACCOUNT_NAME and ACCOUNT_TYPE have same values in - // RawContacts, Groups, and Settings. This assumption simplifies the code somewhat and it - // is true right now and unlikely to ever change. - @VisibleForTesting - static String[] PROJECTION = new String[] { - ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE, - ContactsContract.RawContacts.DATA_SET - }; - - private static final int COL_NAME = 0; - private static final int COL_TYPE = 1; - private static final int COL_DATA_SET = 2; - - private final ContentResolver mResolver; - private final DeviceLocalAccountTypeFactory mAccountTypeFactory; - - private final String mSelection; - private final String[] mSelectionArgs; - - public Cp2DeviceLocalAccountLocator(ContentResolver contentResolver, - DeviceLocalAccountTypeFactory factory, - Set<String> knownAccountTypes) { - mResolver = contentResolver; - mAccountTypeFactory = factory; - - mSelection = getSelection(knownAccountTypes); - mSelectionArgs = getSelectionArgs(knownAccountTypes); - } - - @Override - public List<AccountWithDataSet> getDeviceLocalAccounts() { - - final Set<AccountWithDataSet> localAccounts = new HashSet<>(); - - // Many device accounts have default groups associated with them. - addAccountsFromQuery(ContactsContract.Groups.CONTENT_URI, localAccounts); - addAccountsFromQuery(ContactsContract.Settings.CONTENT_URI, localAccounts); - addAccountsFromQuery(ContactsContract.RawContacts.CONTENT_URI, localAccounts); - - return new ArrayList<>(localAccounts); - } - - private void addAccountsFromQuery(Uri uri, Set<AccountWithDataSet> accounts) { - final Cursor cursor = mResolver.query(uri, PROJECTION, mSelection, mSelectionArgs, null); - - if (cursor == null) return; - - try { - addAccountsFromCursor(cursor, accounts); - } finally { - cursor.close(); - } - } - - private void addAccountsFromCursor(Cursor cursor, Set<AccountWithDataSet> accounts) { - while (cursor.moveToNext()) { - final String name = cursor.getString(COL_NAME); - final String type = cursor.getString(COL_TYPE); - final String dataSet = cursor.getString(COL_DATA_SET); - - if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( - mAccountTypeFactory, type)) { - accounts.add(new AccountWithDataSet(name, type, dataSet)); - } - } - } - - @VisibleForTesting - public String getSelection() { - return mSelection; - } - - @VisibleForTesting - public String[] getSelectionArgs() { - return mSelectionArgs; - } - - private static String getSelection(Set<String> knownAccountTypes) { - final StringBuilder sb = new StringBuilder() - .append(ContactsContract.RawContacts.ACCOUNT_TYPE).append(" IS NULL"); - if (knownAccountTypes.isEmpty()) { - return sb.toString(); - } - sb.append(" OR ").append(ContactsContract.RawContacts.ACCOUNT_TYPE).append(" NOT IN ("); - for (String ignored : knownAccountTypes) { - sb.append("?,"); - } - // Remove trailing ',' - sb.deleteCharAt(sb.length() - 1).append(')'); - return sb.toString(); - } - - private static String[] getSelectionArgs(Set<String> knownAccountTypes) { - if (knownAccountTypes.isEmpty()) return null; - - return knownAccountTypes.toArray(new String[knownAccountTypes.size()]); - } -} diff --git a/src/com/android/contacts/model/DeviceLocalAccountLocator.java b/src/com/android/contacts/model/DeviceLocalAccountLocator.java index 2b987d3e7..e8a2ba0ff 100644 --- a/src/com/android/contacts/model/DeviceLocalAccountLocator.java +++ b/src/com/android/contacts/model/DeviceLocalAccountLocator.java @@ -18,92 +18,40 @@ package com.android.contacts.model; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; -import android.database.Cursor; import android.provider.ContactsContract; -import com.android.contacts.Experiments; import com.android.contacts.model.account.AccountWithDataSet; import com.android.contacts.model.account.GoogleAccountType; -import com.android.contactsbind.ObjectFactory; -import com.android.contactsbind.experiments.Flags; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** * Attempts to detect accounts for device contacts */ -public abstract class DeviceLocalAccountLocator { +public final class DeviceLocalAccountLocator { - /** - * Returns a list of device local accounts - */ - public abstract List<AccountWithDataSet> getDeviceLocalAccounts(); - - // This works on Nexus and AOSP because the local device account is the null account but most - // OEMs have a special account name and type for their device account. - public static final DeviceLocalAccountLocator NULL_ONLY = new DeviceLocalAccountLocator() { - @Override - public List<AccountWithDataSet> getDeviceLocalAccounts() { - return Collections.singletonList(AccountWithDataSet.getNullAccount()); - } - }; + private final Context mContext; + private final AccountManager mAccountManager; + private final List<AccountWithDataSet> mLocalAccount; - public static DeviceLocalAccountLocator create(Context context, - Set<String> knownAccountTypes) { - if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) { - return new Cp2DeviceLocalAccountLocator(context.getContentResolver(), - ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownAccountTypes); - } - return NULL_ONLY; - } - - public static DeviceLocalAccountLocator create(Context context) { - final AccountManager accountManager = - (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); - final Set<String> knownTypes = new HashSet<>(); - for (Account account : accountManager.getAccounts()) { - knownTypes.add(account.type); - } - if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) { - return new Cp2DeviceLocalAccountLocator(context.getContentResolver(), - ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownTypes); - } else { - return new NexusDeviceAccountLocator(context, accountManager); - } + public DeviceLocalAccountLocator(Context context, AccountManager accountManager) { + mContext = context; + mAccountManager = accountManager; + mLocalAccount = Collections.singletonList(AccountWithDataSet.getLocalAccount(context)); } /** - * On Nexus the "device" account uses "null" values for the account name and type columns - * - * <p>However, the focus sync adapter migrates contacts from this null account to a Google - * account if one exists. Hence, the device account should be returned only when there is no - * Google Account added or when there already exists contacts in the null account. - * </p> + * Returns a list of device local accounts */ - public static class NexusDeviceAccountLocator extends DeviceLocalAccountLocator { - private final Context mContext; - private final AccountManager mAccountManager; - + public List<AccountWithDataSet> getDeviceLocalAccounts() { + @SuppressWarnings("MissingPermission") final Account[] accounts = mAccountManager + .getAccountsByType(GoogleAccountType.ACCOUNT_TYPE); - public NexusDeviceAccountLocator(Context context, AccountManager accountManager) { - mContext = context; - mAccountManager = accountManager; - } - - @Override - public List<AccountWithDataSet> getDeviceLocalAccounts() { - @SuppressWarnings("MissingPermission") - final Account[] accounts = mAccountManager - .getAccountsByType(GoogleAccountType.ACCOUNT_TYPE); - - if (accounts.length > 0 && !AccountWithDataSet.getNullAccount().hasData(mContext)) { - return Collections.emptyList(); - } else { - return Collections.singletonList(AccountWithDataSet.getNullAccount()); - } + if (accounts.length > 0 && !mLocalAccount.get(0).hasData(mContext)) { + return Collections.emptyList(); + } else { + return mLocalAccount; } } } diff --git a/src/com/android/contacts/model/account/AccountWithDataSet.java b/src/com/android/contacts/model/account/AccountWithDataSet.java index a16313981..96717624e 100644 --- a/src/com/android/contacts/model/account/AccountWithDataSet.java +++ b/src/com/android/contacts/model/account/AccountWithDataSet.java @@ -28,8 +28,6 @@ import android.provider.ContactsContract; import android.provider.ContactsContract.RawContacts; import android.text.TextUtils; -import com.android.contacts.model.AccountTypeManager; -import com.android.contacts.preference.ContactsPreferences; import com.google.common.base.Objects; import com.google.common.collect.Lists; @@ -88,6 +86,14 @@ public class AccountWithDataSet implements Parcelable { return new AccountWithDataSet(null, null, null); } + public static AccountWithDataSet getLocalAccount(Context context) { + return new AccountWithDataSet( + RawContacts.getLocalAccountName(context), + RawContacts.getLocalAccountType(context), + null + ); + } + public Account getAccountOrNull() { if (name != null && type != null) { return new Account(name, type); diff --git a/src/com/android/contacts/preference/ContactsPreferences.java b/src/com/android/contacts/preference/ContactsPreferences.java index 85da89144..e92c35646 100644 --- a/src/com/android/contacts/preference/ContactsPreferences.java +++ b/src/com/android/contacts/preference/ContactsPreferences.java @@ -16,7 +16,6 @@ package com.android.contacts.preference; -import android.app.backup.BackupAgent; import android.app.backup.BackupManager; import android.content.Context; import android.content.SharedPreferences; @@ -27,9 +26,10 @@ import android.os.Looper; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import android.text.TextUtils; import com.android.contacts.R; import com.android.contacts.model.account.AccountWithDataSet; @@ -256,15 +256,16 @@ public class ContactsPreferences implements OnSharedPreferenceChangeListener { currentWritableAccounts) { final AccountWithDataSet defaultAccount = getDefaultAccount(); + AccountWithDataSet localAccount = AccountWithDataSet.getLocalAccount(mContext); // This shouldn't occur anymore because a "device" account is added in the case that there // are no other accounts but if there are no writable accounts then the default has been // initialized if it is "device" if (currentWritableAccounts.isEmpty()) { - return defaultAccount == null || !defaultAccount.isNullAccount(); + return defaultAccount == null || !defaultAccount.equals(localAccount); } if (currentWritableAccounts.size() == 1 - && !currentWritableAccounts.get(0).isNullAccount()) { + && !currentWritableAccounts.get(0).equals(localAccount)) { return false; } |