Add link/unlink options to QuickContact

Bug: 31122952
Test: Manually tested
1) link contact from 1 raw contact, contact from multiple raw contacts
2) unlink contact from multiple raw contacts
3) for non-editable contact, me contact, link and unlink are not shown

Change-Id: I21137ee353a65ad50e800cbb2dd013c23408a5ea
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
index 6cfe053..a0cbdbb 100644
--- a/res/menu/quickcontact.xml
+++ b/res/menu/quickcontact.xml
@@ -26,6 +26,14 @@
         android:showAsAction="always" />
 
     <item
+        android:id="@+id/menu_split"
+        android:title="@string/menu_splitAggregate" />
+
+    <item
+        android:id="@+id/menu_join"
+        android:title="@string/menu_joinAggregate" />
+
+    <item
         android:id="@+id/menu_delete"
         android:title="@string/menu_deleteContact" />
 
diff --git a/src/com/android/contacts/common/model/Contact.java b/src/com/android/contacts/common/model/Contact.java
index c84ff2a..586f80a 100644
--- a/src/com/android/contacts/common/model/Contact.java
+++ b/src/com/android/contacts/common/model/Contact.java
@@ -472,6 +472,10 @@
         return mIsUserProfile;
     }
 
+    public boolean isMultipleRawContacts() {
+        return mRawContacts.size() > 1;
+    }
+
     /**
      * @return true if all the raw contacts are from SIM accounts, and false otherwise.
      */
diff --git a/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
index a8f88cf..0c04466 100644
--- a/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
+++ b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
@@ -65,7 +65,8 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mHasPendingChanges = getArguments().getBoolean(ARG_HAS_PENDING_CHANGES);
+        mHasPendingChanges = getArguments() == null
+                ? false : getArguments().getBoolean(ARG_HAS_PENDING_CHANGES);
     }
 
     @Override
@@ -80,7 +81,9 @@
                 new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        final Listener targetListener = (Listener) getTargetFragment();
+                        final Listener targetListener = getTargetFragment() == null
+                                ? (Listener) getActivity()
+                                : (Listener) getTargetFragment();
                         targetListener.onSplitContactConfirmed(mHasPendingChanges);
                     }
                 });
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 550f214..8a1b5e9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -107,6 +107,7 @@
 import com.android.contacts.NfcHandler;
 import com.android.contacts.R;
 import com.android.contacts.activities.ContactEditorActivity;
+import com.android.contacts.activities.ContactSelectionActivity;
 import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.ClipboardUtils;
 import com.android.contacts.common.Collapser;
@@ -131,6 +132,7 @@
 import com.android.contacts.common.model.Contact;
 import com.android.contacts.common.model.ContactLoader;
 import com.android.contacts.common.model.RawContact;
+import com.android.contacts.common.model.RawContactDeltaList;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.dataitem.CustomDataItem;
@@ -160,11 +162,13 @@
 import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
 import com.android.contacts.editor.ContactEditorFragment;
 import com.android.contacts.editor.EditorIntents;
+import com.android.contacts.editor.SplitContactConfirmationDialogFragment;
 import com.android.contacts.interactions.CalendarInteractionsLoader;
 import com.android.contacts.interactions.CallLogInteractionsLoader;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.ContactInteraction;
 import com.android.contacts.interactions.SmsInteractionsLoader;
+import com.android.contacts.list.UiIntentActions;
 import com.android.contacts.quickcontact.ExpandingEntryCardView.Entry;
 import com.android.contacts.quickcontact.ExpandingEntryCardView.EntryContextMenuInfo;
 import com.android.contacts.quickcontact.ExpandingEntryCardView.EntryTag;
@@ -201,8 +205,8 @@
  * data asynchronously, and then shows a popup with details centered around
  * {@link Intent#getSourceBounds()}.
  */
-public class QuickContactActivity extends ContactsActivity
-        implements AggregationSuggestionEngine.Listener {
+public class QuickContactActivity extends ContactsActivity implements
+        AggregationSuggestionEngine.Listener, SplitContactConfirmationDialogFragment.Listener {
 
     /**
      * QuickContacts immediately takes up the full screen. All possible information is shown.
@@ -234,11 +238,14 @@
     private static final int SCRIM_COLOR = Color.argb(0xC8, 0, 0, 0);
     private static final int REQUEST_CODE_CONTACT_SELECTION_ACTIVITY = 2;
     private static final String MIMETYPE_SMS = "vnd.android-dir/mms-sms";
+    private static final int REQUEST_CODE_JOIN = 3;
 
     /** This is the Intent action to install a shortcut in the launcher. */
     private static final String ACTION_INSTALL_SHORTCUT =
             "com.android.launcher.action.INSTALL_SHORTCUT";
 
+    public static final String ACTION_SPLIT_COMPLETED = "splitCompleted";
+
     @SuppressWarnings("deprecation")
     private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
 
@@ -943,6 +950,9 @@
         }
 
         mIsRecreatedInstance = savedInstanceState != null;
+        if (mIsRecreatedInstance) {
+            mPreviousContactId = savedInstanceState.getLong(KEY_PREVIOUS_CONTACT_ID);
+        }
         mShouldLog = true;
 
         // There're 3 states for each permission:
@@ -1172,6 +1182,14 @@
         } else if (requestCode == REQUEST_CODE_CONTACT_SELECTION_ACTIVITY &&
                 resultCode != RESULT_CANCELED) {
             processIntent(data);
+        } else if (requestCode == REQUEST_CODE_JOIN) {
+            // Ignore failed requests
+            if (resultCode != Activity.RESULT_OK) {
+                processIntent(data);
+            }
+            if (data != null) {
+                joinAggregate(ContentUris.parseId(data.getData()));
+            }
         }
     }
 
