Add group detail fragment on tablet

- Add action bar on GroupDetailActivity, title, and subtitle
- Make everything use the groupUri instead of passing around
account name, type, group Id, and group title
- Fix some of the callback listeners between the group fragments
and group activities
- Add factory methods to ContactListFilter

Change-Id: I63066ea6eefde1e57562b6396d6dc590ed023c84
diff --git a/res/layout-xlarge/group_detail_fragment.xml b/res/layout-xlarge/group_detail_fragment.xml
new file mode 100644
index 0000000..a824319
--- /dev/null
+++ b/res/layout-xlarge/group_detail_fragment.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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:id="@+id/group_detail"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/panel_content">
+
+    <TextView
+        android:id="@+id/group_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="@dimen/group_detail_border_padding"
+        android:paddingTop="@dimen/group_detail_vertical_padding"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/group_size"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="@dimen/group_detail_border_padding"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorSecondary" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:background="?android:attr/listDivider"
+        android:layout_marginTop="@dimen/group_detail_vertical_padding"
+        android:layout_marginBottom="@dimen/group_detail_vertical_padding"
+        android:layout_marginLeft="@dimen/group_detail_divider_margin"
+        android:layout_marginRight="@dimen/group_detail_divider_margin" />
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="@dimen/group_detail_border_padding"
+        android:layout_marginRight="@dimen/group_detail_border_padding"
+        android:cacheColorHint="#00000000"
+        android:divider="@null" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-xlarge/people_activity.xml b/res/layout-xlarge/people_activity.xml
index 3fbb3d2..969bbf9 100644
--- a/res/layout-xlarge/people_activity.xml
+++ b/res/layout-xlarge/people_activity.xml
@@ -84,10 +84,16 @@
             ex:exitAnimation="@android:animator/fade_out"
             ex:animationDuration="200">
             <fragment
-                android:id="@+id/detail_fragment"
+                android:id="@+id/contact_detail_fragment"
                 class="com.android.contacts.detail.ContactDetailFragment"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent" 
+                android:layout_height="match_parent"
+            />
+            <fragment
+                android:id="@+id/group_detail_fragment"
+                class="com.android.contacts.group.GroupDetailFragment"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
             />
         </view>
 
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
index accbedd..28f4e17 100644
--- a/res/layout/group_browse_list_item.xml
+++ b/res/layout/group_browse_list_item.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<view
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.contacts.group.GroupBrowseListAdapter$GroupListItem"
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
@@ -26,7 +28,8 @@
         android:layout_height="match_parent"
         android:layout_marginLeft="10dip"
         android:layout_marginRight="10dip"
-        android:layout_gravity="center_vertical" />
+        android:layout_gravity="center_vertical"
+        android:src="@drawable/ic_menu_display_all_holo_light" />
 
     <LinearLayout
         android:orientation="vertical"
@@ -57,4 +60,4 @@
 
     </LinearLayout>
 
-</LinearLayout>
+</view>
diff --git a/res/layout/group_detail_fragment.xml b/res/layout/group_detail_fragment.xml
index 70d67b6..76f7214 100644
--- a/res/layout/group_detail_fragment.xml
+++ b/res/layout/group_detail_fragment.xml
@@ -21,7 +21,7 @@
     android:layout_height="match_parent">
 
     <ListView
-        android:id="@+id/member_list"
+        android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:scrollbarStyle="outsideOverlay"/>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ff7d873..68655d8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -111,4 +111,13 @@
 
     <!-- Height of list sections (A, B, C) that show the first character of the contacts -->
     <dimen name="list_section_height">25dip</dimen>
+
+    <!-- Border padding for the group detail fragment header -->
+    <dimen name="group_detail_border_padding">20dip</dimen>
+
+    <!-- Vertical padding for the group detail fragment header -->
+    <dimen name="group_detail_vertical_padding">15dip</dimen>
+
+    <!-- Margins for the group detail fragment divider in the header -->
+    <dimen name="group_detail_divider_margin">15dip</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index aefb719..d57cb10 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -162,7 +162,7 @@
     </style>
 
     <!-- TODO: Clean up this file so themes aren't copied. -->
-    <style name="GroupDetailTheme" parent="@android:Theme">
+    <style name="GroupDetailTheme" parent="android:Theme.Holo.Light">
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index d08d007..927697a 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -17,10 +17,12 @@
 
 import android.content.Context;
 import android.content.CursorLoader;
