diff options
author | blong <blong@codeaurora.org> | 2015-08-19 14:55:56 +0800 |
---|---|---|
committer | blong <blong@codeaurora.org> | 2015-09-10 13:59:38 +0800 |
commit | 8ced6a5c88c57a892ca6cdaefa08d69907171c94 (patch) | |
tree | 84d0e04683bd33a796adaab97d74fc940addf983 | |
parent | 354bdd57c9701a1562a5dee205432fe285fa0112 (diff) | |
download | packages_apps_Contacts-8ced6a5c88c57a892ca6cdaefa08d69907171c94.tar.gz packages_apps_Contacts-8ced6a5c88c57a892ca6cdaefa08d69907171c94.tar.bz2 packages_apps_Contacts-8ced6a5c88c57a892ca6cdaefa08d69907171c94.zip |
Add contacts group feature
- Restore group tab which was deleted
- Add local group feature for phone account
Change-Id: I298642d353492f2ff443fbc5a2f3fbf4f4eb024e
19 files changed, 385 insertions, 24 deletions
diff --git a/res/layout-land/group_editor_view.xml b/res/layout-land/group_editor_view.xml index dded4c3fc..a17b4669a 100644 --- a/res/layout-land/group_editor_view.xml +++ b/res/layout-land/group_editor_view.xml @@ -52,9 +52,26 @@ android:paddingStart="8dip" android:orientation="vertical" > - <include - layout="@layout/group_editor_autocomplete_view" - android:id="@+id/add_member_field"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <include + layout="@layout/group_editor_autocomplete_view" + android:id="@+id/add_member_field"/> + + <ImageView + android:id="@+id/addGroupMember" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:cropToPadding="true" + android:scaleType="centerCrop" + android:src="@drawable/ic_add_contact_holo_light" + android:background="?android:attr/selectableItemBackground" + android:gravity="start" + android:layout_gravity="center"/> + </LinearLayout> + <include layout="@layout/group_editor_existing_member_list" android:id="@android:id/list"/> diff --git a/res/layout-sw600dp/group_editor_view.xml b/res/layout-sw600dp/group_editor_view.xml index 717fc28f4..1541c3a92 100644 --- a/res/layout-sw600dp/group_editor_view.xml +++ b/res/layout-sw600dp/group_editor_view.xml @@ -63,9 +63,24 @@ layout="@layout/editor_account_header" android:visibility="invisible"/> - <include - layout="@layout/group_editor_autocomplete_view" - android:id="@+id/add_member_field"/> + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <include + layout="@layout/group_editor_autocomplete_view" + android:id="@+id/add_member_field"/> + + <ImageView + android:id="@+id/addGroupMember" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:cropToPadding="true" + android:scaleType="centerCrop" + android:src="@drawable/ic_add_contact_holo_light" + android:background="?android:attr/selectableItemBackground" + android:gravity="start" + android:layout_gravity="center"/> + </LinearLayout> <include layout="@layout/group_editor_existing_member_list" diff --git a/res/layout/group_browse_list_fragment.xml b/res/layout/group_browse_list_fragment.xml index 30c2fec3a..c7339b0b4 100644 --- a/res/layout/group_browse_list_fragment.xml +++ b/res/layout/group_browse_list_fragment.xml @@ -18,7 +18,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:background="@color/group_list_background_color"> <!-- See group_browse_list_item.xml for the reason for the transparent android:listSelector --> <view diff --git a/res/layout/group_editor_autocomplete_view.xml b/res/layout/group_editor_autocomplete_view.xml index c8e716a38..4c3fc22ae 100644 --- a/res/layout/group_editor_autocomplete_view.xml +++ b/res/layout/group_editor_autocomplete_view.xml @@ -18,7 +18,8 @@ <AutoCompleteTextView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" + android:layout_width="0dip" + android:layout_weight="1" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorPrimary" diff --git a/res/layout/group_editor_view.xml b/res/layout/group_editor_view.xml index d94853db7..1bda19c73 100644 --- a/res/layout/group_editor_view.xml +++ b/res/layout/group_editor_view.xml @@ -44,9 +44,24 @@ android:paddingLeft="8dip" android:paddingStart="8dip"/> - <include - layout="@layout/group_editor_autocomplete_view" - android:id="@+id/add_member_field"/> + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <include + layout="@layout/group_editor_autocomplete_view" + android:id="@+id/add_member_field"/> + + <ImageView + android:id="@+id/addGroupMember" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:cropToPadding="true" + android:scaleType="centerCrop" + android:src="@drawable/ic_add_contact_holo_light" + android:background="?android:attr/selectableItemBackground" + android:gravity="start" + android:layout_gravity="center"/> + </LinearLayout> <include layout="@layout/group_editor_existing_member_list" diff --git a/res/menu/people_options.xml b/res/menu/people_options.xml index e561f6137..3ccf855c9 100644 --- a/res/menu/people_options.xml +++ b/res/menu/people_options.xml @@ -41,6 +41,10 @@ android:title="@string/menu_settings" /> <item + android:id="@+id/menu_add_group" + android:title="@string/menu_new_group_action_bar" /> + + <item android:id="@+id/menu_memory_status" android:title="@string/menu_memory_status" /> diff --git a/res/menu/view_group.xml b/res/menu/view_group.xml index 669f401c8..eb6cf5f6a 100644 --- a/res/menu/view_group.xml +++ b/res/menu/view_group.xml @@ -23,4 +23,8 @@ <item android:id="@+id/menu_delete_group" android:title="@string/menu_deleteGroup" /> + + <item + android:id="@+id/menu_move_group_members" + android:title="@string/menu_moveGroupMembers" /> </menu> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index c56218ae9..868f3e6aa 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -133,6 +133,7 @@ </plurals> <string name="all_contacts_tab_label" msgid="6250372293594147703">"所有联系人"</string> <string name="favorites_tab_label" msgid="1524869648904016414">"收藏"</string> + <string name="contacts_groups_label">"群组"</string> <string name="callBack" msgid="5498224409038809224">"回电"</string> <string name="callAgain" msgid="3197312117049874778">"重拨"</string> <string name="returnCall" msgid="8171961914203617813">"回拨"</string> diff --git a/res/values/colors.xml b/res/values/colors.xml index a537a9554..7cc8f586a 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -56,6 +56,10 @@ <color name="call_arrow_green">#2aad6f</color> <color name="call_arrow_red">#ff2e58</color> + <color name="contact_all_list_background_color">#FFFFFF</color> + <color name="contact_favorites_list_background_color">#FFFFFF</color> + <color name="group_list_background_color">#FFFFFF</color> + <!-- Background color of pinned header items. --> <color name="list_item_pinned_header_color">@color/background_primary</color> diff --git a/res/values/strings.xml b/res/values/strings.xml index 20d304ee8..f4ff2cece 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -335,6 +335,9 @@ <!-- The title of "favorites" tab. [CHAR LIMIT=14] --> <string name="favorites_tab_label">Favorites</string> + <!-- The title of "groups" tab. [CHAR LIMIT=14] --> + <string name="contacts_groups_label">Groups</string> + <!-- Action string for calling back a number in the call log --> <string name="callBack">Call back</string> diff --git a/src/com/android/contacts/GroupListLoader.java b/src/com/android/contacts/GroupListLoader.java index 2589c9bb4..e7fd6428f 100644 --- a/src/com/android/contacts/GroupListLoader.java +++ b/src/com/android/contacts/GroupListLoader.java @@ -49,8 +49,6 @@ public final class GroupListLoader extends CursorLoader { public GroupListLoader(Context context) { super(context, GROUP_LIST_URI, COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND " + Groups.ACCOUNT_NAME + " NOT NULL AND " + Groups.AUTO_ADD + "=0 AND " + - Groups.FAVORITES + "=0 AND " + Groups.DELETED + "=0", null, - Groups.ACCOUNT_TYPE + ", " + Groups.ACCOUNT_NAME + ", " + Groups.DATA_SET + ", " + - Groups.TITLE + " COLLATE LOCALIZED ASC"); + Groups.FAVORITES + "=0 AND " + Groups.DELETED + "=0", null, "account_id"); } } diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java index 834404138..ea503d017 100644 --- a/src/com/android/contacts/GroupMetaDataLoader.java +++ b/src/com/android/contacts/GroupMetaDataLoader.java @@ -50,7 +50,9 @@ public final class GroupMetaDataLoader extends CursorLoader { public GroupMetaDataLoader(Context context, Uri groupUri) { super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND " - + Groups.ACCOUNT_NAME + " NOT NULL", null, null); + + Groups.ACCOUNT_NAME + " NOT NULL AND " + Groups.DELETED + " != ?" + , new String[] {"1"} + , null); } /** diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java index 5a95c90bc..6a81d066b 100644 --- a/src/com/android/contacts/activities/ActionBarAdapter.java +++ b/src/com/android/contacts/activities/ActionBarAdapter.java @@ -110,8 +110,9 @@ public class ActionBarAdapter implements OnCloseListener { public interface TabState { public static int FAVORITES = 0; public static int ALL = 1; + public static int GROUPS = 2; - public static int COUNT = 2; + public static int COUNT = 3; public static int DEFAULT = ALL; } diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java index aa6f7dc08..daa398563 100644 --- a/src/com/android/contacts/activities/PeopleActivity.java +++ b/src/com/android/contacts/activities/PeopleActivity.java @@ -64,6 +64,9 @@ import com.android.contacts.common.util.ImplicitIntentsUtil; import com.android.contacts.common.widget.FloatingActionButtonController; import com.android.contacts.editor.EditorIntents; import com.android.contacts.common.editor.SelectAccountDialogFragment; +import com.android.contacts.group.GroupBrowseListFragment; +import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener; +import com.android.contacts.group.GroupDetailFragment; import com.android.contacts.interactions.ContactDeletionInteraction; import com.android.contacts.common.interactions.ImportExportDialogFragment; import com.android.contacts.common.interactions.ImportExportDialogFragment.ExportToSimThread; @@ -129,7 +132,8 @@ public class PeopleActivity extends ContactsActivity implements // These values needs to start at 2. See {@link ContactEntryListFragment}. private static final int SUBACTIVITY_ACCOUNT_FILTER = 2; - + private static final int SUBACTIVITY_NEW_GROUP = 4; + private static final int SUBACTIVITY_EDIT_GROUP = 5; private final DialogManager mDialogManager = new DialogManager(this); private ContactsIntentResolver mIntentResolver; @@ -137,6 +141,9 @@ public class PeopleActivity extends ContactsActivity implements private ActionBarAdapter mActionBarAdapter; private FloatingActionButtonController mFloatingActionButtonController; + private GroupDetailFragment mGroupDetailFragment; + private final GroupDetailFragmentListener mGroupDetailFragmentListener = + new GroupDetailFragmentListener(); private View mFloatingActionButtonContainer; private boolean wasLastFabAnimationScaleIn = false; @@ -156,6 +163,7 @@ public class PeopleActivity extends ContactsActivity implements */ private MultiSelectContactsListFragment mAllFragment; private ContactTileListFragment mFavoritesFragment; + private GroupBrowseListFragment mGroupsFragment; /** ViewPager for swipe */ private ViewPager mTabPager; @@ -348,6 +356,7 @@ public class PeopleActivity extends ContactsActivity implements mTabTitles = new String[TabState.COUNT]; mTabTitles[TabState.FAVORITES] = getString(R.string.favorites_tab_label); mTabTitles[TabState.ALL] = getString(R.string.all_contacts_tab_label); + mTabTitles[TabState.GROUPS] = getString(R.string.contacts_groups_label); mTabPager = getView(R.id.tab_pager); mTabPagerAdapter = new TabPagerAdapter(); mTabPager.setAdapter(mTabPagerAdapter); @@ -370,6 +379,7 @@ public class PeopleActivity extends ContactsActivity implements final String FAVORITE_TAG = "tab-pager-favorite"; final String ALL_TAG = "tab-pager-all"; + final String GROUPS_TAG = "tab-pager-groups"; // Create the fragments and add as children of the view pager. // The pager adapter will only change the visibility; it'll never create/destroy @@ -381,13 +391,17 @@ public class PeopleActivity extends ContactsActivity implements fragmentManager.findFragmentByTag(FAVORITE_TAG); mAllFragment = (MultiSelectContactsListFragment) fragmentManager.findFragmentByTag(ALL_TAG); + mGroupsFragment = (GroupBrowseListFragment) + fragmentManager.findFragmentByTag(GROUPS_TAG); if (mFavoritesFragment == null) { mFavoritesFragment = new ContactTileListFragment(); mAllFragment = new MultiSelectContactsListFragment(); + mGroupsFragment = new GroupBrowseListFragment(); transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG); transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG); + transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG); } mFavoritesFragment.setListener(mFavoritesFragmentListener); @@ -395,10 +409,13 @@ public class PeopleActivity extends ContactsActivity implements mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener()); mAllFragment.setCheckBoxListListener(new CheckBoxListListener()); + mGroupsFragment.setListener(new GroupBrowserActionListener()); + // Hide all fragments for now. We adjust visibility when we get onSelectedTabChanged() // from ActionBarAdapter. transaction.hide(mFavoritesFragment); transaction.hide(mAllFragment); + transaction.hide(mGroupsFragment); transaction.commitAllowingStateLoss(); fragmentManager.executePendingTransactions(); @@ -532,6 +549,9 @@ public class PeopleActivity extends ContactsActivity implements case ContactsRequest.ACTION_VIEW_CONTACT: tabToOpen = TabState.ALL; break; + case ContactsRequest.ACTION_GROUP: + tabToOpen = TabState.GROUPS; + break; default: tabToOpen = -1; break; @@ -554,6 +574,7 @@ public class PeopleActivity extends ContactsActivity implements } configureContactListFragment(); + configureGroupListFragment(); invalidateOptionsMenuIfNeeded(); } @@ -673,6 +694,9 @@ public class PeopleActivity extends ContactsActivity implements } invalidateOptionsMenu(); showEmptyStateForTab(tab); + if (tab == TabState.GROUPS) { + mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable()); + } } private void showEmptyStateForTab(int tab) { @@ -682,6 +706,10 @@ public class PeopleActivity extends ContactsActivity implements mContactsUnavailableFragment.setMessageText( R.string.listTotalAllContactsZeroStarred, -1); break; + case TabState.GROUPS: + mContactsUnavailableFragment.setMessageText(R.string.noGroups, + areGroupWritableAccountsAvailable() ? -1 : R.string.noAccounts); + break; case TabState.ALL: mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1); break; @@ -730,6 +758,9 @@ public class PeopleActivity extends ContactsActivity implements mActionBarAdapter.setCurrentTab(position, false); mViewPagerTabs.onPageSelected(position); showEmptyStateForTab(position); + if (position == TabState.GROUPS) { + mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable()); + } invalidateOptionsMenu(); } } @@ -788,6 +819,9 @@ public class PeopleActivity extends ContactsActivity implements if (object == mAllFragment) { return getTabPositionForTextDirection(TabState.ALL); } + if (object == mGroupsFragment) { + return TabState.GROUPS; + } } return POSITION_NONE; } @@ -811,6 +845,8 @@ public class PeopleActivity extends ContactsActivity implements return mFavoritesFragment; } else if (position == TabState.ALL) { return mAllFragment; + } else if (position == TabState.GROUPS) { + return mGroupsFragment; } } throw new IllegalArgumentException("position: " + position); @@ -918,6 +954,11 @@ public class PeopleActivity extends ContactsActivity implements return TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL; } + private void configureGroupListFragment() { + mGroupsFragment.setVerticalScrollbarPosition(getScrollBarPosition()); + mGroupsFragment.setSelectionVisible(false); + } + @Override public void onProviderStatusChange() { updateViewConfiguration(false); @@ -1092,6 +1133,67 @@ public class PeopleActivity extends ContactsActivity implements } } + private final class GroupBrowserActionListener implements OnGroupBrowserActionListener { + + GroupBrowserActionListener() {} + + @Override + public void onViewGroupAction(Uri groupUri) { + Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class); + intent.setData(groupUri); + startActivity(intent); + } + } + + private class GroupDetailFragmentListener implements GroupDetailFragment.Listener { + + GroupDetailFragmentListener() {} + + @Override + public void onGroupSizeUpdated(String size) { + // Nothing needs to be done here because the size will be displayed in the detail + // fragment + } + + @Override + public void onGroupTitleUpdated(String title) { + // Nothing needs to be done here because the title will be displayed in the detail + // fragment + } + + @Override + public void onAccountTypeUpdated(String accountTypeString, String dataSet) { + // Nothing needs to be done here because the group source will be displayed in the + // detail fragment + } + + @Override + public void onEditRequested(Uri groupUri) { + final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); + intent.setData(groupUri); + intent.setAction(Intent.ACTION_EDIT); + startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP); + } + + @Override + public void onContactSelected(Uri contactUri) { + // Nothing needs to be done here because either quickcontact will be displayed + // or activity will take care of selection + } + } + + public void startActivityAndForwardResult(final Intent intent) { + intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + + // Forward extras to the new activity + Bundle extras = getIntent().getExtras(); + if (extras != null) { + intent.putExtras(extras); + } + startActivity(intent); + finish(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { if (!areContactsAvailable()) { @@ -1121,6 +1223,10 @@ public class PeopleActivity extends ContactsActivity implements return true; } + if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) { + return true; + } + return false; } @@ -1133,12 +1239,14 @@ public class PeopleActivity extends ContactsActivity implements // Get references to individual menu items in the menu final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter); + MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group); final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents); final MenuItem helpMenu = menu.findItem(R.id.menu_help); final boolean isSearchOrSelectionMode = mActionBarAdapter.isSearchMode() || mActionBarAdapter.isSelectionMode(); if (isSearchOrSelectionMode) { + addGroupMenu.setVisible(false); contactsFilterMenu.setVisible(false); clearFrequentsMenu.setVisible(false); helpMenu.setVisible(false); @@ -1146,13 +1254,25 @@ public class PeopleActivity extends ContactsActivity implements } else { switch (getTabPositionForTextDirection(mActionBarAdapter.getCurrentTab())) { case TabState.FAVORITES: + addGroupMenu.setVisible(false); contactsFilterMenu.setVisible(false); clearFrequentsMenu.setVisible(hasFrequents()); break; case TabState.ALL: + addGroupMenu.setVisible(false); contactsFilterMenu.setVisible(true); clearFrequentsMenu.setVisible(false); break; + case TabState.GROUPS: + // Do not display the "new group" button if no accounts are available + if (areGroupWritableAccountsAvailable()) { + addGroupMenu.setVisible(true); + } else { + addGroupMenu.setVisible(false); + } + addGroupMenu.setVisible(true); + contactsFilterMenu.setVisible(false); + clearFrequentsMenu.setVisible(false); } helpMenu.setVisible(HelpUtils.isHelpAndFeedbackAvailable()); } @@ -1243,6 +1363,10 @@ public class PeopleActivity extends ContactsActivity implements case R.id.menu_join: joinSelectedContacts(); return true; + case R.id.menu_add_group: { + createNewGroup(); + return true; + } case R.id.menu_delete: { final Intent intent = new Intent(Intent.ACTION_DELETE, Contacts.CONTENT_URI); intent.putExtra(EDITABLE_KEY, mActionBarAdapter.getQueryString()); @@ -1337,6 +1461,12 @@ public class PeopleActivity extends ContactsActivity implements mAllFragment.getSelectedContactIds()); } + private void createNewGroup() { + final Intent intent = new Intent(this, GroupEditorActivity.class); + intent.setAction(Intent.ACTION_INSERT); + startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); + } + @Override public void onDeletionFinished() { mActionBarAdapter.setSelectionMode(false); @@ -1350,6 +1480,14 @@ public class PeopleActivity extends ContactsActivity implements mContactListFilterController, resultCode, data); break; } + case SUBACTIVITY_NEW_GROUP: + case SUBACTIVITY_EDIT_GROUP: { + if (resultCode == RESULT_OK) { + mRequest.setActionCode(ContactsRequest.ACTION_GROUP); + mGroupsFragment.setSelectedUri(data.getData()); + } + break; + } // TODO: Using the new startActivityWithResultFromFragment API this should not be needed // anymore diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java index 48751e72d..0931f3e7a 100644 --- a/src/com/android/contacts/group/GroupBrowseListAdapter.java +++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java @@ -30,6 +30,7 @@ import android.widget.TextView; import com.android.contacts.GroupListLoader; import com.android.contacts.R; import com.android.contacts.common.model.account.AccountType; +import com.android.contacts.common.model.account.PhoneAccountType; import com.android.contacts.common.model.AccountTypeManager; import com.google.common.base.Objects; @@ -198,7 +199,10 @@ public class GroupBrowseListAdapter extends BaseAdapter { AccountType accountType = mAccountTypeManager.getAccountType( entry.getAccountType(), entry.getDataSet()); viewCache.accountType.setText(accountType.getDisplayLabel(mContext)); - viewCache.accountName.setText(entry.getAccountName()); + // According to the UI SPEC, we will not show the account name for Phone account + if (!PhoneAccountType.ACCOUNT_TYPE.equals(entry.getAccountType())) { + viewCache.accountName.setText(entry.getAccountName()); + } } private static Uri getGroupUriFromId(long groupId) { diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java index c9cf6bd58..2039450e3 100644 --- a/src/com/android/contacts/group/GroupDetailFragment.java +++ b/src/com/android/contacts/group/GroupDetailFragment.java @@ -31,6 +31,7 @@ import android.database.Cursor; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; +import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Groups; import android.text.TextUtils; import android.util.Log; @@ -58,6 +59,8 @@ import com.android.contacts.common.list.ContactTileView; import com.android.contacts.list.GroupMemberTileAdapter; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.account.AccountType; +import com.android.contacts.activities.MultiPickContactActivity; +import com.android.contacts.common.SimContactsConstants; /** * Displays the details of a group and shows a list of actions possible for the group. @@ -115,6 +118,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener { private Uri mGroupUri; private long mGroupId; private String mGroupName; + private String mAccountNameString; private String mAccountTypeString; private String mDataSet; private boolean mIsReadOnly; @@ -286,6 +290,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener { Log.e(TAG, "Failed to load group members"); return; } + getActivity().invalidateOptionsMenu(); updateSize(data.getCount()); mAdapter.setContactCursor(data); mMemberListView.setEmptyView(mEmptyView); @@ -298,6 +303,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener { private void bindGroupMetaData(Cursor cursor) { cursor.moveToPosition(-1); if (cursor.moveToNext()) { + mAccountNameString = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME); mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE); mDataSet = cursor.getString(GroupMetaDataLoader.DATA_SET); mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID); @@ -454,6 +460,9 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener { final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group); deleteMenu.setVisible(mOptionsMenuGroupDeletable); + + final MenuItem moveMenu = menu.findItem(R.id.menu_move_group_members); + moveMenu.setVisible(isVisible() && mAdapter != null && mAdapter.getCount() > 0); } @Override @@ -468,6 +477,18 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener { mCloseActivityAfterDelete); return true; } + case R.id.menu_move_group_members: { + Intent intent = new Intent(SimContactsConstants.ACTION_MULTI_PICK); + intent.setType(Contacts.CONTENT_TYPE); + intent.putExtra(SimContactsConstants.IS_CONTACT, true); + intent.putExtra(MultiPickContactActivity.KEY_GROUP_ID, getGroupId()); + intent.putExtra(SimContactsConstants.ACCOUNT_TYPE, mAccountTypeString); + intent.putExtra(SimContactsConstants.ACCOUNT_NAME, mAccountNameString); + intent.putExtra(MultiPickContactActivity.ADD_MOVE_GROUP_MEMBER_KEY, + MultiPickContactActivity.ACTION_MOVE_GROUP_MEMBER); + startActivity(intent); + return true; + } } return false; } diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java index eda5d4f39..25ff4747e 100644 --- a/src/com/android/contacts/group/GroupEditorFragment.java +++ b/src/com/android/contacts/group/GroupEditorFragment.java @@ -63,6 +63,7 @@ import com.android.contacts.GroupMemberLoader.GroupEditorQuery; import com.android.contacts.GroupMetaDataLoader; import com.android.contacts.R; import com.android.contacts.activities.GroupEditorActivity; +import com.android.contacts.activities.MultiPickContactActivity; import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; import com.android.contacts.common.model.account.AccountType; @@ -72,11 +73,14 @@ import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter; import com.android.contacts.common.util.ViewUtil; +import com.android.contacts.common.SimContactsConstants; import com.google.common.base.Objects; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Set; public class GroupEditorFragment extends Fragment implements SelectAccountDialogFragment.Listener { private static final String TAG = "GroupEditorFragment"; @@ -98,6 +102,8 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog private static final String CURRENT_EDITOR_TAG = "currentEditorForAccount"; + public static final int REQUEST_CODE_PICK_GROUP_MEM = 1001; + public static interface Listener { /** * Group metadata was not found, close the fragment now. @@ -183,6 +189,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog private TextView mGroupNameView; private AutoCompleteTextView mAutoCompleteTextView; + private ImageView mAddGroupMemberView; private String mAccountName; private String mAccountType; @@ -311,8 +318,9 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog } private void selectAccountAndCreateGroup() { - final List<AccountWithDataSet> accounts = - AccountTypeManager.getInstance(mContext).getAccounts(true /* writeable */); + final List<AccountWithDataSet> accounts = AccountTypeManager + .getInstance(mContext).getAccounts(true /* writeable */, + AccountTypeManager.FLAG_ALL_ACCOUNTS_WITHOUT_SIM); // No Accounts available if (accounts.isEmpty()) { Log.e(TAG, "No accounts were found."); @@ -404,6 +412,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog mGroupNameView = (TextView) editorView.findViewById(R.id.group_name); mAutoCompleteTextView = (AutoCompleteTextView) editorView.findViewById( R.id.add_member_field); + mAddGroupMemberView = (ImageView) editorView.findViewById(R.id.addGroupMember); mListView = (ListView) editorView.findViewById(android.R.id.list); mListView.setAdapter(mMemberListAdapter); @@ -428,6 +437,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog if (mAutoCompleteTextView != null) { mAutoCompleteAdapter = new SuggestedMemberListAdapter(mContext, android.R.layout.simple_dropdown_item_1line); + mAutoCompleteTextView.setThreshold(1); mAutoCompleteAdapter.setContentResolver(mContentResolver); mAutoCompleteAdapter.setAccountType(mAccountType); mAutoCompleteAdapter.setAccountName(mAccountName); @@ -455,6 +465,23 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog mAutoCompleteAdapter.updateExistingMembersList(mListToDisplay); } + if (mAddGroupMemberView != null) { + mAddGroupMemberView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(SimContactsConstants.ACTION_MULTI_PICK); + intent.setType(Contacts.CONTENT_TYPE); + intent.putExtra(SimContactsConstants.IS_CONTACT, true); + intent.putExtra(SimContactsConstants.ACCOUNT_NAME, mAccountName); + intent.putExtra(SimContactsConstants.ACCOUNT_TYPE, mAccountType); + intent.putExtra(MultiPickContactActivity.ADD_MOVE_GROUP_MEMBER_KEY, + MultiPickContactActivity.ACTION_ADD_GROUP_MEMBER); + intent.putExtra(MultiPickContactActivity.KEY_GROUP_ID, mGroupId); + startActivityForResult(intent, REQUEST_CODE_PICK_GROUP_MEM); + } + }); + } + // If the group name is ready only, don't let the user focus on the field. mGroupNameView.setFocusable(!mGroupNameIsReadOnly); if(isNewEditor) { @@ -463,6 +490,47 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog mStatus = Status.EDITING; } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != Activity.RESULT_OK) { + return; + } + if (requestCode == REQUEST_CODE_PICK_GROUP_MEM) { + Bundle mChoiceSet = data.getExtras(); + Set<String> keys = mChoiceSet.keySet(); + Iterator<String> iterator = keys.iterator(); + String key; + String[] info; + String contactId; + String nameRawContactId; + String displayName; + String lookupKey; + String photoUri; + + while (iterator.hasNext()) { + key = iterator.next(); + info = mChoiceSet.getStringArray(key); + + contactId = info[1]; + + if (!mAutoCompleteAdapter.containsMember(Long.valueOf(contactId))) { + // Retrieve the contact data fields that will be sufficient + // to update + // the adapter with a new entry for this contact + lookupKey = info[0]; + nameRawContactId = info[2]; + photoUri= info[3]; + displayName = info[4]; + + Member member = new Member(Long.valueOf(nameRawContactId), lookupKey, + Long.valueOf(contactId), displayName, photoUri); + addMember(member); + } + } + } + } + public void load(String action, Uri groupUri, Bundle intentExtras) { mAction = action; mGroupUri = groupUri; @@ -713,8 +781,13 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog } private void addMember(Member member) { - // Update the display list - mListMembersToAdd.add(member); + // If the contact was just removed during this session, remove it from + // the list of members to remove + if (mListMembersToRemove.contains(member)) { + mListMembersToRemove.remove(member); + } else { + mListMembersToAdd.add(member); + } mListToDisplay.add(member); mMemberListAdapter.notifyDataSetChanged(); diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java index 19ff61177..18ad34c7c 100644 --- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java +++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java @@ -116,7 +116,13 @@ public class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> { } public void addNewMember(long contactId) { - mExistingMemberContactIds.add(contactId); + if (!containsMember(contactId)) { + mExistingMemberContactIds.add(contactId); + } + } + + public boolean containsMember(long contactId) { + return mExistingMemberContactIds.contains(contactId); } public void removeMember(long contactId) { @@ -186,7 +192,7 @@ public class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> { // and have the same account name and type as specified in this adapter String searchQuery = prefix.toString() + "%"; String accountClause = RawContacts.ACCOUNT_NAME + "=? AND " + - RawContacts.ACCOUNT_TYPE + "=?"; + RawContacts.ACCOUNT_TYPE + "=? AND " + RawContacts.DELETED + "!= 1"; String[] args; if (mDataSet == null) { accountClause += " AND " + RawContacts.DATA_SET + " IS NULL"; diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java index 81a98b55f..ccfd76f44 100644 --- a/src/com/android/contacts/quickcontact/QuickContactActivity.java +++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java @@ -97,6 +97,7 @@ import com.android.contacts.common.CallUtil; import com.android.contacts.common.ClipboardUtils; import com.android.contacts.common.Collapser; import com.android.contacts.common.ContactsUtils; +import com.android.contacts.common.GroupMetaData; import com.android.contacts.common.activity.RequestPermissionsActivity; import com.android.contacts.common.editor.SelectAccountDialogFragment; import com.android.contacts.common.interactions.TouchPointManager; @@ -113,6 +114,7 @@ import com.android.contacts.common.model.dataitem.DataItem; import com.android.contacts.common.model.dataitem.DataKind; import com.android.contacts.common.model.dataitem.EmailDataItem; import com.android.contacts.common.model.dataitem.EventDataItem; +import com.android.contacts.common.model.dataitem.GroupMembershipDataItem; import com.android.contacts.common.model.dataitem.ImDataItem; import com.android.contacts.common.model.dataitem.NicknameDataItem; import com.android.contacts.common.model.dataitem.NoteDataItem; @@ -1183,6 +1185,30 @@ public class QuickContactActivity extends ContactsActivity { } /** + * Maps group ID to the corresponding group name, collapses all synonymous groups. Ignores + * default groups (e.g. My Contacts) and favorites groups. + */ + private static String getGroupName(List<GroupMetaData> groupMetaData, long groupId) { + if (groupMetaData == null) { + return ""; + } + + for (GroupMetaData group : groupMetaData) { + if (group.getGroupId() == groupId) { + if (!group.isDefaultGroup() && !group.isFavorites()) { + String title = group.getTitle(); + if (!TextUtils.isEmpty(title)) { + return title; + } + } + break; + } + } + + return ""; + } + + /** * Create a card that shows "Add email" and "Add phone number" entries in grey. */ private void initializeNoContactDetailCard() { @@ -1607,6 +1633,33 @@ public class QuickContactActivity extends ContactsActivity { aboutCardName.value = res.getString(R.string.about_card_title); } } + } else if (dataItem instanceof GroupMembershipDataItem) { + GroupMembershipDataItem groupMembership = + (GroupMembershipDataItem) dataItem; + Long groupId = groupMembership.getGroupRowId(); + if (groupId != null) { + return new Entry(/* viewId = */-1, + /* icon = */null, + res.getString(R.string.groupsLabel), + getGroupName(contactData.getGroupMetaData(), + groupId), + /* mSubHeaderIcon= */null, + /* text = */null, + /* mTextIcon= */null, + /* primaryContentDescription = */null, + /* intent = */null, + /* alternateIcon = */null, + /* alternateIntent = */null, + /* alternateContentDescription = */null, + /* shouldApplyColor = */false, + /* isEditable = */false, + /* EntryContextMenuInfo = */null, + /* thirdIcon = */null, + /* thirdIntent = */null, + /* thirdContentDescription = */null, + /* iconResourceId = */0); + } + return null; } else { // Custom DataItem header = dataItem.buildDataStringForDisplay(context, kind); |