Group members are now displayed as ContactTiles

-For Tablet
When trying to view a list of all the group member, they
will show up as ContactTiles.

Added GroupMemberLoader as a helper class to query
for all members based on group id.

Change-Id: I7eae9066a2e881c28eca1fc157142e49a9bad725
diff --git a/src/com/android/contacts/GroupMemberLoader.java b/src/com/android/contacts/GroupMemberLoader.java
new file mode 100644
index 0000000..0633bf8
--- /dev/null
+++ b/src/com/android/contacts/GroupMemberLoader.java
@@ -0,0 +1,102 @@
+/*
+ * 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
+ */
+package com.android.contacts;
+
+import com.android.contacts.list.ContactListAdapter;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.net.Uri;
+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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Group Member loader. Loads all group members from the given groupId
+ */
+public final class GroupMemberLoader extends CursorLoader {
+
+    /**
+     * Projection map is taken from {@link ContactListAdapter}
+     */
+    private final String[] PROJECTION_DATA = new String[] {
+        // TODO: Pull Projection_data out into util class
+        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.CONTACT_CHAT_CAPABILITY,           // 6
+        Data.PHOTO_ID,                          // 7
+        Data.PHOTO_THUMBNAIL_URI,               // 8
+        Data.LOOKUP_KEY,                        // 9
+        Data.PHONETIC_NAME,                     // 10
+        Data.HAS_PHONE_NUMBER,                  // 11
+    };
+
+    private final long mGroupId;
+
+    public static final int CONTACT_ID_COLUMN_INDEX = 0;
+    public static final int CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 1;
+    public static final int CONTACT_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX = 2;
+    public static final int CONTACT_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
+    public static final int CONTACT_STARRED_COLUMN_INDEX = 4;
+    public static final int CONTACT_PRESENCE_STATUS_COLUMN_INDEX = 5;
+    public static final int CONTACT_CHAT_CAPABILITY_COLUMN_INDEX = 6;
+    public static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 7;
+    public static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 8;
+    public static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 9;
+    public static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 10;
+    public static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 11;
+
+    public GroupMemberLoader(Context context, long groupId) {
+        super(context);
+        mGroupId = groupId;
+        setUri(createUri());
+        setProjection(PROJECTION_DATA);
+        setSelection(createSelection());
+        setSelectionArgs(createSelectionArgs());
+        setSortOrder(Contacts.SORT_KEY_ALTERNATIVE);
+    }
+
+    private Uri createUri() {
+        Uri uri = Data.CONTENT_URI;
+        uri = uri.buildUpon().appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                String.valueOf(Directory.DEFAULT)).build();
+        // TODO: Bring back dataRestriction
+        // uri = applyDataRestriction(uri);
+        return uri;
+    }
+
+    private String createSelection() {
+        StringBuilder selection = new StringBuilder();
+        selection.append(Data.MIMETYPE + "=?" + " AND " + GroupMembership.GROUP_ROW_ID + "=?");
+        return selection.toString();
+    }
+
+    private String[] createSelectionArgs() {
+        List<String> selectionArgs = new ArrayList<String>();
+        selectionArgs.add(GroupMembership.CONTENT_ITEM_TYPE);
+        selectionArgs.add(String.valueOf(mGroupId));
+        return selectionArgs.toArray(new String[0]);
+    }
+}
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 8d049d5..a0b7fb5 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -17,14 +17,13 @@
 package com.android.contacts.group;
 
 import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.GroupMemberLoader;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
 import com.android.contacts.interactions.GroupRenamingDialogFragment;
-import com.android.contacts.list.ContactListAdapter;
-import com.android.contacts.list.ContactListFilter;
-import com.android.contacts.list.DefaultContactListAdapter;
-import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.list.ContactTileAdapter;
+import com.android.contacts.list.ContactTileAdapter.DisplayType;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -36,8 +35,6 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Directory;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -46,8 +43,6 @@
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -73,6 +68,7 @@
 
     private static final int LOADER_METADATA = 0;
     private static final int LOADER_MEMBERS = 1;
+    private static final int NUM_COLS = 4;
 
     private Context mContext;
 
@@ -83,7 +79,7 @@
 
     private Listener mListener;
 
-    private ContactListAdapter mAdapter;
+    private ContactTileAdapter mAdapter;
     private ContactPhotoManager mPhotoManager;
 
     private Uri mGroupUri;