+import android.net.Uri;
 import android.provider.ContactsContract.Groups;
 
 /**
- * Group meta-data loader.  Loads all groups from the database.
+ * Group meta-data loader. Loads all groups or just a single group from the
+ * database (if given a {@link Uri}).
  */
 public final class GroupMetaDataLoader extends CursorLoader {
 
@@ -40,8 +42,23 @@
     public final static int AUTO_ADD = 4;
     public final static int FAVORITES = 5;
 
-    public GroupMetaDataLoader(Context context) {
-        super(context, Groups.CONTENT_URI, COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
+    public GroupMetaDataLoader(Context context, Uri groupUri) {
+        super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
                 + Groups.ACCOUNT_NAME + " NOT NULL", null, null);
     }
+
+    /**
+     * Ensures that this is a valid group URI. If invalid, then an exception is
+     * thrown. Otherwise, the original URI is returned.
+     */
+    private static Uri ensureIsGroupUri(final Uri groupUri) {
+        // TODO: Fix ContactsProvider2 getType method to resolve the group Uris
+        if (groupUri == null) {
+            throw new IllegalArgumentException("Uri must not be null");
+        }
+        if (!groupUri.toString().startsWith(Groups.CONTENT_URI.toString())) {
+            throw new IllegalArgumentException("Invalid group Uri: " + groupUri);
+        }
+        return groupUri;
+    }
 }
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index a0d77ab..52a2539 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -207,7 +207,7 @@
 
         ContactsIntentResolver resolver = new ContactsIntentResolver(this);
         ContactsRequest request = resolver.resolveIntent(intent);
-        final ContactListFilter filter = new ContactListFilter(
+        final ContactListFilter filter = ContactListFilter.createFilterWithType(
                 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
         mContactsFragment.setFilter(filter, false);
         mContactsFragment.setSearchMode(request.isSearchMode());
@@ -233,7 +233,7 @@
 
         ContactsIntentResolver resolver = new ContactsIntentResolver(this);
         ContactsRequest request = resolver.resolveIntent(intent);
-        final ContactListFilter filter = new ContactListFilter(
+        final ContactListFilter filter = ContactListFilter.createFilterWithType(
                 ContactListFilter.FILTER_TYPE_STARRED);
         mStrequentFragment.setFilter(filter, false);
         mStrequentFragment.setSearchMode(request.isSearchMode());
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index edc460c..95f804c 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -18,6 +18,7 @@
 
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
+import com.android.contacts.group.GroupDetailFragment;
 
 import android.os.Bundle;
 
@@ -25,13 +26,6 @@
 
     private static final String TAG = "GroupDetailActivity";
 
-    public static final String KEY_ACCOUNT_TYPE = "accountType";
-    public static final String KEY_ACCOUNT_NAME = "accountName";
-    public static final String KEY_GROUP_ID = "groupId";
-    public static final String KEY_GROUP_SOURCE_ID = "groupSourceId";
-    public static final String KEY_GROUP_READ_ONLY = "groupReadOnly";
-    public static final String KEY_GROUP_TITLE = "title";
-
     @Override
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
@@ -40,5 +34,26 @@
         // TODO: Handle search or key down
 
         setContentView(R.layout.group_detail_activity);
+
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+        GroupDetailFragment fragment = (GroupDetailFragment) getFragmentManager().findFragmentById(
+                R.id.group_detail_fragment);
+        fragment.setListener(mFragmentListener);
+        fragment.loadGroup(getIntent().getData());
     }
+
+    private final GroupDetailFragment.Listener mFragmentListener =
+            new GroupDetailFragment.Listener() {
+
+        @Override
+        public void onGroupSizeUpdated(String size) {
+            getActionBar().setSubtitle(size);
+        }
+
+        @Override
+        public void onGroupTitleUpdated(String title) {
+            getActionBar().setTitle(title);
+        }
+    };
 }
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index f537c9e..4880d22 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -18,11 +18,14 @@
 
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.ContactsActivity;
+import com.android.contacts.GroupMetaData;
 import com.android.contacts.R;
 import com.android.contacts.calllog.CallLogFragment;
 import com.android.contacts.detail.ContactDetailFragment;
 import com.android.contacts.dialpad.DialpadFragment;
 import com.android.contacts.group.GroupBrowseListFragment;
+import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
+import com.android.contacts.group.GroupDetailFragment;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
 import com.android.contacts.interactions.GroupRenamingDialogFragment;
@@ -112,13 +115,16 @@
     private ContactBrowseListFragment mListFragment;
 
     /**
-     * Whether we have a right-side contact pane for displaying contact info while browsing.
-     * Generally means "this is a tablet".
+     * Whether we have a right-side contact or group detail pane for displaying info on that
+     * contact or group while browsing. Generally means "this is a tablet".
      */
-    private boolean mContactContentDisplayed;
+    private boolean mContentPaneDisplayed;
 
-    private ContactDetailFragment mDetailFragment;
-    private DetailFragmentListener mDetailFragmentListener = new DetailFragmentListener();
+    private ContactDetailFragment mContactDetailFragment;
+    private ContactDetailFragmentListener mContactDetailFragmentListener =
+            new ContactDetailFragmentListener();
+
+    private GroupDetailFragment mGroupDetailFragment;
 
     private PhoneNumberInteraction mPhoneNumberCallInteraction;
     private PhoneNumberInteraction mSendTextMessageInteraction;
@@ -162,15 +168,21 @@
                 mListFragment.setContextMenuAdapter(
                         new ContactBrowseListContextMenuAdapter(mListFragment));
             }
+        } else if (fragment instanceof GroupBrowseListFragment) {
+            mGroupsFragment = (GroupBrowseListFragment) fragment;
+            mGroupsFragment.setListener(new GroupBrowserActionListener());
         } else if (fragment instanceof ContactDetailFragment) {
-            mDetailFragment = (ContactDetailFragment)fragment;
-            mDetailFragment.setListener(mDetailFragmentListener);
-            mContactContentDisplayed = true;
+            mContactDetailFragment = (ContactDetailFragment) fragment;
+            mContactDetailFragment.setListener(mContactDetailFragmentListener);
+            mContentPaneDisplayed = true;
         } else if (fragment instanceof ContactsUnavailableFragment) {
             mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
             mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
             mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
                     new ContactsUnavailableFragmentListener());
