Consolidate the two group metadata classes

* group.GroupMetadata is renamed to GroupMetaData

* The defaultGroup and favorites fields from
  common.GroupMetaData are added to group.GroupMetaData.

Test: manual tests
  - Add and edit a group
  - Load quick contacts w/ and w/o groups
  - Add invisible and directory contacts to default group

Change-Id: I7f53ae6e4409c414918ffc5615fb5e3bd25d116d
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index 4fc6976..01228e3 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -62,7 +62,7 @@
 import com.android.contacts.common.util.ViewUtil;
 import com.android.contacts.editor.ContactEditorFragment;
 import com.android.contacts.group.GroupListItem;
-import com.android.contacts.group.GroupMetadata;
+import com.android.contacts.group.GroupMetaData;
 import com.android.contacts.group.GroupNameEditDialogFragment;
 import com.android.contacts.group.GroupUtil;
 import com.android.contacts.group.GroupsFragment;
@@ -405,18 +405,18 @@
             }
         });
 
-        if (getGroupMetadata() != null) {
-            updateGroupMenu(getGroupMetadata());
+        if (getGroupMetaData() != null) {
+            updateGroupMenu(getGroupMetaData());
         }
     }
 
-    protected void updateGroupMenu(GroupMetadata groupMetadata) {
+    protected void updateGroupMenu(GroupMetaData groupMetaData) {
         clearCheckedMenus();
-        if (groupMetadata != null && mGroupMenuMap != null
-                && mGroupMenuMap.get(groupMetadata.groupId) != null) {
-            mGroupMenuMap.get(groupMetadata.groupId).setCheckable(true);
-            mGroupMenuMap.get(groupMetadata.groupId).setChecked(true);
-            maybeUpdateScrollPosition(mGroupMenuMap.get(groupMetadata.groupId).getOrder());
+        if (groupMetaData != null && mGroupMenuMap != null
+                && mGroupMenuMap.get(groupMetaData.groupId) != null) {
+            mGroupMenuMap.get(groupMetaData.groupId).setCheckable(true);
+            mGroupMenuMap.get(groupMetaData.groupId).setChecked(true);
+            maybeUpdateScrollPosition(mGroupMenuMap.get(groupMetaData.groupId).getOrder());
         }
     }
 
@@ -424,7 +424,7 @@
      * Returns group metadata if the child class is {@link GroupMembersActivity}, and null
      * otherwise.
      */
-    protected GroupMetadata getGroupMetadata() {
+    protected GroupMetaData getGroupMetaData() {
         return null;
     }
 
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index 26cdb44..18a975d 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -28,7 +28,7 @@
  */
 public final class GroupMetaDataLoader extends CursorLoader {
 
-    private final static String[] COLUMNS = new String[] {
+    public final static String[] COLUMNS = new String[] {
         Groups.ACCOUNT_NAME,
         Groups.ACCOUNT_TYPE,
         Groups.DATA_SET,
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 758481e..e887cb9 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
-import android.graphics.drawable.ColorDrawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -30,7 +29,6 @@
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import com.android.contacts.ContactSaveService;
@@ -42,7 +40,7 @@
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupMembersFragment;
-import com.android.contacts.group.GroupMetadata;
+import com.android.contacts.group.GroupMetaData;
 import com.android.contacts.group.GroupNameEditDialogFragment;
 import com.android.contacts.group.GroupUtil;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
@@ -126,8 +124,8 @@
                     rawContactIdsToRemove, GroupMembersActivity.class, action);
         }
 
-        // TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
-        // log a warning if the raw contact is already a member and keep going but it is not ideal.
+        // ContactSaveService will log a warning if the raw contact is already a member and keep
+        // going but it is not ideal, we could also prune raw contacts that are already members.
         private long[] getRawContactIds() {
             final Uri rawContactUri = RawContacts.CONTENT_URI.buildUpon()
                     .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName)
@@ -175,7 +173,7 @@
     private Uri mGroupUri;
     private boolean mIsEditMode;
 
-    private GroupMetadata mGroupMetadata;
+    private GroupMetaData mGroupMetaData;
 
     @Override
     public void onCreate(Bundle savedState) {
@@ -185,7 +183,7 @@
         if (savedState != null) {
             mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
             mIsEditMode = savedState.getBoolean(KEY_IS_EDIT_MODE);
-            mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
+            mGroupMetaData = savedState.getParcelable(KEY_GROUP_METADATA);
         } else {
             mGroupUri = getIntent().getData();
             setTitle(getIntent().getStringExtra(GroupUtil.EXTRA_GROUP_NAME));
@@ -217,7 +215,7 @@
                     mMembersFragment, TAG_GROUP_MEMBERS).commitAllowingStateLoss();
         }
         mMembersFragment.setListener(this);
-        if (mGroupMetadata != null && mGroupMetadata.editable) {
+        if (mGroupMetaData != null && mGroupMetaData.editable) {
             mMembersFragment.setCheckBoxListListener(this);
         }
 
@@ -235,7 +233,7 @@
         }
         outState.putParcelable(KEY_GROUP_URI, mGroupUri);
         outState.putBoolean(KEY_IS_EDIT_MODE, mIsEditMode);
-        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
+        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetaData);
     }
 
     // Invoked with results from the ContactSaveService
@@ -304,14 +302,14 @@
         addGroupsAndFiltersFragments(transaction);
         transaction.replace(R.id.fragment_container_inner, mMembersFragment, TAG_GROUP_MEMBERS)
                 .commitAllowingStateLoss();
-        if (mGroupMetadata != null && mGroupMetadata.editable) {
+        if (mGroupMetaData != null && mGroupMetaData.editable) {
             mMembersFragment.setCheckBoxListListener(this);
         }
     }
 
     @Override
     protected void onGroupMenuItemClicked(long groupId, String title) {
-        if (mGroupMetadata.groupId != groupId) {
+        if (mGroupMetaData.groupId != groupId) {
             super.onGroupMenuItemClicked(groupId, title);
         }
     }
@@ -333,7 +331,7 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        if (mGroupMetadata == null) {
+        if (mGroupMetaData == null) {
             // Hide menu options until metadata is fully loaded
             return false;
         }
@@ -345,8 +343,8 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
-        final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
-        final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;
+        final boolean isGroupEditable = mGroupMetaData != null && mGroupMetaData.editable;
+        final boolean isGroupReadOnly = mGroupMetaData != null && mGroupMetaData.readOnly;
 
         setVisible(menu, R.id.menu_add, isGroupEditable && !isSelectionMode);
         setVisible(menu, R.id.menu_rename_group, !isGroupReadOnly && !isSelectionMode);
@@ -372,7 +370,7 @@
     }
 
     public void startGroupAddMemberActivity() {
-        startActivityForResult(GroupUtil.createPickMemberIntent(this, mGroupMetadata,
+        startActivityForResult(GroupUtil.createPickMemberIntent(this, mGroupMetaData,
                 mMembersFragment.getMemberContactIds()), RESULT_GROUP_ADD_MEMBER);
     }
 
@@ -389,9 +387,9 @@
             }
             case R.id.menu_rename_group: {
                 GroupNameEditDialogFragment.newInstanceForUpdate(
-                        new AccountWithDataSet(mGroupMetadata.accountName,
-                                mGroupMetadata.accountType, mGroupMetadata.dataSet),
-                        ACTION_UPDATE_GROUP, mGroupMetadata.groupId, mGroupMetadata.groupName)
+                        new AccountWithDataSet(mGroupMetaData.accountName,
+                                mGroupMetaData.accountType, mGroupMetaData.dataSet),
+                        ACTION_UPDATE_GROUP, mGroupMetaData.groupId, mGroupMetaData.groupName)
                         .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
                 return true;
             }
@@ -423,12 +421,12 @@
     private void deleteGroup() {
         if (mMembersFragment.getMemberCount() == 0) {
             final Intent intent = ContactSaveService.createGroupDeletionIntent(this,
-                    mGroupMetadata.groupId);
+                    mGroupMetaData.groupId);
             startService(intent);
             finish();
         } else {
-            GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetadata.groupId,
-                    mGroupMetadata.groupName);
+            GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetaData.groupId,
+                    mGroupMetaData.groupName);
         }
     }
 
