Merge "Fix photo selection from Gallery"
diff --git a/res/drawable-hdpi/ic_menu_copy_contact.png b/res/drawable-hdpi/ic_menu_copy_contact.png
new file mode 100644
index 0000000..e6be48b
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_copy_contact.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_copy_contact.png b/res/drawable-mdpi/ic_menu_copy_contact.png
new file mode 100644
index 0000000..750db62
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_copy_contact.png
Binary files differ
diff --git a/res/layout/contact_detail_header_view.xml b/res/layout/contact_detail_header_view.xml
index c201bf4..b7c7988 100644
--- a/res/layout/contact_detail_header_view.xml
+++ b/res/layout/contact_detail_header_view.xml
@@ -57,6 +57,17 @@
             android:visibility="gone"
         />
 
+        <TextView android:id="@+id/directory_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_marginTop="-2dip"
+            android:visibility="gone"
+        />
+
         <TextView android:id="@+id/status"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/res/menu/view.xml b/res/menu/view.xml
index 7a1c26c..ea8fe7f 100644
--- a/res/menu/view.xml
+++ b/res/menu/view.xml
@@ -38,4 +38,8 @@
         android:icon="@android:drawable/ic_menu_delete"
         android:title="@string/menu_deleteContact" />
 
+    <item
+        android:id="@+id/menu_copy"
+        android:icon="@drawable/ic_menu_copy_contact"
+        android:title="@string/menu_copyContact" />
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2458d80..c8ae433 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -199,6 +199,9 @@
     <!-- The label describing the Notes field of a contact. This field allows free form text entry about a contact -->
     <string name="label_notes">Notes</string>
 
+    <!-- The label describing the SIP address field of a contact. [CHAR LIMIT=20] -->
+    <string name="label_sip_address">SIP address</string>
+
     <!-- The label describing the custom ringtone for a contact -->
     <string name="label_ringtone">Ringtone</string>
 
@@ -1292,4 +1295,13 @@
          only be empty.
          Translations require extensive QA! -->
     <string name="visualScrollerAlphabet">A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z</string>
+
+    <!-- The menu item (or button) that creates a local copy of a corporate contact. [CHAR LIMIT=40]-->
+    <string name="menu_copyContact">Make personal copy</string>
+
+    <!-- The description of the directory where the contact was found [CHAR LIMIT=100]-->
+    <string name="contact_directory_description">from <xliff:g id="type" example="Corporate Directory">%1$s</xliff:g></string>
+
+    <!-- The description of the directory where the contact was found [CHAR LIMIT=100]-->
+    <string name="contact_directory_account_description">from <xliff:g id="type" example="Corporate Directory">%1$s</xliff:g> (<xliff:g id="name" example="me@acme.com">%2$s</xliff:g>)</string>
 </resources>
diff --git a/src/com/android/contacts/model/FallbackSource.java b/src/com/android/contacts/model/FallbackSource.java
index e052fed..9de87b7 100644
--- a/src/com/android/contacts/model/FallbackSource.java
+++ b/src/com/android/contacts/model/FallbackSource.java
@@ -32,6 +32,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
@@ -56,6 +57,9 @@
     protected static final int FLAGS_POSTAL = EditorInfo.TYPE_CLASS_TEXT
             | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
             | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+    protected static final int FLAGS_SIP_ADDRESS = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;  // since SIP addresses have the same
+                                                             // basic format as email addresses
 
     public FallbackSource() {
         this.accountType = null;
@@ -77,6 +81,7 @@
         inflateNote(context, inflateLevel);
         inflateWebsite(context, inflateLevel);
         inflateEvent(context, inflateLevel);
+        inflateSipAddress(context, inflateLevel);
 
         setInflatedLevel(inflateLevel);
 
@@ -411,6 +416,27 @@
         return kind;
     }
 
