Add insert, add a contact, and edit name to group activity
* Remove fragment pager and move metadata loading
back to the group members fragment since we're not
going to use the old edit fragment any more.
* Bind the autocomplete adapter in the main group
activity and add an add icon to the options menu
to search for new contacts.
* Add account selection to the main group activity
to supports inserts.
* Change the group name dialog added in ag/1040856
since it supports both adding and updating group
names now.
* Extract Member inner class from the edit fragment
since it is needed for the autocomplete view
and the edit fragment is going away.
Bug 28707265
Bug 18641067
Change-Id: I6b1326ee93dcb75198166e42272c44c983340e27
diff --git a/res/layout/group_members_activity.xml b/res/layout/group_members_activity.xml
index eecec50..387fdd2 100644
--- a/res/layout/group_members_activity.xml
+++ b/res/layout/group_members_activity.xml
@@ -25,10 +25,4 @@
layout="@layout/people_activity_toolbar"
android:id="@+id/toolbar_parent" />
- <com.android.contacts.widget.NoSwipeViewPager
- android:id="@+id/view_pager"
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="1"/>
-
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/create_group_dialog.xml b/res/layout/group_name_edit_dialog.xml
similarity index 95%
rename from res/layout/create_group_dialog.xml
rename to res/layout/group_name_edit_dialog.xml
index 3fefd3d..2858388 100644
--- a/res/layout/create_group_dialog.xml
+++ b/res/layout/group_name_edit_dialog.xml
@@ -30,6 +30,6 @@
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginTop="16dp"
- android:inputType="textCapWords"
+ android:inputType="textCapWords|textNoSuggestions"
android:maxLength="40"/>
</LinearLayout>
\ No newline at end of file
diff --git a/res/menu/view_group.xml b/res/menu/view_group.xml
index 6c5979e..a937598 100644
--- a/res/menu/view_group.xml
+++ b/res/menu/view_group.xml
@@ -18,6 +18,12 @@
xmlns:contacts="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/menu_add"
+ android:icon="@drawable/ic_person_add_tinted_24dp"
+ android:title="@string/menu_addToGroup"
+ contacts:showAsAction="ifRoom" />
+
+ <item
android:id="@+id/menu_edit_group"
android:icon="@drawable/ic_create_24dp"
android:title="@string/menu_editGroup"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 22169e4..6f5b122 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -320,9 +320,24 @@
<!-- Toast displayed when saving a contact photo failed. [CHAR LIMIT=NONE] -->
<string name="contactPhotoSavedErrorToast">Couldn\'t save contact photo changes.</string>
+ <!-- Toast displayed when something goes wrong while loading a label. [CHAR LIMIT=70] -->
+ <string name="groupLoadErrorToast">Failed to load label</string>
+
<!-- Toast displayed when a label is saved [CHAR LIMIT=30] -->
<string name="groupSavedToast">Label saved</string>
+ <!-- Toast displayed when a label name is updated. [CHAR LIMIT=50] -->
+ <string name="groupCreatedToast">Label created</string>
+
+ <!-- Toast displayed when a new label is created. [CHAR LIMIT=50] -->
+ <string name="groupUpdatedToast">Label updated</string>
+
+ <!-- Toast displayed when contacts are removed from a label. [CHAR LIMIT=50] -->
+ <string name="groupMembersRemovedToast">Removed contacts</string>
+
+ <!-- Toast displayed when a contact is added to a label. [CHAR LIMIT=50] -->
+ <string name="groupMembersAddedToast">Added contact</string>
+
<!-- Toast displayed when saving a label failed [CHAR LIMIT=70] -->
<string name="groupSavedErrorToast">Couldn\'t save label changes.</string>
@@ -414,10 +429,10 @@
<string name="dialog_new_group_account">Choose account</string>
<!-- Title for the create new label dialog. CHAR LIMIT=40] -->
- <string name="create_group_dialog_title">Create label</string>
+ <string name="insert_group_dialog_title">Create label</string>
- <!-- Button label to create a new label on the create new label dialog. [CHAR LIMIT=20] -->
- <string name="create_group_dialog_button">Create</string>
+ <!-- Title for the update label dialog. CHAR LIMIT=40] -->
+ <string name="update_group_dialog_title">Update label</string>
<!-- Generic action string for starting an audio chat. Used by AccessibilityService to announce the purpose of the view. [CHAR LIMIT=NONE] -->
<string name="audio_chat">Voice chat</string>
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 7ebb3e0..e27664d 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -15,7 +15,7 @@
*/
package com.android.contacts.activities;
-import android.app.Fragment;
+import android.accounts.Account;
import android.app.FragmentManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
@@ -24,33 +24,41 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
+import android.provider.ContactsContract.Intents;
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.GroupListLoader;
-import com.android.contacts.GroupMetaDataLoader;
+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.model.AccountTypeManager;
-import com.android.contacts.common.model.account.AccountType;
+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.GroupEditorFragment;
import com.android.contacts.group.GroupMembersListFragment;
import com.android.contacts.group.GroupMetadata;
-import com.android.contacts.group.GroupUtil;
+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;
import com.android.contacts.quickcontact.QuickContactActivity;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Displays the members of a group and allows the user to edit it.
*/
@@ -58,166 +66,51 @@
public class GroupMembersActivity extends AppCompatContactsActivity implements
ActionBarAdapter.Listener,
MultiSelectContactsListFragment.OnCheckBoxListActionListener,
+ SelectAccountDialogFragment.Listener,
GroupMembersListFragment.GroupMembersListListener,
- GroupEditorFragment.Listener {
+ GroupNameEditDialogFragment.Listener {
- private static final String TAG = "GroupMembersActivity";
+ private static final String TAG = "GroupMembers";
- private static final boolean DEBUG = false;
-
+ private static final String KEY_IS_INSERT_ACTION = "isInsertAction";
+ private static final String KEY_GROUP_URI = "groupUri";
private static final String KEY_GROUP_METADATA = "groupMetadata";
- private static final int LOADER_GROUP_METADATA = 0;
- private static final int LOADER_GROUP_LIST_DETAILS = 1;
+ private static final String TAG_GROUP_MEMBERS = "groupMembers";
+ private static final String TAG_SELECT_ACCOUNT_DIALOG = "selectAccountDialog";
+ private static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";
- private static final int FRAGMENT_MEMBERS_LIST = -1;
- private static final int FRAGMENT_EDITOR = -2;
+ private static final int LOADER_GROUP_MEMBERS = 0;
- public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
+ private static final String ACTION_CREATE_GROUP = "createGroup";
+ private static final String ACTION_UPDATE_GROUP = "updateGroup";
+ private static final String ACTION_ADD_TO_GROUP = "addToGroup";
+ private static final String ACTION_REMOVE_FROM_GROUP = "removeFromGroup";
- private class GroupPagerAdapter extends FragmentPagerAdapter {
-
- public GroupPagerAdapter(FragmentManager fragmentManager) {
- super(fragmentManager);
- }
-
- @Override
- public int getCount() {
- return mIsInsertAction ? 1 : 2;
- }
-
- public Fragment getItem(int position) {
- if (mIsInsertAction) {
- switch (position) {
- case 0:
- mEditorFragment = GroupEditorFragment.newInstance(
- Intent.ACTION_INSERT, mGroupMetadata, getIntent().getExtras());
- return mEditorFragment;
- }
- throw new IllegalStateException("Unhandled position " + position);
- } else {
- switch (position) {
- case 0:
- mMembersListFragment = GroupMembersListFragment.newInstance(mGroupMetadata);
- return mMembersListFragment;
- case 1:
- // TODO: double check what intent extras need to be supported
- mEditorFragment = GroupEditorFragment.newInstance(
- Intent.ACTION_EDIT, mGroupMetadata, getIntent().getExtras());
- return mEditorFragment;
- }
- throw new IllegalStateException("Unhandled position " + position);
- }
- }
-
- private boolean isCurrentItem(int fragment) {
- if (mIsInsertAction) {
- return FRAGMENT_EDITOR == fragment;
- }
- int currentItem = mViewPager.getCurrentItem();
- switch (fragment) {
- case FRAGMENT_MEMBERS_LIST:
- return currentItem == 0;
- case FRAGMENT_EDITOR:
- return currentItem == 1;
- }
- return false;
- }
-
- private void setCurrentItem(int fragment) {
- if (mIsInsertAction) {
- switch (fragment) {
- case FRAGMENT_EDITOR:
- mViewPager.setCurrentItem(0);
- break;
- default:
- throw new IllegalStateException("Unsupported fragment " + fragment);
- }
- } else {
- switch (fragment) {
- case FRAGMENT_MEMBERS_LIST:
- mViewPager.setCurrentItem(0);
- break;
- case FRAGMENT_EDITOR:
- mViewPager.setCurrentItem(1);
- break;
- default:
- throw new IllegalStateException("Unsupported fragment " + fragment);
- }
- }
- }
- }
-
- /** Step 1 of loading group metadata. */
- private final LoaderCallbacks<Cursor> mGroupMetadataCallbacks = new LoaderCallbacks<Cursor>() {
+ /** Loader callbacks for existing group members for the autocomplete text view. */
+ private final LoaderCallbacks<Cursor> mGroupMemberCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public CursorLoader onCreateLoader(int id, Bundle args) {
- return new GroupMetaDataLoader(GroupMembersActivity.this, mGroupUri);
+ return GroupMemberLoader.constructLoaderForGroupEditorQuery(
+ GroupMembersActivity.this, mGroupMetadata.groupId);
}
@Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
- if (cursor == null || cursor.isClosed()) {
- Log.e(TAG, "Failed to load group metadata");
- return;
+ 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)));
}
- if (cursor.moveToNext()) {
- final boolean deleted = cursor.getInt(GroupMetaDataLoader.DELETED) == 1;
- if (!deleted) {
- mGroupMetadata = new GroupMetadata();
- mGroupMetadata.uri = mGroupUri;
- mGroupMetadata.accountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
- mGroupMetadata.accountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
- mGroupMetadata.dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
- mGroupMetadata.groupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
- mGroupMetadata.groupName = cursor.getString(GroupMetaDataLoader.TITLE);
- mGroupMetadata.readOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
- final AccountTypeManager accountTypeManager =
- AccountTypeManager.getInstance(GroupMembersActivity.this);
- final AccountType accountType = accountTypeManager.getAccountType(
- 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(GroupMembersActivity.this);
-
- // TODO(wjang): modify GroupListLoader to accept this selection criteria more naturally
- groupListLoader.setSelection(groupListLoader.getSelection()
- + " 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();
+ bindAutocompleteGroupMembers(members);
}
@Override
@@ -225,93 +118,173 @@
};
private ActionBarAdapter mActionBarAdapter;
- private ViewPager mViewPager;
- private GroupPagerAdapter mPagerAdapter;
-
- private Uri mGroupUri;
private GroupMetadata mGroupMetadata;
private GroupMembersListFragment mMembersListFragment;
- private GroupEditorFragment mEditorFragment;
+ private SuggestedMemberListAdapter mAutoCompleteAdapter;
+
+ private Uri mGroupUri;
private boolean mIsInsertAction;
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
- mIsInsertAction = Intent.ACTION_INSERT.equals(getIntent().getAction());
-
- mGroupUri = getIntent().getData();
+ // Parse the Intent
if (savedState != null) {
+ mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
+ mIsInsertAction = savedState.getBoolean(KEY_IS_INSERT_ACTION);
mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
+ } else {
+ mGroupUri = getIntent().getData();
+ mIsInsertAction = Intent.ACTION_INSERT.equals(getIntent().getAction());
+ }
+ if (!mIsInsertAction && mGroupUri == null) {
+ setResultCanceledAndFinish(R.string.groupLoadErrorToast);
+ return;
}
- // Setup the view
+ // Set up the view
setContentView(R.layout.group_members_activity);
- mViewPager = (ViewPager) findViewById(R.id.view_pager);
// Set up the action bar
final Toolbar toolbar = getView(R.id.toolbar);
setSupportActionBar(toolbar);
- final ContactsRequest contactsRequest = new ContactsRequest();
- contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP);
mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
/* portraitTabs */ null, /* landscapeTabs */ null, toolbar,
R.string.enter_contact_name);
mActionBarAdapter.setShowHomeIcon(true);
mActionBarAdapter.setShowHomeAsUp(true);
+
+ // Decide whether to prompt for the account and group name or start loading existing members
+ if (mIsInsertAction) {
+ // Check if we are in the middle of the insert flow.
+ if (!isSelectAccountDialogFound() && !isGroupNameEditDialogFound()) {
+
+ // Create metadata to hold the account info
+ mGroupMetadata = new GroupMetadata();
+
+ // Select the account to create the group
+ final Bundle extras = getIntent().getExtras();
+ final Account account = extras == null ? null :
+ (Account) extras.getParcelable(Intents.Insert.EXTRA_ACCOUNT);
+ if (account == null) {
+ selectAccount();
+ } else {
+ final String dataSet = extras == null
+ ? null : extras.getString(Intents.Insert.EXTRA_DATA_SET);
+ final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
+ account.name, account.type, dataSet);
+ onAccountChosen(accountWithDataSet, /* extraArgs */ null);
+ }
+ }
+ } else {
+ // Add the members list fragment
+ final FragmentManager fragmentManager = getFragmentManager();
+ mMembersListFragment = (GroupMembersListFragment)
+ fragmentManager.findFragmentByTag(TAG_GROUP_MEMBERS);
+ if (mMembersListFragment == null) {
+ mMembersListFragment = GroupMembersListFragment.newInstance(getIntent().getData());
+ 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) {
+ mMembersListFragment.setCheckBoxListListener(this);
+ }
+ }
+
+ // Delay action bar initialization until after the fragment is added
+ final ContactsRequest contactsRequest = new ContactsRequest();
+ contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP);
mActionBarAdapter.initialize(savedState, contactsRequest);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.onSaveInstanceState(outState);
+ }
+ outState.putBoolean(KEY_IS_INSERT_ACTION, mIsInsertAction);
+ outState.putParcelable(KEY_GROUP_URI, mGroupUri);
outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
}
- @Override
- public void onStart() {
- super.onStart();
-
- if (mIsInsertAction) {
- mGroupMetadata = new GroupMetadata();
- onGroupMetadataLoaded();
- } else {
- if (mGroupMetadata == null) {
- getLoaderManager().restartLoader(
- LOADER_GROUP_METADATA, null, mGroupMetadataCallbacks);
- } else {
- onGroupMetadataLoaded();
- }
+ private void selectAccount() {
+ final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this)
+ .getAccounts(/* writable */ true);
+ if (accounts.isEmpty()) {
+ setResultCanceledAndFinish();
+ return;
}
+ // If there is a single writable account, use it w/o showing a dialog.
+ if (accounts.size() == 1) {
+ onAccountChosen(accounts.get(0), /* extraArgs */ null);
+ return;
+ }
+ SelectAccountDialogFragment.show(getFragmentManager(), null,
+ R.string.dialog_new_group_account, AccountListFilter.ACCOUNTS_GROUP_WRITABLE,
+ /* extraArgs */ null, TAG_SELECT_ACCOUNT_DIALOG);
}
+ // Invoked with results from the ContactSaveService
@Override
protected void onNewIntent(Intent newIntent) {
super.onNewIntent(newIntent);
- if (ACTION_SAVE_COMPLETED.equals(newIntent.getAction())) {
+ if (isSaveAction(newIntent.getAction())) {
final Uri groupUri = newIntent.getData();
if (groupUri == null) {
Toast.makeText(this, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
- setResult(RESULT_CANCELED);
- finish();
- } else {
- Toast.makeText(this, R.string.groupSavedToast,Toast.LENGTH_SHORT).show();
-
- final Intent intent = GroupUtil.createViewGroupIntent(this, groupUri);
- finish();
- startActivity(intent);
+ setResultCanceledAndFinish();
+ return;
}
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received group URI " + groupUri);
+
+ mGroupUri = groupUri;
+ mIsInsertAction = false;
+
+ Toast.makeText(this, getToastMessageForSaveAction(newIntent.getAction()),
+ Toast.LENGTH_SHORT).show();
+
+ mMembersListFragment = GroupMembersListFragment.newInstance(groupUri);
+ mMembersListFragment.setListener(this);
+ getFragmentManager().beginTransaction()
+ .replace(R.id.fragment_container, mMembersListFragment, TAG_GROUP_MEMBERS)
+ .commit();
+ if (mGroupMetadata != null && mGroupMetadata.editable) {
+ mMembersListFragment.setCheckBoxListListener(this);
+ }
+
+ invalidateOptionsMenu();
}
}
+ private static boolean isSaveAction(String action) {
+ return ACTION_CREATE_GROUP.equals(action)
+ || ACTION_UPDATE_GROUP.equals(action)
+ || ACTION_ADD_TO_GROUP.equals(action)
+ || ACTION_REMOVE_FROM_GROUP.equals(action);
+ }
+
+ private static int getToastMessageForSaveAction(String action) {
+ if (ACTION_CREATE_GROUP.equals(action)) return R.string.groupCreatedToast;
+ if (ACTION_UPDATE_GROUP.equals(action)) return R.string.groupUpdatedToast;
+ if (ACTION_ADD_TO_GROUP.equals(action)) return R.string.groupMembersAddedToast;
+ if (ACTION_REMOVE_FROM_GROUP.equals(action)) return R.string.groupMembersRemovedToast;
+ throw new IllegalArgumentException("Unhanded contact save action " + action);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mGroupMetadata == null || mGroupMetadata.memberCount < 0) {
- // Hide menu options until metatdata is fully loaded
+ // Hide menu options until metadata is fully loaded
return false;
}
super.onCreateOptionsMenu(menu);
@@ -322,22 +295,22 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
- final boolean isSearchMode = false;
+ final boolean isSearchMode = mActionBarAdapter.isSearchMode();
- final boolean isListFragment = mPagerAdapter.isCurrentItem(FRAGMENT_MEMBERS_LIST);
- final boolean isEditorFragment = mPagerAdapter.isCurrentItem(FRAGMENT_EDITOR);
+ final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
+ final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;
- final boolean isGroupEditable = mGroupMetadata.editable;
- final boolean isGroupReadOnly = mGroupMetadata.readOnly;
+ setVisible(menu, R.id.menu_add,
+ isGroupEditable &&!isSelectionMode && !isSearchMode);
- setVisible(menu, R.id.menu_edit_group, isGroupEditable && !isEditorFragment &&
- !isSelectionMode && !isSearchMode);
+ setVisible(menu, R.id.menu_edit_group,
+ isGroupEditable && !isSelectionMode && !isSearchMode);
- setVisible(menu, R.id.menu_delete_group, !isGroupReadOnly && !isEditorFragment &&
- !isSelectionMode && !isSearchMode);
+ setVisible(menu, R.id.menu_delete_group,
+ !isGroupReadOnly && !isSelectionMode && !isSearchMode);
setVisible(menu, R.id.menu_remove_from_group,
- isGroupEditable && isSelectionMode && isListFragment);
+ isGroupEditable && isSelectionMode);
return true;
}
@@ -356,11 +329,19 @@
onBackPressed();
return true;
}
+ case R.id.menu_add: {
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.setSearchMode(true);
+ }
+ return true;
+ }
case R.id.menu_edit_group: {
- mPagerAdapter.setCurrentItem(FRAGMENT_EDITOR);
+ GroupNameEditDialogFragment.showUpdateDialog(
+ getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG, mGroupMetadata.groupName);
return true;
}
case R.id.menu_delete_group: {
+ // TODO(wjang): add a Toast after deletion after deleting the editor fragment
GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetadata.groupId,
mGroupMetadata.groupName, /* endActivity */ true);
return true;
@@ -379,25 +360,10 @@
final Intent intent = ContactSaveService.createGroupUpdateIntent(
this, mGroupMetadata.groupId, /* groupName */ null,
/* rawContactsToAdd */ null, rawContactsToRemove, getClass(),
- GroupMembersActivity.ACTION_SAVE_COMPLETED);
+ GroupMembersActivity.ACTION_REMOVE_FROM_GROUP);
startService(intent);
- }
- private void onGroupMetadataLoaded() {
- if (DEBUG) Log.d(TAG, "Loaded " + mGroupMetadata);
-
- if (mPagerAdapter == null) {
- mPagerAdapter = new GroupPagerAdapter(getFragmentManager());
- mViewPager.setAdapter(mPagerAdapter);
- }
-
- if (mIsInsertAction) {
- mPagerAdapter.setCurrentItem(FRAGMENT_EDITOR);
- getSupportActionBar().setTitle(getString(R.string.editGroupDescription));
- } else {
- getSupportActionBar().setTitle(mGroupMetadata.groupName);
- mPagerAdapter.setCurrentItem(FRAGMENT_MEMBERS_LIST);
- }
+ mActionBarAdapter.setSelectionMode(false);
}
@Override
@@ -411,19 +377,43 @@
}
} else if (mActionBarAdapter.isSearchMode()) {
mActionBarAdapter.setSearchMode(false);
- } else if (mPagerAdapter.isCurrentItem(FRAGMENT_EDITOR)) {
- mPagerAdapter.setCurrentItem(FRAGMENT_MEMBERS_LIST);
} else {
super.onBackPressed();
}
}
- // GroupsMembersListFragment callbacks
+ private boolean isSelectAccountDialogFound() {
+ return getFragmentManager().findFragmentByTag(TAG_SELECT_ACCOUNT_DIALOG) != null;
+ }
+
+ private boolean isGroupNameEditDialogFound() {
+ return getFragmentManager().findFragmentByTag(TAG_GROUP_NAME_EDIT_DIALOG) != null;
+ }
+
+ private void setResultCanceledAndFinish() {
+ setResultCanceledAndFinish(-1);
+ }
+
+ private void setResultCanceledAndFinish(int toastResId) {
+ if (toastResId >= 0) {
+ Toast.makeText(this, toastResId, Toast.LENGTH_SHORT).show();
+ }
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ // SelectAccountDialogFragment.Listener callbacks
@Override
- public void onGroupMemberListItemClicked(Uri contactLookupUri) {
- startActivity(ImplicitIntentsUtil.composeQuickContactIntent(
- contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED));
+ public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
+ mGroupMetadata.setGroupAccountMetadata(account);
+ GroupNameEditDialogFragment.showInsertDialog(
+ getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
+ }
+
+ @Override
+ public void onAccountSelectorCancelled() {
+ setResultCanceledAndFinish();
}
// ActionBarAdapter callbacks
@@ -441,6 +431,7 @@
mMembersListFragment.displayCheckBoxes(true);
}
invalidateOptionsMenu();
+ showFabWithAnimation(/* showFabWithAnimation = */ false);
break;
case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
mActionBarAdapter.setSearchMode(false);
@@ -474,75 +465,113 @@
@Override
public void onStartDisplayingCheckBoxes() {
mActionBarAdapter.setSelectionMode(true);
- invalidateOptionsMenu();
}
@Override
public void onSelectedContactIdsChanged() {
- if (mActionBarAdapter.isSelectionMode() && mMembersListFragment != null) {
- mActionBarAdapter.setSelectionCount(
- mMembersListFragment.getSelectedContactIds().size());
- }
- invalidateOptionsMenu();
+ mActionBarAdapter.setSelectionCount(mMembersListFragment.getSelectedContactIds().size());
}
@Override
public void onStopDisplayingCheckBoxes() {
mActionBarAdapter.setSelectionMode(false);
+ }
+
+ // GroupNameEditDialogFragment.Listener callbacks
+
+ @Override
+ public void onGroupNameEdit(String groupName) {
+ final Intent saveIntent;
+ if (mIsInsertAction) {
+ saveIntent = ContactSaveService.createNewGroupIntent(this,
+ mGroupMetadata.createAccountWithDataSet(), groupName,
+ /* rawContactsToAdd */ null, GroupMembersActivity.class,
+ GroupMembersActivity.ACTION_CREATE_GROUP);
+ } else {
+ saveIntent = ContactSaveService.createGroupRenameIntent(this,
+ mGroupMetadata.groupId, groupName, GroupMembersActivity.class,
+ GroupMembersActivity.ACTION_UPDATE_GROUP);
+ }
+ startService(saveIntent);
+ }
+
+ @Override
+ public void onGroupNameEditCancelled() {
+ if (mIsInsertAction) {
+ setResultCanceledAndFinish();
+ }
+ }
+
+ // GroupsMembersListFragment callbacks
+
+ @Override
+ public void onGroupMetadataLoaded(GroupMetadata groupMetadata) {
+ mGroupMetadata = groupMetadata;
+
+ if (!mIsInsertAction) {
+ getSupportActionBar().setTitle(mGroupMetadata.groupName);
+ }
+
+ bindAutocompleteTextView();
+ getLoaderManager().initLoader(LOADER_GROUP_MEMBERS, null, mGroupMemberCallbacks);
+
invalidateOptionsMenu();
}
- // GroupEditorFragment.Listener callbacks
+ 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, GroupMembersActivity.ACTION_ADD_TO_GROUP);
+ startService(intent);
- @Override
- public void onGroupNotFound() {
- finish();
+ // Update the autocomplete adapter so the contact doesn't get suggested again
+ mAutoCompleteAdapter.addNewMember(member.getContactId());
+
+ // Clear out the text field
+ autoCompleteTextView.setText("");
+ }
+ });
}
- @Override
- public void onReverted() {
- if (mIsInsertAction) {
- finish();
- } else {
- mPagerAdapter.setCurrentItem(FRAGMENT_MEMBERS_LIST);
+ 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 onSaveFinished(int resultCode, Intent resultIntent) {
- if (mIsInsertAction) {
- final Intent intent = GroupUtil.createViewGroupIntent(this, resultIntent.getData());
- finish();
- startActivity(intent);
- }
+ public void onGroupMetadataLoadFailed() {
+ setResultCanceledAndFinish(R.string.groupLoadErrorToast);
}
@Override
- public void onAccountsNotFound() {
- finish();
- }
-
- @Override
- public void onGroupMemberClicked(Uri contactLookupUri) {
+ public void onGroupMemberListItemClicked(Uri contactLookupUri) {
startActivity(ImplicitIntentsUtil.composeQuickContactIntent(
contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED));
}
-
- @Override
- public AutoCompleteTextView getSearchView() {
- return mActionBarAdapter == null
- ? null : (AutoCompleteTextView) mActionBarAdapter.getSearchView();
- }
-
- @Override
- public boolean isSearchMode() {
- return mActionBarAdapter == null ? false : mActionBarAdapter.isSearchMode();
- }
-
- @Override
- public void setSearchMode(boolean searchMode) {
- if (mActionBarAdapter != null) {
- mActionBarAdapter.setSearchMode(searchMode);
- }
- }
}
diff --git a/src/com/android/contacts/group/CreateGroupDialogFragment.java b/src/com/android/contacts/group/CreateGroupDialogFragment.java
deleted file mode 100644
index 777fd44..0000000
--- a/src/com/android/contacts/group/CreateGroupDialogFragment.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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, softwareateCre
- * 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.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.widget.Button;
-import android.widget.EditText;
-
-import com.android.contacts.R;
-
-/**
- * Prompts the user for the name of the new group.
- */
-public final class CreateGroupDialogFragment extends DialogFragment {
-
- private static final String TAG_CREATE_GROUP_DIALOG = "createGroup";
-
- /** Callbacks for hosts of the {@link CreateGroupDialogFragment}. */
- public interface Listener {
- void onCreateGroup(String groupName);
- void onCreateGroupCancelled();
- }
-
- private EditText mGroupNameEditText;
-
- public static <F extends Fragment & Listener> void show(
- FragmentManager fragmentManager, F targetFragment) {
- final CreateGroupDialogFragment dialog = new CreateGroupDialogFragment();
- dialog.setTargetFragment(targetFragment, /* requestCode */ 0);
- dialog.show(fragmentManager, TAG_CREATE_GROUP_DIALOG);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- // Build a dialog with two buttons and a view of a single EditText input field
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.create_group_dialog_title)
- .setView(R.layout.create_group_dialog)
- .setNegativeButton(android.R.string.cancel, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onCreateGroupCancelled();
- dismiss();
- }
- })
- .setPositiveButton(R.string.create_group_dialog_button, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onCreateGroup();
- }
- });
-
- // Disable the create button when the name is empty
- final AlertDialog alertDialog = builder.create();
- alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
- @Override
- public void onShow(DialogInterface dialog) {
- mGroupNameEditText = (EditText) alertDialog.findViewById(android.R.id.text1);
-
- final Button createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
- createButton.setEnabled(!TextUtils.isEmpty(getGroupName()));
- mGroupNameEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- createButton.setEnabled(!TextUtils.isEmpty(s));
- }
- });
- }
- });
- return alertDialog;
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
- onCreateGroupCancelled();
- }
-
- @Override
- public void onSaveInstanceState(Bundle b) {
- setTargetFragment(null, /* requestCode */ -1);
- super.onSaveInstanceState(b);
- }
-
- private void onCreateGroupCancelled() {
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null && targetFragment instanceof Listener) {
- ((Listener) targetFragment).onCreateGroupCancelled();
- }
- }
-
- private void onCreateGroup() {
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null && targetFragment instanceof Listener) {
- ((Listener) targetFragment).onCreateGroup(getGroupName());
- }
- }
-
- private String getGroupName() {
- return mGroupNameEditText == null || mGroupNameEditText.getText() == null
- ? null : mGroupNameEditText.getText().toString();
- }
-}
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index a7d30a4..a22d42f 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -33,8 +33,6 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.text.TextUtils;
@@ -69,8 +67,6 @@
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
-import com.google.common.base.Objects;
-
import java.util.ArrayList;
import java.util.List;
@@ -96,6 +92,8 @@
private static final String CURRENT_EDITOR_TAG = "currentEditorForAccount";
+ private static final String ACTION_SAVE_COMPLETED = "saveCompleted";
+
public interface Listener {
/**
* Group metadata was not found, close the fragment now.
@@ -217,9 +215,9 @@
private ContentResolver mContentResolver;
private SuggestedMemberListAdapter mAutoCompleteAdapter;
- private ArrayList<Member> mListMembersToAdd = new ArrayList<Member>();
- private ArrayList<Member> mListMembersToRemove = new ArrayList<Member>();
- private ArrayList<Member> mListToDisplay = new ArrayList<Member>();
+ private ArrayList<Member> mListMembersToAdd = new ArrayList<>();
+ private ArrayList<Member> mListMembersToRemove = new ArrayList<>();
+ private ArrayList<Member> mListToDisplay = new ArrayList<>();
public static GroupEditorFragment newInstance(String action, GroupMetadata groupMetadata,
Bundle intentExtras) {
@@ -651,8 +649,7 @@
saveIntent = ContactSaveService.createNewGroupIntent(activity,
new AccountWithDataSet(mAccountName, mAccountType, mDataSet),
mGroupNameView.getText().toString(),
- membersToAddArray, activity.getClass(),
- GroupMembersActivity.ACTION_SAVE_COMPLETED);
+ membersToAddArray, activity.getClass(), ACTION_SAVE_COMPLETED);
} else if (Intent.ACTION_EDIT.equals(mAction)) {
// Create array of raw contact IDs for contacts to add to the group
long[] membersToAddArray = convertToArray(mListMembersToAdd);
@@ -664,7 +661,7 @@
saveIntent = ContactSaveService.createGroupUpdateIntent(activity,
mGroupMetadata.groupId,
getUpdatedName(), membersToAddArray, membersToRemoveArray,
- activity.getClass(), GroupMembersActivity.ACTION_SAVE_COMPLETED);
+ activity.getClass(), ACTION_SAVE_COMPLETED);
} else {
throw new IllegalStateException("Invalid intent action type " + mAction);
}
@@ -856,113 +853,6 @@
};
/**
- * This represents a single member of the current group.
- */
- public static class Member implements Parcelable {
-
- // TODO: Switch to just dealing with raw contact IDs everywhere if possible
- private final long mRawContactId;
- private final long mContactId;
- private final Uri mLookupUri;
- private final String mDisplayName;
- private final Uri mPhotoUri;
- private final String mLookupKey;
- private final long mPhotoId;
-
- public Member(long rawContactId, String lookupKey, long contactId, String displayName,
- String photoUri, long photoId) {
- mRawContactId = rawContactId;
- mContactId = contactId;
- mLookupKey = lookupKey;
- mLookupUri = Contacts.getLookupUri(contactId, lookupKey);
- mDisplayName = displayName;
- mPhotoUri = (photoUri != null) ? Uri.parse(photoUri) : null;
- mPhotoId = photoId;
- }
-
- public long getRawContactId() {
- return mRawContactId;
- }
-
- public long getContactId() {
- return mContactId;
- }
-
- public Uri getLookupUri() {
- return mLookupUri;
- }
-
- public String getLookupKey() {
- return mLookupKey;
- }
-
- public String getDisplayName() {
- return mDisplayName;
- }
-
- public Uri getPhotoUri() {
- return mPhotoUri;
- }
-
- public long getPhotoId() {
- return mPhotoId;
- }
-
- @Override
- public boolean equals(Object object) {
- if (object instanceof Member) {
- Member otherMember = (Member) object;
- return Objects.equal(mLookupUri, otherMember.getLookupUri());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mLookupUri == null ? 0 : mLookupUri.hashCode();
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mRawContactId);
- dest.writeLong(mContactId);
- dest.writeParcelable(mLookupUri, flags);
- dest.writeString(mLookupKey);
- dest.writeString(mDisplayName);
- dest.writeParcelable(mPhotoUri, flags);
- dest.writeLong(mPhotoId);
- }
-
- private Member(Parcel in) {
- mRawContactId = in.readLong();
- mContactId = in.readLong();
- mLookupUri = in.readParcelable(getClass().getClassLoader());
- mLookupKey = in.readString();
- mDisplayName = in.readString();
- mPhotoUri = in.readParcelable(getClass().getClassLoader());
- mPhotoId = in.readLong();
- }
-
- public static final Parcelable.Creator<Member> CREATOR = new Parcelable.Creator<Member>() {
- @Override
- public Member createFromParcel(Parcel in) {
- return new Member(in);
- }
-
- @Override
- public Member[] newArray(int size) {
- return new Member[size];
- }
- };
- }
-
- /**
* This adapter displays a list of members for the current group being edited.
*/
private final class MemberListAdapter extends BaseAdapter {
diff --git a/src/com/android/contacts/group/GroupMembersListFragment.java b/src/com/android/contacts/group/GroupMembersListFragment.java
index b896a4e..018f9f8 100644
--- a/src/com/android/contacts/group/GroupMembersListFragment.java
+++ b/src/com/android/contacts/group/GroupMembersListFragment.java
@@ -15,38 +15,135 @@
*/
package com.android.contacts.group;
-import android.content.Context;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
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.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.list.MultiSelectContactsListFragment;
/** Displays the members of a group. */
public class GroupMembersListFragment extends MultiSelectContactsListFragment {
+ private static final String TAG = "GroupMembers";
+
+ private static final String KEY_GROUP_URI = "groupUri";
private static final String KEY_GROUP_METADATA = "groupMetadata";
- private static final String ARG_GROUP_METADATA = "groupMetadata";
+ 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 {
+ /** Invoked after group metadata for the passed in group URI has loaded. */
+ void onGroupMetadataLoaded(GroupMetadata groupMetadata);
+
+ /** Invoked if group metadata can't be loaded for the passed in group URI. */
+ void onGroupMetadataLoadFailed();
+
/** Invoked when a group member in the list is clicked. */
void onGroupMemberListItemClicked(Uri contactLookupUri);
}
+ /** Step 1 of loading group metadata. */
+ private final LoaderCallbacks<Cursor> mGroupMetadataCallbacks = new LoaderCallbacks<Cursor>() {
+
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ return new GroupMetaDataLoader(getActivity(), mGroupUri);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ if (cursor == null || cursor.isClosed() || !cursor.moveToNext()) {
+ Log.e(TAG, "Failed to load group metadata for " + mGroupUri);
+ if (mListener != null) {
+ mListener.onGroupMetadataLoadFailed();
+ }
+ return;
+ }
+ // TODO(wjang): how should we handle deleted groups
+ mGroupMetadata = new GroupMetadata();
+ mGroupMetadata.uri = mGroupUri;
+ mGroupMetadata.accountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ mGroupMetadata.accountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ mGroupMetadata.dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
+ mGroupMetadata.groupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
+ mGroupMetadata.groupName = cursor.getString(GroupMetaDataLoader.TITLE);
+ mGroupMetadata.readOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
+
+ final AccountTypeManager accountTypeManager =
+ AccountTypeManager.getInstance(getActivity());
+ final AccountType accountType = accountTypeManager.getAccountType(
+ 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());
+
+ // TODO(wjang): modify GroupListLoader to accept this selection criteria more naturally
+ groupListLoader.setSelection(groupListLoader.getSelection()
+ + " 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();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {}
+ };
+
+ private Uri mGroupUri;
+
private GroupMembersListListener mListener;
private GroupMetadata mGroupMetadata;
- public static GroupMembersListFragment newInstance(GroupMetadata groupMetadata) {
+ public static GroupMembersListFragment newInstance(Uri groupUri) {
final Bundle args = new Bundle();
- args.putParcelable(ARG_GROUP_METADATA, groupMetadata);
+ args.putParcelable(ARG_GROUP_URI, groupUri);
final GroupMembersListFragment fragment = new GroupMembersListFragment();
fragment.setArguments(args);
@@ -63,53 +160,44 @@
setQuickContactEnabled(false);
}
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- try {
- mListener = (GroupMembersListListener) getActivity();
- } catch (ClassCastException e) {
- throw new ClassCastException(getActivity() + " must implement " +
- GroupMembersListListener.class.getSimpleName());
- }
+ public void setListener(GroupMembersListListener listener) {
+ mListener = listener;
}
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
if (savedState == null) {
- mGroupMetadata = getArguments().getParcelable(ARG_GROUP_METADATA);
+ mGroupUri = getArguments().getParcelable(ARG_GROUP_URI);
} else {
+ mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
}
-
- // Don't attach the multi select check box listener if we can't edit the group
- if (mGroupMetadata.editable) {
- try {
- setCheckBoxListListener((OnCheckBoxListActionListener) getActivity());
- } catch (ClassCastException e) {
- throw new ClassCastException(getActivity() + " must implement " +
- OnCheckBoxListActionListener.class.getSimpleName());
- }
- }
}
@Override
- public View onCreateView (LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View view = super.onCreateView(inflater, container, savedInstanceState);
- bindMembersCount(view);
- return view;
+ protected void startLoading() {
+ if (mGroupMetadata == null || !mGroupMetadata.isValid()) {
+ getLoaderManager().restartLoader(LOADER_GROUP_METADATA, null, mGroupMetadataCallbacks);
+ } else {
+ onGroupMetadataLoaded();
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_GROUP_URI, mGroupUri);
outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
}
- private void bindMembersCount(View view) {
- final View accountFilterContainer = view.findViewById(
+ private void onGroupMetadataLoaded() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Loaded " + mGroupMetadata);
+
+ maybeAttachCheckBoxListener();
+
+ // Bind the members count
+ final View accountFilterContainer = getView().findViewById(
R.id.account_filter_header_container);
if (mGroupMetadata.memberCount >= 0) {
accountFilterContainer.setVisibility(View.VISIBLE);
@@ -122,12 +210,30 @@
} else {
accountFilterContainer.setVisibility(View.GONE);
}
+
+ if (mListener != null) {
+ mListener.onGroupMetadataLoaded(mGroupMetadata);
+ }
+
+ // 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) {
+ try {
+ setCheckBoxListListener((OnCheckBoxListActionListener) getActivity());
+ } catch (ClassCastException e) {
+ throw new ClassCastException(getActivity() + " must implement " +
+ OnCheckBoxListActionListener.class.getSimpleName());
+ }
+ }
}
@Override
protected GroupMembersListAdapter createListAdapter() {
final GroupMembersListAdapter adapter = new GroupMembersListAdapter(getContext());
- adapter.setSectionHeaderDisplayEnabled(true);
adapter.setDisplayPhotos(true);
return adapter;
}
@@ -140,7 +246,9 @@
@Override
protected void configureAdapter() {
super.configureAdapter();
- getAdapter().setGroupId(mGroupMetadata.groupId);
+ if (mGroupMetadata != null) {
+ getAdapter().setGroupId(mGroupMetadata.groupId);
+ }
}
@Override
diff --git a/src/com/android/contacts/group/GroupMetadata.java b/src/com/android/contacts/group/GroupMetadata.java
index 788c1d4..fcf5dc2 100644
--- a/src/com/android/contacts/group/GroupMetadata.java
+++ b/src/com/android/contacts/group/GroupMetadata.java
@@ -18,6 +18,9 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.contacts.common.model.account.AccountWithDataSet;
/** Meta data for a contact group. */
// TODO(wjang): consolidate with com.android.contacts.common.GroupMetaData;
@@ -39,7 +42,7 @@
public String accountName;
public String accountType;
public String dataSet;
- public long groupId;
+ public long groupId = -1;
public String groupName;
public boolean readOnly;
public boolean editable;
@@ -48,7 +51,7 @@
public GroupMetadata() {
}
- public GroupMetadata(Parcel source) {
+ private GroupMetadata(Parcel source) {
readFromParcel(source);
}
@@ -77,6 +80,25 @@
dest.writeInt(memberCount);
}
+ /** Whether all metadata fields are set. */
+ public boolean isValid() {
+ return uri != null
+ && !TextUtils.isEmpty(accountName)
+ && !TextUtils.isEmpty(groupName)
+ && groupId > 0
+ && memberCount >= 0;
+ }
+
+ public AccountWithDataSet createAccountWithDataSet() {
+ return new AccountWithDataSet(accountName, accountType, dataSet);
+ }
+
+ public void setGroupAccountMetadata(AccountWithDataSet account) {
+ accountName = account.name;
+ accountType = account.type;
+ dataSet = account.dataSet;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -93,6 +115,7 @@
" readOnly=" + readOnly +
" editable=" + editable +
" memberCount=" + memberCount +
+ " isValid=" + isValid() +
"]";
}
}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupNameEditDialogFragment.java b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
new file mode 100644
index 0000000..f7ee6bf
--- /dev/null
+++ b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
@@ -0,0 +1,165 @@
+/*
+ * 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, softwareateCre
+ * 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.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.widget.Button;
+import android.widget.EditText;
+
+import com.android.contacts.R;
+
+/**
+ * Edits the name of a group.
+ */
+public final class GroupNameEditDialogFragment extends DialogFragment {
+
+ private static final String KEY_IS_INSERT = "isInsert";
+ private static final String KEY_GROUP_NAME = "groupName";
+
+ private static final String ARG_IS_INSERT = "isInsert";
+ private static final String ARG_GROUP_NAME = "groupName";
+
+ /** Callbacks for hosts of the {@link GroupNameEditDialogFragment}. */
+ public interface Listener {
+ void onGroupNameEdit(String groupName);
+ void onGroupNameEditCancelled();
+ }
+
+ private boolean mIsInsert;
+ private String mGroupName;
+ private EditText mGroupNameEditText;
+
+ public static void showInsertDialog(FragmentManager fragmentManager, String tag) {
+ showDialog(fragmentManager, tag, /* isInsert */ true, /* groupName */ null);
+ }
+
+ public static void showUpdateDialog(FragmentManager fragmentManager,
+ String tag, String groupName) {
+ showDialog(fragmentManager, tag, /* isInsert */ false, groupName);
+ }
+
+ private static void showDialog(FragmentManager fragmentManager,
+ String tag, boolean isInsert, String groupName) {
+ final Bundle args = new Bundle();
+ args.putBoolean(ARG_IS_INSERT, isInsert);
+ args.putString(ARG_GROUP_NAME, groupName);
+
+ final GroupNameEditDialogFragment dialog = new GroupNameEditDialogFragment();
+ dialog.setArguments(args);
+ dialog.show(fragmentManager, tag);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState == null) {
+ final Bundle args = getArguments();
+ mIsInsert = args.getBoolean(KEY_IS_INSERT);
+ mGroupName = args.getString(KEY_GROUP_NAME);
+ } else {
+ mIsInsert = savedInstanceState.getBoolean(ARG_IS_INSERT);
+ mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Build a dialog with two buttons and a view of a single EditText input field
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(mIsInsert ? R.string.insert_group_dialog_title
+ : R.string.update_group_dialog_title)
+ .setView(R.layout.group_name_edit_dialog)
+ .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ getListener().onGroupNameEditCancelled();
+ dismiss();
+ }
+ })
+ .setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ getListener().onGroupNameEdit(getGroupName());
+ }
+ });
+
+ // Disable the create button when the name is empty
+ final AlertDialog alertDialog = builder.create();
+ alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ mGroupNameEditText = (EditText) alertDialog.findViewById(android.R.id.text1);
+ if (!TextUtils.isEmpty(mGroupName)) {
+ mGroupNameEditText.setText(mGroupName);
+ mGroupNameEditText.setSelection(mGroupName.length());
+ }
+
+ final Button createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ createButton.setEnabled(!TextUtils.isEmpty(getGroupName()));
+ mGroupNameEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ createButton.setEnabled(!TextUtils.isEmpty(s));
+ }
+ });
+ }
+ });
+ return alertDialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ getListener().onGroupNameEditCancelled();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_IS_INSERT, mIsInsert);
+ outState.putString(KEY_GROUP_NAME, getGroupName());
+ }
+
+ private Listener getListener() {
+ if (!(getActivity() instanceof Listener)) {
+ throw new ClassCastException(getActivity() + " must implement " +
+ Listener.class.getName());
+ }
+ return (Listener) getActivity();
+ }
+
+ private String getGroupName() {
+ return mGroupNameEditText == null || mGroupNameEditText.getText() == null
+ ? null : mGroupNameEditText.getText().toString();
+ }
+}
diff --git a/src/com/android/contacts/group/Member.java b/src/com/android/contacts/group/Member.java
new file mode 100644
index 0000000..f8c56fd
--- /dev/null
+++ b/src/com/android/contacts/group/Member.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 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
+ */
+
+package com.android.contacts.group;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Contacts;
+
+import com.google.common.base.Objects;
+
+/** A member of the group currently being displayed to the user. */
+public class Member implements Parcelable {
+
+ public static final Parcelable.Creator<Member> CREATOR = new Parcelable.Creator<Member>() {
+ @Override
+ public Member createFromParcel(Parcel in) {
+ return new Member(in);
+ }
+
+ @Override
+ public Member[] newArray(int size) {
+ return new Member[size];
+ }
+ };
+
+ // TODO: Switch to just dealing with raw contact IDs everywhere if possible
+ private final long mRawContactId;
+ private final long mContactId;
+ private final Uri mLookupUri;
+ private final String mDisplayName;
+ private final Uri mPhotoUri;
+ private final String mLookupKey;
+ private final long mPhotoId;
+
+ public Member(long rawContactId, String lookupKey, long contactId, String displayName,
+ String photoUri, long photoId) {
+ mRawContactId = rawContactId;
+ mContactId = contactId;
+ mLookupKey = lookupKey;
+ mLookupUri = Contacts.getLookupUri(contactId, lookupKey);
+ mDisplayName = displayName;
+ mPhotoUri = (photoUri != null) ? Uri.parse(photoUri) : null;
+ mPhotoId = photoId;
+ }
+
+ public long getRawContactId() {
+ return mRawContactId;
+ }
+
+ public long getContactId() {
+ return mContactId;
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ public String getLookupKey() {
+ return mLookupKey;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public Uri getPhotoUri() {
+ return mPhotoUri;
+ }
+
+ public long getPhotoId() {
+ return mPhotoId;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof Member) {
+ Member otherMember = (Member) object;
+ return Objects.equal(mLookupUri, otherMember.getLookupUri());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mLookupUri == null ? 0 : mLookupUri.hashCode();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mRawContactId);
+ dest.writeLong(mContactId);
+ dest.writeParcelable(mLookupUri, flags);
+ dest.writeString(mLookupKey);
+ dest.writeString(mDisplayName);
+ dest.writeParcelable(mPhotoUri, flags);
+ dest.writeLong(mPhotoId);
+ }
+
+ private Member(Parcel in) {
+ mRawContactId = in.readLong();
+ mContactId = in.readLong();
+ mLookupUri = in.readParcelable(getClass().getClassLoader());
+ mLookupKey = in.readString();
+ mDisplayName = in.readString();
+ mPhotoUri = in.readParcelable(getClass().getClassLoader());
+ mPhotoId = in.readLong();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index eb80ac4..efcb79f 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -118,9 +118,9 @@
mContentResolver = resolver;
}
- public void updateExistingMembersList(List<GroupEditorFragment.Member> list) {
+ public void updateExistingMembersList(List<Member> list) {
mExistingMemberContactIds.clear();
- for (GroupEditorFragment.Member member : list) {
+ for (Member member : list) {
mExistingMemberContactIds.add(member.getContactId());
}
}
diff --git a/src/com/android/contacts/widget/NoSwipeViewPager.java b/src/com/android/contacts/widget/NoSwipeViewPager.java
deleted file mode 100644
index b24df39..0000000
--- a/src/com/android/contacts/widget/NoSwipeViewPager.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.widget;
-
-import android.content.Context;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-/**
- * ViewPager with swipe disabled.
- */
-public class NoSwipeViewPager extends ViewPager {
-
- public NoSwipeViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return false;
- }
-}
\ No newline at end of file