+        } else if (fragment instanceof GroupDetailFragment) {
+            mGroupDetailFragment = (GroupDetailFragment) fragment;
+            mContentPaneDisplayed = true;
         }
     }
 
@@ -231,11 +243,17 @@
             transaction.hide(mFavoritesFragment);
             transaction.hide(mContactsFragment);
             transaction.hide(mGroupsFragment);
+            if (mContactDetailFragment != null) {
+                transaction.hide(mContactDetailFragment);
+            }
+            if (mGroupDetailFragment != null) {
+                transaction.hide(mGroupDetailFragment);
+            }
             transaction.commit();
         }
 
         if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
-                && !mContactContentDisplayed) {
+                && !mContentPaneDisplayed) {
             redirect = new Intent(this, ContactDetailActivity.class);
             redirect.setAction(Intent.ACTION_VIEW);
             redirect.setData(mRequest.getContactUri());
@@ -254,17 +272,20 @@
             actionBar.removeAllTabs();
             Tab favoritesTab = actionBar.newTab();
             favoritesTab.setText(getString(R.string.strequentList));
-            favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment));
+            favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment,
+                    mContactDetailFragment));
             actionBar.addTab(favoritesTab);
 
             Tab peopleTab = actionBar.newTab();
             peopleTab.setText(getString(R.string.people));
-            peopleTab.setTabListener(new TabChangeListener(mContactsFragment));
+            peopleTab.setTabListener(new TabChangeListener(mContactsFragment,
+                    mContactDetailFragment));
             actionBar.addTab(peopleTab);
 
             Tab groupsTab = actionBar.newTab();
             groupsTab.setText(getString(R.string.contactsGroupsLabel));
-            groupsTab.setTabListener(new TabChangeListener(mGroupsFragment));
+            groupsTab.setTabListener(new TabChangeListener(mGroupsFragment,
+                    mGroupDetailFragment));
             actionBar.addTab(groupsTab);
             actionBar.setDisplayShowTitleEnabled(true);
 