@@ -444,8 +442,8 @@
     private void removeSelectedContacts() {
         final long[] contactIds = mMembersFragment.getAdapter().getSelectedContactIdsArray();
         new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
-                this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                mGroupMetadata.accountType).execute();
+                this, contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
+                mGroupMetaData.accountType).execute();
 
         mActionBarAdapter.setSelectionMode(false);
     }
@@ -490,8 +488,8 @@
                 }
             }
             new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_ADD,
-                    this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                    mGroupMetadata.accountType).execute();
+                    this, contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
+                    mGroupMetaData.accountType).execute();
         }
     }
 
@@ -579,10 +577,10 @@
     // GroupMembersFragment callbacks
 
     @Override
-    public void onGroupMetadataLoaded(GroupMetadata groupMetadata) {
-        mGroupMetadata = groupMetadata;
-        updateGroupMenu(mGroupMetadata);
-        setTitle(mGroupMetadata.groupName);
+    public void onGroupMetadataLoaded(GroupMetaData groupMetaData) {
+        mGroupMetaData = groupMetaData;
+        updateGroupMenu(mGroupMetaData);
+        setTitle(mGroupMetaData.groupName);
         invalidateOptionsMenu();
     }
 
@@ -591,9 +589,8 @@
         setResultCanceledAndFinish(R.string.groupLoadErrorToast);
     }
 
