Use Toolbar for QC

* Moved star, add and edit buttons into Toolbar
* Fixed implementation of add/edit: copied the
  ContractDetailFragment implementation
* Added unimplemented Share and Place on Home menu items
* Deleted some of the quickcontact_activity.xml files
* No longer handle click events on Contact photo view

Change-Id: Id333b11e89d8755ace600700be15077fd47b6172
diff --git a/res/drawable-hdpi/ic_star_24dp.png b/res/drawable-hdpi/ic_star_24dp.png
new file mode 100644
index 0000000..d053adb
--- /dev/null
+++ b/res/drawable-hdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star_outline_24dp.png b/res/drawable-hdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..809f1ee
--- /dev/null
+++ b/res/drawable-hdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_24dp.png b/res/drawable-mdpi/ic_star_24dp.png
new file mode 100644
index 0000000..f68b428
--- /dev/null
+++ b/res/drawable-mdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_outline_24dp.png b/res/drawable-mdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..743aaae
--- /dev/null
+++ b/res/drawable-mdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_24dp.png b/res/drawable-xhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..49faab8
--- /dev/null
+++ b/res/drawable-xhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_outline_24dp.png b/res/drawable-xhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..0ce8d31
--- /dev/null
+++ b/res/drawable-xhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_24dp.png b/res/drawable-xxhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..0aa8a26
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_outline_24dp.png b/res/drawable-xxhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..1f9a7a2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_star_24dp.png b/res/drawable-xxxhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..7477c14
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_star_outline_24dp.png b/res/drawable-xxxhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..3ae24c7
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/layout-land/quickcontact_activity.xml b/res/layout-land/quickcontact_activity.xml
index 497e3dd..552f568 100644
--- a/res/layout-land/quickcontact_activity.xml
+++ b/res/layout-land/quickcontact_activity.xml
@@ -27,7 +27,23 @@
         android:layout_height="match_parent"
         ex:ratio="1.0"
         ex:direction="heightToWidth">
-        <include layout="@layout/quickcontact_photo_container" />
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <ImageView
+                android:id="@+id/photo"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
+                android:contentDescription="@string/description_contact_photo" />
+            <!-- Need to set a non null background on Toolbar in order for MenuItem
+                ripples to be drawn on this view, instead of another-->
+            <Toolbar
+                android:layout_width="match_parent"
+                android:layout_height="?android:attr/actionBarSize"
+                android:background="#00000000"
+                android:id="@+id/toolbar"/>
+        </FrameLayout>
     </view>
     <com.android.contacts.quickcontact.ExpandingEntryCardView
         style="@style/ExpandingEntryCardStyle"
diff --git a/res/layout-sw600dp-land/quickcontact_activity.xml b/res/layout-sw600dp-land/quickcontact_activity.xml
deleted file mode 100644
index 239c50c..0000000
--- a/res/layout-sw600dp-land/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/content"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:padding="32dip"
-    android:orientation="horizontal">
-    <FrameLayout
-        android:layout_width="360dip"
-        android:layout_height="360dip">
-        <include layout="@layout/quickcontact_photo_container" />
-    </FrameLayout>
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/communication_card"
-        android:layout_marginTop="@dimen/communication_card_marginTop"
-        android:visibility="gone" />
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/recent_card"
-        android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw600dp/quickcontact_activity.xml b/res/layout-sw600dp/quickcontact_activity.xml
deleted file mode 100644
index 6c275b1..0000000
--- a/res/layout-sw600dp/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/content"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:padding="32dip"
-    android:orientation="vertical" >
-    <FrameLayout
-        android:layout_width="360dip"
-        android:layout_height="@dimen/quick_contact_photo_container_height">
-        <include layout="@layout/quickcontact_photo_container" />
-    </FrameLayout>
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/communication_card"
-        android:layout_marginTop="@dimen/communication_card_marginTop"
-        android:visibility="gone" />
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/recent_card"
-        android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw720dp-land/quickcontact_activity.xml b/res/layout-sw720dp-land/quickcontact_activity.xml
deleted file mode 100644
index 239c50c..0000000
--- a/res/layout-sw720dp-land/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/content"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:padding="32dip"
-    android:orientation="horizontal">
-    <FrameLayout
-        android:layout_width="360dip"
-        android:layout_height="360dip">
-        <include layout="@layout/quickcontact_photo_container" />
-    </FrameLayout>
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/communication_card"
-        android:layout_marginTop="@dimen/communication_card_marginTop"
-        android:visibility="gone" />
-    <com.android.contacts.quickcontact.ExpandingEntryCardView
-        style="@style/ExpandingEntryCardStyle"
-        android:id="@+id/recent_card"
-        android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index 9af1079..a7c12dd 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -24,14 +24,28 @@
     android:focusableInTouchMode="true"
     android:descendantFocusability="afterDescendants" >
 