+    protected DataKind inflateSipAddress(Context context, int inflateLevel) {
+        DataKind kind = getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE);
+        if (kind == null) {
+            // TODO: icon here should really be a SIP-specific variant of sym_action_call
+            kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
+                    R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true));
+            kind.isList = false;
+            kind.secondary = true;
+            kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
+            kind.actionBody = new SimpleInflater(SipAddress.SIP_ADDRESS);
+        }
+
+        if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
+            kind.fieldList = Lists.newArrayList();
+            kind.fieldList.add(new EditField(SipAddress.SIP_ADDRESS,
+                                             R.string.label_sip_address, FLAGS_SIP_ADDRESS));
+        }
+
+        return kind;
+    }
+
     /**
      * Simple inflater that assumes a string resource has a "%s" that will be
      * filled from the given column.
diff --git a/src/com/android/contacts/model/GoogleSource.java b/src/com/android/contacts/model/GoogleSource.java
index 149249a..6d4631f 100644
--- a/src/com/android/contacts/model/GoogleSource.java
+++ b/src/com/android/contacts/model/GoogleSource.java
@@ -50,6 +50,7 @@
         inflateNote(context, inflateLevel);
         inflateWebsite(context, inflateLevel);
         inflateEvent(context, inflateLevel);
+        inflateSipAddress(context, inflateLevel);
 
         // TODO: GOOGLE: GROUPMEMBERSHIP
 
@@ -58,16 +59,6 @@
     }
 
     @Override
-    protected DataKind inflateStructuredName(Context context, int inflateLevel) {
-        return super.inflateStructuredName(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflateNickname(Context context, int inflateLevel) {
-        return super.inflateNickname(context, inflateLevel);
-    }
-
-    @Override
     protected DataKind inflatePhone(Context context, int inflateLevel) {
         final DataKind kind = super.inflatePhone(context, ContactsSource.LEVEL_MIMETYPES);
 
@@ -112,36 +103,6 @@
     }
 
     @Override
-    protected DataKind inflateStructuredPostal(Context context, int inflateLevel) {
-        return super.inflateStructuredPostal(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflateIm(Context context, int inflateLevel) {
-        return super.inflateIm(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflateOrganization(Context context, int inflateLevel) {
-        return super.inflateOrganization(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflatePhoto(Context context, int inflateLevel) {
-        return super.inflatePhoto(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflateNote(Context context, int inflateLevel) {
-        return super.inflateNote(context, inflateLevel);
-    }
-
-    @Override
-    protected DataKind inflateWebsite(Context context, int inflateLevel) {
-        return super.inflateWebsite(context, inflateLevel);
-    }
-
-    @Override
     public int getHeaderColor(Context context) {
         return 0xff89c2c2;
     }
diff --git a/src/com/android/contacts/ui/QuickContactWindow.java b/src/com/android/contacts/ui/QuickContactWindow.java
index c90961e..f0c1a05 100644
--- a/src/com/android/contacts/ui/QuickContactWindow.java
+++ b/src/com/android/contacts/ui/QuickContactWindow.java
@@ -50,6 +50,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.provider.ContactsContract.Contacts;
@@ -218,6 +219,7 @@
      */
     private static final String[] PRECEDING_MIMETYPES = new String[] {
             Phone.CONTENT_ITEM_TYPE,
+            SipAddress.CONTENT_ITEM_TYPE,
             Contacts.CONTENT_ITEM_TYPE,
             Constants.MIME_SMS_ADDRESS,
             Email.CONTENT_ITEM_TYPE,
@@ -771,7 +773,17 @@
                     final Uri callUri = Uri.fromParts(Constants.SCHEME_TEL, number, null);
                     mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
                 }
