Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2552c11d authored by Walter Jang's avatar Walter Jang
Browse files

Show distinct group member aggregate contacts

Bug 29455514
Bug 18641067

Change-Id: Icf5c24ab26177dd3b8d46750978fcabc486f9be4
parent ed4a4cc8
Loading
Loading
Loading
Loading
+4 −22
Original line number Diff line number Diff line
@@ -17,10 +17,8 @@ package com.android.contacts.activities;

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;
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 android.widget.Toast;

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.AccountTypeManager;
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.MultiSelectContactsListFragment;
import com.android.contacts.list.UiIntentActions;
import com.android.contacts.quickcontact.QuickContactActivity;

import java.util.ArrayList;
import java.util.List;

/**
@@ -355,7 +348,7 @@ public class GroupMembersActivity extends ContactsDrawerActivity implements

    @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 @@ public class GroupMembersActivity extends ContactsDrawerActivity implements
                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 @@ public class GroupMembersActivity extends ContactsDrawerActivity implements
        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);
+117 −56
Original line number Diff line number Diff line
@@ -19,25 +19,30 @@ import android.app.LoaderManager.LoaderCallbacks;
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 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
    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 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
        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 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
                    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 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {

    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 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
        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,31 +242,40 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(KEY_GROUP_URI, mGroupUri);
        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
    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 onGroupMetadataLoaded() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Loaded " + mGroupMetadata);

        maybeAttachCheckBoxListener();

        // Bind the members count
    private void bindMembersCount(int memberCount) {
        final View accountFilterContainer = getView().findViewById(
                R.id.account_filter_header_container);
        if (mGroupMetadata.memberCount >= 0) {
        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, mGroupMetadata.memberCount,
                    mGroupMetadata.memberCount));
                    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);
        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
    }

    private void onGroupMetadataLoaded() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Loaded " + mGroupMetadata);

        maybeAttachCheckBoxListener();

        if (mListener != null) {
            mListener.onGroupMetadataLoaded(mGroupMetadata);
@@ -238,11 +304,6 @@ public class GroupMembersListFragment extends MultiSelectContactsListFragment {
        return adapter;
    }

    @Override
    public GroupMembersListAdapter getAdapter() {
        return (GroupMembersListAdapter) super.getAdapter();
    }

    @Override
    protected void configureAdapter() {
        super.configureAdapter();
+1 −6
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ public final class GroupMetadata implements Parcelable {
    public String groupName;
    public boolean readOnly;
    public boolean editable;
    public int memberCount = -1;

    public GroupMetadata() {
    }
@@ -64,7 +63,6 @@ public final class GroupMetadata implements Parcelable {
        groupName = source.readString();
        readOnly = source.readInt() == 1;
        editable = source.readInt() == 1;
        memberCount = source.readInt();
    }

    @Override
@@ -77,7 +75,6 @@ public final class GroupMetadata implements Parcelable {
        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 @@ public final class GroupMetadata implements Parcelable {
        return uri != null
                && !TextUtils.isEmpty(accountName)
                && !TextUtils.isEmpty(groupName)
                && groupId > 0
                && memberCount >= 0;
                && groupId > 0;
    }

    public AccountWithDataSet createAccountWithDataSet() {
@@ -114,7 +110,6 @@ public final class GroupMetadata implements Parcelable {
                " groupName=" + groupName +
                " readOnly=" + readOnly +
                " editable=" + editable +
                " memberCount=" + memberCount +
                " isValid=" + isValid() +
                "]";
    }
+3 −6
Original line number Diff line number Diff line
@@ -57,10 +57,7 @@ public class GroupMemberPickerFragment extends
        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 @@ public class GroupMemberPickerFragment extends
            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 @@ public class GroupMemberPickerFragment extends
            super.moveToFirst();

            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "FilterCursorWrapper ending cursor size is" + mCount);
                Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
            }
        }