-    <!-- Will contain ToolBar and image behind ToolBar -->
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="@dimen/quickcontact_maximum_header_height"
         android:layout_marginTop="@dimen/quickcontact_starting_empty_height"
         android:background="@color/card_margin_color"
         android:id="@+id/toolbar_parent">
-        <include layout="@layout/quickcontact_photo_container" />
+
+        <ImageView
+            android:id="@+id/photo"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerCrop"
+            android:contentDescription="@string/description_contact_photo" />
+
+        <!-- Need to set a non null background on Toolbar in order for MenuItem
+            ripples to be drawn on this view, instead of another-->
+        <Toolbar
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:background="#00000000"
+            android:id="@+id/toolbar"/>
+
     </FrameLayout>
 
     <com.android.contacts.widget.TouchlessScrollView
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
deleted file mode 100644
index 5685427..0000000
--- a/res/layout/quickcontact_photo_container.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<merge
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <RelativeLayout
-        android:id="@+id/photo_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical">
-        <ImageView
-            android:id="@+id/photo"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:scaleType="centerCrop"
-            android:clickable="true"
-            android:contentDescription="@string/description_contact_photo" />
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:layout_alignParentTop="true"
-            android:background="#4CFFFFFF" />
-        <View
-            android:id="@+id/photo_text_bar"
-            android:layout_width="0dip"
-            android:layout_height="42dip"
-            android:layout_alignBottom="@id/photo"
-            android:layout_alignLeft="@id/photo"
-            android:layout_alignRight="@id/photo"
-            android:layout_alignStart="@id/photo"
-            android:layout_alignEnd="@id/photo"
-            android:background="@color/quickcontact_name_detail_background" />
-        <ImageView
-            android:id="@+id/contact_edit_image"
-            android:src="@drawable/ic_create_24dp"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_marginRight="16dip"
-            android:layout_marginEnd="16dip"
-            android:layout_marginBottom="5dip"
-            android:layout_alignBottom="@id/photo_text_bar"
-            android:layout_alignRight="@id/photo_text_bar"
-            android:layout_alignEnd="@id/photo_text_bar"
-            android:clickable="true"
-            android:contentDescription="@string/editContactDescription" />
-        <ImageView
-            android:id="@+id/quickcontact_star_button"
-            android:src="@drawable/ic_favorite_off_lt"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_marginBottom="5dip"
-            android:layout_marginRight="16dip"
-            android:layout_marginEnd="16dip"
-            android:layout_alignBottom="@id/photo_text_bar"
-            android:layout_toLeftOf="@id/contact_edit_image"
-            android:layout_toStartOf="@id/contact_edit_image"
-            android:clickable="true"
-            android:contentDescription="@string/menu_addStar" />
-        <TextView
-            android:id="@+id/name"
-            android:layout_width="match_parent"
-            android:layout_height="42dip"
-            android:layout_alignBottom="@id/photo"
-            android:layout_alignLeft="@id/photo"
-            android:layout_alignStart="@id/photo"
-            android:layout_toLeftOf="@id/quickcontact_star_button"
-            android:layout_toStartOf="@id/quickcontact_star_button"
-            android:gravity="center_vertical"
-            android:paddingLeft="8dip"
-            android:paddingStart="8dip"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:textColor="@android:color/white"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-    </RelativeLayout>
-</merge>
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
new file mode 100644
index 0000000..34a6a10
--- /dev/null
+++ b/res/menu/quickcontact.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_star"
+        android:showAsAction="always" />
+
+    <item
+        android:id="@+id/menu_edit"
+        android:icon="@drawable/ic_create_24dp"
+        android:showAsAction="always" />
+
+    <item
+        android:id="@+id/menu_share"
+        android:title="@string/menu_share"
+        android:alphabeticShortcut="s" />
+
+    <item
+        android:id="@+id/menu_create_contact_shortcut"
+        android:title="@string/menu_create_contact_shortcut" />
+</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6be304b..c1bd3c0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -658,6 +658,10 @@
     <string name="timestamp_string_yesterday">Yesterday</string>
     <!-- Timestamp string for interactions from tomorrow. [CHAR LIMIT=40] -->
     <string name="timestamp_string_tomorrow">Tomorrow</string>
