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

Commit 16c29591 authored by Walter Jang's avatar Walter Jang
Browse files

Pick from aggregate contacts when adding to a group (1/2)

* Searching for new group members happens on the standard
  contact seletion activity now so we remove the separate
  search menu item and the old SuggestedMemberListAdapter
  used by the autocomplete text view.
* When a new group member is selected, we add all the raw
  contacts in the selected contact that are owned by the
  group account owner to the group.
* Also use the same unknown name string "(No name)" on the
  the new group member picker that is used on the other
  contact lists.

Bug 28720761
Bug 28716004
Bug 28707265
Bug 18641067

Change-Id: I152bf1caf6652aa474f8f2915248ee19fb5fccf8
parent b658a270
Loading
Loading
Loading
Loading
+0 −64
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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:layout_width="match_parent"
    android:layout_height="48dip"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:background="?android:attr/selectableItemBackground">

    <LinearLayout
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text1"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8dip"
            android:paddingStart="8dip"
            android:singleLine="true"
            android:ellipsize="end"/>

        <TextView android:id="@+id/text2"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8dip"
            android:paddingStart="8dip"
            android:singleLine="true"
            android:ellipsize="end" />

    </LinearLayout>

    <ImageView
        android:id="@+id/icon"
        android:layout_width="48dip"
        android:layout_height="48dip"
        android:cropToPadding="true"
        android:scaleType="centerCrop" />

</LinearLayout>
 No newline at end of file
+0 −5
Original line number Diff line number Diff line
@@ -23,11 +23,6 @@
        android:title="@string/menu_addToGroup"
        contacts:showAsAction="ifRoom" />

    <item
        android:id="@+id/menu_search"
        android:icon="@drawable/ic_ab_search"
        android:title="@string/menu_search" />

    <item
        android:id="@+id/menu_rename_group"
        android:title="@string/menu_renameGroup"/>