@@ -1203,6 +1221,12 @@
             finish();
             return;
         }
+        if (ACTION_SPLIT_COMPLETED.equals(intent.getAction())) {
+            Toast.makeText(this, R.string.contactUnlinkedToast, Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        }
+
         Uri lookupUri = intent.getData();
         if (intent.getBooleanExtra(EXTRA_CONTACT_EDITED, false)) {
             setResult(ContactEditorActivity.RESULT_CODE_EDITED);
@@ -2970,6 +2994,14 @@
                 editMenuItem.setVisible(false);
             }
 
+            final MenuItem splitMenuItem = menu.findItem(R.id.menu_split);
+            splitMenuItem.setVisible(isContactEditable() && !mContactData.isUserProfile()
+                    && mContactData.isMultipleRawContacts());
+
+            final MenuItem joinMenuItem = menu.findItem(R.id.menu_join);
+            joinMenuItem.setVisible(!InvisibleContactUtil.isInvisibleAndAddable(mContactData, this)
+                    && isContactEditable() && !mContactData.isUserProfile());
+
             final MenuItem deleteMenuItem = menu.findItem(R.id.menu_delete);
             deleteMenuItem.setVisible(isContactEditable() && !mContactData.isUserProfile());
 
@@ -3072,6 +3104,10 @@
                     editContact();
                 }
                 return true;
+            case R.id.menu_split:
+                return doSplitContactAction();
+            case R.id.menu_join:
+                return doJoinContactAction();
             case R.id.menu_delete:
                 Logger.logQuickContactEvent(mReferrer, mContactType, CardType.UNKNOWN_CARD,
                         ActionType.REMOVE, /* thirdPartyAction */ null);
@@ -3104,4 +3140,52 @@
                 return super.onOptionsItemSelected(item);
         }
     }
+
+    private boolean doJoinContactAction() {
+        if (mContactData == null) return false;
+
+        mPreviousContactId = mContactData.getId();
+        final Intent intent = new Intent(this, ContactSelectionActivity.class);
+        intent.setAction(UiIntentActions.PICK_JOIN_CONTACT_ACTION);
+        intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, mPreviousContactId);
+        startActivityForResult(intent, REQUEST_CODE_JOIN);
+        return true;
+    }
+
+    /**
+     * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
+     */
+    private void joinAggregate(final long contactId) {
+        final Intent intent = ContactSaveService.createJoinContactsIntent(
+                this, mPreviousContactId, contactId, QuickContactActivity.class,
+                Intent.ACTION_VIEW);
+        this.startService(intent);
+    }
+
+    private boolean doSplitContactAction() {
+        if (mContactData == null) return false;
+
+        final SplitContactConfirmationDialogFragment dialog = new
+                SplitContactConfirmationDialogFragment();
+        dialog.show(getFragmentManager(), "splitContact");
+        return true;
+    }
+
+    @Override
+    public void onSplitContactConfirmed(boolean hasPendingChanges) {
+        final RawContactDeltaList rawContactDeltaList= mContactData.createRawContactDeltaList();
+        rawContactDeltaList.markRawContactsForSplitting();
+        final Intent intent = ContactSaveService.createSaveContactIntent(this,
+                rawContactDeltaList,
+                /* saveModeExtraKey */ "",
+                /* saveMode */ 0,
+                mContactData.isUserProfile(),
+                ((Activity) this).getClass(),
+                ACTION_SPLIT_COMPLETED,
+                /* updatedPhotos */ null,
+                /* joinContactIdExtraKey =*/ null,
+                /* joinContactId =*/ null);
+        ContactSaveService.startService(this, intent,
+                ContactEditorActivity.ContactEditor.SaveMode.SPLIT);
+    }
 }