/* * 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.app.Activity; import android.app.LoaderManager.LoaderCallbacks; import android.content.ContentResolver; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.database.CursorWrapper; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; import com.android.contacts.ContactSaveService; import com.android.contacts.ContactsUtils; import com.android.contacts.GroupMetaDataLoader; import com.android.contacts.R; import com.android.contacts.activities.ActionBarAdapter; import com.android.contacts.activities.PeopleActivity; import com.android.contacts.group.GroupMembersAdapter.GroupMembersQuery; import com.android.contacts.interactions.GroupDeletionDialogFragment; import com.android.contacts.list.ContactsRequest; import com.android.contacts.list.ContactsSectionIndexer; import com.android.contacts.list.MultiSelectContactsListFragment; import com.android.contacts.list.MultiSelectEntryContactListAdapter.DeleteContactListener; import com.android.contacts.list.UiIntentActions; import com.android.contacts.logging.ListEvent; import com.android.contacts.logging.ListEvent.ListType; import com.android.contacts.logging.Logger; import com.android.contacts.logging.ScreenEvent; import com.android.contacts.model.account.AccountWithDataSet; import com.android.contacts.util.ImplicitIntentsUtil; import com.android.contactsbind.FeedbackHelper; import com.google.common.primitives.Longs; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** Displays the members of a group. */ public class GroupMembersFragment extends MultiSelectContactsListFragment { private static final String TAG = "GroupMembers"; private static final String KEY_IS_EDIT_MODE = "editMode"; private static final String KEY_GROUP_URI = "groupUri"; private static final String KEY_GROUP_METADATA = "groupMetadata"; public static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog"; private static final String ARG_GROUP_URI = "groupUri"; private static final int LOADER_GROUP_METADATA = 0; private static final int MSG_FAIL_TO_LOAD = 1; private static final int RESULT_GROUP_ADD_MEMBER = 100; /** 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]; final List indicesToFilter = new ArrayList<>(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Group members CursorWrapper start: " + mCount); } final Bundle bundle = cursor.getExtras(); final String sections[] = bundle.getStringArray(Contacts .EXTRA_ADDRESS_BOOK_INDEX_TITLES); final int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); final ContactsSectionIndexer indexer = (sections == null || counts == null) ? null : new ContactsSectionIndexer(sections, counts); 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); } else { indicesToFilter.add(i); } } if (indexer != null && GroupUtil.needTrimming(mCount, counts, indexer.getPositions())) { GroupUtil.updateBundle(bundle, indexer, indicesToFilter, sections, counts); } 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) { mPos = mCount; return false; } else if (position < 0) { mPos = -1; return false; } mPos = mIndex[position]; return super.moveToPosition(mPos); } @Override public int getCount() { return mCount; } @Override public int getPosition() { return mPos; } } private final LoaderCallbacks mGroupMetaDataCallbacks = new LoaderCallbacks() { @Override public CursorLoader onCreateLoader(int id, Bundle args) { return new GroupMetaDataLoader(mActivity, mGroupUri); } @Override public void onLoadFinished(Loader loader, Cursor cursor) { if (cursor == null || cursor.isClosed() || !cursor.moveToNext()) { Log.e(TAG, "Failed to load group metadata for " + mGroupUri); Toast.makeText(getContext(), R.string.groupLoadErrorToast, Toast.LENGTH_SHORT) .show(); mHandler.sendEmptyMessage(MSG_FAIL_TO_LOAD); return; } mGroupMetaData = new GroupMetaData(getActivity(), cursor); onGroupMetadataLoaded(); } @Override public void onLoaderReset(Loader loader) {} }; private ActionBarAdapter mActionBarAdapter; private PeopleActivity mActivity; private Uri mGroupUri; private boolean mIsEditMode; private GroupMetaData mGroupMetaData; private Set mGroupMemberContactIds = new HashSet(); private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if(msg.what == MSG_FAIL_TO_LOAD) { mActivity.onBackPressed(); } } }; public static GroupMembersFragment newInstance(Uri groupUri) { final Bundle args = new Bundle(); args.putParcelable(ARG_GROUP_URI, groupUri); final GroupMembersFragment fragment = new GroupMembersFragment(); fragment.setArguments(args); return fragment; } public GroupMembersFragment() { setPhotoLoaderEnabled(true); setSectionHeaderDisplayEnabled(true); setHasOptionsMenu(true); setListType(ListType.GROUP); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (mGroupMetaData == null) { // Hide menu options until metadata is fully loaded return; } inflater.inflate(R.menu.view_group, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { final boolean isSelectionMode = mActionBarAdapter.isSelectionMode(); final boolean isGroupEditable = mGroupMetaData != null && mGroupMetaData.editable; final boolean isGroupReadOnly = mGroupMetaData != null && mGroupMetaData.readOnly; setVisible(getContext(), menu, R.id.menu_multi_send_email, !mIsEditMode && !isGroupEmpty()); setVisible(getContext(), menu, R.id.menu_multi_send_message, !mIsEditMode && !isGroupEmpty()); setVisible(getContext(), menu, R.id.menu_add, isGroupEditable && !isSelectionMode); setVisible(getContext(), menu, R.id.menu_rename_group, !isGroupReadOnly && !isSelectionMode); setVisible(getContext(), menu, R.id.menu_delete_group, !isGroupReadOnly && !isSelectionMode); setVisible(getContext(), menu, R.id.menu_edit_group, isGroupEditable && !mIsEditMode && !isSelectionMode && !isGroupEmpty()); setVisible(getContext(), menu, R.id.menu_remove_from_group, isGroupEditable && isSelectionMode && !mIsEditMode); } private boolean isGroupEmpty() { return getAdapter() != null && getAdapter().isEmpty(); } private static void setVisible(Context context, Menu menu, int id, boolean visible) { final MenuItem menuItem = menu.findItem(id); if (menuItem != null) { menuItem.setVisible(visible); final Drawable icon = menuItem.getIcon(); if (icon != null) { icon.mutate().setColorFilter(ContextCompat.getColor(context, R.color.actionbar_icon_color), PorterDuff.Mode.SRC_ATOP); } } } /** * Helper class for cp2 query used to look up all contact's emails and phone numbers. */ public static abstract class Query { public static final String EMAIL_SELECTION = ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'"; public static final String PHONE_SELECTION = ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'"; public static final String[] EMAIL_PROJECTION = { ContactsContract.Data.CONTACT_ID, ContactsContract.CommonDataKinds.Email._ID, ContactsContract.Data.IS_SUPER_PRIMARY, ContactsContract.Data.TIMES_USED, ContactsContract.Data.DATA1 }; public static final String[] PHONE_PROJECTION = { ContactsContract.Data.CONTACT_ID, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Data.IS_SUPER_PRIMARY, ContactsContract.Data.TIMES_USED, ContactsContract.Data.DATA1 }; public static final int CONTACT_ID = 0; public static final int ITEM_ID = 1; public static final int PRIMARY = 2; public static final int TIMES_USED = 3; public static final int DATA1 = 4; } /** * Helper class for managing data related to contacts and emails/phone numbers. */ private class ContactDataHelperClass { private List items = new ArrayList<>(); private String mostUsedItemId = null; private int mostUsedTimes; private String primaryItemId = null; public void addItem(String item, int timesUsed, boolean primaryFlag) { if (mostUsedItemId == null || timesUsed > mostUsedTimes) { mostUsedItemId = item; mostUsedTimes = timesUsed; } if (primaryFlag) { primaryItemId = item; } items.add(item); } public boolean hasDefaultItem() { return primaryItemId != null || items.size() == 1; } public String getDefaultSelectionItemId() { return primaryItemId != null ? primaryItemId : mostUsedItemId; } } private void sendToGroup(long[] ids, String sendScheme, String title) { if (ids == null || ids.length == 0) return; // Get emails or phone numbers // contactMap final Map contactMap = new HashMap<>(); // itemList final List itemList = new ArrayList<>(); final String sIds = GroupUtil.convertArrayToString(ids); final String select = (ContactsUtils.SCHEME_MAILTO.equals(sendScheme) ? Query.EMAIL_SELECTION : Query.PHONE_SELECTION) + " AND " + ContactsContract.Data.CONTACT_ID + " IN (" + sIds + ")"; final ContentResolver contentResolver = getContext().getContentResolver(); final Cursor cursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, ContactsUtils.SCHEME_MAILTO.equals(sendScheme) ? Query.EMAIL_PROJECTION : Query.PHONE_PROJECTION, select, null, null); if (cursor == null) { return; } try { cursor.moveToPosition(-1); while (cursor.moveToNext()) { final String contactId = cursor.getString(Query.CONTACT_ID); final String itemId = cursor.getString(Query.ITEM_ID); final boolean isPrimary = cursor.getInt(Query.PRIMARY) != 0; final int timesUsed = cursor.getInt(Query.TIMES_USED); final String data = cursor.getString(Query.DATA1); if (!TextUtils.isEmpty(data)) { final ContactDataHelperClass contact; if (!contactMap.containsKey(contactId)) { contact = new ContactDataHelperClass(); contactMap.put(contactId, contact); } else { contact = contactMap.get(contactId); } contact.addItem(itemId, timesUsed, isPrimary); itemList.add(data); } } } finally { cursor.close(); } // Start picker if a contact does not have a default for (ContactDataHelperClass i : contactMap.values()) { if (!i.hasDefaultItem()) { // Build list of default selected item ids final List defaultSelection = new ArrayList<>(); for (ContactDataHelperClass j : contactMap.values()) { final String selectionItemId = j.getDefaultSelectionItemId(); if (selectionItemId != null) { defaultSelection.add(Long.parseLong(selectionItemId)); } } final long[] defaultSelectionArray = Longs.toArray(defaultSelection); startSendToSelectionPickerActivity(ids, defaultSelectionArray, sendScheme, title); return; } } if (itemList.size() == 0 || contactMap.size() < ids.length) { Toast.makeText(getContext(), ContactsUtils.SCHEME_MAILTO.equals(sendScheme) ? getString(R.string.groupSomeContactsNoEmailsToast) : getString(R.string.groupSomeContactsNoPhonesToast), Toast.LENGTH_LONG).show(); } if (itemList.size() == 0) { return; } final String itemsString = TextUtils.join(",", itemList); GroupUtil.startSendToSelectionActivity(this, itemsString, sendScheme, title); } private void startSendToSelectionPickerActivity(long[] ids, long[] defaultSelection, String sendScheme, String title) { startActivity(GroupUtil.createSendToSelectionPickerIntent(getContext(), ids, defaultSelection, sendScheme, title)); } private void startGroupAddMemberActivity() { startActivityForResult(GroupUtil.createPickMemberIntent(getContext(), mGroupMetaData, getMemberContactIds()), RESULT_GROUP_ADD_MEMBER); } @Override public boolean onOptionsItemSelected(MenuItem item) { final int id = item.getItemId(); if (id == android.R.id.home) { mActivity.onBackPressed(); } else if (id == R.id.menu_add) { startGroupAddMemberActivity(); } else if (id == R.id.menu_multi_send_email) { final long[] ids = mActionBarAdapter.isSelectionMode() ? getAdapter().getSelectedContactIdsArray() : GroupUtil.convertStringSetToLongArray(mGroupMemberContactIds); sendToGroup(ids, ContactsUtils.SCHEME_MAILTO, getString(R.string.menu_sendEmailOption)); } else if (id == R.id.menu_multi_send_message) { final long[] ids = mActionBarAdapter.isSelectionMode() ? getAdapter().getSelectedContactIdsArray() : GroupUtil.convertStringSetToLongArray(mGroupMemberContactIds); sendToGroup(ids, ContactsUtils.SCHEME_SMSTO, getString(R.string.menu_sendMessageOption)); } else if (id == R.id.menu_rename_group) { GroupNameEditDialogFragment.newInstanceForUpdate( new AccountWithDataSet(mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet), GroupUtil.ACTION_UPDATE_GROUP, mGroupMetaData.groupId, mGroupMetaData.groupName).show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG); } else if (id == R.id.menu_delete_group) { deleteGroup(); } else if (id == R.id.menu_edit_group) { mIsEditMode = true; mActionBarAdapter.setSelectionMode(true); displayDeleteButtons(true); } else if (id == R.id.menu_remove_from_group) { logListEvent(); removeSelectedContacts(); } else { return super.onOptionsItemSelected(item); } return true; } private void removeSelectedContacts() { final long[] contactIds = getAdapter().getSelectedContactIdsArray(); new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE, getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet).execute(); mActionBarAdapter.setSelectionMode(false); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK || data == null || requestCode != RESULT_GROUP_ADD_MEMBER) { return; } long[] contactIds = data.getLongArrayExtra( UiIntentActions.TARGET_CONTACT_IDS_EXTRA_KEY); if (contactIds == null) { final long contactId = data.getLongExtra( UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1); if (contactId > -1) { contactIds = new long[1]; contactIds[0] = contactId; } } new UpdateGroupMembersAsyncTask( UpdateGroupMembersAsyncTask.TYPE_ADD, getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet).execute(); } private final ActionBarAdapter.Listener mActionBarListener = new ActionBarAdapter.Listener() { @Override public void onAction(int action) { switch (action) { case ActionBarAdapter.Listener.Action.START_SELECTION_MODE: if (mIsEditMode) { displayDeleteButtons(true); mActionBarAdapter.setActionBarTitle(getString(R.string.title_edit_group)); } else { displayCheckBoxes(true); } mActivity.invalidateOptionsMenu(); break; case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE: mActionBarAdapter.setSearchMode(false); if (mIsEditMode) { displayDeleteButtons(false); } else { displayCheckBoxes(false); } mActivity.invalidateOptionsMenu(); break; case ActionBarAdapter.Listener.Action.BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE: break; } } @Override public void onUpButtonPressed() { mActivity.onBackPressed(); } }; private final OnCheckBoxListActionListener mCheckBoxListener = new OnCheckBoxListActionListener() { @Override public void onStartDisplayingCheckBoxes() { mActionBarAdapter.setSelectionMode(true); } @Override public void onSelectedContactIdsChanged() { if (mActionBarAdapter == null) { return; } if (mIsEditMode) { mActionBarAdapter.setActionBarTitle(getString(R.string.title_edit_group)); } else { mActionBarAdapter.setSelectionCount(getSelectedContactIds().size()); } } @Override public void onStopDisplayingCheckBoxes() { mActionBarAdapter.setSelectionMode(false); } }; private void logListEvent() { Logger.logListEvent( ListEvent.ActionType.REMOVE_LABEL, getListType(), getAdapter().getCount(), /* clickedIndex */ -1, getAdapter().getSelectedContactIdsArray().length); } private void deleteGroup() { if (getMemberCount() == 0) { final Intent intent = ContactSaveService.createGroupDeletionIntent( getContext(), mGroupMetaData.groupId); getContext().startService(intent); mActivity.switchToAllContacts(); } else { GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetaData.groupId, mGroupMetaData.groupName); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mActivity = (PeopleActivity) getActivity(); mActionBarAdapter = new ActionBarAdapter(mActivity, mActionBarListener, mActivity.getSupportActionBar(), mActivity.getToolbar(), R.string.enter_contact_name); mActionBarAdapter.setShowHomeIcon(true); final ContactsRequest contactsRequest = new ContactsRequest(); contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP); mActionBarAdapter.initialize(savedInstanceState, contactsRequest); if (mGroupMetaData != null) { mActivity.setTitle(mGroupMetaData.groupName); if (mGroupMetaData.editable) { setCheckBoxListListener(mCheckBoxListener); } } } @Override public ActionBarAdapter getActionBarAdapter() { return mActionBarAdapter; } public void displayDeleteButtons(boolean displayDeleteButtons) { getAdapter().setDisplayDeleteButtons(displayDeleteButtons); } public ArrayList getMemberContactIds() { return new ArrayList<>(mGroupMemberContactIds); } public int getMemberCount() { return mGroupMemberContactIds.size(); } public boolean isEditMode() { return mIsEditMode; } @Override public void onCreate(Bundle savedState) { super.onCreate(savedState); if (savedState == null) { mGroupUri = getArguments().getParcelable(ARG_GROUP_URI); } else { mIsEditMode = savedState.getBoolean(KEY_IS_EDIT_MODE); mGroupUri = savedState.getParcelable(KEY_GROUP_URI); mGroupMetaData = savedState.getParcelable(KEY_GROUP_METADATA); } maybeAttachCheckBoxListener(); } @Override public void onResume() { super.onResume(); // Re-register the listener, which may have been cleared when onSaveInstanceState was // called. See also: onSaveInstanceState mActionBarAdapter.setListener(mActionBarListener); } @Override protected void startLoading() { if (mGroupMetaData == null || !mGroupMetaData.isValid()) { getLoaderManager().restartLoader(LOADER_GROUP_METADATA, null, mGroupMetaDataCallbacks); } else { onGroupMetadataLoaded(); } } @Override public void onLoadFinished(Loader loader, Cursor data) { if (data != null) { // Wait until contacts are loaded before showing the scrollbar setVisibleScrollbarEnabled(true); final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data); bindMembersCount(cursorWrapper.getCount()); super.onLoadFinished(loader, cursorWrapper); // Update state of menu items (e.g. "Remove contacts") based on number of group members. mActivity.invalidateOptionsMenu(); mActionBarAdapter.updateOverflowButtonColor(); } } private void bindMembersCount(int memberCount) { final View accountFilterContainer = getView().findViewById( R.id.account_filter_header_container); final View emptyGroupView = getView().findViewById(R.id.empty_group); if (memberCount > 0) { final AccountWithDataSet accountWithDataSet = new AccountWithDataSet( mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet); bindListHeader(getContext(), getListView(), accountFilterContainer, accountWithDataSet, memberCount); emptyGroupView.setVisibility(View.GONE); } else { hideHeaderAndAddPadding(getContext(), getListView(), accountFilterContainer); emptyGroupView.setVisibility(View.VISIBLE); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mActionBarAdapter != null) { mActionBarAdapter.setListener(null); mActionBarAdapter.onSaveInstanceState(outState); } outState.putBoolean(KEY_IS_EDIT_MODE, mIsEditMode); 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(); mActivity.setTitle(mGroupMetaData.groupName); mActivity.invalidateOptionsMenu(); mActivity.updateDrawerGroupMenu(mGroupMetaData.groupId); // Start loading the group members super.startLoading(); } private void maybeAttachCheckBoxListener() { // Don't attach the multi select check box listener if we can't edit the group if (mGroupMetaData != null && mGroupMetaData.editable) { setCheckBoxListListener(mCheckBoxListener); } } @Override protected GroupMembersAdapter createListAdapter() { final GroupMembersAdapter adapter = new GroupMembersAdapter(getContext()); adapter.setSectionHeaderDisplayEnabled(true); adapter.setDisplayPhotos(true); adapter.setDeleteContactListener(new DeletionListener()); return adapter; } @Override protected void configureAdapter() { super.configureAdapter(); if (mGroupMetaData != null) { getAdapter().setGroupId(mGroupMetaData.groupId); } } @Override protected View inflateView(LayoutInflater inflater, ViewGroup container) { final View view = inflater.inflate(R.layout.contact_list_content, /* root */ null); final View emptyGroupView = inflater.inflate(R.layout.empty_group_view, null); final ImageView image = (ImageView) emptyGroupView.findViewById(R.id.empty_group_image); final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) image.getLayoutParams(); final int screenHeight = getResources().getDisplayMetrics().heightPixels; params.setMargins(0, screenHeight / getResources().getInteger(R.integer.empty_group_view_image_margin_divisor), 0, 0); params.gravity = Gravity.CENTER_HORIZONTAL; image.setLayoutParams(params); final FrameLayout contactListLayout = (FrameLayout) view.findViewById(R.id.contact_list); contactListLayout.addView(emptyGroupView); final Button addContactsButton = (Button) emptyGroupView.findViewById(R.id.add_member_button); addContactsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivityForResult(GroupUtil.createPickMemberIntent(getContext(), mGroupMetaData, getMemberContactIds()), RESULT_GROUP_ADD_MEMBER); } }); return view; } @Override protected void onItemClick(int position, long id) { final Uri uri = getAdapter().getContactUri(position); if (uri == null) { return; } if (getAdapter().isDisplayingCheckBoxes()) { super.onItemClick(position, id); return; } final int count = getAdapter().getCount(); Logger.logListEvent(ListEvent.ActionType.CLICK, ListEvent.ListType.GROUP, count, /* clickedIndex */ position, /* numSelected */ 0); ImplicitIntentsUtil.startQuickContact( getActivity(), uri, ScreenEvent.ScreenType.LIST_GROUP); } @Override protected boolean onItemLongClick(int position, long id) { if (mActivity != null && mIsEditMode) { return true; } return super.onItemLongClick(position, id); } private final class DeletionListener implements DeleteContactListener { @Override public void onContactDeleteClicked(int position) { final long contactId = getAdapter().getContactId(position); final long[] contactIds = new long[1]; contactIds[0] = contactId; new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE, getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName, mGroupMetaData.accountType, mGroupMetaData.dataSet).execute(); } } public GroupMetaData getGroupMetaData() { return mGroupMetaData; } public boolean isCurrentGroup(long groupId) { return mGroupMetaData != null && mGroupMetaData.groupId == groupId; } /** * Return true if the fragment is not yet added, being removed, or detached. */ public boolean isInactive() { return !isAdded() || isRemoving() || isDetached(); } @Override public void onDestroy() { if (mActionBarAdapter != null) { mActionBarAdapter.setListener(null); } super.onDestroy(); } public void updateExistingGroupFragment(Uri newGroupUri, String action) { toastForSaveAction(action); if (isEditMode() && getGroupCount() == 1) { // If we're deleting the last group member, exit edit mode exitEditMode(); } else if (!GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action)) { mGroupUri = newGroupUri; mGroupMetaData = null; // Clear mGroupMetaData to trigger a new load. reloadData(); mActivity.invalidateOptionsMenu(); } } public void toastForSaveAction(String action) { int id = -1; switch(action) { case GroupUtil.ACTION_UPDATE_GROUP: id = R.string.groupUpdatedToast; break; case GroupUtil.ACTION_REMOVE_FROM_GROUP: id = R.string.groupMembersRemovedToast; break; case GroupUtil.ACTION_CREATE_GROUP: id = R.string.groupCreatedToast; break; case GroupUtil.ACTION_ADD_TO_GROUP: id = R.string.groupMembersAddedToast; break; case GroupUtil.ACTION_SWITCH_GROUP: // No toast associated with this action. break; default: FeedbackHelper.sendFeedback(getContext(), TAG, "toastForSaveAction passed unknown action: " + action, new IllegalArgumentException("Unhandled contact save action " + action)); } toast(id); } private void toast(int resId) { if (resId >= 0) { Toast.makeText(getContext(), resId, Toast.LENGTH_SHORT).show(); } } private int getGroupCount() { return getAdapter() != null ? getAdapter().getCount() : -1; } public void exitEditMode() { mIsEditMode = false; mActionBarAdapter.setSelectionMode(false); displayDeleteButtons(false); } }