@@ -99,6 +95,8 @@
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         mContext = activity;
+        mAdapter = new ContactTileAdapter(activity, mContactTileListener, NUM_COLS,
+                DisplayType.GROUP_MEMBERS);
         configurePhotoLoader();
     }
 
@@ -115,12 +113,7 @@
         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) {
-                // TODO: Open contact detail for this person
-            }
-        });
+
         return mRootView;
     }
 
@@ -130,16 +123,7 @@
     }
 
     private void configureAdapter(long groupId) {
-        mAdapter = new DefaultContactListAdapter(getActivity());
-        mAdapter.setSectionHeaderDisplayEnabled(false);
-        mAdapter.setDisplayPhotos(true);
-        mAdapter.setHasHeader(0, false);
-        mAdapter.setQuickContactEnabled(false);
-        mAdapter.setPinnedPartitionHeadersEnabled(false);
-        mAdapter.setContactNameDisplayOrder(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY);
-        mAdapter.setSortOrder(ContactsContract.Preferences.SORT_ORDER_PRIMARY);
-        mAdapter.setPhotoLoader(mPhotoManager);
-        mAdapter.setFilter(ContactListFilter.createGroupFilter(groupId));
+        mGroupId = groupId;
         mMemberListView.setAdapter(mAdapter);
     }
 
@@ -177,6 +161,15 @@
         getLoaderManager().restartLoader(LOADER_MEMBERS, null, mGroupMemberListLoaderListener);
     }
 
+    private final ContactTileAdapter.Listener mContactTileListener =
+            new ContactTileAdapter.Listener() {
+
+        @Override
+        public void onContactSelected(Uri contactUri) {
+            // TODO: Launch Quick Contact
+        }
+    };
+
     /**
      * The listener for the group metadata loader.
      */
@@ -209,15 +202,13 @@
 
         @Override
         public CursorLoader onCreateLoader(int id, Bundle args) {
-            CursorLoader loader = new CursorLoader(mContext, null, null, null, null, null);
-            mAdapter.configureLoader(loader, Directory.DEFAULT);
-            return loader;
+            return new GroupMemberLoader(mContext, mGroupId);
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
             updateSize(Integer.toString(data.getCount()));
-            mAdapter.changeCursor(0, data);
+            mAdapter.loadFromCursor(data);
         }
 
         @Override
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 961eeb8..f3d7dae 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -16,8 +16,10 @@
 package com.android.contacts.list;
 
 import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.GroupMemberLoader;
 import com.android.contacts.R;
 import com.android.contacts.StrequentMetaDataLoader;
+
 import android.content.ContentUris;
 import android.content.Context;
 import android.database.Cursor;
@@ -58,6 +60,11 @@
     private int mColumnCount;
     private int mDividerRowIndex;
     private ContactPhotoManager mPhotoManager;
+    private int mIdIndex;
+    private int mLookupIndex;
+    private int mPhotoUriIndex;
+    private int mNameIndex;
+    private int mStarredIndex;
 
     /**
      * Configures the adapter to filter and display contacts using different view types.
@@ -85,7 +92,8 @@
 
         /**
          * Display all contacts from a group in the cursor in a
-         * regular {@link ContactTileView} layout.
+         * regular {@link ContactTileView} layout. Use {@link GroupMemberLoader}
+         * when passing {@link Cursor} into loadFromCusor method.
          */
         GROUP_MEMBERS
     }
@@ -95,10 +103,46 @@
         mListener = listener;
         mContext = context;
         mColumnCount = numCols;
-        mPhotoManager = ContactPhotoManager.getInstance(context);
         mDisplayType = displayType;
+
+        bindColumnIndices();
     }
 
