Initial cut of "Join contact" functionality in the UI
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index b8940fa..2b3af38 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
-import android.app.SearchManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -31,7 +30,6 @@
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
@@ -42,15 +40,12 @@
import android.provider.Contacts.Intents;
import android.provider.Contacts.People;
import android.provider.Contacts.Phones;
-import android.provider.Contacts.Presence;
-import android.provider.Contacts.Intents.Insert;
import android.provider.Contacts.Intents.UI;
-import android.provider.ContactsContract;
import android.provider.ContactsContract.Aggregates;
import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Postal;
-import android.provider.ContactsContract.Data;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -110,6 +105,22 @@
private static final int SUBACTIVITY_NEW_CONTACT = 1;
private static final int SUBACTIVITY_VIEW_CONTACT = 2;
+ /**
+ * The action for the join contact activity.
+ *
+ * TODO: move to {@link Contacts}.
+ */
+ public static final String JOIN_AGGREGATE =
+ "com.android.contacts.action.JOIN_AGGREGATE";
+
+ /**
+ * Used with {@link #JOIN_AGGREGATE} to give it the target for aggregation.
+ * <p>
+ * Type: LONG
+ */
+ public static final String EXTRA_AGGREGATE_ID =
+ "com.android.contacts.action.AGGREGATE_ID";
+
/** Mask for picker mode */
static final int MODE_MASK_PICKER = 0x80000000;
/** Mask for no presence mode */
@@ -120,6 +131,8 @@
static final int MODE_MASK_CREATE_NEW = 0x10000000;
/** Mask for showing photos in the list */
static final int MODE_MASK_SHOW_PHOTOS = 0x08000000;
+ /** Mask for hiding additional information e.g. primary phone number in the list */
+ static final int MODE_MASK_NO_DATA = 0x04000000;
/** Unknown mode */
static final int MODE_UNKNOWN = 0;
@@ -151,6 +164,10 @@
// /** Run a search query in PICK mode, but that still launches to VIEW */
// static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER;
+ /** Show join suggestions followed by an A-Z list */
+ static final int MODE_JOIN_AGGREGATE = 70 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE
+ | MODE_MASK_NO_DATA;
+
static final int DEFAULT_MODE = MODE_ALL_CONTACTS;
/**
@@ -278,6 +295,8 @@
*/
private int mQueryPersonIdIndex;
+ private long mQueryAggregateId;
+
/**
* Used to keep track of the scroll state of the list.
*/
@@ -435,6 +454,17 @@
return;
} */
+ if (JOIN_AGGREGATE.equals(action)) {
+ mMode = MODE_JOIN_AGGREGATE;
+ mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
+ if (mQueryAggregateId == -1) {
+ Log.e(TAG, "Intent " + action + " is missing required extra: "
+ + EXTRA_AGGREGATE_ID);
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
if (mMode == MODE_UNKNOWN) {
mMode = DEFAULT_MODE;
}
@@ -1008,7 +1038,12 @@
Intent intent = new Intent(Intent.ACTION_VIEW,
ContentUris.withAppendedId(Aggregates.CONTENT_URI, id));
startActivityForResult(intent, SUBACTIVITY_VIEW_CONTACT);
- } /*else if (mMode == MODE_QUERY_PICK_TO_VIEW) {
+ } else if (mMode == MODE_JOIN_AGGREGATE) {
+ Uri uri = ContentUris.withAppendedId(Aggregates.CONTENT_URI, id);
+ returnPickerResult(null, uri);
+ }
+
+ /*else if (mMode == MODE_QUERY_PICK_TO_VIEW) {
// Started with query that should launch to view contact
Cursor c = (Cursor) mAdapter.getItem(position);
long personId = c.getLong(mQueryPersonIdIndex);
@@ -1202,6 +1237,14 @@
mQueryHandler.startQuery(QUERY_TOKEN, null, Postal.CONTENT_URI,
POSTALS_PROJECTION, null, null, getSortOrder(POSTALS_PROJECTION));
break;
+
+ case MODE_JOIN_AGGREGATE:
+ mQueryHandler.startQuery(QUERY_TOKEN, null,
+ Aggregates.CONTENT_URI, AGGREGATES_PROJECTION,
+ Aggregates._ID + " != " + mQueryAggregateId, null,
+ getSortOrder(AGGREGATES_PROJECTION));
+
+ break;
}
}
@@ -1447,6 +1490,7 @@
private CharSequence mUnknownNameText;
private CharSequence[] mLocalizedLabels;
private boolean mDisplayPhotos = false;
+ private boolean mDisplayAdditionalData = true;
private SparseArray<SoftReference<Bitmap>> mBitmapCache = null;
private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
private boolean mDisplaySectionHeaders = true;
@@ -1475,6 +1519,18 @@
break;
}
+ // Do not display the second line of text if in a specific SEARCH query mode, usually for
+ // matching a specific E-mail or phone number. Any contact details
+ // shown would be identical, and columns might not even be present
+ // in the returned cursor.
+ if (mQueryMode != QUERY_MODE_NONE) {
+ mDisplayAdditionalData = false;
+ }
+
+ if ((mMode & MODE_MASK_NO_DATA) == MODE_MASK_NO_DATA) {
+ mDisplayAdditionalData = false;
+ }
+
if ((mMode & MODE_MASK_SHOW_PHOTOS) == MODE_MASK_SHOW_PHOTOS) {
mDisplayPhotos = true;
setViewResource(R.layout.contacts_list_item_photo);
@@ -1635,11 +1691,7 @@
cache.nameView.setText(mUnknownNameText);
}
- // Bail out early if using a specific SEARCH query mode, usually for
- // matching a specific E-mail or phone number. Any contact details
- // shown would be identical, and columns might not even be present
- // in the returned cursor.
- if (mQueryMode != QUERY_MODE_NONE) {
+ if (!mDisplayAdditionalData) {
cache.dataView.setVisibility(View.GONE);
cache.labelView.setVisibility(View.GONE);
cache.presenceView.setVisibility(View.GONE);
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 451caa8..5fe9fb8 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -58,6 +58,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Aggregates;
import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.CommonDataKinds;
@@ -94,15 +95,13 @@
private static final int DIALOG_CONFIRM_DELETE = 1;
+ private static final int REQUEST_JOIN_AGGREGATE = 1;
+
public static final int MENU_ITEM_DELETE = 1;
public static final int MENU_ITEM_MAKE_DEFAULT = 2;
public static final int MENU_ITEM_SHOW_BARCODE = 3;
public static final int MENU_ITEM_SPLIT_AGGREGATE = 4;
-
- private static final String[] AGGREGATION_EXCEPTIONS_PROJECTION =
- new String[] { AggregationExceptions._ID};
-
- private static final int AGGREGATION_EXCEPTIONS_COL_ID = 0;
+ public static final int MENU_ITEM_JOIN_AGGREGATE = 5;
private Uri mUri;
private Uri mAggDataUri;
@@ -307,6 +306,8 @@
.setIcon(android.R.drawable.ic_menu_delete);
menu.add(0, MENU_ITEM_SPLIT_AGGREGATE, 0, R.string.menu_splitAggregate)
.setIcon(android.R.drawable.ic_menu_share);
+ menu.add(0, MENU_ITEM_JOIN_AGGREGATE, 0, R.string.menu_joinAggregate)
+ .setIcon(android.R.drawable.ic_menu_add);
return true;
}
@@ -387,6 +388,11 @@
return true;
}
+ case MENU_ITEM_JOIN_AGGREGATE: {
+ showJoinAggregateActivity();
+ return true;
+ }
+
// TODO(emillar) Bring this back.
/*case MENU_ITEM_SHOW_BARCODE:
if (mCursor.moveToFirst()) {
@@ -502,18 +508,55 @@
}
/**
- * Given an ID of a constituent contact, splits it off into a separate aggregate.
+ * Shows a list of aggregates that can be joined into the currently viewed aggregate.
*/
- protected void splitContact(long contactToSplit) {
+ public void showJoinAggregateActivity() {
+ Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
+ intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, ContentUris.parseId(mUri));
+ startActivityForResult(intent, REQUEST_JOIN_AGGREGATE);
+ }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_JOIN_AGGREGATE && resultCode == RESULT_OK && intent != null) {
+ final long aggregateId = ContentUris.parseId(intent.getData());
+ joinAggregate(aggregateId);
+ }
+ }
+
+ private void splitContact(long contactId) {
+ setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_OUT);
+ Toast.makeText(this, R.string.contactSplitMessage, Toast.LENGTH_SHORT);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void joinAggregate(final long aggregateId) {
+ Cursor c = mResolver.query(Contacts.CONTENT_URI, new String[] {Contacts._ID},
+ Contacts.AGGREGATE_ID + "=" + aggregateId, null, null);
+
+ try {
+ while(c.moveToNext()) {
+ long contactId = c.getLong(0);
+ setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_IN);
+ }
+ } finally {
+ c.close();
+ }
+
+ Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_SHORT);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Given a contact ID sets an aggregation exception to either join the contact with the
+ * current aggregate or split off.
+ */
+ protected void setAggregationException(long contactId, int exceptionType) {
ContentValues values = new ContentValues(3);
values.put(AggregationExceptions.AGGREGATE_ID, ContentUris.parseId(mUri));
- values.put(AggregationExceptions.CONTACT_ID, contactToSplit);
- values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_OUT);
-
+ values.put(AggregationExceptions.CONTACT_ID, contactId);
+ values.put(AggregationExceptions.TYPE, exceptionType);
mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
-
- mAdapter.notifyDataSetChanged();
}
private ViewEntry getViewEntryForMenuItem(MenuItem item) {