Loading src/com/android/contacts/activities/PeopleActivity.java +2 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,8 @@ public class PeopleActivity extends ContactsDrawerActivity implements if (getSupportActionBar() != null) { String actionBarTitle; if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) { if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS && filter.accountName == null) { actionBarTitle = getString(R.string.account_phone); } else if (!TextUtils.isEmpty(filter.accountName)) { actionBarTitle = getActionBarTitleForAccount(filter); Loading src/com/android/contacts/common/list/ContactListFilter.java +7 −3 Original line number Diff line number Diff line Loading @@ -93,6 +93,12 @@ public final class ContactListFilter implements Comparable<ContactListFilter>, P /* accountType= */ null, /* accountName= */ null, /* dataSet= */ null, icon); } public static ContactListFilter createDeviceContactsFilter(Drawable icon, AccountWithDataSet account) { return new ContactListFilter(ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS, account.type, account.name, account.dataSet, icon); } /** * Whether the given {@link ContactListFilter} has a filter type that should be displayed as * the default contacts list view. Loading Loading @@ -333,10 +339,8 @@ public final class ContactListFilter implements Comparable<ContactListFilter>, P } public AccountWithDataSet toAccountWithDataSet() { if (filterType == FILTER_TYPE_ACCOUNT) { if (filterType == FILTER_TYPE_ACCOUNT || filterType == FILTER_TYPE_DEVICE_CONTACTS) { return new AccountWithDataSet(accountName, accountType, dataSet); } else if (filterType == FILTER_TYPE_DEVICE_CONTACTS) { return AccountWithDataSet.getLocalAccount(); } else { throw new IllegalStateException("Cannot create Account from filter type " + filterTypeToString(filterType)); Loading src/com/android/contacts/common/model/DeviceLocalAccountLocator.java +64 −29 Original line number Diff line number Diff line Loading @@ -18,10 +18,10 @@ package com.android.contacts.common.model; import android.accounts.AccountManager; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import android.support.annotation.VisibleForTesting; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contacts.common.util.DeviceLocalAccountTypeFactory; Loading @@ -39,6 +39,9 @@ import java.util.Set; */ public class 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, Loading @@ -49,30 +52,59 @@ public class DeviceLocalAccountLocator { 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 Set<String> mKnownAccountTypes; private final String mSelection; private final String[] mSelectionArgs; public DeviceLocalAccountLocator(ContentResolver contentResolver, DeviceLocalAccountTypeFactory factory, List<AccountWithDataSet> knownAccounts) { mResolver = contentResolver; mAccountTypeFactory = factory; mKnownAccountTypes = new HashSet<>(); final Set<String> knownAccountTypes = new HashSet<>(); for (AccountWithDataSet account : knownAccounts) { mKnownAccountTypes.add(account.type); knownAccountTypes.add(account.type); } mSelection = getSelection(knownAccountTypes); mSelectionArgs = getSelectionArgs(knownAccountTypes); } public List<AccountWithDataSet> getDeviceLocalAccounts() { final String[] selectionArgs = getSelectionArgs(); final Cursor cursor = mResolver.query(ContactsContract.RawContacts.CONTENT_URI, PROJECTION, getSelection(), selectionArgs, null); 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); if (localAccounts.isEmpty()) { // It's probably safe to assume that if one of the earlier queries found a "device" // account then this query isn't going to find any different device accounts. // We skip this query because it probably is kind of expensive (relative to the other // queries). 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); Loading @@ -80,36 +112,39 @@ public class DeviceLocalAccountLocator { if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( mAccountTypeFactory, type)) { localAccounts.add(new AccountWithDataSet(name, type, dataSet)); accounts.add(new AccountWithDataSet(name, type, dataSet)); } } } finally { cursor.close(); } return new ArrayList<>(localAccounts); @VisibleForTesting public String getSelection() { return mSelection; } @VisibleForTesting public String getSelection() { final StringBuilder sb = new StringBuilder(); sb.append(ContactsContract.RawContacts.DELETED).append(" =0 AND (") 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 (mKnownAccountTypes.isEmpty()) { return sb.append(')').toString(); if (knownAccountTypes.isEmpty()) { return sb.toString(); } sb.append(" OR ").append(ContactsContract.RawContacts.ACCOUNT_TYPE).append(" NOT IN ("); for (String ignored : mKnownAccountTypes) { for (String ignored : knownAccountTypes) { sb.append("?,"); } // Remove trailing ',' sb.deleteCharAt(sb.length() - 1).append(')').append(')'); sb.deleteCharAt(sb.length() - 1).append(')'); return sb.toString(); } @VisibleForTesting public String[] getSelectionArgs() { return mKnownAccountTypes.toArray(new String[mKnownAccountTypes.size()]); private static String[] getSelectionArgs(Set<String> knownAccountTypes) { if (knownAccountTypes.isEmpty()) return null; return knownAccountTypes.toArray(new String[knownAccountTypes.size()]); } } src/com/android/contacts/common/model/account/AccountWithDataSet.java +1 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ public class AccountWithDataSet implements Parcelable { args = new String[] {type, name, dataSet}; } } selection += " AND " + RawContacts.DELETED + "=0"; final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1, ID_PROJECTION, selection, args, null); Loading src/com/android/contacts/common/util/AccountFilterUtil.java +15 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,9 @@ import android.content.AsyncTaskLoader; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log; Loading @@ -31,8 +33,10 @@ import com.android.contacts.common.list.AccountFilterActivity; import com.android.contacts.common.list.ContactListFilter; import com.android.contacts.common.list.ContactListFilterController; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.RawContact; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contactsbind.ObjectFactory; import com.google.common.collect.Lists; import java.util.ArrayList; Loading Loading @@ -90,15 +94,17 @@ public class AccountFilterUtil { */ public static class FilterLoader extends AsyncTaskLoader<List<ContactListFilter>> { private Context mContext; private DeviceLocalAccountTypeFactory mDeviceLocalFactory; public FilterLoader(Context context) { super(context); mContext = context; mDeviceLocalFactory = ObjectFactory.getDeviceLocalAccountTypeFactory(context); } @Override public List<ContactListFilter> loadInBackground() { return loadAccountFilters(mContext); return loadAccountFilters(mContext, mDeviceLocalFactory); } @Override Loading @@ -117,7 +123,8 @@ public class AccountFilterUtil { } } private static List<ContactListFilter> loadAccountFilters(Context context) { private static List<ContactListFilter> loadAccountFilters(Context context, DeviceLocalAccountTypeFactory deviceAccountTypeFactory) { final ArrayList<ContactListFilter> accountFilters = Lists.newArrayList(); final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context); accountTypeManager.sortAccounts(/* defaultAccount */ getDefaultAccount(context)); Loading @@ -127,13 +134,15 @@ public class AccountFilterUtil { for (AccountWithDataSet account : accounts) { final AccountType accountType = accountTypeManager.getAccountType(account.type, account.dataSet); if (accountType.isExtension() && !account.hasData(context)) { // Hide extensions with no raw_contacts. if ((accountType.isExtension() || DeviceLocalAccountTypeFactory.Util.isLocalAccountType( deviceAccountTypeFactory, account.type)) && !account.hasData(context)) { // Hide extensions and device accounts with no raw_contacts. continue; } final Drawable icon = accountType != null ? accountType.getDisplayIcon(context) : null; if (account.isLocalAccount()) { accountFilters.add(ContactListFilter.createDeviceContactsFilter(icon)); if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( deviceAccountTypeFactory, account.type)) { accountFilters.add(ContactListFilter.createDeviceContactsFilter(icon, account)); } else { accountFilters.add(ContactListFilter.createAccountFilter( account.type, account.name, account.dataSet, icon)); Loading Loading
src/com/android/contacts/activities/PeopleActivity.java +2 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,8 @@ public class PeopleActivity extends ContactsDrawerActivity implements if (getSupportActionBar() != null) { String actionBarTitle; if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) { if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS && filter.accountName == null) { actionBarTitle = getString(R.string.account_phone); } else if (!TextUtils.isEmpty(filter.accountName)) { actionBarTitle = getActionBarTitleForAccount(filter); Loading
src/com/android/contacts/common/list/ContactListFilter.java +7 −3 Original line number Diff line number Diff line Loading @@ -93,6 +93,12 @@ public final class ContactListFilter implements Comparable<ContactListFilter>, P /* accountType= */ null, /* accountName= */ null, /* dataSet= */ null, icon); } public static ContactListFilter createDeviceContactsFilter(Drawable icon, AccountWithDataSet account) { return new ContactListFilter(ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS, account.type, account.name, account.dataSet, icon); } /** * Whether the given {@link ContactListFilter} has a filter type that should be displayed as * the default contacts list view. Loading Loading @@ -333,10 +339,8 @@ public final class ContactListFilter implements Comparable<ContactListFilter>, P } public AccountWithDataSet toAccountWithDataSet() { if (filterType == FILTER_TYPE_ACCOUNT) { if (filterType == FILTER_TYPE_ACCOUNT || filterType == FILTER_TYPE_DEVICE_CONTACTS) { return new AccountWithDataSet(accountName, accountType, dataSet); } else if (filterType == FILTER_TYPE_DEVICE_CONTACTS) { return AccountWithDataSet.getLocalAccount(); } else { throw new IllegalStateException("Cannot create Account from filter type " + filterTypeToString(filterType)); Loading
src/com/android/contacts/common/model/DeviceLocalAccountLocator.java +64 −29 Original line number Diff line number Diff line Loading @@ -18,10 +18,10 @@ package com.android.contacts.common.model; import android.accounts.AccountManager; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import android.support.annotation.VisibleForTesting; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contacts.common.util.DeviceLocalAccountTypeFactory; Loading @@ -39,6 +39,9 @@ import java.util.Set; */ public class 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, Loading @@ -49,30 +52,59 @@ public class DeviceLocalAccountLocator { 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 Set<String> mKnownAccountTypes; private final String mSelection; private final String[] mSelectionArgs; public DeviceLocalAccountLocator(ContentResolver contentResolver, DeviceLocalAccountTypeFactory factory, List<AccountWithDataSet> knownAccounts) { mResolver = contentResolver; mAccountTypeFactory = factory; mKnownAccountTypes = new HashSet<>(); final Set<String> knownAccountTypes = new HashSet<>(); for (AccountWithDataSet account : knownAccounts) { mKnownAccountTypes.add(account.type); knownAccountTypes.add(account.type); } mSelection = getSelection(knownAccountTypes); mSelectionArgs = getSelectionArgs(knownAccountTypes); } public List<AccountWithDataSet> getDeviceLocalAccounts() { final String[] selectionArgs = getSelectionArgs(); final Cursor cursor = mResolver.query(ContactsContract.RawContacts.CONTENT_URI, PROJECTION, getSelection(), selectionArgs, null); 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); if (localAccounts.isEmpty()) { // It's probably safe to assume that if one of the earlier queries found a "device" // account then this query isn't going to find any different device accounts. // We skip this query because it probably is kind of expensive (relative to the other // queries). 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); Loading @@ -80,36 +112,39 @@ public class DeviceLocalAccountLocator { if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( mAccountTypeFactory, type)) { localAccounts.add(new AccountWithDataSet(name, type, dataSet)); accounts.add(new AccountWithDataSet(name, type, dataSet)); } } } finally { cursor.close(); } return new ArrayList<>(localAccounts); @VisibleForTesting public String getSelection() { return mSelection; } @VisibleForTesting public String getSelection() { final StringBuilder sb = new StringBuilder(); sb.append(ContactsContract.RawContacts.DELETED).append(" =0 AND (") 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 (mKnownAccountTypes.isEmpty()) { return sb.append(')').toString(); if (knownAccountTypes.isEmpty()) { return sb.toString(); } sb.append(" OR ").append(ContactsContract.RawContacts.ACCOUNT_TYPE).append(" NOT IN ("); for (String ignored : mKnownAccountTypes) { for (String ignored : knownAccountTypes) { sb.append("?,"); } // Remove trailing ',' sb.deleteCharAt(sb.length() - 1).append(')').append(')'); sb.deleteCharAt(sb.length() - 1).append(')'); return sb.toString(); } @VisibleForTesting public String[] getSelectionArgs() { return mKnownAccountTypes.toArray(new String[mKnownAccountTypes.size()]); private static String[] getSelectionArgs(Set<String> knownAccountTypes) { if (knownAccountTypes.isEmpty()) return null; return knownAccountTypes.toArray(new String[knownAccountTypes.size()]); } }
src/com/android/contacts/common/model/account/AccountWithDataSet.java +1 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ public class AccountWithDataSet implements Parcelable { args = new String[] {type, name, dataSet}; } } selection += " AND " + RawContacts.DELETED + "=0"; final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1, ID_PROJECTION, selection, args, null); Loading
src/com/android/contacts/common/util/AccountFilterUtil.java +15 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,9 @@ import android.content.AsyncTaskLoader; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log; Loading @@ -31,8 +33,10 @@ import com.android.contacts.common.list.AccountFilterActivity; import com.android.contacts.common.list.ContactListFilter; import com.android.contacts.common.list.ContactListFilterController; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.RawContact; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contactsbind.ObjectFactory; import com.google.common.collect.Lists; import java.util.ArrayList; Loading Loading @@ -90,15 +94,17 @@ public class AccountFilterUtil { */ public static class FilterLoader extends AsyncTaskLoader<List<ContactListFilter>> { private Context mContext; private DeviceLocalAccountTypeFactory mDeviceLocalFactory; public FilterLoader(Context context) { super(context); mContext = context; mDeviceLocalFactory = ObjectFactory.getDeviceLocalAccountTypeFactory(context); } @Override public List<ContactListFilter> loadInBackground() { return loadAccountFilters(mContext); return loadAccountFilters(mContext, mDeviceLocalFactory); } @Override Loading @@ -117,7 +123,8 @@ public class AccountFilterUtil { } } private static List<ContactListFilter> loadAccountFilters(Context context) { private static List<ContactListFilter> loadAccountFilters(Context context, DeviceLocalAccountTypeFactory deviceAccountTypeFactory) { final ArrayList<ContactListFilter> accountFilters = Lists.newArrayList(); final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context); accountTypeManager.sortAccounts(/* defaultAccount */ getDefaultAccount(context)); Loading @@ -127,13 +134,15 @@ public class AccountFilterUtil { for (AccountWithDataSet account : accounts) { final AccountType accountType = accountTypeManager.getAccountType(account.type, account.dataSet); if (accountType.isExtension() && !account.hasData(context)) { // Hide extensions with no raw_contacts. if ((accountType.isExtension() || DeviceLocalAccountTypeFactory.Util.isLocalAccountType( deviceAccountTypeFactory, account.type)) && !account.hasData(context)) { // Hide extensions and device accounts with no raw_contacts. continue; } final Drawable icon = accountType != null ? accountType.getDisplayIcon(context) : null; if (account.isLocalAccount()) { accountFilters.add(ContactListFilter.createDeviceContactsFilter(icon)); if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( deviceAccountTypeFactory, account.type)) { accountFilters.add(ContactListFilter.createDeviceContactsFilter(icon, account)); } else { accountFilters.add(ContactListFilter.createAccountFilter( account.type, account.name, account.dataSet, icon)); Loading