+    public void setPhotoLoader(ContactPhotoManager photoLoader) {
+        mPhotoManager = photoLoader;
+    }
+
+    /**
+     * Sets the column indices for expected {@link Cursor}
+     * based on {@link DisplayType}.
+     */
+    private void bindColumnIndices() {
+        /**
+         * Need to check for {@link DisplayType#GROUP_MEMBERS} because
+         * it has different projections than all other {@link DisplayType}s
+         * By using {@link GroupMemberLoader} and {@link StrequentMetaDataLoader}
+         * the correct {@link Cursor}s will be given.
+         */
+        if (mDisplayType == DisplayType.GROUP_MEMBERS) {
+            mIdIndex = GroupMemberLoader.CONTACT_PHOTO_ID_COLUMN_INDEX;
+            mLookupIndex = GroupMemberLoader.CONTACT_LOOKUP_KEY_COLUMN_INDEX;
+            mPhotoUriIndex = GroupMemberLoader.CONTACT_PHOTO_URI_COLUMN_INDEX;
+            mNameIndex = GroupMemberLoader.CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
+            mStarredIndex = GroupMemberLoader.CONTACT_STARRED_COLUMN_INDEX;
+        } else {
+            mIdIndex = StrequentMetaDataLoader.CONTACT_ID;
+            mLookupIndex = StrequentMetaDataLoader.LOOKUP_KEY;
+            mPhotoUriIndex = StrequentMetaDataLoader.PHOTO_URI;
+            mNameIndex = StrequentMetaDataLoader.DISPLAY_NAME;
+            mStarredIndex = StrequentMetaDataLoader.STARRED;
+        }
+    }
+
+    /**
+     * Creates {@link ContactTileView}s for each item in {@link Cursor}.
+     * If {@link DisplayType} is {@link DisplayType#GROUP_MEMBERS} use {@link GroupMemberLoader}
+     * Else use {@link StrequentMetaDataLoader}
+     */
     public void loadFromCursor(Cursor cursor) {
         mContacts.clear();
         mContacts2.clear();
@@ -107,19 +151,16 @@
         // In that case, show an empty list of contacts.
         if (cursor != null) {
             while (cursor.moveToNext()) {
+                long id = cursor.getLong(mIdIndex);
+                String photoUri = cursor.getString(mPhotoUriIndex);
+                String lookupKey = cursor.getString(mLookupIndex);
+                boolean isStarred = (cursor.getInt(mStarredIndex) == 1);
+
                 ContactEntry contact = new ContactEntry();
-
-                long id = cursor.getLong(StrequentMetaDataLoader.CONTACT_ID);
-                String lookupKey = cursor.getString(StrequentMetaDataLoader.LOOKUP_KEY);
-                String photoUri = cursor.getString(StrequentMetaDataLoader.PHOTO_URI);
-
+                contact.name = cursor.getString(mNameIndex);
                 contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
-
                 contact.lookupKey = ContentUris.withAppendedId(
                         Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
-                contact.name = cursor.getString(StrequentMetaDataLoader.DISPLAY_NAME);
-
-                boolean isStarred = (cursor.getInt(StrequentMetaDataLoader.STARRED) == 1);
 
                 switch (mDisplayType) {
                     case STREQUENT:
@@ -157,11 +198,6 @@
         return numRows;
     }
 
-    public void setColumnCount(int colCount) {
-        mColumnCount = colCount;
-        notifyDataSetChanged();
-    }
-
     /**
      * Returns the number of rows required to show the provided number of entries
      * with the current number of columns.
diff --git a/src/com/android/contacts/list/StrequentContactListFragment.java b/src/com/android/contacts/list/StrequentContactListFragment.java
index d2e2ca3..d226950 100644
--- a/src/com/android/contacts/list/StrequentContactListFragment.java
+++ b/src/com/android/contacts/list/StrequentContactListFragment.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.list;
 
+import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.R;
 import com.android.contacts.StrequentMetaDataLoader;
 import com.android.contacts.list.ContactTileAdapter.DisplayType;
@@ -23,7 +24,6 @@
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.database.Cursor;
@@ -49,29 +49,24 @@
     private Listener mListener;
     private ContactTileAdapter mAdapter;
     private ListView mListView;
-    private Context mContext;
 
     @Override
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         mAdapter = new ContactTileAdapter(activity, mAdapterListener,
                 NUM_COLS, DisplayType.STREQUENT);
-        mContext = activity;
+        mAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        View v = inflater.inflate(R.layout.strequent_fragment, container, false);
-        mListView = (ListView) v.findViewById(R.id.strequent_list);
+        View v = inflater.inflate(R.layout.contact_tile_list, container, false);
+        mListView = (ListView) v.findViewById(R.id.contact_tile_list);
         mListView.setItemsCanFocus(true);
         return v;
     }
 
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
-
     @Override
     public void onStart(){
         super.onStart();
@@ -86,7 +81,7 @@
 
         @Override
         public CursorLoader onCreateLoader(int id, Bundle args) {
-            return new StrequentMetaDataLoader(mContext);
+            return new StrequentMetaDataLoader(getActivity());
         }
 
         @Override
@@ -101,6 +96,10 @@
         }
     };
 
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
     private ContactTileAdapter.Listener mAdapterListener =
             new ContactTileAdapter.Listener() {
         @Override