Implement linked contact menu option
Remove unlink option from QuickContact.
Link now appears when the contact has only one raw contact.
Otherwise a new "View linked contacts" menu option appears.
This dialog shows all linked contacts (including read-only) and
has buttons for adding another contact or unlinking all of them.
Show progress dialogs for each of these actions.
Test: Manually verified:
* View linked contacts only appears when there are more than one
raw contacts
* Link appears otherwise
* Pressing add goes to the contact picker and correctly joins the
selected contact
* Pressing unlink opens the confirmation dialog
* Canceling or dismissing the confirmation closes everything
* Unlinking from the confirmation does the unlink
* Confirmed the above with rotating on each dialog and during the
progress dialogs
Bug: 32707898
Change-Id: I39435a07fefce4276e34ba302001ff3dab352516
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index d2a65a8..24bcbfd 100755
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -16,6 +16,8 @@
package com.android.contacts;
+import static android.Manifest.permission.WRITE_CONTACTS;
+
import android.app.Activity;
import android.app.IntentService;
import android.content.ContentProviderOperation;
@@ -45,8 +47,6 @@
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.os.ResultReceiver;
import android.telephony.SubscriptionInfo;
@@ -72,6 +72,7 @@
import com.android.contacts.compat.PinnedPositionsCompat;
import com.android.contacts.util.ContactPhotoUtils;
import com.android.contactsbind.FeedbackHelper;
+
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -81,8 +82,6 @@
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import static android.Manifest.permission.WRITE_CONTACTS;
-
/**
* A service responsible for saving changes to the content provider.
*/
@@ -133,6 +132,7 @@
public static final String EXTRA_DATA_ID = "dataId";
public static final String ACTION_SPLIT_CONTACT = "splitContact";
+ public static final String EXTRA_HARD_SPLIT = "extraHardSplit";
public static final String ACTION_JOIN_CONTACTS = "joinContacts";
public static final String ACTION_JOIN_SEVERAL_CONTACTS = "joinSeveralContacts";
@@ -159,6 +159,8 @@
public static final String BROADCAST_GROUP_DELETED = "groupDeleted";
public static final String BROADCAST_SIM_IMPORT_COMPLETE = "simImportComplete";
+ public static final String BROADCAST_LINK_COMPLETE = "linkComplete";
+ public static final String BROADCAST_UNLINK_COMPLETE = "unlinkComplete";
public static final String BROADCAST_SERVICE_STATE_CHANGED = "serviceStateChanged";
@@ -1244,7 +1246,7 @@
/**
* Creates an intent that can be sent to this service to split a contact into it's constituent
- * pieces. This will set the raw contact ids to TYPE_AUTOMATIC for AggregationExceptions so
+ * pieces. This will set the raw contact ids to {@link AggregationExceptions#TYPE_AUTOMATIC} so
* they may be re-merged by the auto-aggregator.
*/
public static Intent createSplitContactIntent(Context context, long[][] rawContactIds,
@@ -1256,10 +1258,24 @@
return serviceIntent;
}
+ /**
+ * Creates an intent that can be sent to this service to split a contact into it's constituent
+ * pieces. This will explicitly set the raw contact ids to
+ * {@link AggregationExceptions#TYPE_KEEP_SEPARATE}.
+ */
+ public static Intent createHardSplitContactIntent(Context context, long[][] rawContactIds) {
+ final Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SPLIT_CONTACT);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACT_IDS, rawContactIds);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_HARD_SPLIT, true);
+ return serviceIntent;
+ }
+
private void splitContact(Intent intent) {
final long rawContactIds[][] = (long[][]) intent
.getSerializableExtra(EXTRA_RAW_CONTACT_IDS);
final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
+ final boolean hardSplit = intent.getBooleanExtra(EXTRA_HARD_SPLIT, false);
if (rawContactIds == null) {
Log.e(TAG, "Invalid argument for splitContact request");
if (receiver != null) {
@@ -1273,7 +1289,8 @@
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
- if (!buildSplitTwoContacts(operations, rawContactIds[i], rawContactIds[j])) {
+ if (!buildSplitTwoContacts(operations, rawContactIds[i], rawContactIds[j],
+ hardSplit)) {
if (receiver != null) {
receiver.send(CP2_ERROR, new Bundle());
return;
@@ -1288,6 +1305,8 @@
}
return;
}
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_UNLINK_COMPLETE));
if (receiver != null) {
receiver.send(CONTACTS_SPLIT, new Bundle());
} else {
@@ -1301,7 +1320,7 @@
* @return false if an error occurred, true otherwise.
*/
private boolean buildSplitTwoContacts(ArrayList<ContentProviderOperation> operations,
- long[] rawContactIds1, long[] rawContactIds2) {
+ long[] rawContactIds1, long[] rawContactIds2, boolean hardSplit) {
if (rawContactIds1 == null || rawContactIds2 == null) {
Log.e(TAG, "Invalid arguments for splitContact request");
return false;
@@ -1312,7 +1331,7 @@
final int batchSize = MAX_CONTACTS_PROVIDER_BATCH_SIZE;
for (int i = 0; i < rawContactIds1.length; i++) {
for (int j = 0; j < rawContactIds2.length; j++) {
- buildSplitContactDiff(operations, rawContactIds1[i], rawContactIds2[j]);
+ buildSplitContactDiff(operations, rawContactIds1[i], rawContactIds2[j], hardSplit);
// Before we get to 500 we need to flush the operations list
if (operations.size() > 0 && operations.size() % batchSize == 0) {
if (!applyOperations(resolver, operations)) {
@@ -1453,6 +1472,8 @@
showToast(R.string.contactsJoinedNamedMessage, name);
}
}
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_LINK_COMPLETE));
} else {
if (receiver != null) {
receiver.send(CP2_ERROR, new Bundle());
@@ -1591,6 +1612,8 @@
Uri uri = RawContacts.getContactLookupUri(resolver,
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
callbackIntent.setData(uri);
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_LINK_COMPLETE));
}
deliverCallback(callbackIntent);
}
@@ -1710,13 +1733,18 @@
}
/**
- * Construct a {@link AggregationExceptions#TYPE_AUTOMATIC} ContentProviderOperation.
+ * Construct a {@link AggregationExceptions#TYPE_AUTOMATIC} or a
+ * {@link AggregationExceptions#TYPE_KEEP_SEPARATE} ContentProviderOperation if a hard split is
+ * requested.
*/
private void buildSplitContactDiff(ArrayList<ContentProviderOperation> operations,
- long rawContactId1, long rawContactId2) {
+ long rawContactId1, long rawContactId2, boolean hardSplit) {
final Builder builder =
ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_AUTOMATIC);
+ builder.withValue(AggregationExceptions.TYPE,
+ hardSplit
+ ? AggregationExceptions.TYPE_KEEP_SEPARATE
+ : AggregationExceptions.TYPE_AUTOMATIC);
builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
operations.add(builder.build());