-    @Override
-    protected GroupMetadata getGroupMetadata() {
-        return mGroupMetadata;
+    protected GroupMetaData getGroupMetaData() {
+        return mGroupMetaData;
     }
 
     @Override
@@ -612,7 +609,7 @@
         final long[] contactIds = new long[1];
         contactIds[0] = contactId;
         new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
-                this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                mGroupMetadata.accountType).execute();
+                this, contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
+                mGroupMetaData.accountType).execute();
     }
 }
diff --git a/src/com/android/contacts/common/GroupMetaData.java b/src/com/android/contacts/common/GroupMetaData.java
deleted file mode 100644
index fa86ae2..0000000
--- a/src/com/android/contacts/common/GroupMetaData.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.common;
-
-/**
- * Meta-data for a contact group.  We load all groups associated with the contact's
- * constituent accounts.
- */
-public final class GroupMetaData {
-    private String mAccountName;
-    private String mAccountType;
-    private String mDataSet;
-    private long mGroupId;
-    private String mTitle;
-    private boolean mDefaultGroup;
-    private boolean mFavorites;
-
-    public GroupMetaData(String accountName, String accountType, String dataSet, long groupId,
-            String title, boolean defaultGroup, boolean favorites) {
-        this.mAccountName = accountName;
-        this.mAccountType = accountType;
-        this.mDataSet = dataSet;
-        this.mGroupId = groupId;
-        this.mTitle = title;
-        this.mDefaultGroup = defaultGroup;
-        this.mFavorites = favorites;
-    }
-
-    public String getAccountName() {
-        return mAccountName;
-    }
-
-    public String getAccountType() {
-        return mAccountType;
-    }
-
-    public String getDataSet() {
-        return mDataSet;
-    }
-
-    public long getGroupId() {
-        return mGroupId;
-    }
-
-    public String getTitle() {
-        return mTitle;
-    }
-
-    public boolean isDefaultGroup() {
-        return mDefaultGroup;
-    }
-
-    public boolean isFavorites() {
-        return mFavorites;
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/common/model/Contact.java b/src/com/android/contacts/common/model/Contact.java
index 27bf13e..2187b1d 100644
--- a/src/com/android/contacts/common/model/Contact.java
+++ b/src/com/android/contacts/common/model/Contact.java
@@ -24,16 +24,15 @@
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.DisplayNameSources;
 
-import com.android.contacts.common.GroupMetaData;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.util.DataStatus;
+import com.android.contacts.group.GroupMetaData;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.ArrayList;
-import java.util.Collections;
 
 /**
  * A Contact represents a single person or logical entity as perceived by the user.  The information
diff --git a/src/com/android/contacts/common/model/ContactLoader.java b/src/com/android/contacts/common/model/ContactLoader.java
index f721379..a8deff4 100644
--- a/src/com/android/contacts/common/model/ContactLoader.java
+++ b/src/com/android/contacts/common/model/ContactLoader.java
@@ -38,8 +38,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.GroupMetaData;
 import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountTypeWithDataSet;
@@ -50,6 +50,8 @@
 import com.android.contacts.common.model.dataitem.DataItem;
 import com.android.contacts.common.model.dataitem.PhoneDataItem;
 import com.android.contacts.common.model.dataitem.PhotoDataItem;
+import com.android.contacts.group.GroupMetaData;
+
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
@@ -292,26 +294,6 @@
         public static final int EXPORT_SUPPORT = 5;
     }
 
-    private static class GroupQuery {
-        static final String[] COLUMNS = new String[] {
-            Groups.ACCOUNT_NAME,
-            Groups.ACCOUNT_TYPE,
-            Groups.DATA_SET,
-            Groups._ID,
-            Groups.TITLE,
-            Groups.AUTO_ADD,
-            Groups.FAVORITES,
-        };
-
-        public static final int ACCOUNT_NAME = 0;
-        public static final int ACCOUNT_TYPE = 1;
-        public static final int DATA_SET = 2;
-        public static final int ID = 3;
-        public static final int TITLE = 4;
-        public static final int AUTO_ADD = 5;
-        public static final int FAVORITES = 6;
-    }
-
     public void setLookupUri(Uri lookupUri) {
         mLookupUri = lookupUri;
     }
@@ -859,29 +841,14 @@
                 selection.append(")");
             }
         }
-        final ImmutableList.Builder<GroupMetaData> groupListBuilder =
-                new ImmutableList.Builder<GroupMetaData>();
+        final ImmutableList.Builder<GroupMetaData> groupListBuilder = new ImmutableList.Builder<>();
         final Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
-                GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
-                null);
+                GroupMetaDataLoader.COLUMNS, selection.toString(),
+                selectionArgs.toArray(new String[0]), null);
         if (cursor != null) {
             try {
                 while (cursor.moveToNext()) {
-                    final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
-                    final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
-                    final String dataSet = cursor.getString(GroupQuery.DATA_SET);
-                    final long groupId = cursor.getLong(GroupQuery.ID);
-                    final String title = cursor.getString(GroupQuery.TITLE);
-                    final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
-                            ? false
-                            : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
-                    final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
-                            ? false
-                            : cursor.getInt(GroupQuery.FAVORITES) != 0;
-
-                    groupListBuilder.add(new GroupMetaData(
-                                    accountName, accountType, dataSet, groupId, title, defaultGroup,
-                                    favorites));
+                    groupListBuilder.add(new GroupMetaData(getContext(), cursor));
                 }
             } finally {
                 cursor.close();
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 48dccfe..d7070f0 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -40,8 +40,6 @@
 import com.android.contacts.common.list.ContactsSectionIndexer;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
 import com.android.contacts.common.logging.ListEvent.ListType;
-import com.android.contacts.common.model.AccountTypeManager;
-import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.group.GroupMembersAdapter.GroupMembersQuery;
 import com.android.contacts.list.MultiSelectContactsListFragment;
@@ -68,7 +66,7 @@
     public interface GroupMembersListener {
 
         /** Invoked after group metadata for the passed in group URI has loaded. */
-        void onGroupMetadataLoaded(GroupMetadata groupMetadata);
+        void onGroupMetadataLoaded(GroupMetaData groupMetaData);
 
         /** Invoked if group metadata can't be loaded for the passed in group URI. */
         void onGroupMetadataLoadFailed();
@@ -196,21 +194,7 @@
                 }
                 return;
             }
