summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrian Attwell <brianattwell@google.com>2015-03-03 11:13:49 -0800
committerBrian Attwell <brianattwell@google.com>2015-03-03 15:51:26 -0800
commitd3946cae17273ed1c2fceb507990882e3f828ba9 (patch)
treec3877cb532d6905963c077e7a07d0ba820ac614a /src
parentd2962a3bb669a381d31a586df3b906033a8fa571 (diff)
downloadpackages_apps_Contacts-d3946cae17273ed1c2fceb507990882e3f828ba9.tar.gz
packages_apps_Contacts-d3946cae17273ed1c2fceb507990882e3f828ba9.tar.bz2
packages_apps_Contacts-d3946cae17273ed1c2fceb507990882e3f828ba9.zip
Batch join contacts
* Add new action to ContactSaveService to support joining more than two contacts toghether. * Add new dialog fragment for the join Bug: 19549465 Change-Id: Ib0b1d5e7652e429f8e78d81dd3d98d03b3129e1e
Diffstat (limited to 'src')
-rw-r--r--src/com/android/contacts/ContactSaveService.java98
-rw-r--r--src/com/android/contacts/activities/PeopleActivity.java28
-rw-r--r--src/com/android/contacts/editor/CompactContactEditorFragment.java3
-rw-r--r--src/com/android/contacts/editor/ContactEditorBaseFragment.java5
-rw-r--r--src/com/android/contacts/editor/ContactEditorFragment.java4
-rw-r--r--src/com/android/contacts/interactions/JoinContactsDialogFragment.java107
6 files changed, 214 insertions, 31 deletions
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 166852188..cf36edf9a 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -109,9 +109,9 @@ public class ContactSaveService extends IntentService {
public static final String EXTRA_DATA_ID = "dataId";
public static final String ACTION_JOIN_CONTACTS = "joinContacts";
+ public static final String ACTION_JOIN_SEVERAL_CONTACTS = "joinSeveralContacts";
public static final String EXTRA_CONTACT_ID1 = "contactId1";
public static final String EXTRA_CONTACT_ID2 = "contactId2";
- public static final String EXTRA_CONTACT_WRITABLE = "contactWritable";
public static final String ACTION_SET_SEND_TO_VOICEMAIL = "sendToVoicemail";
public static final String EXTRA_SEND_TO_VOICEMAIL_FLAG = "sendToVoicemailFlag";
@@ -211,6 +211,8 @@ public class ContactSaveService extends IntentService {
deleteContact(intent);
} else if (ACTION_JOIN_CONTACTS.equals(action)) {
joinContacts(intent);
+ } else if (ACTION_JOIN_SEVERAL_CONTACTS.equals(action)) {
+ joinSeveralContacts(intent);
} else if (ACTION_SET_SEND_TO_VOICEMAIL.equals(action)) {
setSendToVoicemail(intent);
} else if (ACTION_SET_RINGTONE.equals(action)) {
@@ -985,15 +987,14 @@ public class ContactSaveService extends IntentService {
/**
* Creates an intent that can be sent to this service to join two contacts.
+ * The resulting contact uses the name from {@param contactId1} if possible.
*/
public static Intent createJoinContactsIntent(Context context, long contactId1,
- long contactId2, boolean contactWritable,
- Class<? extends Activity> callbackActivity, String callbackAction) {
+ long contactId2, Class<? extends Activity> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_JOIN_CONTACTS);
serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID1, contactId1);
serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID2, contactId2);
- serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_WRITABLE, contactWritable);
// Callback intent will be invoked by the service once the contacts are joined.
Intent callbackIntent = new Intent(context, callbackActivity);
@@ -1003,6 +1004,17 @@ public class ContactSaveService extends IntentService {
return serviceIntent;
}
+ /**
+ * Creates an intent to join all raw contacts inside {@param contactIds}'s contacts.
+ * No special attention is paid to where the resulting contact's name is taken from.
+ */
+ public static Intent createJoinSeveralContactsIntent(Context context, long[] contactIds) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_JOIN_SEVERAL_CONTACTS);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_IDS, contactIds);
+ return serviceIntent;
+ }
+
private interface JoinContactQuery {
String[] PROJECTION = {
@@ -1011,8 +1023,6 @@ public class ContactSaveService extends IntentService {
RawContacts.DISPLAY_NAME_SOURCE,
};
- String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
-
int _ID = 0;
int CONTACT_ID = 1;
int DISPLAY_NAME_SOURCE = 2;
@@ -1034,22 +1044,48 @@ public class ContactSaveService extends IntentService {
int IS_SUPER_PRIMARY = 2;
}
- private void joinContacts(Intent intent) {
- long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1);
- long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1);
+ private void joinSeveralContacts(Intent intent) {
+ final long[] contactIds = intent.getLongArrayExtra(EXTRA_CONTACT_IDS);
- if (contactId1 == -1 || contactId2 == -1) {
- Log.e(TAG, "Invalid arguments for joinContacts request");
+ // Load raw contact IDs for all contacts involved.
+ long rawContactIds[] = getRawContactIdsForAggregation(contactIds);
+ if (rawContactIds == null) {
+ Log.e(TAG, "Invalid arguments for joinSeveralContacts request");
return;
}
+ // For each pair of raw contacts, insert an aggregation exception
final ContentResolver resolver = getContentResolver();
+ final ArrayList<ContentProviderOperation> operations
+ = new ArrayList<ContentProviderOperation>();
+ for (int i = 0; i < rawContactIds.length; i++) {
+ for (int j = 0; j < rawContactIds.length; j++) {
+ if (i != j) {
+ buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
+ }
+ }
+ }
+
+ // Apply all aggregation exceptions as one batch
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, operations);
+ showToast(R.string.contactsJoinedMessage);
+ } catch (RemoteException | OperationApplicationException e) {
+ Log.e(TAG, "Failed to apply aggregation exception batch", e);
+ showToast(R.string.contactSavedErrorToast);
+ }
+ }
+
+
+ private void joinContacts(Intent intent) {
+ long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1);
+ long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1);
// Load raw contact IDs for all raw contacts involved - currently edited and selected
// in the join UIs.
long rawContactIds[] = getRawContactIdsForAggregation(contactId1, contactId2);
if (rawContactIds == null) {
- // Error.
+ Log.e(TAG, "Invalid arguments for joinContacts request");
return;
}
@@ -1064,6 +1100,8 @@ public class ContactSaveService extends IntentService {
}
}
+ final ContentResolver resolver = getContentResolver();
+
// Use the name for contactId1 as the name for the newly aggregated contact.
final Uri contactId1Uri = ContentUris.withAppendedId(
Contacts.CONTENT_URI, contactId1);
@@ -1101,10 +1139,7 @@ public class ContactSaveService extends IntentService {
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
showToast(R.string.contactsJoinedMessage);
success = true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to apply aggregation exception batch", e);
- showToast(R.string.contactSavedErrorToast);
- } catch (OperationApplicationException e) {
+ } catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to apply aggregation exception batch", e);
showToast(R.string.contactSavedErrorToast);
}
@@ -1118,13 +1153,32 @@ public class ContactSaveService extends IntentService {
deliverCallback(callbackIntent);
}
- private long[] getRawContactIdsForAggregation(long contactId1, long contactId2) {
+ private long[] getRawContactIdsForAggregation(long[] contactIds) {
+ if (contactIds == null) {
+ return null;
+ }
+
final ContentResolver resolver = getContentResolver();
long rawContactIds[];
+
+ final StringBuilder queryBuilder = new StringBuilder();
+ final String stringContactIds[] = new String[contactIds.length];
+ for (int i = 0; i < contactIds.length; i++) {
+ queryBuilder.append(RawContacts.CONTACT_ID + "=?");
+ stringContactIds[i] = String.valueOf(contactIds[i]);
+ if (contactIds[i] == -1) {
+ return null;
+ }
+ if (i == contactIds.length -1) {
+ break;
+ }
+ queryBuilder.append(" OR ");
+ }
+
final Cursor c = resolver.query(RawContacts.CONTENT_URI,
JoinContactQuery.PROJECTION,
- JoinContactQuery.SELECTION,
- new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
+ queryBuilder.toString(),
+ stringContactIds, null);
if (c == null) {
Log.e(TAG, "Unable to open Contacts DB cursor");
showToast(R.string.contactSavedErrorToast);
@@ -1132,7 +1186,7 @@ public class ContactSaveService extends IntentService {
}
try {
if (c.getCount() < 2) {
- Log.e(TAG, "Not enough raw contacts to aggregate toghether.");
+ Log.e(TAG, "Not enough raw contacts to aggregate together.");
return null;
}
rawContactIds = new long[c.getCount()];
@@ -1147,6 +1201,10 @@ public class ContactSaveService extends IntentService {
return rawContactIds;
}
+ private long[] getRawContactIdsForAggregation(long contactId1, long contactId2) {
+ return getRawContactIdsForAggregation(new long[] {contactId1, contactId2});
+ }
+
/**
* Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
*/
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 577bf57f1..d660f4fa3 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -64,6 +64,8 @@ import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
import com.android.contacts.interactions.ContactMultiDeletionInteraction;
import com.android.contacts.interactions.ContactMultiDeletionInteraction.MultiContactDeleteListener;
+import com.android.contacts.interactions.JoinContactsDialogFragment;
+import com.android.contacts.interactions.JoinContactsDialogFragment.JoinContactsListener;
import com.android.contacts.list.MultiSelectContactsListFragment;
import com.android.contacts.list.MultiSelectContactsListFragment.OnCheckBoxListActionListener;
import com.android.contacts.list.ContactTileListFragment;
@@ -100,7 +102,8 @@ public class PeopleActivity extends ContactsActivity implements
DialogManager.DialogShowingViewActivity,
ContactListFilterController.ContactListFilterListener,
ProviderStatusListener,
- MultiContactDeleteListener {
+ MultiContactDeleteListener,
+ JoinContactsListener {
private static final String TAG = "PeopleActivity";
@@ -1065,6 +1068,8 @@ public class PeopleActivity extends ContactsActivity implements
&& mAllFragment.getSelectedContactIds().size() != 0;
makeMenuItemVisible(menu, R.id.menu_share, showSelectedContactOptions);
makeMenuItemVisible(menu, R.id.menu_delete, showSelectedContactOptions);
+ makeMenuItemVisible(menu, R.id.menu_join, showSelectedContactOptions);
+ makeMenuItemEnabled(menu, R.id.menu_join, mAllFragment.getSelectedContactIds().size() > 1);
// Debug options need to be visible even in search mode.
makeMenuItemVisible(menu, R.id.export_database, mEnableDebugMenuOptions);
@@ -1081,12 +1086,19 @@ public class PeopleActivity extends ContactsActivity implements
}
private void makeMenuItemVisible(Menu menu, int itemId, boolean visible) {
- MenuItem item =menu.findItem(itemId);
+ final MenuItem item = menu.findItem(itemId);
if (item != null) {
item.setVisible(visible);
}
}
+ private void makeMenuItemEnabled(Menu menu, int itemId, boolean visible) {
+ final MenuItem item = menu.findItem(itemId);
+ if (item != null) {
+ item.setEnabled(visible);
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDisableOptionItemSelected) {
@@ -1130,6 +1142,9 @@ public class PeopleActivity extends ContactsActivity implements
case R.id.menu_share:
shareSelectedContacts();
return true;
+ case R.id.menu_join:
+ joinSelectedContacts();
+ return true;
case R.id.menu_delete:
deleteSelectedContacts();
return true;
@@ -1197,6 +1212,15 @@ public class PeopleActivity extends ContactsActivity implements
ImplicitIntentsUtil.startActivityOutsideApp(this, intent);
}
+ private void joinSelectedContacts() {
+ JoinContactsDialogFragment.start(this, mAllFragment.getSelectedContactIds());
+ }
+
+ @Override
+ public void onContactsJoined() {
+ mAllFragment.clearCheckBoxes();
+ }
+
private void deleteSelectedContacts() {
ContactMultiDeletionInteraction.start(PeopleActivity.this,
mAllFragment.getSelectedContactIds());
diff --git a/src/com/android/contacts/editor/CompactContactEditorFragment.java b/src/com/android/contacts/editor/CompactContactEditorFragment.java
index f25d01aff..94e22632e 100644
--- a/src/com/android/contacts/editor/CompactContactEditorFragment.java
+++ b/src/com/android/contacts/editor/CompactContactEditorFragment.java
@@ -304,8 +304,7 @@ public class CompactContactEditorFragment extends ContactEditorBaseFragment impl
@Override
protected void joinAggregate(final long contactId) {
final Intent intent = ContactSaveService.createJoinContactsIntent(
- mContext, mContactIdForJoin, contactId, mContactWritableForJoin,
- CompactContactEditorActivity.class,
+ mContext, mContactIdForJoin, contactId, CompactContactEditorActivity.class,
CompactContactEditorActivity.ACTION_JOIN_COMPLETED);
mContext.startService(intent);
}
diff --git a/src/com/android/contacts/editor/ContactEditorBaseFragment.java b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
index 57905d129..d8c20fb95 100644
--- a/src/com/android/contacts/editor/ContactEditorBaseFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
@@ -139,7 +139,6 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// 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;
@@ -332,7 +331,6 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// Join Activity
protected long mContactIdForJoin;
- protected boolean mContactWritableForJoin;
//
// Editor state for {@link ContactEditorView}.
@@ -465,7 +463,6 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// 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
@@ -582,7 +579,6 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// Join Activity
outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
- outState.putBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN, mContactWritableForJoin);
super.onSaveInstanceState(outState);
}
@@ -1337,7 +1333,6 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
}
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);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 96aaecde1..9e5148d48 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -509,8 +509,8 @@ public class ContactEditorFragment extends ContactEditorBaseFragment implements
@Override
protected void joinAggregate(final long contactId) {
final Intent intent = ContactSaveService.createJoinContactsIntent(
- mContext, mContactIdForJoin, contactId, mContactWritableForJoin,
- ContactEditorActivity.class, ContactEditorActivity.ACTION_JOIN_COMPLETED);
+ mContext, mContactIdForJoin, contactId, ContactEditorActivity.class,
+ ContactEditorActivity.ACTION_JOIN_COMPLETED);
mContext.startService(intent);
}
diff --git a/src/com/android/contacts/interactions/JoinContactsDialogFragment.java b/src/com/android/contacts/interactions/JoinContactsDialogFragment.java
new file mode 100644
index 000000000..a9a1aa976
--- /dev/null
+++ b/src/com/android/contacts/interactions/JoinContactsDialogFragment.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 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.interactions;
+
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentTransaction;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.TreeSet;
+
+/**
+ * An interaction invoked to join multiple contacts together.
+ */
+public class JoinContactsDialogFragment extends DialogFragment {
+
+ private static final String FRAGMENT_TAG = "joinDialog";
+ private static final String KEY_CONTACT_IDS = "contactIds";
+
+ public interface JoinContactsListener {
+ void onContactsJoined();
+ }
+
+ public static void start(Activity activity, TreeSet<Long> contactIds) {
+ final FragmentTransaction ft = activity.getFragmentManager().beginTransaction();
+ final JoinContactsDialogFragment newFragment
+ = JoinContactsDialogFragment.newInstance(contactIds);
+ newFragment.show(ft, FRAGMENT_TAG);
+ }
+
+ private static JoinContactsDialogFragment newInstance(TreeSet<Long> contactIds) {
+ final JoinContactsDialogFragment fragment = new JoinContactsDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putSerializable(KEY_CONTACT_IDS, contactIds);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final TreeSet<Long> contactIds =
+ (TreeSet<Long>) getArguments().getSerializable(KEY_CONTACT_IDS);
+ if (contactIds.size() <= 1) {
+ return new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.batch_merge_single_contact_warning)
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ }
+ return new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.batch_merge_confirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ joinContacts(contactIds);
+ }
+ }
+ )
+ .create();
+ }
+
+ private void joinContacts(TreeSet<Long> contactIds) {
+ final Long[] contactIdsArray = contactIds.toArray(new Long[contactIds.size()]);
+ final long[] contactIdsArray2 = new long[contactIdsArray.length];
+ for (int i = 0; i < contactIds.size(); i++) {
+ contactIdsArray2[i] = contactIdsArray[i];
+ }
+
+ final Intent intent = ContactSaveService.createJoinSeveralContactsIntent(getActivity(),
+ contactIdsArray2);
+ getActivity().startService(intent);
+
+ notifyListener();
+ }
+
+ private void notifyListener() {
+ if (getActivity() instanceof JoinContactsListener) {
+ ((JoinContactsListener) getActivity()).onContactsJoined();
+ }
+ }
+
+}