+
     <!-- Title of sms action entry. [CHAR LIMIT=60] -->
     <string name="send_message">Send message</string>
+
+    <!-- Toast that appears when you are copying a directory contact into your personal contacts -->
+    <string name="toast_making_personal_copy">Creating a personal copy...</string>
 </resources>
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 48aa0f7..67d14e2 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -176,8 +176,8 @@
         if (!isDirectoryEntry && !isUserProfile) {
             starredMenuItem.setVisible(true);
             final int resId = isStarred
-                    ? R.drawable.btn_star_on_normal_holo_light
-                    : R.drawable.btn_star_off_normal_holo_light;
+                    ? R.drawable.ic_star_24dp
+                    : R.drawable.ic_star_outline_24dp;
             starredMenuItem.setIcon(resId);
             starredMenuItem.setChecked(isStarred);
             starredMenuItem.setTitle(isStarred ? R.string.menu_removeStar : R.string.menu_addStar);
diff --git a/src/com/android/contacts/quickcontact/DirectoryContactUtil.java b/src/com/android/contacts/quickcontact/DirectoryContactUtil.java
new file mode 100644
index 0000000..d483af3
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/DirectoryContactUtil.java
@@ -0,0 +1,83 @@
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
+import com.android.contacts.quickcontact.QuickContactActivity.SelectAccountDialogFragmentListener;
+
+import android.app.FragmentManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.ContactsContract.Directory;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to support adding directory contacts.
+ *
+ * This class is coupled with {@link QuickContactActivity}, but is left out of
+ * QuickContactActivity.java to avoid ballooning the size of the file.
+ */
+public class DirectoryContactUtil {
+
+    public static boolean isDirectoryContact(Contact contactData) {
+        // Not a directory contact? Nothing to fix here
+        if (contactData == null || !contactData.isDirectoryEntry()) return false;
+
+        // No export support? Too bad
+        return contactData.getDirectoryExportSupport() != Directory.EXPORT_SUPPORT_NONE;
+    }
+
+    public static void addToMyContacts(Contact contactData, Context context,
+            FragmentManager fragmentManager,
+            SelectAccountDialogFragmentListener selectAccountCallbacks) {
+        int exportSupport = contactData.getDirectoryExportSupport();
+        switch (exportSupport) {
+            case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: {
+                createCopy(contactData.getContentValues(),
+                        new AccountWithDataSet(contactData.getDirectoryAccountName(),
+                        contactData.getDirectoryAccountType(), null),
+                        context);
+                break;
+            }
+            case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: {
+                final List<AccountWithDataSet> accounts =
+                        AccountTypeManager.getInstance(context).getAccounts(true);
+                if (accounts.isEmpty()) {
+                    createCopy(contactData.getContentValues(), null, context);
+                    return;  // Don't show a dialog.
+                }
+
+                // In the common case of a single writable account, auto-select
+                // it without showing a dialog.
+                if (accounts.size() == 1) {
+                    createCopy(contactData.getContentValues(), accounts.get(0), context);
+                    return;  // Don't show a dialog.
+                }
+
+                SelectAccountDialogFragment.show(fragmentManager,
+                        selectAccountCallbacks, R.string.dialog_new_contact_account,
+                        AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, null);
+                break;
+            }
+        }
+    }
+
+    public static void createCopy(
+            ArrayList<ContentValues> values, AccountWithDataSet account,
+            Context context) {
+        Toast.makeText(context, R.string.toast_making_personal_copy,
+                Toast.LENGTH_LONG).show();
+        Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
+                context, values, account,
+                QuickContactActivity.class, Intent.ACTION_VIEW);
+        context.startService(serviceIntent);
+    }
+}
diff --git a/src/com/android/contacts/quickcontact/InvisibleContactUtil.java b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
new file mode 100644
index 0000000..3609fbc
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
@@ -0,0 +1,113 @@
+package com.android.contacts.quickcontact;
+
+
+import com.google.common.collect.Iterables;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.RawContact;
+import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.model.RawContactDeltaList;
+import com.android.contacts.common.model.RawContactModifier;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.dataitem.DataItem;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.model.dataitem.GroupMembershipDataItem;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+
+import java.util.List;
+
+/**
+ * Utility class to support adding invisible contacts. Ie, contacts that don't belong to the
+ * default group.
+ */
+public class InvisibleContactUtil {
+
+    public static boolean isInvisibleAndAddable(Contact contactData, Context context) {
+        // Only local contacts
+        if (contactData == null || contactData.isDirectoryEntry()) return false;
+
+        // User profile cannot be added to contacts
+        if (contactData.isUserProfile()) return false;
+
+        // Only if exactly one raw contact
+        if (contactData.getRawContacts().size() != 1) return false;
+
+        // test if the default group is assigned
+        final List<GroupMetaData> groups = contactData.getGroupMetaData();
+
+        // For accounts without group support, groups is null
+        if (groups == null) return false;
+
+        // remember the default group id. no default group? bail out early
+        final long defaultGroupId = getDefaultGroupId(groups);
+        if (defaultGroupId == -1) return false;
+
+        final RawContact rawContact = (RawContact) contactData.getRawContacts().get(0);
+        final AccountType type = rawContact.getAccountType(context);
+        // Offline or non-writeable account? Nothing to fix
+        if (type == null || !type.areContactsWritable()) return false;
+
+        // Check whether the contact is in the default group
+        boolean isInDefaultGroup = false;
+        for (DataItem dataItem : Iterables.filter(
+                rawContact.getDataItems(), GroupMembershipDataItem.class)) {
+            GroupMembershipDataItem groupMembership = (GroupMembershipDataItem) dataItem;
+            final Long groupId = groupMembership.getGroupRowId();
+            if (groupId != null && groupId == defaultGroupId) {
+                isInDefaultGroup = true;
+                break;
+            }
+        }
+
+        return !isInDefaultGroup;
+    }
+
+    public static void addToDefaultGroup(Contact contactData, Context context) {
+        final long defaultGroupId = getDefaultGroupId(contactData.getGroupMetaData());
+        // there should always be a default group (otherwise the button would be invisible),
+        // but let's be safe here
+        if (defaultGroupId == -1) return;
+
+        // add the group membership to the current state
+        final RawContactDeltaList contactDeltaList = contactData.createRawContactDeltaList();
+        final RawContactDelta rawContactEntityDelta = contactDeltaList.get(0);
+
+        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
+                context);
+        final AccountType type = rawContactEntityDelta.getAccountType(accountTypes);
+        final DataKind groupMembershipKind = type.getKindForMimetype(
+                GroupMembership.CONTENT_ITEM_TYPE);
+        final ValuesDelta entry = RawContactModifier.insertChild(rawContactEntityDelta,
+                groupMembershipKind);
+        if (entry == null) return;
+        entry.setGroupRowId(defaultGroupId);
+
+        // and fire off the intent. we don't need a callback, as the database listener
+        // should update the ui
+        final Intent intent = ContactSaveService.createSaveContactIntent(
+                context,
+                contactDeltaList, "", 0, false, QuickContactActivity.class,
+                Intent.ACTION_VIEW, null);
+        context.startService(intent);
+    }
+
+    /** return default group id or -1 if no group or several groups are marked as default */
+    private static long getDefaultGroupId(List<GroupMetaData> groups) {
+        long defaultGroupId = -1;
+        for (GroupMetaData group : groups) {
+            if (group.isDefaultGroup()) {
+                // two default groups? return neither
+                if (defaultGroupId != -1) return -1;
+                defaultGroupId = group.getGroupId();
+            }
+        }
+        return defaultGroupId;
+    }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index b672d93..1e703a0 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -19,9 +19,9 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
+import android.app.Fragment;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.ContentUris;
-import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
 import android.content.pm.PackageManager;
