Bringing display options to the browser screen
Change-Id: Ic316e51146ced2aef73a85d1ee5135c21d03a06c
diff --git a/res/layout-xlarge/contacts_list_content.xml b/res/layout-xlarge/contacts_list_content.xml
index 2b279be..0ef0544 100644
--- a/res/layout-xlarge/contacts_list_content.xml
+++ b/res/layout-xlarge/contacts_list_content.xml
@@ -35,6 +35,15 @@
android:layout_weight="1"
>
+ <Spinner
+ android:id="@+id/filter_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/list_filter_prompt"
+ android:spinnerMode="dropdown"
+ android:visibility="gone"
+ />
+
<view
class="com.android.contacts.ContactEntryListView"
android:id="@android:id/list"
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml
index 8d1c50a..0737bbc 100644
--- a/res/layout/contacts_list_content.xml
+++ b/res/layout/contacts_list_content.xml
@@ -29,6 +29,14 @@
android:layout_weight="1"
>
+ <Spinner
+ android:id="@+id/filter_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/list_filter_prompt"
+ android:visibility="gone"
+ />
+
<view
class="com.android.contacts.ContactEntryListView"
android:id="@android:id/list"
diff --git a/res/layout/filter_spinner_item.xml b/res/layout/filter_spinner_item.xml
new file mode 100644
index 0000000..79c871c
--- /dev/null
+++ b/res/layout/filter_spinner_item.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="52dip"
+ android:layout_width="fill_parent"
+ android:paddingLeft="7dip"
+ android:paddingRight="7dip"
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:gravity="left">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:scaleType="fitCenter"
+ android:layout_width="48dip"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="7dip" />
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/indented_label"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:ellipsize="end"
+ android:paddingLeft="80dip" />
+</LinearLayout>
+
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 13844a3..1b381ab 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -57,4 +57,7 @@
<!-- ContactDetailFragment ContextMenu Ids -->
<item type="id" name="menu_detail_makeDefault" />
+
+ <!-- Loader ID for contact filters -->
+ <item type="id" name="contact_list_filter_loader" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6d68404..5478665 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1312,4 +1312,16 @@
<!-- Toast shown when creating a personal copy of a contact [CHAR LIMIT=100] -->
<string name="toast_making_personal_copy">Creating a personal copy</string>
+
+ <!-- Prompt for selection of a contact list filter [CHAR LIMIT=64] -->
+ <string name="list_filter_prompt">Choose contact list</string>
+
+ <!-- Contact list filter label indicating that the list is showing all available accounts [CHAR LIMIT=64] -->
+ <string name="list_filter_all_accounts">All accounts</string>
+
+ <!-- Contact list filter indicating that the list shows groups chosen by the user [CHAR LIMIT=64] -->
+ <string name="list_filter_custom">Custom list</string>
+
+ <!-- Contact list filter selection indicating that the list shows groups chosen by the user [CHAR LIMIT=64] -->
+ <string name="list_filter_customize">Customize...</string>
</resources>
diff --git a/src/com/android/contacts/list/ContactGroupFilterLoader.java b/src/com/android/contacts/list/ContactGroupFilterLoader.java
new file mode 100644
index 0000000..757994e
--- /dev/null
+++ b/src/com/android/contacts/list/ContactGroupFilterLoader.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.Sources;
+
+import android.accounts.Account;
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.Groups;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A loader for the data needed for the group selector.
+ */
+public class ContactGroupFilterLoader extends AsyncTaskLoader<List<ContactListFilter>> {
+
+ private static final class GroupQuery {
+ public static final String[] COLUMNS = {
+ Groups._ID,
+ Groups.ACCOUNT_TYPE,
+ Groups.ACCOUNT_NAME,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ };
+
+ public static final int ID = 0;
+ public static final int ACCOUNT_TYPE = 1;
+ public static final int ACCOUNT_NAME = 2;
+ public static final int TITLE = 3;
+ public static final int IS_DEFAULT_GROUP = 4; // Using the AUTO_ADD group as default
+
+ private static final String SELECTION =
+ Groups.DELETED + "=0 AND " + Groups.FAVORITES + "=0";
+ }
+
+ public ContactGroupFilterLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List<ContactListFilter> loadInBackground() {
+
+ ArrayList<ContactListFilter> results = new ArrayList<ContactListFilter>();
+ Context context = getContext();
+ final Sources sources = Sources.getInstance(context);
+ ArrayList<Account> accounts = sources.getAccounts(false);
+ for (Account account : accounts) {
+ ContactsSource source = sources.getInflatedSource(
+ account.type, ContactsSource.LEVEL_SUMMARY);
+ Drawable icon = source != null ? source.getDisplayIcon(getContext()) : null;
+ results.add(new ContactListFilter(account.type, account.name, icon, account.name));
+ }
+
+ HashSet<Account> accountsWithGroups = new HashSet<Account>();
+ ContentResolver resolver = context.getContentResolver();
+
+ Cursor cursor = resolver.query(
+ Groups.CONTENT_URI, GroupQuery.COLUMNS, GroupQuery.SELECTION, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ long groupId = cursor.getLong(GroupQuery.ID);
+ String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ boolean defaultGroup = false;
+ if (!cursor.isNull(GroupQuery.IS_DEFAULT_GROUP)) {
+ defaultGroup = cursor.getInt(GroupQuery.IS_DEFAULT_GROUP) != 0;
+ }
+ if (defaultGroup) {
+ // Find the filter for this account and set the default group ID
+ for (ContactListFilter filter : results) {
+ if (filter.accountName.equals(accountName)
+ && filter.accountType.equals(accountType)) {
+ filter.groupId = groupId;
+ break;
+ }
+ }
+ } else {
+ String title = cursor.getString(GroupQuery.TITLE);
+ results.add(new ContactListFilter(accountType, accountName, groupId, title));
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+
+ Collections.sort(results);
+
+ return results;
+ }
+
+ @Override
+ public void destroy() {
+ stopLoading();
+ }
+
+ @Override
+ public void startLoading() {
+ cancelLoad();
+ forceLoad();
+ }
+
+ @Override
+ public void stopLoading() {
+ cancelLoad();
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 38f1017..2b1970e 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -21,6 +21,7 @@
import android.provider.ContactsContract;
import android.provider.ContactsContract.ContactCounts;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.SearchSnippetColumns;
import android.text.TextUtils;
@@ -29,12 +30,14 @@
import android.widget.ListView;
import android.widget.QuickContactBadge;
+import java.util.List;
+
/**
* A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
*/
public abstract class ContactListAdapter extends ContactEntryListAdapter {
- protected static final String[] PROJECTION = new String[] {
+ protected static final String[] PROJECTION_CONTACT = new String[] {
Contacts._ID, // 0
Contacts.DISPLAY_NAME_PRIMARY, // 1
Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
@@ -47,6 +50,19 @@
Contacts.HAS_PHONE_NUMBER, // 9
};
+ protected static final String[] PROJECTION_DATA = new String[] {
+ Data.CONTACT_ID, // 0
+ Data.DISPLAY_NAME_PRIMARY, // 1
+ Data.DISPLAY_NAME_ALTERNATIVE, // 2
+ Data.SORT_KEY_PRIMARY, // 3
+ Data.STARRED, // 4
+ Data.CONTACT_PRESENCE, // 5
+ Data.PHOTO_ID, // 6
+ Data.LOOKUP_KEY, // 7
+ Data.PHONETIC_NAME, // 8
+ Data.HAS_PHONE_NUMBER, // 9
+ };
+
protected static final String[] FILTER_PROJECTION = new String[] {
Contacts._ID, // 0
Contacts.DISPLAY_NAME_PRIMARY, // 1
@@ -84,6 +100,9 @@
private long mSelectedContactDirectoryId;
private String mSelectedContactLookupKey;
+ private ContactListFilter mFilter;
+ private List<ContactListFilter> mAllFilters;
+
public ContactListAdapter(Context context) {
super(context);
@@ -94,6 +113,25 @@
return mUnknownNameText;
}
+ /**
+ * Returns a full set of all available list filters.
+ */
+ public List<ContactListFilter> getAllFilters() {
+ return mAllFilters;
+ }
+
+ /**
+ * Returns the currently selected filter.
+ */
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter, List<ContactListFilter> allFilters) {
+ mFilter = filter;
+ mAllFilters = allFilters;
+ }
+
public long getSelectedContactDirectoryId() {
return mSelectedContactDirectoryId;
}
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
new file mode 100644
index 0000000..3d32dd0
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+
+/**
+ * Contact list filter parameters.
+ */
+public final class ContactListFilter implements Comparable<ContactListFilter> {
+
+ public static final int FILTER_TYPE_ALL_ACCOUNTS = -1;
+ public static final int FILTER_TYPE_CUSTOM = -2;
+ public static final int FILTER_TYPE_ACCOUNT = 0;
+ public static final int FILTER_TYPE_GROUP = 1;
+
+ public int filterType;
+ public String accountType;
+ public String accountName;
+ public Drawable icon;
+ public long groupId;
+ public String title;
+
+ public ContactListFilter(int filterType) {
+ this.filterType = filterType;
+ }
+
+ public ContactListFilter(
+ String accountType, String accountName, Drawable icon, String title) {
+ this.filterType = ContactListFilter.FILTER_TYPE_ACCOUNT;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.icon = icon;
+ this.title = title;
+ }
+
+ public ContactListFilter(
+ String accountType, String accountName, long groupId, String title) {
+ this.filterType = ContactListFilter.FILTER_TYPE_GROUP;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.groupId = groupId;
+ this.title = title;
+ }
+
+ @Override
+ public String toString() {
+ switch (filterType) {
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ return "account: " + accountType + " " + accountName;
+ case ContactListFilter.FILTER_TYPE_GROUP:
+ return "group: " + accountType + " " + accountName + " " + title + "(" + groupId
+ + ")";
+ }
+ return super.toString();
+ }
+
+ @Override
+ public int compareTo(ContactListFilter another) {
+ int res = accountType.compareTo(another.accountType);
+ if (res != 0) {
+ return res;
+ }
+
+ res = accountName.compareTo(another.accountName);
+ if (res != 0) {
+ return res;
+ }
+
+ if (filterType != another.filterType) {
+ return filterType - another.filterType;
+ }
+
+ String title1 = title != null ? title : "";
+ String title2 = another.title != null ? another.title : "";
+ return title1.compareTo(title2);
+ }
+
+ @Override
+ public int hashCode() {
+ int code = filterType;
+ if (accountType != null) {
+ code = code * 31 + accountType.hashCode();
+ code = code * 31 + accountName.hashCode();
+ }
+ if (groupId != 0) {
+ code = code * 31 + (int) groupId;
+ }
+ return code;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof ContactListFilter)) {
+ return false;
+ }
+
+ ContactListFilter otherFilter = (ContactListFilter) other;
+ return filterType == otherFilter.filterType
+ && TextUtils.equals(accountName, otherFilter.accountName)
+ && TextUtils.equals(accountType, otherFilter.accountType)
+ && groupId == otherFilter.groupId;
+ }
+}
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 76ba490..3e6dbe5 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -18,21 +18,33 @@
import com.android.contacts.R;
import com.android.contacts.ui.ContactsPreferencesActivity.Prefs;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.Spinner;
import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Fragment containing a contact list used for browsing (as compared to
* picking a contact with one of the PICK intents).
*/
-public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
+public class DefaultContactBrowseListFragment extends ContactBrowseListFragment
+ implements OnItemSelectedListener {
private static final String KEY_EDIT_MODE = "editMode";
private static final String KEY_CREATE_CONTACT_ENABLED = "createContactEnabled";
@@ -45,6 +57,29 @@
private boolean mVisibleContactsRestrictionEnabled = true;
private View mHeaderView;
+ private boolean mFilterEnabled = true;
+ private SparseArray<ContactListFilter> mFilters;
+ private ArrayList<ContactListFilter> mFilterList;
+ private int mNextFilterId = 1;
+ private Spinner mFilterSpinner;
+ private ContactListFilter mFilter;
+ private boolean mFiltersLoaded;
+
+ private LoaderCallbacks<List<ContactListFilter>> mGroupFilterLoaderCallbacks =
+ new LoaderCallbacks<List<ContactListFilter>>() {
+
+ @Override
+ public ContactGroupFilterLoader onCreateLoader(int id, Bundle args) {
+ return new ContactGroupFilterLoader(getContext());
+ }
+
+ @Override
+ public void onLoadFinished(
+ Loader<List<ContactListFilter>> loader, List<ContactListFilter> data) {
+ onGroupFilterLoadFinished(data);
+ }
+ };
+
public DefaultContactBrowseListFragment() {
setPhotoLoaderEnabled(true);
setSectionHeaderDisplayEnabled(true);
@@ -139,6 +174,7 @@
if (adapter != null) {
adapter.setContactsWithPhoneNumbersOnly(isShowingContactsWithPhonesOnly());
adapter.setVisibleContactsOnly(mVisibleContactsRestrictionEnabled);
+ adapter.setFilter(mFilter, mFilterList);
}
}
@@ -158,6 +194,20 @@
headerContainer.addView(mHeaderView);
getListView().addHeaderView(headerContainer);
checkHeaderViewVisibility();
+ configureFilterSpinner();
+ }
+
+ protected void configureFilterSpinner() {
+ mFilterSpinner = (Spinner)getView().findViewById(R.id.filter_spinner);
+ if (mFilterSpinner == null) {
+ return;
+ }
+
+ if (!mFilterEnabled) {
+ mFilterSpinner.setVisibility(View.GONE);
+ return;
+ }
+ mFilterSpinner.setOnItemSelectedListener(this);
}
@Override
@@ -203,4 +253,201 @@
public void setCreateContactEnabled(boolean flag) {
this.mCreateContactEnabled = flag;
}
+
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ setContactListFilter((int) id);
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ setContactListFilter(0);
+ }
+
+ @Override
+ public void onStart() {
+ if (mFilterEnabled) {
+ mFiltersLoaded = false;
+ }
+ super.onStart();
+ }
+
+ @Override
+ protected void startLoading() {
+ // We need to load filters before we can load the list contents
+ if (mFilterEnabled && !mFiltersLoaded) {
+ getLoaderManager().restartLoader(
+ R.id.contact_list_filter_loader, null, mGroupFilterLoaderCallbacks);
+ } else {
+ super.startLoading();
+ }
+ }
+
+ protected void onGroupFilterLoadFinished(List<ContactListFilter> filters) {
+ if (mFilters == null) {
+ mFilters = new SparseArray<ContactListFilter>(filters.size());
+ mFilterList = new ArrayList<ContactListFilter>();
+ } else {
+ mFilters.clear();
+ mFilterList.clear();
+ }
+
+ boolean filterValid = mFilter != null
+ && (mFilter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
+ || mFilter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM);
+
+ int accountCount = 0;
+ int count = filters.size();
+ for (int index = 0; index < count; index++) {
+ if (filters.get(index).filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ accountCount++;
+ }
+ }
+
+ if (accountCount > 1) {
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM));
+ }
+
+ boolean firstAccount = true;
+ for (int index = 0; index < count; index++) {
+ ContactListFilter filter = filters.get(index);
+ mFilters.append(mNextFilterId++, filter);
+ mFilterList.add(filter);
+ filterValid |= filter.equals(mFilter);
+
+ if (firstAccount && filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT
+ && accountCount == 1) {
+ firstAccount = false;
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM));
+ }
+ }
+
+ mFiltersLoaded = true;
+ if (mFilter == null || !filterValid) {
+ mFilter = getDefaultFilter();
+ }
+
+ mFilterSpinner.setAdapter(new FilterSpinnerAdapter());
+ updateFilterView();
+
+ startLoading();
+ }
+
+ protected void setContactListFilter(int filterId) {
+ ContactListFilter filter;
+ if (filterId == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ } else if (filterId == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM);
+ } else {
+ filter = mFilters.get(filterId);
+ if (filter == null) {
+ filter = getDefaultFilter();
+ }
+ }
+
+ if (!filter.equals(mFilter)) {
+ mFilter = filter;
+ updateFilterView();
+ reloadData();
+ }
+ }
+
+ private ContactListFilter getDefaultFilter() {
+ return mFilters.valueAt(0);
+ }
+
+ protected void updateFilterView() {
+ if (mFiltersLoaded) {
+ mFilterSpinner.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private class FilterSpinnerAdapter extends BaseAdapter {
+ private LayoutInflater mLayoutInflater;
+
+ public FilterSpinnerAdapter() {
+ mLayoutInflater = LayoutInflater.from(getContext());
+ }
+
+ @Override
+ public int getCount() {
+ return mFilters.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mFilters.keyAt(position);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mFilters.valueAt(position);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return getView(position, convertView, parent, true);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getView(position, convertView, parent, false);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent, boolean dropdown) {
+ View view = convertView != null ? convertView
+ : mLayoutInflater.inflate(R.layout.filter_spinner_item, parent, false);
+ ImageView icon = (ImageView) view.findViewById(R.id.icon);
+ TextView label = (TextView) view.findViewById(R.id.label);
+ TextView indentedLabel = (TextView) view.findViewById(R.id.indented_label);
+ ContactListFilter filter = mFilters.valueAt(position);
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ icon.setVisibility(View.GONE);
+ label.setText(R.string.list_filter_all_accounts);
+ label.setVisibility(View.VISIBLE);
+ indentedLabel.setVisibility(View.GONE);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ icon.setVisibility(View.GONE);
+ label.setText(dropdown
+ ? R.string.list_filter_customize
+ : R.string.list_filter_custom);
+ label.setVisibility(View.VISIBLE);
+ indentedLabel.setVisibility(View.GONE);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ icon.setVisibility(View.VISIBLE);
+ if (filter.icon != null) {
+ icon.setImageDrawable(filter.icon);
+ } else {
+ icon.setImageResource(R.drawable.unknown_source);
+ }
+ label.setText(filter.accountName);
+ label.setVisibility(View.VISIBLE);
+ indentedLabel.setVisibility(View.GONE);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_GROUP: {
+ icon.setVisibility(View.GONE);
+ if (dropdown) {
+ label.setVisibility(View.GONE);
+ indentedLabel.setText(filter.title);
+ indentedLabel.setVisibility(View.VISIBLE);
+ } else {
+ label.setText(filter.title);
+ label.setVisibility(View.VISIBLE);
+ indentedLabel.setVisibility(View.GONE);
+ }
+ break;
+ }
+ }
+ return view;
+ }
+ }
}
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 593b9ba..5549820 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -21,11 +21,17 @@
import android.net.Uri;
import android.net.Uri.Builder;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
*/
@@ -48,8 +54,8 @@
@Override
public void configureLoader(CursorLoader loader, long directoryId) {
- Uri uri;
+ ContactListFilter filter = getFilter();
if (isSearchMode()) {
String query = getQueryString();
Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
@@ -61,29 +67,14 @@
builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(directoryId));
- uri = builder.build();
+ loader.setUri(builder.build());
loader.setProjection(FILTER_PROJECTION);
} else {
- uri = Contacts.CONTENT_URI;
- loader.setProjection(PROJECTION);
+ configureUri(loader, directoryId, filter);
+ configureProjection(loader, directoryId, filter);
+ configureSelection(loader, directoryId, filter);
}
- if (directoryId == Directory.DEFAULT) {
- if (mVisibleContactsOnly && mContactsWithPhoneNumbersOnly) {
- loader.setSelection(Contacts.IN_VISIBLE_GROUP + "=1"
- + " AND " + Contacts.HAS_PHONE_NUMBER + "=1");
- } else if (mVisibleContactsOnly) {
- loader.setSelection(Contacts.IN_VISIBLE_GROUP + "=1");
- } else if (mContactsWithPhoneNumbersOnly) {
- loader.setSelection(Contacts.HAS_PHONE_NUMBER + "=1");
- }
- if (isSectionHeaderDisplayEnabled()) {
- uri = buildSectionIndexerUri(uri);
- }
- }
-
- loader.setUri(uri);
-
String sortOrder;
if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
sortOrder = Contacts.SORT_KEY_PRIMARY;
@@ -94,6 +85,115 @@
loader.setSortOrder(sortOrder);
}
+ protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) {
+ Uri uri;
+ if (filter != null && filter.groupId != 0) {
+ uri = Data.CONTENT_URI;
+ } else {
+ uri = Contacts.CONTENT_URI;
+ }
+
+ if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) {
+ uri = buildSectionIndexerUri(uri);
+ }
+ loader.setUri(uri);
+ }
+
+ protected void configureProjection(
+ CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter != null && filter.groupId != 0) {
+ loader.setProjection(PROJECTION_DATA);
+ } else {
+ loader.setProjection(PROJECTION_CONTACT);
+ }
+ }
+
+ private void configureSelection(
+ CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter == null) {
+ return;
+ }
+
+ if (directoryId != Directory.DEFAULT || filter == null) {
+ loader.setSelection(Contacts.IN_VISIBLE_GROUP + "=1");
+ return;
+ }
+
+ StringBuilder selection = new StringBuilder();
+ List<String> selectionArgs = new ArrayList<String>();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ List<ContactListFilter> filters = getAllFilters();
+ for (ContactListFilter aFilter : filters) {
+ if (aFilter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ if (selection.length() > 0) {
+ selection.append(" OR ");
+ }
+ selection.append("(");
+ if (aFilter.groupId != 0) {
+ // TODO: avoid the use of private API
+ // TODO: optimize the query
+ selection.append(
+ Contacts._ID + " IN ("
+ + "SELECT " + RawContacts.CONTACT_ID
+ + " FROM view_data"
+ + " WHERE " + Data.MIMETYPE + "=?"
+ + " AND " + GroupMembership.GROUP_ROW_ID + "=?)");
+ selectionArgs.add(GroupMembership.CONTENT_ITEM_TYPE);
+ selectionArgs.add(String.valueOf(aFilter.groupId));
+ } else {
+ // TODO: avoid the use of private API
+ selection.append(
+ Contacts._ID + " IN ("
+ + "SELECT " + RawContacts.CONTACT_ID
+ + " FROM raw_contacts"
+ + " WHERE " + RawContacts.ACCOUNT_TYPE + "=?"
+ + " AND " + RawContacts.ACCOUNT_NAME + "=?)");
+ selectionArgs.add(aFilter.accountType);
+ selectionArgs.add(aFilter.accountName);
+ }
+ selection.append(")");
+ }
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ if (mVisibleContactsOnly && mContactsWithPhoneNumbersOnly) {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1"
+ + " AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ } else if (mVisibleContactsOnly) {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ } else if (mContactsWithPhoneNumbersOnly) {
+ selection.append(Contacts.HAS_PHONE_NUMBER + "=1");
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ case ContactListFilter.FILTER_TYPE_GROUP: {
+ if (filter.groupId != 0) {
+ selection.append(Data.MIMETYPE + "=?"
+ + " AND " + GroupMembership.GROUP_ROW_ID + "=?");
+ selectionArgs.add(GroupMembership.CONTENT_ITEM_TYPE);
+ selectionArgs.add(String.valueOf(filter.groupId));
+ } else {
+ // TODO: avoid the use of private API
+ selection.append(
+ Contacts._ID + " IN ("
+ + "SELECT " + RawContacts.CONTACT_ID
+ + " FROM raw_contacts"
+ + " WHERE " + RawContacts.ACCOUNT_TYPE + "=?"
+ + " AND " + RawContacts.ACCOUNT_NAME + "=?)");
+ selectionArgs.add(filter.accountType);
+ selectionArgs.add(filter.accountName);
+ }
+ break;
+ }
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
@Override
protected void bindView(View itemView, int partition, Cursor cursor, int position) {
final ContactListItemView view = (ContactListItemView)itemView;
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
index 14cfa8f..b1bb586 100644
--- a/src/com/android/contacts/list/JoinContactListAdapter.java
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -97,7 +97,7 @@
loader.setSuggestionUri(builder.build());
// TODO simplify projection
- loader.setProjection(PROJECTION);
+ loader.setProjection(PROJECTION_CONTACT);
loader.setUri(buildSectionIndexerUri(Contacts.CONTENT_URI));
loader.setSelection(Contacts.IN_VISIBLE_GROUP + "=1 AND " + Contacts._ID + "!=?");
loader.setSelectionArgs(new String[]{String.valueOf(mTargetContactId)});
@@ -218,8 +218,8 @@
}
public Cursor getShowAllContactsLabelCursor() {
- MatrixCursor matrixCursor = new MatrixCursor(PROJECTION);
- Object[] row = new Object[PROJECTION.length];
+ MatrixCursor matrixCursor = new MatrixCursor(PROJECTION_CONTACT);
+ Object[] row = new Object[PROJECTION_CONTACT.length];
matrixCursor.addRow(row);
return matrixCursor;
}
diff --git a/src/com/android/contacts/list/StrequentContactListAdapter.java b/src/com/android/contacts/list/StrequentContactListAdapter.java
index 712876e..b8fedb1 100644
--- a/src/com/android/contacts/list/StrequentContactListAdapter.java
+++ b/src/com/android/contacts/list/StrequentContactListAdapter.java
@@ -80,7 +80,7 @@
+ "FrequentlyContactedContactsIncluded is set");
}
- loader.setProjection(PROJECTION);
+ loader.setProjection(PROJECTION_CONTACT);
loader.setSortOrder(sortOrder);
}