-
+            } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
+                if (!TextUtils.isEmpty(address)) {
+                    final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
+                    mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
+                    // TODO: This item currently appears with the same "phone"
+                    // icon as regular phone numbers, which is confusing if a
+                    // contact has both a regular number *and* a SIP address.
+                    // Need to figure out a way for this item to have a
+                    // special SIP-specific variant of that icon.
+                }
             } else if (Constants.MIME_SMS_ADDRESS.equals(mimeType)) {
                 final String number = getAsString(cursor, Phone.NUMBER);
                 if (!TextUtils.isEmpty(number)) {
diff --git a/src/com/android/contacts/util/Constants.java b/src/com/android/contacts/util/Constants.java
index 4e80345..1d1e4e2 100644
--- a/src/com/android/contacts/util/Constants.java
+++ b/src/com/android/contacts/util/Constants.java
@@ -29,5 +29,6 @@
     public static final String SCHEME_SMSTO = "smsto";
     public static final String SCHEME_MAILTO = "mailto";
     public static final String SCHEME_IMTO = "imto";
+    public static final String SCHEME_SIP = "sip";
 
 }
diff --git a/src/com/android/contacts/views/ContactLoader.java b/src/com/android/contacts/views/ContactLoader.java
index 1cb003e..12d98b5 100644
--- a/src/com/android/contacts/views/ContactLoader.java
+++ b/src/com/android/contacts/views/ContactLoader.java
@@ -17,6 +17,7 @@
 package com.android.contacts.views;
 
 import com.android.contacts.util.DataStatus;
+import com.android.contacts.views.ContactLoader.Result;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -24,6 +25,10 @@
 import android.content.Context;
 import android.content.Entity;
 import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -31,8 +36,10 @@
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.DisplayNameSources;
 import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -70,8 +77,9 @@
         public static final Result ERROR = new Result();
 
         private final Uri mLookupUri;
-        private final String mLookupKey;
         private final Uri mUri;
+        private final long mDirectoryId;
+        private final String mLookupKey;
         private final long mId;
         private final long mNameRawContactId;
         private final int mDisplayNameSource;
@@ -87,14 +95,20 @@
         private final Integer mStatusLabel;
         private final String mStatusResPackage;
 
+        private String mDirectoryDisplayName;
+        private String mDirectoryType;
+        private String mDirectoryAccountName;
+        private int mDirectoryExportSupport;
+
         /**
          * Constructor for case "no contact found". This must only be used for the
          * final {@link Result#NOT_FOUND} singleton
          */
         private Result() {
             mLookupUri = null;
-            mLookupKey = null;
             mUri = null;
+            mDirectoryId = -1;
+            mLookupKey = null;
             mId = -1;
             mEntities = null;
             mStatuses = null;
@@ -114,13 +128,14 @@
         /**
          * Constructor to call when contact was found
          */
-        private Result(Uri lookupUri, String lookupKey, Uri uri, long id, long nameRawContactId,
-                int displayNameSource, long photoId, String displayName, String phoneticName,
-                boolean starred, Integer presence, String status, Long statusTimestamp,
-                Integer statusLabel, String statusResPackage) {
+        private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
+                long nameRawContactId, int displayNameSource, long photoId, String displayName,
+                String phoneticName, boolean starred, Integer presence, String status,
+                Long statusTimestamp, Integer statusLabel, String statusResPackage) {
             mLookupUri = lookupUri;
-            mLookupKey = lookupKey;
             mUri = uri;
+            mDirectoryId = directoryId;
+            mLookupKey = lookupKey;
             mId = id;
             mEntities = new ArrayList<Entity>();
             mStatuses = new HashMap<Long, DataStatus>();
@@ -137,6 +152,17 @@
             mStatusResPackage = statusResPackage;
         }
 
+        /**
+         * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
+         */
+        public void setDirectoryMetaData(String displayName, String directoryType,
+                String accountName, int exportSupport) {
+            mDirectoryDisplayName = displayName;
+            mDirectoryType = directoryType;
+            mDirectoryAccountName = accountName;
+            mDirectoryExportSupport = exportSupport;
+        }
+
         public Uri getLookupUri() {
             return mLookupUri;
         }
@@ -188,6 +214,31 @@
         public HashMap<Long, DataStatus> getStatuses() {
             return mStatuses;
         }
+
+        public long getDirectoryId() {
+            return mDirectoryId;
+        }
+
+        public boolean isDirectoryEntry() {
+            return mDirectoryId != Directory.DEFAULT && mDirectoryId != Directory.LOCAL_INVISIBLE;
+        }
+
+        public int getDirectoryExportSupport() {
+            return mDirectoryExportSupport;
+        }
+
+        public String getDirectoryDisplayName() {
+            return mDirectoryDisplayName;
+        }
+
+        public String getDirectoryType() {
+            return mDirectoryType;
+        }
+
+        public String getDirectoryAccountName() {
+            return mDirectoryAccountName;
+        }
+
     }
 
     private static class ContactQuery {
@@ -319,6 +370,23 @@
         public final static int STATUS = 54;
     }
 
+    private static class DirectoryQuery {
+        // Projection used for the query that loads all data for the entire contact.
+        final static String[] COLUMNS = new String[] {
+            Directory.DISPLAY_NAME,
+            Directory.PACKAGE_NAME,
+            Directory.TYPE_RESOURCE_ID,
+            Directory.ACCOUNT_NAME,
+            Directory.EXPORT_SUPPORT,
+        };
+
+        public final static int DISPLAY_NAME = 0;
+        public final static int PACKAGE_NAME = 1;
+        public final static int TYPE_RESOURCE_ID = 2;
+        public final static int ACCOUNT_NAME = 3;
+        public final static int EXPORT_SUPPORT = 4;
+    }
+
     private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
 
         @Override
@@ -326,7 +394,11 @@
             try {
                 final ContentResolver resolver = getContext().getContentResolver();
                 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
-                return loadContactEntity(resolver, uriCurrentFormat);
+                Result result = loadContactEntity(resolver, uriCurrentFormat);
+                if (result.isDirectoryEntry()) {
+                    loadDirectoryMetaData(result);
+                }
+                return result;
             } catch (Exception e) {
                 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
                 return Result.ERROR;
@@ -424,6 +496,11 @@
          * Extracts Contact level columns from the cursor.
          */
         private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
+            final String directoryParameter =
+                    contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+            final long directoryId = directoryParameter == null
+                    ? Directory.DEFAULT
+                    : Long.parseLong(directoryParameter);
             final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
             final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
             final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
@@ -447,9 +524,9 @@
 
             Uri lookupUri = ContentUris.withAppendedId(
                     Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
-            return new Result(lookupUri, lookupKey, contactUri, contactId, nameRawContactId,
-                    displayNameSource, photoId, displayName, phoneticName, starred, presence,
-                    status, statusTimestamp, statusLabel, statusResPackage);
+            return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
+                    nameRawContactId, displayNameSource, photoId, displayName, phoneticName,
+                    starred, presence, status, statusTimestamp, statusLabel, statusResPackage);
         }
 
         /**
@@ -536,6 +613,42 @@
             }
         }
 
+        private void loadDirectoryMetaData(Result result) {
+            long directoryId = result.getDirectoryId();
+
+            Cursor cursor = getContext().getContentResolver().query(
+                    ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
+                    DirectoryQuery.COLUMNS, null, null, null);
+            if (cursor == null) {
+                return;
+            }
+            try {
+                if (cursor.moveToFirst()) {
+                    final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+                    final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+                    final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+                    final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
+                    final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
+                    String directoryType = null;
+                    if (!TextUtils.isEmpty(packageName)) {
+                        PackageManager pm = getContext().getPackageManager();
+                        try {
+                            Resources resources = pm.getResourcesForApplication(packageName);
+                            directoryType = resources.getString(typeResourceId);
+                        } catch (NameNotFoundException e) {
+                            Log.w(TAG, "Contact directory resource not found: "
+                                    + packageName + "." + typeResourceId);
+                        }
+                    }
+
+                    result.setDirectoryMetaData(
+                            displayName, directoryType, accountName, exportSupport);
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+
         @Override
         protected void onPostExecute(Result result) {
             // The creator isn't interested in any further updates
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 6ac36a8..436e9b0 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -56,6 +56,7 @@
 import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.DisplayNameSources;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.StatusUpdates;
@@ -65,6 +66,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Note;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
@@ -236,6 +238,8 @@
             mAdapter.notifyDataSetChanged();
         }
         mListView.setEmptyView(mEmptyView);
+
+        getActivity().invalidateOptionsMenu();
     }
 
     /**
@@ -427,7 +431,7 @@
                     entry.maxLines = 100;
                     mOtherEntries.add(entry);
                 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    // Build note entries
+                    // Build Website entries
                     entry.uri = null;
                     entry.maxLines = 10;
                     try {
@@ -438,6 +442,19 @@
                         Log.e(TAG, "Couldn't parse website: " + entry.data);
                     }
                     mOtherEntries.add(entry);
+                } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+                    // Build SipAddress entries
+                    entry.uri = null;
+                    entry.maxLines = 1;
+                    entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                            Uri.fromParts(Constants.SCHEME_SIP, entry.data, null));
+                    mOtherEntries.add(entry);
+                    // TODO: Consider moving the SipAddress into its own
+                    // section (rather than lumping it in with mOtherEntries)
+                    // so that we can reposition it right under the phone number.
+                    // (Then, we'd also update FallbackSource.java to set
+                    // secondary=false for this field, and tweak the weight
+                    // of its DataKind.)
                 } else {
                     // Handle showing custom rows
                     entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
@@ -796,11 +813,35 @@
 
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
+        if (mContactData == null) {
+            return;
+        }
+
+        boolean isDirectoryEntry = mContactData.isDirectoryEntry();
+
         // Options only shows telephony-related settings (ringtone, send to voicemail).
         // ==> Hide if we don't have a telephone
         final MenuItem optionsMenu = menu.findItem(R.id.menu_options);
         final boolean deviceHasPhone = PhoneCapabilityTester.isPhone(mContext);
-        optionsMenu.setVisible(deviceHasPhone);
+        optionsMenu.setVisible(!isDirectoryEntry && deviceHasPhone);
+
+        final MenuItem editMenu = menu.findItem(R.id.menu_edit);
+        editMenu.setVisible(!isDirectoryEntry);
+
+        final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
+        deleteMenu.setVisible(!isDirectoryEntry);
+
+        final MenuItem shareMenu = menu.findItem(R.id.menu_share);
+        shareMenu.setVisible(!isDirectoryEntry && !mAllRestricted);
+
+        final MenuItem copyMenu = menu.findItem(R.id.menu_copy);
+        if (isDirectoryEntry) {
+            int exportSupport = mContactData.getDirectoryExportSupport();
+            copyMenu.setVisible(exportSupport == Directory.EXPORT_SUPPORT_ANY_ACCOUNT
+                    || exportSupport == Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY);
+        } else {
+            copyMenu.setVisible(false);
+        }
     }
 
     @Override
@@ -841,6 +882,10 @@
                 }
                 return true;
             }
+            case R.id.menu_copy: {
+                Toast.makeText(mContext, "Not implemented yet", Toast.LENGTH_SHORT).show();
+                return true;
+            }
         }
         return false;
     }
diff --git a/src/com/android/contacts/views/detail/ContactDetailHeaderView.java b/src/com/android/contacts/views/detail/ContactDetailHeaderView.java
index 00203da..553637d 100644
--- a/src/com/android/contacts/views/detail/ContactDetailHeaderView.java
+++ b/src/com/android/contacts/views/detail/ContactDetailHeaderView.java
@@ -64,6 +64,7 @@
     private ImageView mPresenceView;
     private TextView mStatusView;
     private TextView mStatusAttributionView;
+    private TextView mDirectoryNameView;
 
     private Uri mContactUri;
     private Listener mListener;
@@ -101,9 +102,10 @@
         mPhotoView = (QuickContactBadge) findViewById(R.id.photo);
 
         mPresenceView = (ImageView) findViewById(R.id.presence);
-
         mStatusView = (TextView)findViewById(R.id.status);
         mStatusAttributionView = (TextView)findViewById(R.id.status_date);
+
+        mDirectoryNameView = (TextView) findViewById(R.id.directory_name);
     }
 
     /**
@@ -121,6 +123,8 @@
         setStatus(
                 contactData.getStatus(), contactData.getStatusTimestamp(),
                 contactData.getStatusLabel(), contactData.getStatusResPackage());
+        setDirectoryName(contactData.isDirectoryEntry(), contactData.getDirectoryDisplayName(),
+                contactData.getDirectoryType(), contactData.getDirectoryAccountName());
     }
 
     /**
@@ -323,6 +327,27 @@
         setStatusAttribution(attribution);
     }
 
+    private void setDirectoryName(boolean isDirectoryEntry, String directoryDisplayName,
+            String directoryType, String directoryAccountName) {
+        if (isDirectoryEntry) {
+            String name = TextUtils.isEmpty(directoryDisplayName)
+                    ? directoryAccountName
+                    : directoryDisplayName;
+            String text;
+            if (TextUtils.isEmpty(name)) {
+                text = getContext().getString(
+                        R.string.contact_directory_description, directoryType);
+            } else {
+                text = getContext().getString(
+                        R.string.contact_directory_account_description, directoryType, name);
+            }
+            mDirectoryNameView.setText(text);
+            mDirectoryNameView.setVisibility(View.VISIBLE);
+        } else {
+            mDirectoryNameView.setVisibility(View.GONE);
+        }
+    }
+
     public void onClick(View view) {
         switch (view.getId()) {
             case R.id.star: {