@@ -42,38 +42,40 @@
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.Intents.Insert;
-import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsContract.RawContacts;
 import android.support.v7.graphics.Palette;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.WindowManager;
 import android.widget.ImageView;
-import android.widget.TextView;
 import android.widget.Toast;
+import android.widget.Toolbar;
 
 import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
 import com.android.contacts.common.Collapser;
 import com.android.contacts.R;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
 import com.android.contacts.common.lettertiles.LetterTileDrawable;
 import com.android.contacts.common.model.AccountTypeManager;
 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.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.dataitem.DataItem;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.common.model.dataitem.EmailDataItem;
 import com.android.contacts.common.model.dataitem.ImDataItem;
 import com.android.contacts.common.model.dataitem.PhoneDataItem;
 import com.android.contacts.common.util.DataStatus;
-import com.android.contacts.common.util.UriUtils;
+import com.android.contacts.detail.ContactDetailDisplayUtils;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.ContactInteraction;
 import com.android.contacts.interactions.SmsInteractionsLoader;
@@ -101,7 +103,7 @@
  * data asynchronously, and then shows a popup with details centered around
  * {@link Intent#getSourceBounds()}.
  */
-public class QuickContactActivity extends Activity {
+public class QuickContactActivity extends ContactsActivity {
 
     /**
      * QuickContacts immediately takes up the full screen. All possible information is shown.
@@ -128,14 +130,11 @@
     private int mStatusBarColor;
     private boolean mHasAlreadyBeenOpened;
 
-    private View mPhotoContainer;
-
     private ImageView mPhotoView;
-    private ImageView mEditOrAddContactImage;
-    private ImageView mStarImage;
     private ExpandingEntryCardView mCommunicationCard;
     private ExpandingEntryCardView mRecentCard;
     private MultiShrinkScroller mScroller;
+    private SelectAccountDialogFragmentListener mSelectAccountFragmentListener;
     private AsyncTask<Void, Void, Void> mEntriesAndActionsTask;
 
     private static final int MIN_NUM_COMMUNICATION_ENTRIES_SHOWN = 3;
@@ -191,35 +190,7 @@
     private static final int[] mRecentLoaderIds = new int[LOADER_SMS_ID];
     private Map<Integer, List<ContactInteraction>> mRecentLoaderResults;
 
-    final OnClickListener mEditContactClickHandler = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            final Intent intent = new Intent(Intent.ACTION_EDIT, mLookupUri);
-            mContactLoader.cacheResult();
-            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-            startActivityForResult(intent, REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
-        }
-    };
-
-    final OnClickListener mAddToContactsClickHandler = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (mContactData == null) {
-                Log.e(TAG, "Empty contact data when trying to add to contact");
-                return;
-            }
-            final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
-            intent.setType(Contacts.CONTENT_ITEM_TYPE);
-
-            // Only pre-fill the name field if the provided display name is an organization
-            // name or better (e.g. structured name, nickname)
-            if (mContactData.getDisplayNameSource() >= DisplayNameSources.ORGANIZATION) {
-                intent.putExtra(Insert.NAME, mContactData.getDisplayName());
-            }
-            intent.putExtra(Insert.DATA, mContactData.getContentValues());
-            startActivity(intent);
-        }
-    };
+    private static final String FRAGMENT_TAG_SELECT_ACCOUNT = "select_account_fragment";
 
     final OnClickListener mEntryClickHandler = new OnClickListener() {
         @Override
@@ -233,6 +204,36 @@
         }
     };
 
+    /**
+     * Headless fragment used to handle account selection callbacks invoked from
+     * {@link DirectoryContactUtil}.
+     */
+    public static class SelectAccountDialogFragmentListener extends Fragment
+            implements SelectAccountDialogFragment.Listener {
+
+        private QuickContactActivity mQuickContactActivity;
+
+        public SelectAccountDialogFragmentListener() {}
+
+        @Override
+        public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
+            DirectoryContactUtil.createCopy(mQuickContactActivity.mContactData.getContentValues(),
+                    account, mQuickContactActivity);
+        }
+
+        @Override
+        public void onAccountSelectorCancelled() {}
+
+        /**
+         * Set the parent activity. Since rotation can cause this fragment to be used across
+         * more than one activity instance, we need to explicitly set this value instead
+         * of making this class non-static.
+         */
+        public void setQuickContactActivity(QuickContactActivity quickContactActivity) {
+            mQuickContactActivity = quickContactActivity;
+        }
+    }
+
     final MultiShrinkScrollerListener mMultiShrinkScrollerListener
             = new MultiShrinkScrollerListener() {
         @Override
@@ -261,27 +262,7 @@
         // silliness of the animation by setting the navigation bar transparent.
         getWindow().setNavigationBarColor(Color.TRANSPARENT);
 
-        // Parse intent
-        final Intent intent = getIntent();
-
-        Uri lookupUri = intent.getData();
-
-        // Check to see whether it comes from the old version.
-        if (lookupUri != null && LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {
-            final long rawContactId = ContentUris.parseId(lookupUri);
-            lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
-                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
-        }
-
-        mExtraMode = getIntent().getIntExtra(QuickContact.EXTRA_MODE,
-                QuickContact.MODE_LARGE);
-
-        mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
-
-        mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
-
-        mContactLoader = (ContactLoader) getLoaderManager().initLoader(
-                LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+        processIntent(getIntent());
 
         // Show QuickContact in front of soft input
         getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
@@ -289,14 +270,10 @@
 
         setContentView(R.layout.quickcontact_activity);
 
-        mEditOrAddContactImage = (ImageView) findViewById(R.id.contact_edit_image);
-        mStarImage = (ImageView) findViewById(R.id.quickcontact_star_button);
         mCommunicationCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
         mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
         mScroller = (MultiShrinkScroller) findViewById(R.id.multiscroller);
 
-        mEditOrAddContactImage.setOnClickListener(mEditContactClickHandler);
-
         mCommunicationCard.setOnClickListener(mEntryClickHandler);
         mCommunicationCard.setTitle(getResources().getString(R.string.communication_card_title));
         mCommunicationCard.setExpandButtonText(
@@ -305,13 +282,11 @@
         mRecentCard.setOnClickListener(mEntryClickHandler);
         mRecentCard.setTitle(getResources().getString(R.string.recent_card_title));
 
-        // find and prepare correct header view
-        mPhotoContainer = findViewById(R.id.photo_container);
+        mPhotoView = (ImageView) findViewById(R.id.photo);
 
-        setHeaderNameText(R.id.name, R.string.missing_name);
-
-        mPhotoView = (ImageView) mPhotoContainer.findViewById(R.id.photo);
-        mPhotoView.setOnClickListener(mEditContactClickHandler);
+        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setActionBar(toolbar);
+        setHeaderNameText(R.string.missing_name);
 
         mHasAlreadyBeenOpened = savedInstanceState != null;
 
@@ -332,8 +307,16 @@
             }
         }
 
-
         mDrawablesToTint = new ArrayList<>();
+        mSelectAccountFragmentListener= (SelectAccountDialogFragmentListener) getFragmentManager()
+                .findFragmentByTag(FRAGMENT_TAG_SELECT_ACCOUNT);
+        if (mSelectAccountFragmentListener == null) {
+            mSelectAccountFragmentListener = new SelectAccountDialogFragmentListener();
+            getFragmentManager().beginTransaction().add(0, mSelectAccountFragmentListener,
+                    FRAGMENT_TAG_SELECT_ACCOUNT).commit();
+            mSelectAccountFragmentListener.setRetainInstance(true);
+        }
+        mSelectAccountFragmentListener.setQuickContactActivity(this);
 
         Trace.endSection();
     }
@@ -351,6 +334,33 @@
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         mHasAlreadyBeenOpened = true;
+        processIntent(intent);
+    }
+
+    private void processIntent(Intent intent) {
+        Uri lookupUri = intent.getData();
+
+        // Check to see whether it comes from the old version.
+        if (lookupUri != null && LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {
+            final long rawContactId = ContentUris.parseId(lookupUri);
+            lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+        }
+        mExtraMode = getIntent().getIntExtra(QuickContact.EXTRA_MODE,
+                QuickContact.MODE_LARGE);
+        final Uri oldLookupUri = mLookupUri;
+
+        mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+        mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+        if (oldLookupUri == null) {
+            mContactLoader = (ContactLoader) getLoaderManager().initLoader(
+                    LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+        } else if (oldLookupUri != mLookupUri) {
+            // After copying a directory contact, the contact URI changes. Therefore,
+            // we need to restart the loader and reload the new contact.
+            mContactLoader = (ContactLoader) getLoaderManager().restartLoader(
+                    LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+        }
     }
 
     private void runEntranceAnimation() {
@@ -367,17 +377,14 @@
     }
 
     /** Assign this string to the view if it is not empty. */
-    private void setHeaderNameText(int id, int resId) {
-        setHeaderNameText(id, getText(resId));
+    private void setHeaderNameText(int resId) {
+        getActionBar().setTitle(getText(resId));
     }
 
     /** Assign this string to the view if it is not empty. */
-    private void setHeaderNameText(int id, CharSequence value) {
-        final View view = mPhotoContainer.findViewById(id);
-        if (view instanceof TextView) {
-            if (!TextUtils.isEmpty(value)) {
-                ((TextView)view).setText(value);
-            }
+    private void setHeaderNameText(CharSequence value) {
+        if (!TextUtils.isEmpty(value)) {
+            getActionBar().setTitle(value);
         }
     }
 
@@ -401,58 +408,7 @@
     private void bindContactData(final Contact data) {
         Trace.beginSection("bindContactData");
         mContactData = data;
-        final Context context = this;
-
-        mEditOrAddContactImage.setVisibility(isMimeExcluded(Contacts.CONTENT_ITEM_TYPE) ?
-                View.GONE : View.VISIBLE);
-        final boolean isStarred = data.getStarred();
-        if (isStarred) {
-            mStarImage.setImageResource(R.drawable.ic_favorite_on_lt);
-            mStarImage.setContentDescription(
-                getResources().getString(R.string.menu_removeStar));
-        } else {
-            mStarImage.setImageResource(R.drawable.ic_favorite_off_lt);
-            mStarImage.setContentDescription(
-                getResources().getString(R.string.menu_addStar));
-        }
-        final Uri lookupUri = data.getLookupUri();
-
-        // If this is a json encoded URI, there is no local contact to star
-        if (UriUtils.isEncodedContactUri(lookupUri)) {
-            mStarImage.setVisibility(View.GONE);
-
-            // If directory export support is not allowed, then don't allow the user to add
-            // to contacts
-            if (mContactData.getDirectoryExportSupport() == Directory.EXPORT_SUPPORT_NONE) {
-                configureHeaderClickActions(false);
-            } else {
-                configureHeaderClickActions(true);
-            }
-        } else {
-            configureHeaderClickActions(false);
-            mStarImage.setVisibility(View.VISIBLE);
-            mStarImage.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View view) {
-                    // Toggle "starred" state
-                    // Make sure there is a contact
-                    if (lookupUri != null) {
-                        // Changes the state of the image already before sending updates to the
-                        // database
-                        if (isStarred) {
-                            mStarImage.setImageResource(R.drawable.ic_favorite_off_lt);
-                        } else {
-                            mStarImage.setImageResource(R.drawable.ic_favorite_on_lt);
-                        }
-
-                        // Now perform the real save
-                        final Intent intent = ContactSaveService.createSetStarredIntent(context,
-                                lookupUri, !isStarred);
-                        context.startService(intent);
-                    }
-                }
-            });
-        }
+        invalidateOptionsMenu();
 
         mDefaultsMap.clear();
 
@@ -461,7 +417,7 @@
 
         mPhotoSetter.setupContactPhoto(data, mPhotoView);
         extractAndApplyTintFromPhotoViewAsynchronously();
-        setHeaderNameText(R.id.name, data.getDisplayName());
+        setHeaderNameText(data.getDisplayName());
 
         Trace.endSection();
 
@@ -517,7 +473,7 @@
         }
 
         final boolean hasData = !sortedActionMimeTypes.isEmpty();
-        mCommunicationCard.setVisibility(hasData ? View.VISIBLE: View.GONE);
+        mCommunicationCard.setVisibility(hasData ? View.VISIBLE : View.GONE);
 
         Trace.endSection();
     }
@@ -748,24 +704,6 @@
     }
 
     /**
-     * Bind the correct image resource and click handlers to the header views
-     *
-     * @param canAdd Whether or not the user can directly add information in this quick contact
-     * to their local contacts
-     */
-    private void configureHeaderClickActions(boolean canAdd) {
-        if (canAdd) {
-            mEditOrAddContactImage.setImageResource(R.drawable.ic_person_add_24dp);
-            mEditOrAddContactImage.setOnClickListener(mAddToContactsClickHandler);
-            mPhotoView.setOnClickListener(mAddToContactsClickHandler);
-        } else {
-            mEditOrAddContactImage.setImageResource(R.drawable.ic_create_24dp);
-            mEditOrAddContactImage.setOnClickListener(mEditContactClickHandler);
-            mPhotoView.setOnClickListener(mEditContactClickHandler);
-        }
-    }
-
-    /**
      * Converts a list of Action into a list of Entry
      * @param actions The list of Action to convert
      * @return The converted list of Entry
@@ -869,8 +807,10 @@
             if (mLookupUri == null) {
                 Log.wtf(TAG, "Lookup uri wasn't initialized. Loader was started too early");
             }
+            // Load all contact data. We need loadGroupMetaData=true to determine whether the
+            // contact is invisible. If it is, we need to display an "Add to Contacts" MenuItem.
             return new ContactLoader(getApplicationContext(), mLookupUri,
-                    false /*loadGroupMetaData*/, false /*loadInvitableAccountTypes*/,
+                    true /*loadGroupMetaData*/, false /*loadInvitableAccountTypes*/,
                     false /*postViewNotification*/, true /*computeFormattedPhoneNumber*/);
         }
     };
