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);
+ }
+ }
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index faeb0c5..2dc7bc4 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -38,6 +38,7 @@
import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.model.EntityModifier;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
+import com.android.contacts.util.ClipboardUtils;
import com.android.contacts.util.Constants;
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.DateUtils;
@@ -1904,17 +1905,7 @@
// Checking for empty string
if (TextUtils.isEmpty(textToCopy)) return;
- // Adding item to clipboard
- ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(
- Context.CLIPBOARD_SERVICE);
- String[] mimeTypes = new String[]{detailViewEntry.mimetype};
- ClipData.Item clipDataItem = new ClipData.Item(textToCopy);
- ClipData cd = new ClipData(detailViewEntry.typeString, mimeTypes, clipDataItem);
- clipboardManager.setPrimaryClip(cd);
-
- // Display Confirmation Toast
- String toastText = getString(R.string.toast_text_copied);
- Toast.makeText(getActivity(), toastText, Toast.LENGTH_SHORT).show();
+ ClipboardUtils.copyText(getActivity(), detailViewEntry.typeString, textToCopy, true);
}
@Override
diff --git a/src/com/android/contacts/util/ClipboardUtils.java b/src/com/android/contacts/util/ClipboardUtils.java
new file mode 100644
index 0000000..a160668
--- /dev/null
+++ b/src/com/android/contacts/util/ClipboardUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.util;
+
+import com.android.contacts.R;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+public class ClipboardUtils {
+ private static final String TAG = "ClipboardUtils";
+
+ private ClipboardUtils() { }
+
+ /**
+ * Copy a text to clipboard.
+ *
+ * @param context Context
+ * @param label Label to show to the user describing this clip.
+ * @param text Text to copy.
+ * @param showToast If {@code true}, a toast is shown to the user.
+ */
+ public static void copyText(Context context, CharSequence label, CharSequence text,
+ boolean showToast) {
+ if (TextUtils.isEmpty(text)) return;
+
+ ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ ClipData clipData = ClipData.newPlainText(label == null ? "" : label, text);
+ clipboardManager.setPrimaryClip(clipData);
+
+ if (showToast) {
+ String toastText = context.getString(R.string.toast_text_copied);
+ Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show();
+ }
+ }
+}