Make phone number copyable on call detail

Now the phone number is long-pressable, which starts the CAB
for copying phone number.

Tapping the phone number again while will unselect it. (Or press back or UP.)

Also tweak the highlight area for the phone number view, per UX suggestion.
Now when you press the phone number view, the entire row will be highlighted,
including the SMS action view.

Tested:
- Regular phone number.
- SIP address.
- No-caller ID, or "private number" -> The "Call XXX" view is gone, so
nothing to long-press.
- "Voicemail".  The text "Voicemail" will by copied, which is iffy, but I
don't think "fixing" it is worth introducing more complication to the code.

Bug 6354066

Change-Id: I54ee67589a2e5aaba8e4a5dd9589e8c9e46c18b8
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 12a1592..1fac6c8 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -25,6 +25,7 @@
 import com.android.contacts.format.FormatUtils;
 import com.android.contacts.util.AsyncTaskExecutor;
 import com.android.contacts.util.AsyncTaskExecutors;
+import com.android.contacts.util.ClipboardUtils;
 import com.android.contacts.util.Constants;
 import com.android.contacts.voicemail.VoicemailPlaybackFragment;
 import com.android.contacts.voicemail.VoicemailStatusHelper;
@@ -40,6 +41,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -53,6 +55,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ActionMode;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -132,6 +135,15 @@
     private ProximitySensorManager mProximitySensorManager;
     private final ProximitySensorListener mProximitySensorListener = new ProximitySensorListener();
 
+    /**
+     * The action mode used when the phone number is selected.  This will be non-null only when the
+     * phone number is selected.
+     */
+    private ActionMode mPhoneNumberActionMode;
+
+    private CharSequence mPhoneNumberLabelToCopy;
+    private CharSequence mPhoneNumberToCopy;
+
     /** Listener to changes in the proximity sensor state. */
     private class ProximitySensorListener implements ProximitySensorManager.Listener {
         /** Used to show a blank view and hide the action bar. */
@@ -202,6 +214,9 @@
     private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
         @Override
         public void onClick(View view) {
+            if (finishPhoneNumerSelectedActionModeIfShown()) {
+                return;
+            }
             startActivity(((ViewEntry) view.getTag()).primaryIntent);
         }
     };
@@ -209,10 +224,25 @@
     private final View.OnClickListener mSecondaryActionListener = new View.OnClickListener() {
         @Override
         public void onClick(View view) {
+            if (finishPhoneNumerSelectedActionModeIfShown()) {
+                return;
+            }
             startActivity(((ViewEntry) view.getTag()).secondaryIntent);
         }
     };
 
+    private final View.OnLongClickListener mPrimaryLongClickListener =
+            new View.OnLongClickListener() {
+        @Override
+        public boolean onLongClick(View v) {
+            if (finishPhoneNumerSelectedActionModeIfShown()) {
+                return true;
+            }
+            startPhoneNumberSelectedActionMode(v);
+            return true;
+        }
+    };
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -497,8 +527,12 @@
                     }
 
                     configureCallButton(entry);
+                    mPhoneNumberToCopy = displayNumber;
+                    mPhoneNumberLabelToCopy = entry.label;
                 } else {
                     disableCallButton();
+                    mPhoneNumberToCopy = null;
+                    mPhoneNumberLabelToCopy = null;
                 }
 
                 mHasEditNumberBeforeCallOption =
@@ -656,6 +690,7 @@
         mainAction.setOnClickListener(mPrimaryActionListener);
         mainAction.setTag(entry);
         mainAction.setContentDescription(entry.primaryDescription);
+        mainAction.setOnLongClickListener(mPrimaryLongClickListener);
 
         if (entry.secondaryIntent != null) {
             icon.setOnClickListener(mSecondaryActionListener);
@@ -829,4 +864,64 @@
     public void disableProximitySensor(boolean waitForFarState) {
         mProximitySensorManager.disable(waitForFarState);
     }
+
+    /**
+     * If the phone number is selected, unselect it and return {@code true}.
+     * Otherwise, just {@code false}.
+     */
+    private boolean finishPhoneNumerSelectedActionModeIfShown() {
+        if (mPhoneNumberActionMode == null) return false;
+        mPhoneNumberActionMode.finish();
+        return true;
+    }
+
+    private void startPhoneNumberSelectedActionMode(View targetView) {
+        mPhoneNumberActionMode = startActionMode(new PhoneNumberActionModeCallback(targetView));
+    }
+
+    private class PhoneNumberActionModeCallback implements ActionMode.Callback {
+        private final View mTargetView;
+        private final Drawable mOriginalViewBackground;
+
+        public PhoneNumberActionModeCallback(View targetView) {
+            mTargetView = targetView;
+
+            // Highlight the phone number view.  Remember the old background, and put a new one.
+            mOriginalViewBackground = mTargetView.getBackground();
+            mTargetView.setBackgroundColor(getResources().getColor(R.color.item_selected));
+        }
+
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            if (TextUtils.isEmpty(mPhoneNumberToCopy)) return false;
+
+            getMenuInflater().inflate(R.menu.call_details_cab, menu);
+            return true;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            switch (item.getItemId()) {
+                case R.id.copy_phone_number:
+                    ClipboardUtils.copyText(CallDetailActivity.this, mPhoneNumberLabelToCopy,
+                            mPhoneNumberToCopy, true);
+                    mode.finish(); // Close the CAB
+                    return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            mPhoneNumberActionMode = null;
+
+            // Restore the view background.
+            mTargetView.setBackground(mOriginalViewBackground);
+        }
+    }
 }