Showing directory meta-data in contact view

Also, adding "Make personal copy" to the menu
(for now unimplemented)

Change-Id: I779ff81370eaf8021e0e740a7c70f3d8c3caee6b
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 f7eaebb..c8ae433 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1295,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/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 af194db..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;
@@ -237,6 +238,8 @@
             mAdapter.notifyDataSetChanged();
         }
         mListView.setEmptyView(mEmptyView);
+
+        getActivity().invalidateOptionsMenu();
     }
 
     /**
@@ -810,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
@@ -855,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: {