@@ -284,20 +305,33 @@
      * support library in our app.
      */
     private class TabChangeListener implements TabListener {
-        private final Fragment mFragment;
+        private final Fragment mBrowseListFragment;
 
-        public TabChangeListener(Fragment fragment) {
-            mFragment = fragment;
+        /**
+         * Right pane fragment that is present on larger screen sizes (can be
+         * null for smaller screen sizes).
+         */
+        private final Fragment mDetailFragment;
+
+        public TabChangeListener(Fragment listFragment, Fragment detailFragment) {
+            mBrowseListFragment = listFragment;
+            mDetailFragment = detailFragment;
         }
 
         @Override
         public void onTabUnselected(Tab tab, FragmentTransaction ft) {
-            ft.hide(mFragment);
+            ft.hide(mBrowseListFragment);
+            if (mDetailFragment != null) {
+                ft.hide(mDetailFragment);
+            }
         }
 
         @Override
         public void onTabSelected(Tab tab, FragmentTransaction ft) {
-            ft.show(mFragment);
+            ft.show(mBrowseListFragment);
+            if (mDetailFragment != null) {
+                ft.show(mDetailFragment);
+            }
         }
 
         @Override
@@ -341,10 +375,11 @@
             int actionCode = mRequest.getActionCode();
             switch (actionCode) {
                 case ContactsRequest.ACTION_ALL_CONTACTS:
-                    filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+                    filter = ContactListFilter.createFilterWithType(
+                            ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
                     break;
                 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
-                    filter = new ContactListFilter(
+                    filter = ContactListFilter.createFilterWithType(
                             ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
                     break;
 
@@ -353,7 +388,8 @@
                 case ContactsRequest.ACTION_STREQUENT:
                     // For now they are treated the same as STARRED
                 case ContactsRequest.ACTION_STARRED:
-                    filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED);
+                    filter = ContactListFilter.createFilterWithType(
+                            ContactListFilter.FILTER_TYPE_STARRED);
                     break;
             }
 
@@ -362,7 +398,8 @@
                 mContactListFilterController.setContactListFilter(filter, false);
                 mSearchMode = false;
             } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) {
-                mContactListFilterController.setContactListFilter(new ContactListFilter(
+                mContactListFilterController.setContactListFilter(
+                        ContactListFilter.createFilterWithType(
                         ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
             }
 
@@ -411,7 +448,11 @@
     }
 
     private void setupContactDetailFragment(final Uri contactLookupUri) {
-        mDetailFragment.loadUri(contactLookupUri);
+        mContactDetailFragment.loadUri(contactLookupUri);
+    }
+
+    private void setupGroupDetailFragment(Uri groupUri) {
+        mGroupDetailFragment.loadGroup(groupUri);
     }
 
     /**
@@ -462,11 +503,11 @@
 
         mListFragment.setVisibleScrollbarEnabled(!mSearchMode);
         mListFragment.setVerticalScrollbarPosition(
-                mContactContentDisplayed
+                mContentPaneDisplayed
                         ? View.SCROLLBAR_POSITION_LEFT
                         : View.SCROLLBAR_POSITION_RIGHT);
-        mListFragment.setSelectionVisible(mContactContentDisplayed);
-        mListFragment.setQuickContactEnabled(!mContactContentDisplayed);
+        mListFragment.setSelectionVisible(mContentPaneDisplayed);
+        mListFragment.setQuickContactEnabled(!mContentPaneDisplayed);
     }
 
     @Override
@@ -521,14 +562,14 @@
 
         @Override
         public void onSelectionChange() {
-            if (mContactContentDisplayed) {
+            if (mContentPaneDisplayed) {
                 setupContactDetailFragment(mListFragment.getSelectedContactUri());
             }
         }
 
         @Override
         public void onViewContactAction(Uri contactLookupUri) {
-            if (mContactContentDisplayed) {
+            if (mContentPaneDisplayed) {
                 setupContactDetailFragment(contactLookupUri);
             } else {
                 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
@@ -595,17 +636,19 @@
             ContactListFilter currentFilter = mListFragment.getFilter();
             if (currentFilter != null
                     && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
-                filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+                filter = ContactListFilter.createFilterWithType(
+                        ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
                 mListFragment.setFilter(filter);
             } else {
-                filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
+                filter = ContactListFilter.createFilterWithType(
+                        ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
                 mListFragment.setFilter(filter, false);
             }
             mContactListFilterController.setContactListFilter(filter, true);
         }
     }
 
-    private class DetailFragmentListener implements ContactDetailFragment.Listener {
+    private class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
         @Override
         public void onContactNotFound() {
             // Nothing needs to be done here
@@ -670,6 +713,20 @@
         }
     }
 
+    private final class GroupBrowserActionListener implements OnGroupBrowserActionListener {
+
+        @Override
+        public void onViewGroupAction(Uri groupUri) {
+            if (mContentPaneDisplayed) {
+                setupGroupDetailFragment(groupUri);
+            } else {
+                Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class);
+                intent.setData(groupUri);
+                startActivity(intent);
+            }
+        }
+    }
+
     public void startActivityAndForwardResult(final Intent intent) {
         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
@@ -751,7 +808,7 @@
             return true;
         }
 
-        if (mDetailFragment != null && mDetailFragment.isOptionsMenuChanged()) {
+        if (mContactDetailFragment != null && mContactDetailFragment.isOptionsMenuChanged()) {
             return true;
         }
 
@@ -897,7 +954,7 @@
 
             case SUBACTIVITY_EDIT_CONTACT:
             case SUBACTIVITY_NEW_CONTACT: {
-                if (resultCode == RESULT_OK && mContactContentDisplayed) {
+                if (resultCode == RESULT_OK && mContentPaneDisplayed) {
                     mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
                     mListFragment.reloadDataAndSetSelectedUri(data.getData());
                 }
@@ -1039,6 +1096,6 @@
 
     // Visible for testing
     public ContactDetailFragment getDetailFragment() {
-        return mDetailFragment;
+        return mContactDetailFragment;
     }
 }
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 96d5843..8cab92a 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -66,6 +66,7 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.MediaStore;
@@ -1585,14 +1586,14 @@
     };
 
     /**
-     * The listener for the group meta data loader
+     * The listener for the group meta data loader for all groups.
      */
     private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
             new LoaderCallbacks<Cursor>() {
 
         @Override
         public CursorLoader onCreateLoader(int id, Bundle args) {
-            return new GroupMetaDataLoader(mContext);
+            return new GroupMetaDataLoader(mContext, Groups.CONTENT_URI);
         }
 
         @Override
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index bccc207..c17fc08 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -16,15 +16,19 @@
 
 package com.android.contacts.group;
 
-import com.android.contacts.R;
 import com.android.contacts.GroupMetaData;
+import com.android.contacts.R;
 
+import android.content.ContentUris;
 import android.content.Context;
+import android.net.Uri;
+import android.provider.ContactsContract.Groups;
+import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import java.util.List;
@@ -59,21 +63,50 @@
 
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
-        if (convertView == null) {
-            convertView = mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false);
-        }
-        GroupMetaData group = getItem(position);
-        ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
-        TextView label = (TextView) convertView.findViewById(R.id.label);
-        TextView account = (TextView) convertView.findViewById(R.id.account);
-        icon.setImageResource(R.drawable.ic_menu_display_all_holo_light);
-        label.setText(group.getTitle());
-        account.setText(group.getAccountName());
-
-        // Set the tag to be the GroupMetaData object, in order to extract group attributes from the
-        // view later.
-        convertView.setTag(group);
-        return convertView;
+        GroupListItem result = (GroupListItem) (convertView == null ?
+                mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false) :
+                convertView);
+        result.loadFromGroup(getItem(position));
+        return result;
     }
 
+    /**
+     * A row in a list of groups, where this row displays a single group's title
+     * and associated account.
+     */
+    public static class GroupListItem extends LinearLayout {
+
+        private TextView mLabel;
+        private TextView mAccount;
+        private Uri mUri;
+
+        public GroupListItem(Context context, AttributeSet attrs, int defStyle) {
+            super(context, attrs, defStyle);
+        }
+
+        public GroupListItem(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public GroupListItem(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onFinishInflate() {
+            super.onFinishInflate();
+            mLabel = (TextView) findViewById(R.id.label);
+            mAccount = (TextView) findViewById(R.id.account);
+        }
+
+        public void loadFromGroup(GroupMetaData group) {
+            mLabel.setText(group.getTitle());
+            mAccount.setText(group.getAccountName());
+            mUri = ContentUris.withAppendedId(Groups.CONTENT_URI, group.getGroupId());
+        }
+
+        public Uri getUri() {
+            return mUri;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index d9d020e..150a00f 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -19,7 +19,7 @@
 import com.android.contacts.GroupMetaData;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
-import com.android.contacts.activities.GroupDetailActivity;
+import com.android.contacts.group.GroupBrowseListAdapter.GroupListItem;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -27,10 +27,11 @@
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.Context;
 import android.content.CursorLoader;
-import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract.Groups;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -51,6 +52,19 @@
 public class GroupBrowseListFragment extends Fragment
         implements OnFocusChangeListener, OnTouchListener {
 
+    /**
+     * Action callbacks that can be sent by a group list.
+     */
+    public interface OnGroupBrowserActionListener  {
+
+        /**
+         * Opens the specified group for viewing.
+         *
+         * @param groupUri for the group that the user wishes to view.
+         */
+        void onViewGroupAction(Uri groupUri);
+    }
+
     private static final String TAG = "GroupBrowseListFragment";
 
     private static final int LOADER_GROUPS = 1;
@@ -63,6 +77,8 @@
     private ListView mListView;
     private View mEmptyView;
 
+    private OnGroupBrowserActionListener mListener;
+
     public GroupBrowseListFragment() {
     }
 
@@ -96,14 +112,14 @@
     }
 
     /**
-     * The listener for the group meta data loader.
+     * The listener for the group meta data loader for all groups.
      */
     private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
             new LoaderCallbacks<Cursor>() {
 
         @Override
         public CursorLoader onCreateLoader(int id, Bundle args) {
-            return new GroupMetaDataLoader(mContext);
+            return new GroupMetaDataLoader(mContext, Groups.CONTENT_URI);
         }
 
         @Override
@@ -144,21 +160,18 @@
         mListView.setOnItemClickListener(new OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                startGroupDetailActivity((GroupMetaData) view.getTag());
+                GroupListItem groupListItem = (GroupListItem) view;
+                viewGroup(groupListItem.getUri());
             }
         });
     }
 
-    private void startGroupDetailActivity(GroupMetaData group) {
-        if (group == null) {
-            return;
-        }
-        Intent intent = new Intent(mContext, GroupDetailActivity.class);
-        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE, group.getAccountType());
-        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_NAME, group.getAccountName());
-        intent.putExtra(GroupDetailActivity.KEY_GROUP_ID, group.getGroupId());
-        intent.putExtra(GroupDetailActivity.KEY_GROUP_TITLE, group.getTitle());
-        mContext.startActivity(intent);
+    public void setListener(OnGroupBrowserActionListener listener) {
+        mListener = listener;
+    }
+
+    private void viewGroup(Uri groupUri) {
+        if (mListener != null) mListener.onViewGroupAction(groupUri);
     }
 
     private void hideSoftKeyboard() {
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 98babac..8cffd05 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -17,21 +17,26 @@
 package com.android.contacts.group;
 
 import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.GroupMetaData;
+import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
 import com.android.contacts.activities.GroupDetailActivity;
 import com.android.contacts.list.ContactListAdapter;
 import com.android.contacts.list.ContactListFilter;
 import com.android.contacts.list.DefaultContactListAdapter;
 
+import android.accounts.Account;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Directory;
@@ -43,24 +48,47 @@
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
 
 /**
  * Displays the details of a group and shows a list of actions possible for the group.
  */
 public class GroupDetailFragment extends Fragment implements OnScrollListener {
 
+    public static interface Listener {
+        /**
+         * The group title has been loaded
+         */
+        public void onGroupTitleUpdated(String title);
+
+        /**
+         * The number of group members has been determined
+         */
+        public void onGroupSizeUpdated(String size);
+    }
+
     private static final String TAG = "GroupDetailFragment";
 
-    private static final int LOADER_MEMBERS = 0;
+    private static final int LOADER_METADATA = 0;
+    private static final int LOADER_MEMBERS = 1;
 
     private Context mContext;
 
     private View mRootView;
+    private TextView mGroupTitle;
+    private TextView mGroupSize;
     private ListView mMemberListView;
 
+    private Listener mListener;
+
     private ContactListAdapter mAdapter;
     private ContactPhotoManager mPhotoManager;
 
+    private Uri mGroupUri;
+    private long mGroupId;
+
     public GroupDetailFragment() {
     }
 
@@ -80,7 +108,9 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
         mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
-        mMemberListView = (ListView) mRootView.findViewById(R.id.member_list);
+        mGroupTitle = (TextView) mRootView.findViewById(R.id.group_title);
+        mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
+        mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
         mMemberListView.setOnItemClickListener(new OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -90,22 +120,12 @@
         return mRootView;
     }
 
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        Intent intent = getActivity().getIntent();
-        String accountType = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE);
-        String accountName = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_NAME);
-        long groupId = intent.getLongExtra(GroupDetailActivity.KEY_GROUP_ID, -1);
-        String groupTitle = intent.getStringExtra(GroupDetailActivity.KEY_GROUP_TITLE);
-
-        configureAdapter(accountType, accountName, groupId, groupTitle);
-        startGroupMembersLoader();
+    public void loadGroup(Uri groupUri) {
+        mGroupUri= groupUri;
+        startGroupMetadataLoader();
     }
 
-    private void configureAdapter(String accountType, String accountName,
-                long groupId, String groupTitle) {
+    private void configureAdapter(long groupId) {
         mAdapter = new DefaultContactListAdapter(getActivity());
         mAdapter.setSectionHeaderDisplayEnabled(false);
         mAdapter.setDisplayPhotos(true);
@@ -115,8 +135,7 @@
         mAdapter.setContactNameDisplayOrder(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY);
         mAdapter.setSortOrder(ContactsContract.Preferences.SORT_ORDER_PRIMARY);
         mAdapter.setPhotoLoader(mPhotoManager);
-        mAdapter.setFilter(new ContactListFilter(accountType, accountName, groupId, "", false,
-                groupTitle));
+        mAdapter.setFilter(ContactListFilter.createGroupFilter(groupId));
         mMemberListView.setAdapter(mAdapter);
     }
 
@@ -134,6 +153,18 @@
         }
     }
 
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    /**
+     * Start the loader to retrieve the metadata for this group.
+     */
+    private void startGroupMetadataLoader() {
+        getLoaderManager().destroyLoader(LOADER_METADATA);
+        getLoaderManager().restartLoader(LOADER_METADATA, null, mGroupMetadataLoaderListener);
+    }
+
     /**
      * Start the loader to retrieve the list of group members.
      */
@@ -143,6 +174,30 @@
     }
 
     /**
+     * The listener for the group metadata loader.
+     */
+    private final LoaderManager.LoaderCallbacks<Cursor> mGroupMetadataLoaderListener =
+            new LoaderCallbacks<Cursor>() {
+
+        @Override
+        public CursorLoader onCreateLoader(int id, Bundle args) {
+            return new GroupMetaDataLoader(mContext, mGroupUri);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            bindGroupMetaData(data);
+
+            // Retrieve the list of members
+            configureAdapter(mGroupId);
+            startGroupMembersLoader();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {}
+    };
+
+    /**
      * The listener for the group members list loader
      */
     private final LoaderManager.LoaderCallbacks<Cursor> mGroupMemberListLoaderListener =
@@ -157,13 +212,38 @@
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-            mAdapter.changeCursor(loader.getId(), data);
+            updateSize(Integer.toString(data.getCount()));
+            mAdapter.changeCursor(0, data);
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {}
     };
 
+    private void bindGroupMetaData(Cursor cursor) {
+        cursor.moveToPosition(-1);
+        if (cursor.moveToNext()) {
+            mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
+            updateTitle(cursor.getString(GroupMetaDataLoader.TITLE));
+        }
+    }
+
+    private void updateTitle(String title) {
+        if (mGroupTitle != null) {
+            mGroupTitle.setText(title);
+        } else {
+            mListener.onGroupTitleUpdated(title);
+        }
+    }
+
+    private void updateSize(String size) {
+        if (mGroupSize != null) {
+            mGroupSize.setText(size);
+        } else {
+            mListener.onGroupSizeUpdated(size);
+        }
+    }
+
     @Override
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
             int totalItemCount) {
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 1ecfff1..7f7dad4 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -658,7 +658,8 @@
     private void restoreFilter() {
         mFilter = ContactListFilter.restoreFromPreferences(mPrefs);
         if (mFilter == null) {
-            mFilter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+            mFilter = ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
         }
     }
 
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
index c53859b..0651986 100644
--- a/src/com/android/contacts/list/ContactListFilter.java
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -45,40 +45,49 @@
     private static final String KEY_GROUP_READ_ONLY = "filter.groupReadOnly";
     private static final String KEY_GROUP_TITLE = "filter.groupTitle";
 
-    public int filterType;
-    public String accountType;
-    public String accountName;
-    public Drawable icon;
+    public final int filterType;
+    public final String accountType;
+    public final String accountName;
+    public final Drawable icon;
     public long groupId;
     public String groupSourceId;
-    public boolean groupReadOnly;
-    public String title;
+    public final boolean groupReadOnly;
+    public final String title;
     private String mId;
 
-    public ContactListFilter(int filterType) {
+    public ContactListFilter(int filterType, String accountType, String accountName, Drawable icon,
+            long groupId, String groupSourceId, boolean groupReadOnly, String title) {
         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 groupSourceId, boolean groupReadOnly, String title) {
-        this.filterType = ContactListFilter.FILTER_TYPE_GROUP;
-        this.accountType = accountType;
-        this.accountName = accountName;
         this.groupId = groupId;
         this.groupSourceId = groupSourceId;
         this.groupReadOnly = groupReadOnly;
         this.title = title;
     }
 
+    public static ContactListFilter createFilterWithType(int filterType) {
+        return new ContactListFilter(filterType, null, null, null, 0, null, false, null);
+    }
+
+    public static ContactListFilter createGroupFilter(long groupId) {
+        return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP, null, null, null, groupId,
+                null, false, null);
+    }
+
+    public static ContactListFilter createGroupFilter(String accountType, String accountName,
+            long groupId, String groupSourceId, boolean groupReadOnly, String title) {
+        return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP, accountType, accountName,
+                null, groupId, groupSourceId, groupReadOnly, title);
+    }
+
+    public static ContactListFilter createAccountFilter(String accountType, String accountName,
+            Drawable icon, String title) {
+        return new ContactListFilter(ContactListFilter.FILTER_TYPE_ACCOUNT, accountType,
+                accountName, icon, 0, null, false, title);
+    }
+
     /**
      * Returns true if this filter is based on data and may become invalid over time.
      */
@@ -188,14 +197,14 @@
             return null;
         }
 