+0 −8
Original line number Diff line number Diff line
@@ -54,14 +54,6 @@ public final class GroupMemberLoader extends CursorLoader {

    private final long mGroupId;

    /**
     * @return GroupMemberLoader object which can be used in group editor.
     */
    public static GroupMemberLoader constructLoaderForGroupEditorQuery(
            Context context, long groupId) {
        return new GroupMemberLoader(context, groupId, GroupEditorQuery.PROJECTION);
    }

    private GroupMemberLoader(Context context, long groupId, String[] projection) {
        super(context);
        mGroupId = groupId;
+8 −7
Original line number Diff line number Diff line
@@ -139,10 +139,9 @@ public class ContactSelectionActivity extends ContactsActivity
                .inflate(R.layout.custom_action_bar, null);
        mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view);

        // Postal address  group member,and legacy pickers don't support search, so just show
        // Postal address pickers (and legacy pickers) don't support search, so just show
        // "HomeAsUp" button and title.
        if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL ||
                mRequest.getActionCode() == ContactsRequest.ACTION_PICK_GROUP_MEMBERS ||
                mRequest.isLegacyCompatibilityMode()) {
            mSearchView.setVisibility(View.GONE);
            if (actionBar != null) {
@@ -359,9 +358,9 @@ public class ContactSelectionActivity extends ContactsActivity
            case ContactsRequest.ACTION_PICK_GROUP_MEMBERS: {
                final AccountWithDataSet account = getIntent().getParcelableExtra(
                        UiIntentActions.GROUP_ACCOUNT_WITH_DATA_SET);
                final ArrayList<String> rawContactIds = getIntent().getStringArrayListExtra(
                        UiIntentActions.GROUP_RAW_CONTACT_IDS);
                mListFragment = GroupMemberPickerFragment.newInstance(account, rawContactIds);
                final ArrayList<String> contactIds = getIntent().getStringArrayListExtra(
                        UiIntentActions.GROUP_CONTACT_IDS);
                mListFragment = GroupMemberPickerFragment.newInstance(account, contactIds);
                break;
            }

@@ -483,8 +482,10 @@ public class ContactSelectionActivity extends ContactsActivity
    private final class GroupMemberPickerListener implements GroupMemberPickerFragment.Listener {

        @Override
        public void onGroupMemberClicked(Uri uri) {
            returnPickerResult(uri);
        public void onGroupMemberClicked(long contactId) {
            final Intent intent = new Intent();
            intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, contactId);
            returnPickerResult(intent);
        }
    }

+79 −146
Original line number Diff line number Diff line
@@ -17,29 +17,23 @@ package com.android.contacts.activities;

import android.accounts.Account;
import android.app.FragmentManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.RawContacts;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;

import com.android.contacts.AppCompatContactsActivity;
import com.android.contacts.ContactSaveService;
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;
@@ -53,9 +47,6 @@ 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;
import com.android.contacts.group.Member;
import com.android.contacts.group.SuggestedMemberListAdapter;
import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
import com.android.contacts.interactions.GroupDeletionDialogFragment;
import com.android.contacts.list.ContactsRequest;
import com.android.contacts.list.MultiSelectContactsListFragment;
@@ -85,8 +76,6 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
    private static final String TAG_SELECT_ACCOUNT_DIALOG = "selectAccountDialog";
    private static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";

    private static final int LOADER_GROUP_MEMBERS = 0;

    private static final String ACTION_DELETE_GROUP = "deleteGroup";
    private static final String ACTION_CREATE_GROUP = "createGroup";
    private static final String ACTION_UPDATE_GROUP = "updateGroup";
@@ -95,35 +84,74 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements

    private static final int RESULT_GROUP_ADD_MEMBER = 100;

    /** Loader callbacks for existing group members for the autocomplete text view. */
    private final LoaderCallbacks<Cursor> mGroupMemberCallbacks = new LoaderCallbacks<Cursor>() {
    /**
     * Starts an Intent to add the raw contacts for a given contact ID to a group.
     * Only the raw contacts that belong to the specified account are added.
     */
    private static class AddGroupMembersAsyncTask extends AsyncTask<Void, Void, Intent> {

        @Override
        public CursorLoader onCreateLoader(int id, Bundle args) {
            return GroupMemberLoader.constructLoaderForGroupEditorQuery(
                    GroupMembersActivity.this, mGroupMetadata.groupId);
        private final Context mContext;
        private final long mContactId;
        private final long mGroupId;
        private final String mAccountName;
        private final String mAccountType;

        AddGroupMembersAsyncTask(Context context, long contactId, long groupId, String accountName,
                String accountType) {
            mContext = context;
            mContactId = contactId;
            mGroupId = groupId;
            mAccountName = accountName;
            mAccountType = accountType;
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
            final List<Member> members = new ArrayList<>();
            data.moveToPosition(-1);
            while (data.moveToNext()) {
                members.add(new Member(
                        data.getLong(GroupEditorQuery.RAW_CONTACT_ID),
                        data.getString(GroupEditorQuery.CONTACT_LOOKUP_KEY),
                        data.getLong(GroupEditorQuery.CONTACT_ID),
                        data.getString(GroupEditorQuery.CONTACT_DISPLAY_NAME_PRIMARY),
                        data.getString(GroupEditorQuery.CONTACT_PHOTO_URI),
                        data.getLong(GroupEditorQuery.CONTACT_PHOTO_ID)));
        protected Intent doInBackground(Void... params) {
            final long[] rawContactIdsToAdd = getRawContactIdsToAdd();
            if (rawContactIdsToAdd.length == 0) {
                return null;
            }
            return ContactSaveService.createGroupUpdateIntent(
                    mContext, mGroupId, /* newLabel */ null,
                    rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
                    GroupMembersActivity.class, GroupMembersActivity.ACTION_ADD_TO_GROUP);
        }

            bindAutocompleteGroupMembers(members);
        // 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.
        private long[] getRawContactIdsToAdd() {
            final Uri rawContactUri = RawContacts.CONTENT_URI.buildUpon()
                    .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName)
                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType)
                    .build();
            final String[] projection = new String[]{RawContacts._ID};
            final String selection = RawContacts.CONTACT_ID + "=?";
            final String[] selectionArgs = new String[1];
            selectionArgs[0] = Long.toString(mContactId);
            final Cursor cursor = mContext.getContentResolver().query(
                    rawContactUri, projection, selection, selectionArgs, null, null);
            final long[] rawContactIds = new long[cursor.getCount()];
            try {
                int i = 0;
                while (cursor.moveToNext()) {
                    rawContactIds[i] = cursor.getLong(0);
                    i++;
                }
            } finally {
                cursor.close();
            }
            return rawContactIds;
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {}
    };
        protected void onPostExecute(Intent intent) {
            if (intent == null) {
                Toast.makeText(mContext, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
            } else {
                mContext.startService(intent);
            }
        }
    }

    private ActionBarAdapter mActionBarAdapter;

@@ -131,8 +159,6 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements

    private GroupMembersListFragment mMembersListFragment;

    private SuggestedMemberListAdapter mAutoCompleteAdapter;

    private Uri mGroupUri;
    private boolean mIsInsertAction;

@@ -198,8 +224,6 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
                fragmentManager.beginTransaction()
                        .replace(R.id.fragment_container, mMembersListFragment, TAG_GROUP_MEMBERS)
                        .commit();
            } else {
                getLoaderManager().initLoader(LOADER_GROUP_MEMBERS, null, mGroupMemberCallbacks);
            }
            mMembersListFragment.setListener(this);
            if (mGroupMetadata != null && mGroupMetadata.editable) {
@@ -311,25 +335,13 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
        final boolean isSearchMode = mActionBarAdapter.isSearchMode();

        final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
        final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;

        setVisible(menu, R.id.menu_add,
                isGroupEditable && !isSelectionMode && !isSearchMode);

        setVisible(menu, R.id.menu_search,
                isGroupEditable && !isSelectionMode && !isSearchMode);

        setVisible(menu, R.id.menu_rename_group,
                isGroupEditable && !isSelectionMode && !isSearchMode);

        setVisible(menu, R.id.menu_delete_group,
                !isGroupReadOnly && !isSelectionMode && !isSearchMode);

        setVisible(menu, R.id.menu_remove_from_group,
                isGroupEditable && isSelectionMode);
        setVisible(menu, R.id.menu_add, isGroupEditable && !isSelectionMode);
        setVisible(menu, R.id.menu_rename_group, isGroupEditable && !isSelectionMode);
        setVisible(menu, R.id.menu_delete_group, !isGroupReadOnly && !isSelectionMode);
        setVisible(menu, R.id.menu_remove_from_group, isGroupEditable && isSelectionMode);

        return true;
    }
@@ -353,17 +365,11 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
                intent.setType(ContactsContract.Groups.CONTENT_ITEM_TYPE);
                intent.putExtra(UiIntentActions.GROUP_ACCOUNT_WITH_DATA_SET,
                        mGroupMetadata.createAccountWithDataSet());
                intent.putExtra(UiIntentActions.GROUP_RAW_CONTACT_IDS,
                        getExistingGroupMemberRawContactIds());
                intent.putExtra(UiIntentActions.GROUP_CONTACT_IDS,
                        getExistingGroupMemberContactIds());
                startActivityForResult(intent, RESULT_GROUP_ADD_MEMBER);
                return true;
            }
            case R.id.menu_search: {
                if (mActionBarAdapter != null) {
                    mActionBarAdapter.setSearchMode(true);
                }
                return true;
            }
            case R.id.menu_rename_group: {
                GroupNameEditDialogFragment.showUpdateDialog(
                        getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG, mGroupMetadata.groupName);
@@ -385,15 +391,15 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
        return super.onOptionsItemSelected(item);
    }

    private ArrayList<String> getExistingGroupMemberRawContactIds() {
        final ArrayList<String> rawContactIds = new ArrayList<>();
    private ArrayList<String> getExistingGroupMemberContactIds() {
        final ArrayList<String> contactIds = new ArrayList<>();
        final Cursor cursor = mMembersListFragment.getAdapter().getCursor(/* partition */ 0);
        if (cursor != null && cursor.moveToFirst()) {
            do {
                rawContactIds.add(cursor.getString(GroupMembersQuery.RAW_CONTACT_ID));
                contactIds.add(cursor.getString(GroupMembersQuery.CONTACT_ID));
            } while (cursor.moveToNext());
        }
        return rawContactIds;
        return contactIds;
    }

    private void deleteGroup() {
@@ -408,7 +414,6 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
        }
    }

    // TODO(wjang): replace this with group events
    private void logListEvent() {
        Logger.logListEvent(
                ListEvent.ActionType.REMOVE_LABEL,
@@ -446,30 +451,14 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == RESULT_GROUP_ADD_MEMBER && resultCode == RESULT_OK && data != null) {
            final Uri rawContactUri = data.getData();
            if (rawContactUri != null) {
                long rawContactId = -1;
                try {
                    rawContactId = Long.parseLong(rawContactUri.getLastPathSegment());
                } catch (NumberFormatException ignored) {}
                if (rawContactId < 0) {
                    Toast.makeText(this, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
                    Log.w(TAG, "Failed to parse ID from pick group member result uri " +
                            rawContactUri);
                    return;
                }
                final long[] rawContactIdsToAdd = new long[1];
                rawContactIdsToAdd[0] = rawContactId;
                final Intent intent = ContactSaveService.createGroupUpdateIntent(
                        GroupMembersActivity.this, mGroupMetadata.groupId, /* newLabel */ null,
                        rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
                        GroupMembersActivity.class, GroupMembersActivity.ACTION_ADD_TO_GROUP);
                startService(intent);
            }
            final long contactId = data.getLongExtra(
                    UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1);
            new AddGroupMembersAsyncTask(this, contactId, mGroupMetadata.groupId,
                    mGroupMetadata.accountName, mGroupMetadata.accountType)
                    .execute();
        }
    }

@@ -512,11 +501,6 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
    @Override
    public void onAction(int action) {
        switch (action) {
            case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
                mActionBarAdapter.setSearchMode(true);
                invalidateOptionsMenu();
                showFabWithAnimation(/* showFabWithAnimation = */ false);
                break;
            case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
                if (mMembersListFragment != null) {
                    mMembersListFragment.displayCheckBoxes(true);
@@ -539,7 +523,7 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
    }

    private void showFabWithAnimation(boolean showFab) {
        // TODO(wjang): b/28497108
        // TODO: b/28497108
    }

    @Override
@@ -598,63 +582,12 @@ public class GroupMembersActivity extends AppCompatContactsActivity implements
    @Override
    public void onGroupMetadataLoaded(GroupMetadata groupMetadata) {
        mGroupMetadata = groupMetadata;

        if (!mIsInsertAction) {
            getSupportActionBar().setTitle(mGroupMetadata.groupName);
        }

        bindAutocompleteTextView();
        getLoaderManager().initLoader(LOADER_GROUP_MEMBERS, null, mGroupMemberCallbacks);

        invalidateOptionsMenu();
    }

    private void bindAutocompleteTextView() {
        final AutoCompleteTextView autoCompleteTextView =
                (AutoCompleteTextView) mActionBarAdapter.getSearchView();
        if (autoCompleteTextView == null) return;
        mAutoCompleteAdapter = createAutocompleteAdapter();
        autoCompleteTextView.setAdapter(mAutoCompleteAdapter);
        autoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final SuggestedMember member = (SuggestedMember) view.getTag();
                if (member == null) {
                    return;
                }
                final long[] rawContactIdsToAdd = new long[1];
                rawContactIdsToAdd[0] = member.getRawContactId();
                final Intent intent = ContactSaveService.createGroupUpdateIntent(
                        GroupMembersActivity.this, mGroupMetadata.groupId, /* newLabel */ null,
                        rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
                        GroupMembersActivity.class, ACTION_ADD_TO_GROUP);
                startService(intent);

                // Update the autocomplete adapter so the contact doesn't get suggested again
                mAutoCompleteAdapter.addNewMember(member.getContactId());

                // Clear out the text field
                autoCompleteTextView.setText("");
            }
        });
    }

    private SuggestedMemberListAdapter createAutocompleteAdapter() {
        final SuggestedMemberListAdapter adapter = new SuggestedMemberListAdapter(
                this, android.R.layout.simple_dropdown_item_1line);
        adapter.setContentResolver(this.getContentResolver());
        adapter.setAccountType(mGroupMetadata.accountType);
        adapter.setAccountName(mGroupMetadata.accountName);
        adapter.setDataSet(mGroupMetadata.dataSet);
        return adapter;
    }

    private void bindAutocompleteGroupMembers(List<Member> members) {
        if (mAutoCompleteAdapter != null) {
            mAutoCompleteAdapter.updateExistingMembersList(members);
        }
    }

    @Override
    public void onGroupMetadataLoadFailed() {
        setResultCanceledAndFinish(R.string.groupLoadErrorToast);
Loading