summaryrefslogtreecommitdiffstats
path: root/src/com/android/contacts/editor/ContactEditorBaseFragment.java
diff options
context:
space:
mode:
authorWalter Jang <wjang@google.com>2015-02-05 13:41:43 -0800
committerWalter Jang <wjang@google.com>2015-02-05 15:23:13 -0800
commit3e9a62449b8ca3a38b1d51019c6ea13972263bc2 (patch)
treeb5d4ea5eee637909dd9f0f5b0bfe341c1935aa2e /src/com/android/contacts/editor/ContactEditorBaseFragment.java
parent1eb21f12372b31794ef5a567013c1d2d98081120 (diff)
downloadpackages_apps_Contacts-3e9a62449b8ca3a38b1d51019c6ea13972263bc2.tar.gz
packages_apps_Contacts-3e9a62449b8ca3a38b1d51019c6ea13972263bc2.tar.bz2
packages_apps_Contacts-3e9a62449b8ca3a38b1d51019c6ea13972263bc2.zip
Move suggest joins popup & activiy to BaseEditContactFragment
Bug 19124091 Change-Id: Ic5a6b8b6359dc9691cfcac2348ba895c542d05d9
Diffstat (limited to 'src/com/android/contacts/editor/ContactEditorBaseFragment.java')
-rw-r--r--src/com/android/contacts/editor/ContactEditorBaseFragment.java398
1 files changed, 393 insertions, 5 deletions
diff --git a/src/com/android/contacts/editor/ContactEditorBaseFragment.java b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
index e1a841a39..d4f5a5a41 100644
--- a/src/com/android/contacts/editor/ContactEditorBaseFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
@@ -23,6 +23,7 @@ import com.android.contacts.ContactSaveService;
import com.android.contacts.GroupMetaDataLoader;
import com.android.contacts.R;
import com.android.contacts.activities.ContactEditorAccountsChangedActivity;
+import com.android.contacts.activities.ContactEditorActivity;
import com.android.contacts.activities.ContactEditorBaseActivity;
import com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor;
import com.android.contacts.common.model.AccountTypeManager;
@@ -35,9 +36,12 @@ import com.android.contacts.common.model.RawContactModifier;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
+import com.android.contacts.list.UiIntentActions;
import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contacts.util.HelpUtils;
import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.util.UiClosables;
import android.accounts.Account;
import android.app.Activity;
@@ -66,11 +70,16 @@ import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
import android.widget.Toast;
import java.util.ArrayList;
@@ -80,7 +89,8 @@ import java.util.List;
* Base Fragment for contact editors.
*/
abstract public class ContactEditorBaseFragment extends Fragment implements
- ContactEditor, SplitContactConfirmationDialogFragment.Listener {
+ ContactEditor, SplitContactConfirmationDialogFragment.Listener,
+ AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener {
protected static final String TAG = "ContactEditor";
@@ -115,6 +125,14 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
private static final String KEY_ENABLED = "enabled";
+ // Aggregation PopupWindow
+ private static final String KEY_AGGREGATION_SUGGESTIONS_RAW_CONTACT_ID =
+ "aggregationSuggestionsRawContactId";
+
+ // Join Activity
+ private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
+ private static final String KEY_CONTACT_WRITABLE_FOR_JOIN = "contactwritableforjoin";
+
protected static final int REQUEST_CODE_JOIN = 0;
protected static final int REQUEST_CODE_ACCOUNTS_CHANGED = 1;
protected static final int REQUEST_CODE_PICK_RINGTONE = 2;
@@ -189,6 +207,52 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
void onDeleteRequested(Uri contactUri);
}
+ /**
+ * Adapter for aggregation suggestions displayed in a PopupWindow when
+ * editor fields change.
+ */
+ protected static final class AggregationSuggestionAdapter extends BaseAdapter {
+ private final LayoutInflater mLayoutInflater;
+ private final boolean mSetNewContact;
+ private final AggregationSuggestionView.Listener mListener;
+ private final List<AggregationSuggestionEngine.Suggestion> mSuggestions;
+
+ public AggregationSuggestionAdapter(Activity activity, boolean setNewContact,
+ AggregationSuggestionView.Listener listener, List<Suggestion> suggestions) {
+ mLayoutInflater = activity.getLayoutInflater();
+ mSetNewContact = setNewContact;
+ mListener = listener;
+ mSuggestions = suggestions;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final Suggestion suggestion = (Suggestion) getItem(position);
+ final AggregationSuggestionView suggestionView =
+ (AggregationSuggestionView) mLayoutInflater.inflate(
+ R.layout.aggregation_suggestions_item, null);
+ suggestionView.setNewContact(mSetNewContact);
+ suggestionView.setListener(mListener);
+ suggestionView.bindSuggestion(suggestion);
+ return suggestionView;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mSuggestions.get(position);
+ }
+
+ @Override
+ public int getCount() {
+ return mSuggestions.size();
+ }
+ }
+
protected Context mContext;
protected Listener mListener;
@@ -197,6 +261,7 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
//
protected LinearLayout mContent;
protected View mAggregationSuggestionView;
+ protected ListPopupWindow mAggregationSuggestionPopup;
//
// Parameters passed in on {@link #load}
@@ -214,6 +279,7 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
protected ContactEditorUtils mEditorUtils;
protected RawContactDeltaComparator mComparator;
protected ViewIdGenerator mViewIdGenerator;
+ private AggregationSuggestionEngine mAggregationSuggestionEngine;
//
// Loaded data
@@ -248,6 +314,13 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// Whether editor views and options menu items should be enabled
private boolean mEnabled = true;
+ // Aggregation PopupWindow
+ private long mAggregationSuggestionsRawContactId;
+
+ // Join Activity
+ private long mContactIdForJoin;
+ private boolean mContactWritableForJoin;
+
//
// Editor state for {@link ContactEditorView}.
// (Not saved/restored on rotates)
@@ -372,6 +445,14 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
mCustomRingtone = savedState.getString(KEY_CUSTOM_RINGTONE);
mEnabled = savedState.getBoolean(KEY_ENABLED);
+
+ // Aggregation PopupWindow
+ mAggregationSuggestionsRawContactId = savedState.getLong(
+ KEY_AGGREGATION_SUGGESTIONS_RAW_CONTACT_ID);
+
+ // Join Activity
+ mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
+ mContactWritableForJoin = savedState.getBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN);
}
// mState can still be null because it may not have have finished loading before
@@ -481,12 +562,49 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
outState.putBoolean(KEY_ENABLED, mEnabled);
+ // Aggregation PopupWindow
+ outState.putLong(KEY_AGGREGATION_SUGGESTIONS_RAW_CONTACT_ID,
+ mAggregationSuggestionsRawContactId);
+
+ // Join Activity
+ outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
+ outState.putBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN, mContactWritableForJoin);
+
super.onSaveInstanceState(outState);
}
@Override
+ public void onStop() {
+ super.onStop();
+
+ UiClosables.closeQuietly(mAggregationSuggestionPopup);
+
+ // If anything was left unsaved, save it now but keep the editor open.
+ if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
+ save(SaveMode.RELOAD);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mAggregationSuggestionEngine != null) {
+ mAggregationSuggestionEngine.quit();
+ }
+ }
+
+ @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
+ case REQUEST_CODE_JOIN: {
+ // Ignore failed requests
+ if (resultCode != Activity.RESULT_OK) return;
+ if (data != null) {
+ final long contactId = ContentUris.parseId(data.getData());
+ joinAggregate(contactId);
+ }
+ break;
+ }
case REQUEST_CODE_ACCOUNTS_CHANGED: {
// Bail if the account selector was not successful.
if (resultCode != Activity.RESULT_OK) {
@@ -643,9 +761,17 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
@Override
public void onSplitContactConfirmed() {
- // When this Fragment is closed we don't want it to auto-save
- mStatus = Status.CLOSING;
- if (mListener != null) mListener.onReverted();
+ if (mState.isEmpty()) {
+ // This may happen when this Fragment is recreated by the system during users
+ // confirming the split action (and thus this method is called just before onCreate()),
+ // for example.
+ Log.e(TAG, "mState became null during the user's confirming split action. " +
+ "Cannot perform the save action.");
+ return;
+ }
+
+ mState.markRawContactsForSplitting();
+ save(SaveMode.SPLIT);;
}
private boolean doSplitContactAction() {
@@ -947,7 +1073,7 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
}
/**
- * Bind editors using {@link #mState} and other members intialized from the loaded (or new)
+ * Bind editors using {@link #mState} and other members initialized from the loaded (or new)
* Contact.
*/
abstract void bindEditors();
@@ -1008,6 +1134,268 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
}
}
+ @Override
+ public void onJoinCompleted(Uri uri) {
+ onSaveCompleted(false, SaveMode.RELOAD, uri != null, uri);
+ }
+
+ @Override
+ public void onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded,
+ Uri contactLookupUri) {
+ if (hadChanges) {
+ if (saveSucceeded) {
+ if (saveMode != SaveMode.JOIN) {
+ Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+ }
+ } else {
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ }
+ }
+ switch (saveMode) {
+ case SaveMode.CLOSE:
+ case SaveMode.HOME:
+ final Intent resultIntent;
+ if (saveSucceeded && contactLookupUri != null) {
+ final Uri lookupUri = maybeConvertToLegacyLookupUri(
+ mContext, contactLookupUri, mLookupUri);
+ resultIntent = composeQuickContactsIntent(mContext, lookupUri);
+ } else {
+ resultIntent = null;
+ }
+ // It is already saved, so prevent that it is saved again
+ mStatus = Status.CLOSING;
+ if (mListener != null) mListener.onSaveFinished(resultIntent);
+ break;
+
+ case SaveMode.RELOAD:
+ case SaveMode.JOIN:
+ if (saveSucceeded && contactLookupUri != null) {
+ // If it was a JOIN, we are now ready to bring up the join activity.
+ if (saveMode == SaveMode.JOIN && hasValidState()) {
+ showJoinAggregateActivity(contactLookupUri);
+ }
+
+ // If this was in INSERT, we are changing into an EDIT now.
+ // If it already was an EDIT, we are changing to the new Uri now
+ mState = new RawContactDeltaList();
+ load(Intent.ACTION_EDIT, contactLookupUri, null);
+ mStatus = Status.LOADING;
+ getLoaderManager().restartLoader(LOADER_DATA, null, mDataLoaderListener);
+ }
+ break;
+
+ case SaveMode.SPLIT:
+ mStatus = Status.CLOSING;
+ if (mListener != null) {
+ mListener.onContactSplit(contactLookupUri);
+ } else {
+ Log.d(TAG, "No listener registered, can not call onSplitFinished");
+ }
+ break;
+ }
+ }
+
+ /**
+ * Shows a list of aggregates that can be joined into the currently viewed aggregate.
+ *
+ * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
+ */
+ private void showJoinAggregateActivity(Uri contactLookupUri) {
+ if (contactLookupUri == null || !isAdded()) {
+ return;
+ }
+
+ mContactIdForJoin = ContentUris.parseId(contactLookupUri);
+ mContactWritableForJoin = isContactWritable();
+ final Intent intent = new Intent(UiIntentActions.PICK_JOIN_CONTACT_ACTION);
+ intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, mContactIdForJoin);
+ startActivityForResult(intent, REQUEST_CODE_JOIN);
+ }
+
+ /**
+ * Returns true if there is at least one writable raw contact in the current contact.
+ */
+ private boolean isContactWritable() {
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ int size = mState.size();
+ for (int i = 0; i < size; i++) {
+ RawContactDelta entity = mState.get(i);
+ final AccountType type = entity.getAccountType(accountTypes);
+ if (type.areContactsWritable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //
+ // Aggregation PopupWindow
+ //
+
+ /**
+ * Triggers an asynchronous search for aggregation suggestions.
+ */
+ protected void acquireAggregationSuggestions(Context context,
+ RawContactEditorView rawContactEditor) {
+ long rawContactId = rawContactEditor.getRawContactId();
+ if (mAggregationSuggestionsRawContactId != rawContactId
+ && mAggregationSuggestionView != null) {
+ mAggregationSuggestionView.setVisibility(View.GONE);
+ mAggregationSuggestionView = null;
+ mAggregationSuggestionEngine.reset();
+ }
+
+ mAggregationSuggestionsRawContactId = rawContactId;
+
+ if (mAggregationSuggestionEngine == null) {
+ mAggregationSuggestionEngine = new AggregationSuggestionEngine(context);
+ mAggregationSuggestionEngine.setListener(this);
+ mAggregationSuggestionEngine.start();
+ }
+
+ mAggregationSuggestionEngine.setContactId(getContactId());
+
+ LabeledEditorView nameEditor = rawContactEditor.getNameEditor();
+ mAggregationSuggestionEngine.onNameChange(nameEditor.getValues());
+ }
+
+ /**
+ * Returns the contact ID for the currently edited contact or 0 if the contact is new.
+ */
+ private long getContactId() {
+ for (RawContactDelta rawContact : mState) {
+ Long contactId = rawContact.getValues().getAsLong(RawContacts.CONTACT_ID);
+ if (contactId != null) {
+ return contactId;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void onAggregationSuggestionChange() {
+ final Activity activity = getActivity();
+ if ((activity != null && activity.isFinishing())
+ || !isVisible() || mState.isEmpty() || mStatus != Status.EDITING) {
+ return;
+ }
+
+ UiClosables.closeQuietly(mAggregationSuggestionPopup);
+
+ if (mAggregationSuggestionEngine.getSuggestedContactCount() == 0) {
+ return;
+ }
+
+ final RawContactEditorView rawContactView = (RawContactEditorView)
+ getRawContactEditorView(mAggregationSuggestionsRawContactId);
+ if (rawContactView == null) {
+ return; // Raw contact deleted?
+ }
+ final View anchorView = rawContactView.findViewById(R.id.anchor_view);
+ mAggregationSuggestionPopup = new ListPopupWindow(mContext, null);
+ mAggregationSuggestionPopup.setAnchorView(anchorView);
+ mAggregationSuggestionPopup.setWidth(anchorView.getWidth());
+ mAggregationSuggestionPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mAggregationSuggestionPopup.setAdapter(
+ new AggregationSuggestionAdapter(
+ getActivity(),
+ mState.size() == 1 && mState.get(0).isContactInsert(),
+ /* listener =*/ this,
+ mAggregationSuggestionEngine.getSuggestions()));
+ mAggregationSuggestionPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final AggregationSuggestionView suggestionView = (AggregationSuggestionView) view;
+ suggestionView.handleItemClickEvent();
+ UiClosables.closeQuietly(mAggregationSuggestionPopup);
+ mAggregationSuggestionPopup = null;
+ }
+ });
+ mAggregationSuggestionPopup.show();
+ }
+
+ /**
+ * Finds raw contact editor view for the given rawContactId.
+ */
+ private BaseRawContactEditorView getRawContactEditorView(long rawContactId) {
+ for (int i = 0; i < mContent.getChildCount(); i++) {
+ final View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseRawContactEditorView) {
+ final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
+ if (editor.getRawContactId() == rawContactId) {
+ return editor;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Whether the given raw contact ID matches the one used to last load aggregation
+ * suggestions.
+ */
+ protected boolean isAggregationSuggestionRawContactId(long rawContactId) {
+ return mAggregationSuggestionsRawContactId == rawContactId;
+ }
+
+ @Override
+ public void onJoinAction(long contactId, List<Long> rawContactIdList) {
+ final long rawContactIds[] = new long[rawContactIdList.size()];
+ for (int i = 0; i < rawContactIds.length; i++) {
+ rawContactIds[i] = rawContactIdList.get(i);
+ }
+ try {
+ JoinSuggestedContactDialogFragment.show(this, rawContactIds);
+ } catch (Exception ignored) {
+ // No problem - the activity is no longer available to display the dialog
+ }
+ }
+
+ /**
+ * Joins the suggested contact (specified by the id's of constituent raw
+ * contacts), save all changes, and stay in the editor.
+ */
+ protected void doJoinSuggestedContact(long[] rawContactIds) {
+ if (!hasValidState() || mStatus != Status.EDITING) {
+ return;
+ }
+
+ mState.setJoinWithRawContacts(rawContactIds);
+ save(SaveMode.RELOAD);
+ }
+
+ @Override
+ public void onEditAction(Uri contactLookupUri) {
+ SuggestionEditConfirmationDialogFragment.show(this, contactLookupUri);
+ }
+
+ /**
+ * Abandons the currently edited contact and switches to editing the suggested
+ * one, transferring all the data there
+ */
+ protected void doEditSuggestedContact(Uri contactUri) {
+ if (mListener != null) {
+ // make sure we don't save this contact when closing down
+ mStatus = Status.CLOSING;
+ mListener.onEditOtherContactRequested(
+ contactUri, mState.get(0).getContentValues());
+ }
+ }
+
+ //
+ // Join Activity
+ //
+
+ /**
+ * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
+ */
+ private void joinAggregate(final long contactId) {
+ Intent intent = ContactSaveService.createJoinContactsIntent(mContext, mContactIdForJoin,
+ contactId, mContactWritableForJoin,
+ ContactEditorActivity.class, ContactEditorActivity.ACTION_JOIN_COMPLETED);
+ mContext.startService(intent);
+ }
+
//
// Utility methods
//