Initial cut of "Split Aggregate" UI.
diff --git a/src/com/android/contacts/ContactEntryAdapter.java b/src/com/android/contacts/ContactEntryAdapter.java
index bf7bf01..aac578d 100644
--- a/src/com/android/contacts/ContactEntryAdapter.java
+++ b/src/com/android/contacts/ContactEntryAdapter.java
@@ -34,39 +34,41 @@
public static final String[] AGGREGATE_PROJECTION = new String[] {
Aggregates.DISPLAY_NAME, // 0
Aggregates.STARRED, //1
- Data._ID, // 2
- Data.PACKAGE, //3
- Data.MIMETYPE, //4
- Data.IS_PRIMARY, //5
- Data.IS_SUPER_PRIMARY, //6
- Data.DATA1, //7
- Data.DATA2, //8
- Data.DATA3, //9
- Data.DATA4, //10
- Data.DATA5, //11
- Data.DATA6, //12
- Data.DATA7, //13
- Data.DATA8, //14
- Data.DATA9, //15
- Data.DATA10, //16
+ Data._ID, //2
+ Data.CONTACT_ID, //3
+ Data.PACKAGE, //4
+ Data.MIMETYPE, //5
+ Data.IS_PRIMARY, //6
+ Data.IS_SUPER_PRIMARY, //7
+ Data.DATA1, //8
+ Data.DATA2, //9
+ Data.DATA3, //10
+ Data.DATA4, //11
+ Data.DATA5, //12
+ Data.DATA6, //13
+ Data.DATA7, //14
+ Data.DATA8, //15
+ Data.DATA9, //16
+ Data.DATA10, //17
};
public static final int AGGREGATE_DISPLAY_NAME_COLUMN = 0;
public static final int AGGREGATE_STARRED_COLUMN = 1;
public static final int DATA_ID_COLUMN = 2;
- public static final int DATA_PACKAGE_COLUMN = 3;
- public static final int DATA_MIMETYPE_COLUMN = 4;
- public static final int DATA_IS_PRIMARY_COLUMN = 5;
- public static final int DATA_IS_SUPER_PRIMARY_COLUMN = 6;
- public static final int DATA_1_COLUMN = 7;
- public static final int DATA_2_COLUMN = 8;
- public static final int DATA_3_COLUMN = 9;
- public static final int DATA_4_COLUMN = 10;
- public static final int DATA_5_COLUMN = 11;
- public static final int DATA_6_COLUMN = 12;
- public static final int DATA_7_COLUMN = 13;
- public static final int DATA_8_COLUMN = 14;
- public static final int DATA_9_COLUMN = 15;
- public static final int DATA_10_COLUMN = 16;
+ public static final int DATA_CONTACT_ID_COLUMN = 3;
+ public static final int DATA_PACKAGE_COLUMN = 4;
+ public static final int DATA_MIMETYPE_COLUMN = 5;
+ public static final int DATA_IS_PRIMARY_COLUMN = 6;
+ public static final int DATA_IS_SUPER_PRIMARY_COLUMN = 7;
+ public static final int DATA_1_COLUMN = 8;
+ public static final int DATA_2_COLUMN = 9;
+ public static final int DATA_3_COLUMN = 10;
+ public static final int DATA_4_COLUMN = 11;
+ public static final int DATA_5_COLUMN = 12;
+ public static final int DATA_6_COLUMN = 13;
+ public static final int DATA_7_COLUMN = 14;
+ public static final int DATA_8_COLUMN = 15;
+ public static final int DATA_9_COLUMN = 16;
+ public static final int DATA_10_COLUMN = 17;
protected ArrayList<ArrayList<E>> mSections;
protected LayoutInflater mInflater;
@@ -81,6 +83,7 @@
public String data;
public Uri uri;
public long id = 0;
+ public long contactId;
public int maxLines = 1;
public String mimetype;
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index f6d714a..f104781 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -106,6 +106,7 @@
public static final int MENU_DISPLAY_GROUP = 11;
private static final int SUBACTIVITY_NEW_CONTACT = 1;
+ private static final int SUBACTIVITY_VIEW_CONTACT = 2;
/** Mask for picker mode */
static final int MODE_MASK_PICKER = 0x80000000;
@@ -798,6 +799,13 @@
returnPickerResult(data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME),
data.getData());
}
+ break;
+
+ case SUBACTIVITY_VIEW_CONTACT:
+ if (resultCode == RESULT_OK) {
+ mAdapter.notifyDataSetChanged();
+ }
+ break;
}
}
@@ -971,7 +979,7 @@
if ((mMode & MODE_MASK_PICKER) == 0) {
Intent intent = new Intent(Intent.ACTION_VIEW,
ContentUris.withAppendedId(Aggregates.CONTENT_URI, id));
- startActivity(intent);
+ startActivityForResult(intent, SUBACTIVITY_VIEW_CONTACT);
} /*else if (mMode == MODE_QUERY_PICK_TO_VIEW) {
// Started with query that should launch to view contact
Cursor c = (Cursor) mAdapter.getItem(position);
diff --git a/src/com/android/contacts/SplitAggregateView.java b/src/com/android/contacts/SplitAggregateView.java
new file mode 100644
index 0000000..8d20b6c
--- /dev/null
+++ b/src/com/android/contacts/SplitAggregateView.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2006 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;
+
+import com.google.common.util.text.TextUtil;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.provider.ContactsContract.Aggregates.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A list view for constituent contacts of an aggregate. Shows the contact name, source icon
+ * and additional data such as a nickname, email address or phone number, whichever
+ * is available.
+ */
+public class SplitAggregateView extends ListView {
+
+ private static final String[] AGGREGATE_DATA_PROJECTION = new String[] {
+ Data.MIMETYPE, Data.PACKAGE, Data.CONTACT_ID, Data.DATA1, Data.DATA2, Data.IS_PRIMARY,
+ StructuredName.DISPLAY_NAME
+ };
+
+ private static final int COL_MIMETYPE = 0;
+ private static final int COL_PACKAGE = 1;
+ private static final int COL_CONTACT_ID = 2;
+ private static final int COL_DATA1 = 3;
+ private static final int COL_DATA2 = 4;
+ private static final int COL_IS_PRIMARY = 5;
+ private static final int COL_DISPLAY_NAME = 6;
+
+
+ private final Uri mAggregateUri;
+ private OnContactSelectedListener mListener;
+
+ /**
+ * Listener interface that gets the contact ID of the user-selected contact.
+ */
+ public interface OnContactSelectedListener {
+ void onContactSelected(long contactId);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SplitAggregateView(Context context, Uri aggregateUri) {
+ super(context);
+
+ mAggregateUri = aggregateUri;
+
+ final List<ContactInfo> list = loadData();
+
+ setAdapter(new SplitAggregateAdapter(context, list));
+ setOnItemClickListener(new OnItemClickListener() {
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mListener.onContactSelected(list.get(position).contactId);
+ }
+ });
+ }
+
+ /**
+ * Sets a contact selection listener.
+ */
+ public void setOnContactSelectedListener(OnContactSelectedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Contact information loaded from the content provider.
+ */
+ private static class ContactInfo implements Comparable<ContactInfo> {
+ final long contactId;
+ String packageName;
+ String name;
+ String phone;
+ String email;
+ String nickname;
+
+ public ContactInfo(long contactId) {
+ this.contactId = contactId;
+ }
+
+ public String getAdditionalData() {
+ if (nickname != null) {
+ return nickname;
+ }
+
+ if (email != null) {
+ return email;
+ }
+
+ if (phone != null) {
+ return phone;
+ }
+
+ return "";
+ }
+
+ public int compareTo(ContactInfo another) {
+ return packageName.compareTo(another.packageName);
+ }
+ }
+
+ /**
+ * Loads data from the content provider, organizes it into {@link ContactInfo} objects
+ * and returns a sorted list of {@link ContactInfo}'s.
+ */
+ private List<ContactInfo> loadData() {
+ HashMap<Long, ContactInfo> contactInfos = new HashMap<Long, ContactInfo>();
+ Uri dataUri = Uri.withAppendedPath(mAggregateUri, Data.CONTENT_DIRECTORY);
+ Cursor cursor = getContext().getContentResolver().query(dataUri,
+ AGGREGATE_DATA_PROJECTION, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ long contactId = cursor.getLong(COL_CONTACT_ID);
+ ContactInfo info = contactInfos.get(contactId);
+ if (info == null) {
+ info = new ContactInfo(contactId);
+ contactInfos.put(contactId, info);
+ info.packageName = cursor.getString(COL_PACKAGE);
+ }
+
+ String mimetype = cursor.getString(COL_MIMETYPE);
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ loadStructuredName(cursor, info);
+ } else if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ loadPhoneNumber(cursor, info);
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ loadEmail(cursor, info);
+ } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ loadNickname(cursor, info);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+
+ List<ContactInfo> list = new ArrayList<ContactInfo>(contactInfos.values());
+ Collections.sort(list);
+ return list;
+ }
+
+ private void loadStructuredName(Cursor cursor, ContactInfo info) {
+ info.name = cursor.getString(COL_DISPLAY_NAME);
+ if (info.name != null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ String firstName = cursor.getString(COL_DATA1);
+ String lastName = cursor.getString(COL_DATA2);
+ if (!TextUtil.isEmpty(firstName)) {
+ sb.append(firstName);
+ }
+ if (!TextUtil.isEmpty(firstName) && !TextUtil.isEmpty(lastName)) {
+ sb.append(" ");
+ }
+ if (!TextUtil.isEmpty(lastName)) {
+ sb.append(lastName);
+ }
+
+ if (sb.length() != 0) {
+ info.name = sb.toString();
+ }
+ }
+
+ private void loadNickname(Cursor cursor, ContactInfo info) {
+ if (info.nickname == null || cursor.getInt(COL_IS_PRIMARY) != 0) {
+ info.nickname = cursor.getString(COL_DATA2);
+ }
+ }
+
+ private void loadEmail(Cursor cursor, ContactInfo info) {
+ if (info.email == null || cursor.getInt(COL_IS_PRIMARY) != 0) {
+ info.email = cursor.getString(COL_DATA2);
+ }
+ }
+
+ private void loadPhoneNumber(Cursor cursor, ContactInfo info) {
+ if (info.phone == null || cursor.getInt(COL_IS_PRIMARY) != 0) {
+ info.phone = cursor.getString(COL_DATA2);
+ }
+ }
+
+ /**
+ * List adapter for the list of {@link ContactInfo} objects.
+ */
+ private static class SplitAggregateAdapter extends ArrayAdapter<ContactInfo> {
+
+ private static class SplitAggregateItemCache {
+ TextView name;
+ TextView additionalData;
+ ImageView sourceIcon;
+ }
+
+ private LayoutInflater mInflater;
+
+ public SplitAggregateAdapter(Context context, List<ContactInfo> sources) {
+ super(context, 0, sources);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.split_aggregate_list_item, parent, false);
+ }
+
+ SplitAggregateItemCache cache = (SplitAggregateItemCache)convertView.getTag();
+ if (cache == null) {
+ cache = new SplitAggregateItemCache();
+ cache.name = (TextView)convertView.findViewById(R.id.name);
+ cache.additionalData = (TextView)convertView.findViewById(R.id.additionalData);
+ cache.sourceIcon = (ImageView)convertView.findViewById(R.id.sourceIcon);
+ convertView.setTag(cache);
+ }
+
+ final ContactInfo info = getItem(position);
+ cache.name.setText(info.name);
+ cache.additionalData.setText(info.getAdditionalData());
+
+ Bitmap sourceBitmap = getSourceIcon(info.packageName);
+ if (sourceBitmap != null) {
+ cache.sourceIcon.setImageBitmap(sourceBitmap);
+ } else {
+ cache.sourceIcon.setImageResource(R.drawable.unknown_source);
+ }
+ return convertView;
+ }
+
+ /**
+ * Returns the icon corresponding to the source of contact information.
+ */
+ private Bitmap getSourceIcon(String packageName) {
+
+ // TODO: obtain from PackageManager
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 9dc898f..3b80da5 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -16,25 +16,20 @@
package com.android.contacts;
-import static com.android.contacts.ContactEntryAdapter.AGGREGATE_DISPLAY_NAME_COLUMN;
import static com.android.contacts.ContactEntryAdapter.AGGREGATE_PROJECTION;
import static com.android.contacts.ContactEntryAdapter.AGGREGATE_STARRED_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_PACKAGE_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_IS_PRIMARY_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_6_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_7_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_8_COLUMN;
import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_10_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.DATA_CONTACT_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
+import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
import com.android.internal.telephony.ITelephony;
import android.app.AlertDialog;
@@ -47,13 +42,13 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -63,14 +58,14 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Aggregates;
-import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Data;
-import android.provider.Im;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
+import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@@ -85,7 +80,6 @@
import android.widget.Toast;
import java.util.ArrayList;
-import java.util.List;
/**
* Displays the details of a specific contact.
@@ -103,6 +97,12 @@
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;
private Uri mUri;
private Uri mAggDataUri;
@@ -110,6 +110,11 @@
private ViewAdapter mAdapter;
private int mNumPhoneNumbers = 0;
+ /**
+ * A list of distinct contact IDs included in the current aggregate.
+ */
+ private ArrayList<Long> mContactIds = new ArrayList<Long>();
+
/* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
/* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
/* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
@@ -300,7 +305,8 @@
.setAlphabeticShortcut('e');
menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
.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);
return true;
}
@@ -317,6 +323,9 @@
} else {
menu.removeItem(MENU_ITEM_SHOW_BARCODE);
}
+
+ boolean isAggregate = mContactIds.size() > 1;
+ menu.findItem(MENU_ITEM_SPLIT_AGGREGATE).setEnabled(isAggregate);
return true;
}
@@ -373,6 +382,11 @@
return true;
}
+ case MENU_ITEM_SPLIT_AGGREGATE: {
+ showSplitAggregateDialog();
+ return true;
+ }
+
// TODO(emillar) Bring this back.
/*case MENU_ITEM_SHOW_BARCODE:
if (mCursor.moveToFirst()) {
@@ -423,32 +437,131 @@
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_MAKE_DEFAULT: {
- AdapterView.AdapterContextMenuInfo info;
- try {
- info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- } catch (ClassCastException e) {
- Log.e(TAG, "bad menuInfo", e);
- break;
+ if (makeItemDefault(item)) {
+ return true;
}
-
- ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position,
- SHOW_SEPARATORS);
-
- // Update the primary values in the data record.
- ContentValues values = new ContentValues(1);
- values.put(Data.IS_PRIMARY, 1);
- values.put(Data.IS_SUPER_PRIMARY, 1);
-
- Log.i(TAG, mUri.toString());
- getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
- values, null, null);
- dataChanged();
- return true;
+ break;
}
}
+
return super.onContextItemSelected(item);
}
+ private boolean makeItemDefault(MenuItem item) {
+ ViewEntry entry = getViewEntryForMenuItem(item);
+ if (entry == null) {
+ return false;
+ }
+
+ // Update the primary values in the data record.
+ ContentValues values = new ContentValues(2);
+ values.put(Data.IS_PRIMARY, 1);
+ values.put(Data.IS_SUPER_PRIMARY, 1);
+
+ Log.i(TAG, mUri.toString());
+ getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
+ values, null, null);
+ dataChanged();
+ return true;
+ }
+
+ /**
+ * Shows a dialog that contains a list of all constituent contacts in this aggregate.
+ * The user picks a contact to be split into its own aggregate or clicks Cancel.
+ */
+ private void showSplitAggregateDialog() {
+
+ // Wrap this dialog in a specific theme so that list items have correct text color.
+ final ContextThemeWrapper dialogContext =
+ new ContextThemeWrapper(this, android.R.style.Theme_Light);
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(dialogContext);
+ builder.setTitle(getString(R.string.splitAggregate_title));
+
+ final SplitAggregateView view = new SplitAggregateView(dialogContext, mUri);
+ builder.setView(view);
+
+ builder.setInverseBackgroundForced(true);
+ builder.setCancelable(true);
+ builder.setNegativeButton(android.R.string.cancel,
+ new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ final AlertDialog dialog = builder.create();
+
+ view.setOnContactSelectedListener(new OnContactSelectedListener() {
+ public void onContactSelected(long contactId) {
+ dialog.dismiss();
+ splitContact(contactId);
+ }
+ });
+
+ dialog.show();
+ }
+
+ /**
+ * Given an ID of a constituent contact, splits it off into a separate aggregate.
+ */
+ protected void splitContact(long contactToSplit) {
+ long contactToKeep;
+
+ // We are guaranteed to have at least two items in the mContactIds array, because
+ // otherwise the "Split" menu item would be disabled.
+ if (mContactIds.get(0) != contactToSplit) {
+ contactToKeep = mContactIds.get(0);
+ } else {
+ contactToKeep = mContactIds.get(1);
+ }
+
+ ContentValues values = new ContentValues(3);
+ values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_NEVER_MATCH);
+
+ boolean updated = false;
+
+ // First see if there is an existing exception for this pair of contacts
+ Cursor c = mResolver.query(AggregationExceptions.CONTENT_URI,
+ AGGREGATION_EXCEPTIONS_PROJECTION,
+ "(" + AggregationExceptions.CONTACT_ID1 + "=" + contactToSplit
+ + " AND " + AggregationExceptions.CONTACT_ID2 + "=" + contactToKeep
+ + ") OR (" + AggregationExceptions.CONTACT_ID1 + "=" + contactToKeep
+ + " AND " + AggregationExceptions.CONTACT_ID2 + "=" + contactToSplit + ")",
+ null, null);
+
+ try {
+ if (c.moveToFirst()) {
+ long exceptionId = c.getLong(AGGREGATION_EXCEPTIONS_COL_ID);
+ mResolver.update(ContentUris.withAppendedId(AggregationExceptions.CONTENT_URI,
+ exceptionId), values, null, null);
+ updated = true;
+ }
+ } finally {
+ c.close();
+ }
+
+ // Otherwise, insert a new exception
+ if (!updated) {
+ values.put(AggregationExceptions.CONTACT_ID1, contactToSplit);
+ values.put(AggregationExceptions.CONTACT_ID2, contactToKeep);
+ mResolver.insert(AggregationExceptions.CONTENT_URI, values);
+ }
+
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private ViewEntry getViewEntryForMenuItem(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo info;
+ try {
+ info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ } catch (ClassCastException e) {
+ Log.e(TAG, "bad menuInfo", e);
+ return null;
+ }
+
+ return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
+ }
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
@@ -535,6 +648,8 @@
mSections.get(i).clear();
}
+ mContactIds.clear();
+
// Build up method entries
if (mUri != null) {
Bitmap photoBitmap = null;
@@ -548,6 +663,10 @@
entry.id = id;
entry.uri = uri;
entry.mimetype = mimetype;
+ entry.contactId = aggCursor.getLong(DATA_CONTACT_ID_COLUMN);
+ if (!mContactIds.contains(entry.contactId)) {
+ mContactIds.add(entry.contactId);
+ }
if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|| mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)