-        ContactListFilter filter = new ContactListFilter(filterType);
-        filter.accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
-        filter.accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
-        filter.groupId = prefs.getLong(KEY_GROUP_ID, -1);
-        filter.groupSourceId = prefs.getString(KEY_GROUP_SOURCE_ID, null);
-        filter.groupReadOnly = prefs.getBoolean(KEY_GROUP_READ_ONLY, false);
-        filter.title = prefs.getString(KEY_GROUP_TITLE, "group");
-        return filter;
+        String accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
+        String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
+        long groupId = prefs.getLong(KEY_GROUP_ID, -1);
+        String groupSourceId = prefs.getString(KEY_GROUP_SOURCE_ID, null);
+        boolean groupReadOnly = prefs.getBoolean(KEY_GROUP_READ_ONLY, false);
+        String title = prefs.getString(KEY_GROUP_TITLE, "group");
+        return new ContactListFilter(filterType, accountType, accountName, null, groupId,
+                groupSourceId, groupReadOnly, title);
     }
 
 
@@ -210,18 +219,17 @@
     }
 
     public static final Parcelable.Creator<ContactListFilter> CREATOR =
-            new Parcelable.Creator<ContactListFilter>()
-    {
+            new Parcelable.Creator<ContactListFilter>() {
         @Override
         public ContactListFilter createFromParcel(Parcel source) {
             int filterType = source.readInt();
-            ContactListFilter filter = new ContactListFilter(filterType);
-            filter.accountName = source.readString();
-            filter.accountType = source.readString();
-            filter.groupId = source.readLong();
-            filter.groupSourceId = source.readString();
-            filter.groupReadOnly = source.readInt() != 0;
-            return filter;
+            String accountName = source.readString();
+            String accountType = source.readString();
+            long groupId = source.readLong();
+            String groupSourceId = source.readString();
+            boolean groupReadOnly = source.readInt() != 0;
+            return new ContactListFilter(filterType, accountType, accountName, null, groupId,
+                    groupSourceId, groupReadOnly, null);
         }
 
         @Override
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
index 4da5baa..533f24a 100644
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -160,10 +160,10 @@
         }
 
         if (mAccountCount != 1) {
-            mFilters.append(mNextFilterId++,
-                    new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
-            mFilters.append(mNextFilterId++,
-                    new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED));
+            mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+            mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_STARRED));
         }
 
         for (int index = 0; index < count; index++) {
@@ -174,7 +174,8 @@
 
             // If we only have one account, don't show it as "account", instead show it as "all"
             if (firstAndOnly) {
-                filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+                filter = ContactListFilter.createFilterWithType(
+                        ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
             }
 
             mFilters.append(mNextFilterId++, filter);
@@ -186,14 +187,14 @@
             }
 
             if (firstAndOnly) {
-                mFilters.append(mNextFilterId++,
-                        new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED));
+                mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
+                        ContactListFilter.FILTER_TYPE_STARRED));
             }
         }
 
         if (mAccountCount > 0) {
-            mFilters.append(
-                    mNextFilterId++, new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM));
+            mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_CUSTOM));
         }
 
         boolean filterChanged = false;