-            mGroupMetadata = new GroupMetadata();
-            mGroupMetadata.uri = mGroupUri;
-            mGroupMetadata.accountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
-            mGroupMetadata.accountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
-            mGroupMetadata.dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
-            mGroupMetadata.groupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
-            mGroupMetadata.groupName = cursor.getString(GroupMetaDataLoader.TITLE);
-            mGroupMetadata.readOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
-
-            final AccountTypeManager accountTypeManager =
-                    AccountTypeManager.getInstance(getActivity());
-            final AccountType accountType = accountTypeManager.getAccountType(
-                    mGroupMetadata.accountType, mGroupMetadata.dataSet);
-            mGroupMetadata.editable = accountType.isGroupMembershipEditable();
-
+            mGroupMetaData = new GroupMetaData(getActivity(), cursor);
             onGroupMetadataLoaded();
         }
 
@@ -222,7 +206,7 @@
 
     private GroupMembersListener mListener;
 
-    private GroupMetadata mGroupMetadata;
+    private GroupMetaData mGroupMetaData;
 
     private Set<String> mGroupMemberContactIds = new HashSet();
 
@@ -266,13 +250,13 @@
             mGroupUri = getArguments().getParcelable(ARG_GROUP_URI);
         } else {
             mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
-            mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
+            mGroupMetaData = savedState.getParcelable(KEY_GROUP_METADATA);
         }
     }
 
     @Override
     protected void startLoading() {
-        if (mGroupMetadata == null || !mGroupMetadata.isValid()) {
+        if (mGroupMetaData == null || !mGroupMetaData.isValid()) {
             getLoaderManager().restartLoader(LOADER_GROUP_METADATA, null, mGroupMetadataCallbacks);
         } else {
             onGroupMetadataLoaded();
@@ -299,7 +283,7 @@
         final View emptyGroupView = getView().findViewById(R.id.empty_group);
         if (memberCount > 0) {
             final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
-                    mGroupMetadata.accountName, mGroupMetadata.accountType, mGroupMetadata.dataSet);
+                    mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet);
             bindListHeader(getContext(), getListView(), accountFilterContainer,
                     accountWithDataSet, memberCount);
             emptyGroupView.setVisibility(View.GONE);
@@ -313,16 +297,16 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putParcelable(KEY_GROUP_URI, mGroupUri);
-        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
+        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetaData);
     }
 
     private void onGroupMetadataLoaded() {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Loaded " + mGroupMetadata);
+        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Loaded " + mGroupMetaData);
 
         maybeAttachCheckBoxListener();
 
         if (mListener != null) {
-            mListener.onGroupMetadataLoaded(mGroupMetadata);
+            mListener.onGroupMetadataLoaded(mGroupMetaData);
         }
 
         // Start loading the group members
@@ -331,7 +315,7 @@
 
     private void maybeAttachCheckBoxListener() {
         // Don't attach the multi select check box listener if we can't edit the group
-        if (mGroupMetadata != null && mGroupMetadata.editable) {
+        if (mGroupMetaData != null && mGroupMetaData.editable) {
             try {
                 setCheckBoxListListener((OnCheckBoxListActionListener) getActivity());
             } catch (ClassCastException e) {
@@ -353,8 +337,8 @@
     @Override
     protected void configureAdapter() {
         super.configureAdapter();
-        if (mGroupMetadata != null) {
-            getAdapter().setGroupId(mGroupMetadata.groupId);
+        if (mGroupMetaData != null) {
+            getAdapter().setGroupId(mGroupMetaData.groupId);
         }
     }
 
diff --git a/src/com/android/contacts/group/GroupMetaData.java b/src/com/android/contacts/group/GroupMetaData.java
new file mode 100644
index 0000000..3fe87d8
--- /dev/null
+++ b/src/com/android/contacts/group/GroupMetaData.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.contacts.group;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+
+import com.google.common.base.Objects;
+
+/** Meta data for a contact group. */
+public final class GroupMetaData implements Parcelable {
+
+    public static final Creator<GroupMetaData> CREATOR = new Creator<GroupMetaData>() {
+
+        public GroupMetaData createFromParcel(Parcel in) {
+            return new GroupMetaData(in);
+        }
+
+        public GroupMetaData[] newArray(int size) {
+            return new GroupMetaData[size];
+        }
+    };
+
+    public final Uri uri;
+    public final String accountName;
+    public final String accountType;
+    public final String dataSet;
+    public final long groupId;
+    public final String groupName;
+    public final boolean readOnly;
+    public final boolean defaultGroup;
+    public final boolean favorites;
+    public final boolean editable;
+
+    /**
+     * @param cursor Cursor loaded with {@link GroupMetaDataLoader#COLUMNS} as the projection.
+     */
+    public GroupMetaData(Context context, Cursor cursor) {
+        final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
+        final long groupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
+        final Uri groupUri = ContentUris.withAppendedId(
+                ContactsContract.Groups.CONTENT_URI, groupId);
+        final AccountType accountType = accountTypeManager.getAccountType(
+                cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE),
+                cursor.getString(GroupMetaDataLoader.DATA_SET));
+        final boolean editable = accountType == null
+                ? false : accountType.isGroupMembershipEditable();
+
+        this.uri = groupUri;
+        this.accountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+        this.accountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+        this.dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
+        this.groupId = groupId;
+        this.groupName = cursor.getString(GroupMetaDataLoader.TITLE);
+        this.readOnly = getBoolean(cursor, GroupMetaDataLoader.IS_READ_ONLY);
+        this.defaultGroup = getBoolean(cursor, GroupMetaDataLoader.AUTO_ADD);
+        this.favorites = getBoolean(cursor, GroupMetaDataLoader.FAVORITES);
+        this.editable = editable;
+    }
+
+    private static boolean getBoolean(Cursor cursor, int columnIndex) {
+         return cursor.isNull(columnIndex) ? false : cursor.getInt(columnIndex) != 0;
+    }
+
+    private GroupMetaData(Parcel source) {
+        uri = source.readParcelable(Uri.class.getClassLoader());
+        accountName = source.readString();
+        accountType = source.readString();
+        dataSet = source.readString();
+        groupId = source.readLong();
+        groupName = source.readString();
+        readOnly = source.readInt() == 1;
+        defaultGroup = source.readInt() == 1;
+        favorites = source.readInt() == 1;
+        editable = source.readInt() == 1;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(uri, 0);
+        dest.writeString(accountName);
+        dest.writeString(accountType);
+        dest.writeString(dataSet);
+        dest.writeLong(groupId);
+        dest.writeString(groupName);
+        dest.writeInt(readOnly ? 1 : 0);
+        dest.writeInt(defaultGroup ? 1 : 0);
+        dest.writeInt(favorites ? 1 : 0);
+        dest.writeInt(editable ? 1 : 0);
+    }
+
+    /** Whether all metadata fields are set. */
+    public boolean isValid() {
+        return uri != null
+                && !TextUtils.isEmpty(accountName)
+                && !TextUtils.isEmpty(groupName)
+                && groupId > 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("accountName", accountName)
+                .add("accountType", accountType)
+                .add("dataSet", dataSet)
+                .add("groupId", groupId)
+                .add("groupName", groupName)
+                .add("readOnly", readOnly)
+                .add("defaultGroup", defaultGroup)
+                .add("favorites", favorites)
+                .add("editable", editable)
+                .add("isValid", isValid())
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupMetadata.java b/src/com/android/contacts/group/GroupMetadata.java
deleted file mode 100644
index f3dfec24..0000000
--- a/src/com/android/contacts/group/GroupMetadata.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.contacts.group;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.contacts.common.model.account.AccountWithDataSet;
-
-/** Meta data for a contact group. */
-// TODO(wjang): consolidate with com.android.contacts.common.GroupMetaData;
-public final class GroupMetadata implements Parcelable {
-
-    public static final Creator<GroupMetadata> CREATOR = new Creator<GroupMetadata>() {
-
-        public GroupMetadata createFromParcel(Parcel in) {
-            return new GroupMetadata(in);
-        }
-
-        public GroupMetadata[] newArray(int size) {
-            return new GroupMetadata[size];
-        }
-    };
-
-    // TODO(wjang): make them all final and add getters
-    public Uri uri;
-    public String accountName;
-    public String accountType;
-    public String dataSet;
-    public long groupId = -1;
-    public String groupName;
-    public boolean readOnly;
-    public boolean editable;
-
-    public GroupMetadata() {
-    }
-
-    private GroupMetadata(Parcel source) {
-        readFromParcel(source);
-    }
-
-    private void readFromParcel(Parcel source) {
-        uri = source.readParcelable(Uri.class.getClassLoader());
-        accountName = source.readString();
-        accountType = source.readString();
-        dataSet = source.readString();
-        groupId = source.readLong();
-        groupName = source.readString();
-        readOnly = source.readInt() == 1;
-        editable = source.readInt() == 1;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(uri, 0);
-        dest.writeString(accountName);
-        dest.writeString(accountType);
-        dest.writeString(dataSet);
-        dest.writeLong(groupId);
-        dest.writeString(groupName);
-        dest.writeInt(readOnly ? 1 : 0);
-        dest.writeInt(editable ? 1 : 0);
-    }
-
-    /** Whether all metadata fields are set. */
-    public boolean isValid() {
-        return uri != null
-                && !TextUtils.isEmpty(accountName)
-                && !TextUtils.isEmpty(groupName)
-                && groupId > 0;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public String toString() {
-        return "GroupMetadata[uri=" + uri +
-                " accountName=" + accountName +
-                " accountType=" + accountType +
-                " dataSet=" + dataSet +
-                " groupId=" + groupId +
-                " groupName=" + groupName +
-                " readOnly=" + readOnly +
-                " editable=" + editable +
-                " isValid=" + isValid() +
-                "]";
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupUtil.java b/src/com/android/contacts/group/GroupUtil.java
index d1780e1..6539bc8 100644
--- a/src/com/android/contacts/group/GroupUtil.java
+++ b/src/com/android/contacts/group/GroupUtil.java
@@ -105,13 +105,13 @@
 
     /** Returns an Intent to pick contacts to add to a group. */
     public static Intent createPickMemberIntent(Context context,
-            GroupMetadata groupMetadata, ArrayList<String> memberContactIds) {
+            GroupMetaData groupMetaData, ArrayList<String> memberContactIds) {
         final Intent intent = new Intent(context, ContactSelectionActivity.class);
         intent.setAction(Intent.ACTION_PICK);
         intent.setType(Groups.CONTENT_TYPE);
-        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_NAME, groupMetadata.accountName);
-        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_TYPE, groupMetadata.accountType);
-        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET, groupMetadata.dataSet);
+        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_NAME, groupMetaData.accountName);
+        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_TYPE, groupMetaData.accountType);
+        intent.putExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET, groupMetaData.dataSet);
         intent.putExtra(UiIntentActions.GROUP_CONTACT_IDS, memberContactIds);
         return intent;
     }
diff --git a/src/com/android/contacts/quickcontact/InvisibleContactUtil.java b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
index fa1132b..c8bb98d 100644
--- a/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
+++ b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
@@ -4,7 +4,6 @@
 import com.google.common.collect.Iterables;
 
 import com.android.contacts.ContactSaveService;
-import com.android.contacts.common.GroupMetaData;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.Contact;
 import com.android.contacts.common.model.RawContact;
@@ -16,6 +15,7 @@
 import com.android.contacts.common.model.dataitem.DataItem;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.common.model.dataitem.GroupMembershipDataItem;
+import com.android.contacts.group.GroupMetaData;
 
 import android.content.Context;
 import android.content.Intent;
@@ -103,10 +103,10 @@
     private static long getDefaultGroupId(List<GroupMetaData> groups) {
         long defaultGroupId = -1;
         for (GroupMetaData group : groups) {
-            if (group.isDefaultGroup()) {
+            if (group.defaultGroup) {
                 // two default groups? return neither
                 if (defaultGroupId != -1) return -1;
-                defaultGroupId = group.getGroupId();
+                defaultGroupId = group.groupId;
             }
         }
         return defaultGroupId;