Merge "Show distinct group member aggregate contacts"
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 01999bb..7ee8653 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -17,10 +17,8 @@
import android.accounts.Account;
import android.app.FragmentManager;
-import android.content.Context;
import android.app.FragmentTransaction;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.CursorLoader;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -30,7 +28,6 @@
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.RawContacts;
import android.support.v4.view.GravityCompat;
-import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -38,8 +35,6 @@
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsDrawerActivity;
-import com.android.contacts.GroupMemberLoader;
-import com.android.contacts.GroupMemberLoader.GroupEditorQuery;
import com.android.contacts.R;
import com.android.contacts.common.editor.SelectAccountDialogFragment;
import com.android.contacts.common.logging.ListEvent;
@@ -49,7 +44,6 @@
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.common.util.ImplicitIntentsUtil;
-import com.android.contacts.group.GroupMembersListAdapter.GroupMembersQuery;
import com.android.contacts.group.GroupMembersListFragment;
import com.android.contacts.group.GroupMetadata;
import com.android.contacts.group.GroupNameEditDialogFragment;
@@ -59,7 +53,6 @@
import com.android.contacts.list.UiIntentActions;
import com.android.contacts.quickcontact.QuickContactActivity;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -355,7 +348,7 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- if (mGroupMetadata == null || mGroupMetadata.memberCount < 0) {
+ if (mGroupMetadata == null) {
// Hide menu options until metadata is fully loaded
return false;
}
@@ -399,7 +392,7 @@
intent.putExtra(UiIntentActions.GROUP_ACCOUNT_TYPE, mGroupMetadata.accountType);
intent.putExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET, mGroupMetadata.dataSet);
intent.putExtra(UiIntentActions.GROUP_CONTACT_IDS,
- getExistingGroupMemberContactIds());
+ mMembersListFragment.getMemberContactIds());
startActivityForResult(intent, RESULT_GROUP_ADD_MEMBER);
return true;
}
@@ -424,19 +417,8 @@
return super.onOptionsItemSelected(item);
}
- private ArrayList<String> getExistingGroupMemberContactIds() {
- final ArrayList<String> contactIds = new ArrayList<>();
- final Cursor cursor = mMembersListFragment.getAdapter().getCursor(/* partition */ 0);
- if (cursor != null && cursor.moveToFirst()) {
- do {
- contactIds.add(cursor.getString(GroupMembersQuery.CONTACT_ID));
- } while (cursor.moveToNext());
- }
- return contactIds;
- }
-
private void deleteGroup() {
- if (mGroupMetadata.memberCount == 0) {
+ if (mMembersListFragment.getMemberCount() == 0) {
final Intent intent = ContactSaveService.createGroupDeletionIntent(
this, mGroupMetadata.groupId,
GroupMembersActivity.class, ACTION_DELETE_GROUP);
diff --git a/src/com/android/contacts/group/GroupMembersListFragment.java b/src/com/android/contacts/group/GroupMembersListFragment.java
index 47eed0c..056e251 100644
--- a/src/com/android/contacts/group/GroupMembersListFragment.java
+++ b/src/com/android/contacts/group/GroupMembersListFragment.java
@@ -19,25 +19,30 @@
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
+import android.database.CursorWrapper;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.ContactsContract;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import com.android.contacts.GroupListLoader;
import com.android.contacts.GroupMetaDataLoader;
import com.android.contacts.R;
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.group.GroupMembersListAdapter.GroupMembersQuery;
import com.android.contacts.list.MultiSelectContactsListFragment;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
/** Displays the members of a group. */
-public class GroupMembersListFragment extends MultiSelectContactsListFragment {
+public class GroupMembersListFragment extends
+ MultiSelectContactsListFragment<GroupMembersListAdapter> {
private static final String TAG = "GroupMembers";
@@ -47,7 +52,6 @@
private static final String ARG_GROUP_URI = "groupUri";
private static final int LOADER_GROUP_METADATA = 0;
- private static final int LOADER_GROUP_LIST_DETAILS = 1;
/** Callbacks for hosts of {@link GroupMembersListFragment}. */
public interface GroupMembersListListener {
@@ -62,7 +66,83 @@
void onGroupMemberListItemClicked(int position, Uri contactLookupUri);
}
- /** Step 1 of loading group metadata. */
+ /** Filters out duplicate contacts. */
+ private class FilterCursorWrapper extends CursorWrapper {
+
+ private int[] mIndex;
+ private int mCount = 0;
+ private int mPos = 0;
+
+ public FilterCursorWrapper(Cursor cursor) {
+ super(cursor);
+
+ mCount = super.getCount();
+ mIndex = new int[mCount];
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Group members CursorWrapper start: " + mCount);
+ }
+
+ mGroupMemberContactIds.clear();
+ for (int i = 0; i < mCount; i++) {
+ super.moveToPosition(i);
+ final String contactId = getString(GroupMembersQuery.CONTACT_ID);
+ if (!mGroupMemberContactIds.contains(contactId)) {
+ mIndex[mPos++] = i;
+ mGroupMemberContactIds.add(contactId);
+ }
+ }
+ mCount = mPos;
+ mPos = 0;
+ super.moveToFirst();
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Group members CursorWrapper end: " + mCount);
+ }
+ }
+
+ @Override
+ public boolean move(int offset) {
+ return moveToPosition(mPos + offset);
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return moveToPosition(mPos + 1);
+ }
+
+ @Override
+ public boolean moveToPrevious() {
+ return moveToPosition(mPos - 1);
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ return moveToPosition(0);
+ }
+
+ @Override
+ public boolean moveToLast() {
+ return moveToPosition(mCount - 1);
+ }
+
+ @Override
+ public boolean moveToPosition(int position) {
+ if (position >= mCount || position < 0) return false;
+ return super.moveToPosition(mIndex[position]);
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPos;
+ }
+ }
+
private final LoaderCallbacks<Cursor> mGroupMetadataCallbacks = new LoaderCallbacks<Cursor>() {
@Override
@@ -94,39 +174,6 @@
mGroupMetadata.accountType, mGroupMetadata.dataSet);
mGroupMetadata.editable = accountType.isGroupMembershipEditable();
- getLoaderManager().restartLoader(LOADER_GROUP_LIST_DETAILS, null, mGroupListCallbacks);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {}
- };
-
- /** Step 2 of loading group metadata. */
- private final LoaderCallbacks<Cursor> mGroupListCallbacks = new LoaderCallbacks<Cursor>() {
-
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- final GroupListLoader groupListLoader = new GroupListLoader(getActivity());
-
- groupListLoader.setSelection(GroupListLoader.DEFAULT_SELECTION
- + " AND " + ContactsContract.Groups._ID + "=?");
-
- final String[] selectionArgs = new String[1];
- selectionArgs[0] = Long.toString(mGroupMetadata.groupId);
- groupListLoader.setSelectionArgs(selectionArgs);
-
- return groupListLoader;
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
- if (cursor == null || cursor.isClosed()) {
- Log.e(TAG, "Failed to load group list details");
- return;
- }
- if (cursor.moveToNext()) {
- mGroupMetadata.memberCount = cursor.getInt(GroupListLoader.MEMBER_COUNT);
- }
onGroupMetadataLoaded();
}
@@ -140,6 +187,8 @@
private GroupMetadata mGroupMetadata;
+ private Set<String> mGroupMemberContactIds = new HashSet();
+
public static GroupMembersListFragment newInstance(Uri groupUri) {
final Bundle args = new Bundle();
args.putParcelable(ARG_GROUP_URI, groupUri);
@@ -164,6 +213,14 @@
mListener = listener;
}
+ public ArrayList<String> getMemberContactIds() {
+ return new ArrayList<>(mGroupMemberContactIds);
+ }
+
+ public int getMemberCount() {
+ return mGroupMemberContactIds.size();
+ }
+
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
@@ -185,6 +242,30 @@
}
@Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (data != null) {
+ final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data);
+ bindMembersCount(cursorWrapper.getCount());
+ super.onLoadFinished(loader, cursorWrapper);
+ }
+ }
+
+ private void bindMembersCount(int memberCount) {
+ final View accountFilterContainer = getView().findViewById(
+ R.id.account_filter_header_container);
+ if (memberCount >= 0) {
+ accountFilterContainer.setVisibility(View.VISIBLE);
+
+ final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
+ R.id.account_filter_header);
+ accountFilterHeader.setText(getResources().getQuantityString(
+ R.plurals.group_members_count, memberCount, memberCount));
+ } else {
+ accountFilterContainer.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_GROUP_URI, mGroupUri);
@@ -196,21 +277,6 @@
maybeAttachCheckBoxListener();
- // Bind the members count
- final View accountFilterContainer = getView().findViewById(
- R.id.account_filter_header_container);
- if (mGroupMetadata.memberCount >= 0) {
- accountFilterContainer.setVisibility(View.VISIBLE);
-
- final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
- R.id.account_filter_header);
- accountFilterHeader.setText(getResources().getQuantityString(
- R.plurals.group_members_count, mGroupMetadata.memberCount,
- mGroupMetadata.memberCount));
- } else {
- accountFilterContainer.setVisibility(View.GONE);
- }
-
if (mListener != null) {
mListener.onGroupMetadataLoaded(mGroupMetadata);
}
@@ -239,11 +305,6 @@
}
@Override
- public GroupMembersListAdapter getAdapter() {
- return (GroupMembersListAdapter) super.getAdapter();
- }
-
- @Override
protected void configureAdapter() {
super.configureAdapter();
if (mGroupMetadata != null) {
diff --git a/src/com/android/contacts/group/GroupMetadata.java b/src/com/android/contacts/group/GroupMetadata.java
index fcf5dc2..712500a 100644
--- a/src/com/android/contacts/group/GroupMetadata.java
+++ b/src/com/android/contacts/group/GroupMetadata.java
@@ -46,7 +46,6 @@
public String groupName;
public boolean readOnly;
public boolean editable;
- public int memberCount = -1;
public GroupMetadata() {
}
@@ -64,7 +63,6 @@
groupName = source.readString();
readOnly = source.readInt() == 1;
editable = source.readInt() == 1;
- memberCount = source.readInt();
}
@Override
@@ -77,7 +75,6 @@
dest.writeString(groupName);
dest.writeInt(readOnly ? 1 : 0);
dest.writeInt(editable ? 1 : 0);
- dest.writeInt(memberCount);
}
/** Whether all metadata fields are set. */
@@ -85,8 +82,7 @@
return uri != null
&& !TextUtils.isEmpty(accountName)
&& !TextUtils.isEmpty(groupName)
- && groupId > 0
- && memberCount >= 0;
+ && groupId > 0;
}
public AccountWithDataSet createAccountWithDataSet() {
@@ -114,7 +110,6 @@
" groupName=" + groupName +
" readOnly=" + readOnly +
" editable=" + editable +
- " memberCount=" + memberCount +
" isValid=" + isValid() +
"]";
}
diff --git a/src/com/android/contacts/list/GroupMemberPickerFragment.java b/src/com/android/contacts/list/GroupMemberPickerFragment.java
index 605758f..de856a4 100644
--- a/src/com/android/contacts/list/GroupMemberPickerFragment.java
+++ b/src/com/android/contacts/list/GroupMemberPickerFragment.java
@@ -57,10 +57,7 @@
void onGroupMemberClicked(long contactId);
}
- /**
- * Filters out raw contacts that are already in the group and also handles queries for contact
- * photo IDs and lookup keys which cannot be retrieved from the raw contact table directly.
- */
+ /** Filters out raw contacts that are already in the group. */
private class FilterCursorWrapper extends CursorWrapper {
private int[] mIndex;
@@ -74,7 +71,7 @@
mIndex = new int[mCount];
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "FilterCursorWrapper starting cursor size is " + mCount);
+ Log.v(TAG, "RawContacts CursorWrapper start: " + mCount);
}
for (int i = 0; i < mCount; i++) {
@@ -89,7 +86,7 @@
super.moveToFirst();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "FilterCursorWrapper ending cursor size is" + mCount);
+ Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
}
}