@@ -222,11 +223,14 @@
     private void setContactListFilter(int filterId) {
         ContactListFilter filter;
         if (filterId == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
-            filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+            filter = ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
         } else if (filterId == ContactListFilter.FILTER_TYPE_CUSTOM) {
-            filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM);
+            filter = ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_CUSTOM);
         } else if (filterId == ContactListFilter.FILTER_TYPE_STARRED) {
-            filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED);
+            filter = ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_STARRED);
         } else {
             filter = mFilters.get(filterId);
             if (filter == null) {
@@ -286,7 +290,7 @@
     }
 
     public void selectCustomFilter() {
-        mFilter = new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM);
+        mFilter = ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_CUSTOM);
         notifyContactListFilterChanged();
     }
 
diff --git a/src/com/android/contacts/list/ContactListFilterLoader.java b/src/com/android/contacts/list/ContactListFilterLoader.java
index 82749ff..6bca4ad 100644
--- a/src/com/android/contacts/list/ContactListFilterLoader.java
+++ b/src/com/android/contacts/list/ContactListFilterLoader.java
@@ -81,7 +81,8 @@
         for (Account account : accounts) {
             AccountType accountType = accountTypes.getAccountType(account.type);
             Drawable icon = accountType != null ? accountType.getDisplayIcon(getContext()) : null;
-            results.add(new ContactListFilter(account.type, account.name, icon, account.name));
+            results.add(ContactListFilter.createAccountFilter(account.type, account.name, icon,
+                    account.name));
         }
 
         ContentResolver resolver = context.getContentResolver();
@@ -111,8 +112,8 @@
                     }
                 } else {
                     String title = cursor.getString(GroupQuery.TITLE);
-                    results.add(new ContactListFilter(accountType, accountName, groupId,
-                            groupSourceId, groupReadOnly, title));
+                    results.add(ContactListFilter.createGroupFilter(accountType, accountName,
+                            groupId, groupSourceId, groupReadOnly, title));
                 }
             }
         } finally {
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 17e3fb9..6bfca57 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -151,8 +151,8 @@
     protected ContactEntryListAdapter createListAdapter() {
         if (!isLegacyCompatibilityMode()) {
             DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
-            adapter.setFilter(
-                    new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+            adapter.setFilter(ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
             adapter.setSectionHeaderDisplayEnabled(true);
             adapter.setDisplayPhotos(true);
             adapter.setQuickContactEnabled(false);