@@ -974,7 +914,7 @@
 
     /**
      * Applies the theme color as extracted in
-     * {@link extractAndApplyTintFromPhotoViewAsynchonously()} if available. If the color is not
+     * {@link #extractAndApplyTintFromPhotoViewAsynchronously()} if available. If the color is not
      * available, store a reference to the drawable to tint when a color becomes available.
      */
     private Drawable applyThemeColorIfAvailable(Drawable drawable) {
@@ -983,6 +923,90 @@
         } else {
             mDrawablesToTint.add(drawable);
         }
-        return drawable;
+        return drawable; 
+    }
+
+    /**
+     * Returns true if it is possible to edit the current contact.
+     */
+    private boolean isContactEditable() {
+        return mContactData != null && !mContactData.isDirectoryEntry();
+    }
+
+    private void editContact() {
+        final Intent intent = new Intent(Intent.ACTION_EDIT, mLookupUri);
+        mContactLoader.cacheResult();
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+        startActivityForResult(intent, REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
+    }
+
+    private void toggleStar(MenuItem starredMenuItem) {
+        // Make sure there is a contact
+        if (mLookupUri != null) {
+            // Read the current starred value from the UI instead of using the last
+            // loaded state. This allows rapid tapping without writing the same
+            // value several times
+            final boolean isStarred = starredMenuItem.isChecked();
+
+            // To improve responsiveness, swap out the picture (and tag) in the UI already
+            ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
+                    mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
+                    !isStarred);
+
+            // Now perform the real save
+            Intent intent = ContactSaveService.createSetStarredIntent(
+                    QuickContactActivity.this, mLookupUri, !isStarred);
+            startService(intent);
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.quickcontact, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        if (mContactData != null) {
+            final MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
+            ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
+                    mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
+                    mContactData.getStarred());
+            // Configure edit MenuItem
+            final MenuItem editMenuItem = menu.findItem(R.id.menu_edit);
+            editMenuItem.setVisible(true);
+            if (DirectoryContactUtil.isDirectoryContact(mContactData) || InvisibleContactUtil
+                    .isInvisibleAndAddable(mContactData, this)) {
+                editMenuItem.setIcon(R.drawable.ic_person_add_24dp);
+            } else if (isContactEditable()) {
+                editMenuItem.setIcon(R.drawable.ic_create_24dp);
+            } else {
+                editMenuItem.setVisible(false);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_star:
+                toggleStar(item);
+                return true;
+            case R.id.menu_edit:
+                if (DirectoryContactUtil.isDirectoryContact(mContactData)) {
+                    DirectoryContactUtil.addToMyContacts(mContactData, this, getFragmentManager(),
+                            mSelectAccountFragmentListener);
+                } else if (InvisibleContactUtil.isInvisibleAndAddable(mContactData, this)) {
+                    InvisibleContactUtil.addToDefaultGroup(mContactData, this);
+                } else if (isContactEditable()) {
+                    editContact();
+                }
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
     }
 }