Merge "UI improvements in the call details."
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_container.xml b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
new file mode 100644
index 0000000..b6a6319
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+
+<!--
+  Two-column layout for a contact with social updates. If the contact does not
+  have social updates, then the second fragment view will just be hidden.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:scrollbars="none"
+    android:orientation="horizontal">
+
+    <fragment class="com.android.contacts.detail.ContactDetailFragment"
+        android:id="@+id/about_fragment"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="3" />
+
+    <fragment class="com.android.contacts.detail.ContactDetailUpdatesFragment"
+        android:id="@+id/updates_fragment"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="2" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 6d702e7..7301d24 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -79,16 +79,27 @@
             ex:animationDuration="200"
             android:visibility="gone">
 
-            <fragment
-                android:id="@+id/contact_detail_fragment"
-                class="com.android.contacts.detail.ContactDetailFragment"
+            <!-- This layout includes all possible views needed for a contact detail page -->
+            <include
+                layout="@layout/contact_detail_container"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent" />
+                android:layout_height="match_parent"/>
+
+            <!-- This invisible worker fragment loads the contact's details -->
+            <fragment
+                android:id="@+id/contact_detail_loader_fragment"
+                class="com.android.contacts.detail.ContactLoaderFragment"
+                android:layout_height="0dip"
+                android:layout_width="0dip"
+                android:visibility="gone"/>
+
+            <!-- This is the group detail page -->
             <fragment
                 android:id="@+id/group_detail_fragment"
                 class="com.android.contacts.group.GroupDetailFragment"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent" />
+                android:layout_height="match_parent"
+                android:visibility="gone" />
         </view>
 
         <view
diff --git a/res/layout/contact_detail_container.xml b/res/layout/contact_detail_container.xml
new file mode 100644
index 0000000..0b5b85a
--- /dev/null
+++ b/res/layout/contact_detail_container.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<!--
+  Layout for the contact card page. If the contact has social updates, then
+  the ViewPager and ContactDetailTabCarousel are shown together. If there
+  aren't any social updates, then just the ContactDetailFragment will be
+  shown. We include all 3 views even though they are never shown
+  simultaneously because this layout is reused but set with different data
+  each time (i.e. on a tablet).
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/pager"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:paddingTop="20dip"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <include
+        android:id="@+id/tab_carousel"
+        layout="@layout/contact_detail_tab_carousel"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="20dip"
+        android:paddingLeft="10dip"
+        android:paddingRight="10dip"/>
+
+    <fragment
+        android:id="@+id/contact_detail_fragment"
+        class="com.android.contacts.detail.ContactDetailFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-sw580dp-w1000dp/dimens.xml
index 45da0a8..dfa1b81 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-sw580dp-w1000dp/dimens.xml
@@ -21,4 +21,5 @@
     <dimen name="action_bar_search_spacing">32dip</dimen>
     <dimen name="detail_header_view_margin">16dip</dimen>
     <dimen name="detail_header_attribution_height">56dip</dimen>
+    <dimen name="detail_tab_carousel_height">0dip</dimen>
 </resources>
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index f1e5736..0c1e5fb 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -32,5 +32,5 @@
     <dimen name="shortcut_icon_size">64dip</dimen>
     <dimen name="list_section_height">37dip</dimen>
     <dimen name="directory_header_height">56dip</dimen>
-    <dimen name="detail_tab_carousel_height">150dip</dimen>
+    <dimen name="detail_tab_carousel_height">256dip</dimen>
 </resources>
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index b45ba39..800bfb1 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -62,6 +62,7 @@
 
 import java.util.ArrayList;
 
+// TODO: Use {@link ContactDetailLayoutController} so there isn't duplicated code
 public class ContactDetailActivity extends ContactsActivity {
     private static final String TAG = "ContactDetailActivity";
 
@@ -216,19 +217,24 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
+        // First check if the {@link ContactLoaderFragment} can handle the key
+        if (mLoaderFragment.handleKeyDown(keyCode)) return true;
+
+        // Otherwise find the correct fragment to handle the event
         FragmentKeyListener mCurrentFragment;
         switch (getCurrentPage()) {
             case 0:
-                mCurrentFragment = (FragmentKeyListener) mDetailFragment;
+                mCurrentFragment = mDetailFragment;
                 break;
             case 1:
-                mCurrentFragment = (FragmentKeyListener) mUpdatesFragment;
+                mCurrentFragment = mUpdatesFragment;
                 break;
             default:
                 throw new IllegalStateException("Invalid current item for ViewPager");
         }
         if (mCurrentFragment.handleKeyDown(keyCode)) return true;
 
+        // In the last case, give the key event to the superclass.
         return super.onKeyDown(keyCode, event);
     }
 
@@ -260,6 +266,11 @@
     private final ContactLoaderFragmentListener mLoaderFragmentListener =
             new ContactLoaderFragmentListener() {
         @Override
+        public void onContactNotFound() {
+            finish();
+        }
+
+        @Override
         public void onDetailsLoaded(final ContactLoader.Result result) {
             if (result == null) {
                 return;
@@ -283,6 +294,16 @@
                 }
             });
         }
+
+        @Override
+        public void onEditRequested(Uri contactLookupUri) {
+            startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
+        }
+
+        @Override
+        public void onDeleteRequested(Uri contactUri) {
+            ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
+        }
     };
 
     /**
@@ -337,16 +358,6 @@
     private final ContactDetailFragment.Listener mFragmentListener =
             new ContactDetailFragment.Listener() {
         @Override
-        public void onContactNotFound() {
-            finish();
-        }
-
-        @Override
-        public void onEditRequested(Uri contactLookupUri) {
-            startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
-        }
-
-        @Override
         public void onItemClicked(Intent intent) {
             try {
                 startActivity(intent);
@@ -356,11 +367,6 @@
         }
 
         @Override
-        public void onDeleteRequested(Uri contactUri) {
-            ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
-        }
-
-        @Override
         public void onCreateRawContactRequested(
                 ArrayList<ContentValues> values, Account account) {
             Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index eecb375..d5bdd61 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -16,10 +16,16 @@
 
 package com.android.contacts.activities;
 
+import com.android.contacts.ContactLoader;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
 import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.detail.ContactDetailLayoutController;
+import com.android.contacts.detail.ContactDetailTabCarousel;
+import com.android.contacts.detail.ContactDetailUpdatesFragment;
+import com.android.contacts.detail.ContactLoaderFragment;
+import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
 import com.android.contacts.group.GroupBrowseListFragment;
 import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
 import com.android.contacts.group.GroupDetailFragment;
@@ -64,11 +70,13 @@
 import android.content.res.TypedArray;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.ProviderStatus;
 import android.provider.Settings;
+import android.support.v4.view.ViewPager;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -122,12 +130,17 @@
     private boolean mContentPaneDisplayed;
 
     private ContactDetailFragment mContactDetailFragment;
+    private ContactDetailUpdatesFragment mContactDetailUpdatesFragment;
     private final ContactDetailFragmentListener mContactDetailFragmentListener =
             new ContactDetailFragmentListener();
-    private final GroupDetailFragmentListener mGroupDetailFragmentListener =
-            new GroupDetailFragmentListener();
+
+    private ContactLoaderFragment mContactDetailLoaderFragment;
+    private final ContactDetailLoaderFragmentListener mContactDetailLoaderFragmentListener =
+            new ContactDetailLoaderFragmentListener();
 
     private GroupDetailFragment mGroupDetailFragment;
+    private final GroupDetailFragmentListener mGroupDetailFragmentListener =
+            new GroupDetailFragmentListener();
 
     private StrequentContactListFragment.Listener mFavoritesFragmentListener =
             new StrequentContactListFragmentListener();
@@ -153,6 +166,10 @@
 
     private View mAddGroupImageView;
 
+    private ContactDetailLayoutController mContactDetailLayoutController;
+
+    private Handler mHandler = new Handler();
+
     private enum TabState {
         FAVORITES, CONTACTS, GROUPS
     }
@@ -188,11 +205,17 @@
             mContactDetailFragment = (ContactDetailFragment) fragment;
             mContactDetailFragment.setListener(mContactDetailFragmentListener);
             mContentPaneDisplayed = true;
+        } else if (fragment instanceof ContactDetailUpdatesFragment) {
+            mContactDetailUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
         } else if (fragment instanceof ContactsUnavailableFragment) {
             mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
             mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
             mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
                     new ContactsUnavailableFragmentListener());
+        } else if (fragment instanceof ContactLoaderFragment) {
+            mContactDetailLoaderFragment = (ContactLoaderFragment) fragment;
+            mContactDetailLoaderFragment.setListener(mContactDetailLoaderFragmentListener);
+            mContentPaneDisplayed = true;
         } else if (fragment instanceof GroupDetailFragment) {
             mGroupDetailFragment = (GroupDetailFragment) fragment;
             mGroupDetailFragment.setListener(mGroupDetailFragmentListener);
@@ -283,6 +306,13 @@
         mActionBarAdapter.onCreate(savedState, mRequest, getActionBar(), !mContentPaneDisplayed);
         mActionBarAdapter.setContactListFilterController(mContactListFilterController);
 
+        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
+        ContactDetailTabCarousel tabCarousel = (ContactDetailTabCarousel)
+                findViewById(R.id.tab_carousel);
+        mContactDetailLayoutController = new ContactDetailLayoutController(
+                getFragmentManager(), viewPager, tabCarousel,
+                mContactDetailFragmentListener);
+
         if (createContentView) {
             actionBar.removeAllTabs();
             Tab favoritesTab = actionBar.newTab();
@@ -294,7 +324,7 @@
             Tab peopleTab = actionBar.newTab();
             peopleTab.setText(getString(R.string.people));
             peopleTab.setTabListener(new TabChangeListener(mContactsFragment,
-                    mContactDetailFragment, TabState.CONTACTS));
+                    mContactDetailLoaderFragment, TabState.CONTACTS));
             actionBar.addTab(peopleTab);
 
             Tab groupsTab = actionBar.newTab();
@@ -487,7 +517,7 @@
     }
 
     private void setupContactDetailFragment(final Uri contactLookupUri) {
-        mContactDetailFragment.loadUri(contactLookupUri);
+        mContactDetailLoaderFragment.loadUri(contactLookupUri);
         invalidateOptionsMenuIfNeeded();
     }
 
@@ -726,19 +756,49 @@
         }
     }
 
-    private class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
+    private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener {
         @Override
         public void onContactNotFound() {
             // Nothing needs to be done here
         }
 
         @Override
+        public void onDetailsLoaded(final ContactLoader.Result result) {
+            if (result == null) {
+                return;
+            }
+            // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
+            // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
+            // on the main thread to execute later.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (!mContactDetailLayoutController.isInitialized()) {
+                        mContactDetailLayoutController.setContactDetailFragment(
+                                mContactDetailFragment);
+                        mContactDetailLayoutController.setContactDetailUpdatesFragment(
+                                mContactDetailUpdatesFragment);
+                        mContactDetailLayoutController.initialize();
+                    }
+                    mContactDetailLayoutController.setContactData(result);
+                }
+            });
+        }
+
+        @Override
         public void onEditRequested(Uri contactLookupUri) {
             startActivityForResult(
                     new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
         }
 
         @Override
+        public void onDeleteRequested(Uri contactUri) {
+            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
+        }
+    }
+
+    public class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
+        @Override
         public void onItemClicked(Intent intent) {
             try {
                 startActivity(intent);
@@ -748,11 +808,6 @@
         }
 
         @Override
-        public void onDeleteRequested(Uri contactUri) {
-            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
-        }
-
-        @Override
         public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
             Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
                     Toast.LENGTH_LONG).show();
@@ -925,7 +980,8 @@
             return true;
         }
 
-        if (mContactDetailFragment != null && mContactDetailFragment.isOptionsMenuChanged()) {
+        if (mContactDetailLoaderFragment != null &&
+                mContactDetailLoaderFragment.isOptionsMenuChanged()) {
             return true;
         }
 
@@ -1194,6 +1250,9 @@
         if (mActionBarAdapter != null) {
             mActionBarAdapter.onSaveInstanceState(outState);
         }
+        if (mContactDetailLayoutController != null) {
+            mContactDetailLayoutController.onSaveInstanceState(outState);
+        }
     }
 
     @Override
@@ -1203,6 +1262,9 @@
         if (mActionBarAdapter != null) {
             mActionBarAdapter.onRestoreInstanceState(inState);
         }
+        if (mContactDetailLayoutController != null) {
+            mContactDetailLayoutController.onRestoreInstanceState(inState);
+        }
     }
 
     @Override
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 1f7fac8..677b73a 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -19,7 +19,6 @@
 import com.android.contacts.Collapser;
 import com.android.contacts.Collapser.Collapsible;
 import com.android.contacts.ContactLoader;
-import com.android.contacts.ContactOptionsActivity;
 import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.ContactsUtils;
@@ -41,13 +40,9 @@
 import com.android.internal.telephony.ITelephony;
 
 import android.accounts.Account;
-import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Fragment;
-import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
 import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
 import android.content.ClipboardManager;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -55,7 +50,6 @@
 import android.content.Entity;
 import android.content.Entity.NamedContentValues;
 import android.content.Intent;
-import android.content.Loader;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.net.ParseException;
@@ -90,9 +84,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
@@ -137,17 +128,12 @@
     private Uri mPrimaryPhoneUri = null;
 
     private Button mCopyGalToLocalButton;
-    private boolean mAllRestricted;
     private final ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
     private int mNumPhoneNumbers = 0;
     private String mDefaultCountryIso;
     private boolean mContactDataDisplayed;
     private boolean mContactPhotoDisplayedInHeader = true;
 
-    private boolean mOptionsMenuOptions;
-    private boolean mOptionsMenuEditable;
-    private boolean mOptionsMenuShareable;
-
     /**
      * Device capability: Set during buildEntries and used in the long-press context menu
      */
@@ -245,8 +231,6 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
         mView = inflater.inflate(R.layout.contact_detail_fragment, container, false);
 
-        setHasOptionsMenu(true);
-
         mInflater = inflater;
 
         mPhotoView = (ImageView) mView.findViewById(R.id.photo);
@@ -336,37 +320,6 @@
         return mLookupUri;
     }
 
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        if (mLookupUri != null) {
-            Bundle args = new Bundle();
-            args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
-            getLoaderManager().initLoader(LOADER_DETAILS, args, mDetailLoaderListener);
-        }
-    }
-
-    public void loadUri(Uri lookupUri) {
-        if ((lookupUri != null && lookupUri.equals(mLookupUri))
-                || (lookupUri == null && mLookupUri == null)) {
-            return;
-        }
-
-        mLookupUri = lookupUri;
-        mTransitionAnimationRequested = mContactDataDisplayed;
-        mContactDataDisplayed = true;
-        if (mLookupUri == null) {
-            getLoaderManager().destroyLoader(LOADER_DETAILS);
-            mContactData = null;
-            bindData();
-        } else if (getActivity() != null) {
-            Bundle args = new Bundle();
-            args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
-            getLoaderManager().restartLoader(LOADER_DETAILS, args, mDetailLoaderListener);
-        }
-    }
-
     /**
      * Sets whether or not the contact photo should be shown in the list of contact details in this
      * {@link Fragment}.
@@ -458,7 +411,6 @@
 
         mRawContactIds.clear();
 
-        mAllRestricted = true;
         mPrimaryPhoneUri = null;
         mNumPhoneNumbers = 0;
 
@@ -477,11 +429,6 @@
             final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
             final long rawContactId = entValues.getAsLong(RawContacts._ID);
 
-            // Mark when this contact has any unrestricted components
-            Integer restricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED);
-            final boolean isRestricted = restricted != null && restricted != 0;
-            if (!isRestricted) mAllRestricted = false;
-
             if (!mRawContactIds.contains(rawContactId)) {
                 mRawContactIds.add(rawContactId);
             }
@@ -1392,95 +1339,6 @@
         }
     }
 
-    @Override
-    public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
-        inflater.inflate(R.menu.view_contact, menu);
-    }
-
-    public boolean isOptionsMenuChanged() {
-        return mOptionsMenuOptions != isContactOptionsChangeEnabled()
-                || mOptionsMenuEditable != isContactEditable()
-                || mOptionsMenuShareable != isContactShareable();
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        mOptionsMenuOptions = isContactOptionsChangeEnabled();
-        mOptionsMenuEditable = isContactEditable();
-        mOptionsMenuShareable = isContactShareable();
-
-        // 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);
-        optionsMenu.setVisible(mOptionsMenuOptions);
-
-        final MenuItem editMenu = menu.findItem(R.id.menu_edit);
-        editMenu.setVisible(mOptionsMenuEditable);
-
-        final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
-        deleteMenu.setVisible(mOptionsMenuEditable);
-
-        final MenuItem shareMenu = menu.findItem(R.id.menu_share);
-        shareMenu.setVisible(mOptionsMenuShareable);
-    }
-
-    public boolean isContactOptionsChangeEnabled() {
-        return mContactData != null && !mContactData.isDirectoryEntry()
-                && PhoneCapabilityTester.isPhone(mContext);
-    }
-
-    public boolean isContactEditable() {
-        return mContactData != null && !mContactData.isDirectoryEntry();
-    }
-
-    public boolean isContactShareable() {
-        return mContactData != null && !mContactData.isDirectoryEntry() && !mAllRestricted;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.menu_edit: {
-                if (mListener != null) mListener.onEditRequested(mLookupUri);
-                break;
-            }
-            case R.id.menu_delete: {
-                if (mListener != null) mListener.onDeleteRequested(mLookupUri);
-                return true;
-            }
-            case R.id.menu_options: {
-                if (mContactData == null) return false;
-                final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
-                intent.setData(mContactData.getLookupUri());
-                mContext.startActivity(intent);
-                return true;
-            }
-            case R.id.menu_share: {
-                if (mAllRestricted) return false;
-                if (mContactData == null) return false;
-
-                final String lookupKey = mContactData.getLookupKey();
-                final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
-
-                final Intent intent = new Intent(Intent.ACTION_SEND);
-                intent.setType(Contacts.CONTENT_VCARD_TYPE);
-                intent.putExtra(Intent.EXTRA_STREAM, shareUri);
-
-                // Launch chooser to share contact via
-                final CharSequence chooseTitle = mContext.getText(R.string.share_via);
-                final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
-
-                try {
-                    mContext.startActivity(chooseIntent);
-                } catch (ActivityNotFoundException ex) {
-                    Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void makePersonalCopy() {
         if (mListener == null) {
             return;
@@ -1588,76 +1446,18 @@
                 }
                 return false;
             }
-
-            case KeyEvent.KEYCODE_DEL: {
-                if (mListener != null) mListener.onDeleteRequested(mLookupUri);
-                return true;
-            }
         }
 
         return false;
     }
 
-    /**
-     * The listener for the detail loader
-     */
-    private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDetailLoaderListener =
-            new LoaderCallbacks<ContactLoader.Result>() {
-        @Override
-        public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
-            Uri lookupUri = args.getParcelable(LOADER_ARG_CONTACT_URI);
-            return new ContactLoader(mContext, lookupUri, true /* loadGroupMetaData */);
-        }
-
-        @Override
-        public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
-            if (!mLookupUri.equals(data.getUri())) {
-                return;
-            }
-
-            if (data != ContactLoader.Result.NOT_FOUND && data != ContactLoader.Result.ERROR) {
-                mContactData = data;
-            } else {
-                Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
-                mContactData = null;
-            }
-
-            bindData();
-
-            if (mContactData == null && mListener != null) {
-                mListener.onContactNotFound();
-            }
-        }
-
-        public void onLoaderReset(Loader<ContactLoader.Result> loader) {
-            mContactData = null;
-            bindData();
-        }
-    };
-
     public static interface Listener {
         /**
-         * Contact was not found, so somehow close this fragment. This is raised after a contact
-         * is removed via Menu/Delete
-         */
-        public void onContactNotFound();
-
-        /**
-         * User decided to go to Edit-Mode
-         */
-        public void onEditRequested(Uri lookupUri);
-
-        /**
          * User clicked a single item (e.g. mail)
          */
         public void onItemClicked(Intent intent);
 
         /**
-         * User decided to delete the contact
-         */
-        public void onDeleteRequested(Uri lookupUri);
-
-        /**
          * User requested creation of a new contact with the specified values.
          *
          * @param values ContentValues containing data rows for the new contact.
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
new file mode 100644
index 0000000..826d720
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+package com.android.contacts.detail;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.activities.PeopleActivity.ContactDetailFragmentListener;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+
+/**
+ * Determines the layout of the contact card.
+ */
+public class ContactDetailLayoutController {
+
+    public static final int FRAGMENT_COUNT = 2;
+
+    private static final String KEY_DETAIL_FRAGMENT_TAG = "detailFragTag";
+    private static final String KEY_UPDATES_FRAGMENT_TAG = "updatesFragTag";
+
+    private String mDetailFragmentTag;
+    private String mUpdatesFragmentTag;
+
+    private enum LayoutMode {
+        TWO_COLUMN, VIEW_PAGER_AND_CAROUSEL,
+    }
+
+    private final FragmentManager mFragmentManager;
+
+    private ContactDetailFragment mContactDetailFragment;
+    private ContactDetailUpdatesFragment mContactDetailUpdatesFragment;
+
+    private final ViewPager mViewPager;
+    private final ContactDetailTabCarousel mTabCarousel;
+    private ContactDetailFragment mPagerContactDetailFragment;
+    private ContactDetailUpdatesFragment mPagerContactDetailUpdatesFragment;
+
+    private ContactDetailFragmentListener mContactDetailFragmentListener;
+
+    private ContactLoader.Result mContactData;
+
+    private boolean mIsInitialized;
+
+    private LayoutMode mLayoutMode;
+
+    public ContactDetailLayoutController(FragmentManager fragmentManager, ViewPager viewPager,
+            ContactDetailTabCarousel tabCarousel, ContactDetailFragmentListener
+            contactDetailFragmentListener) {
+        if (fragmentManager == null) {
+            throw new IllegalStateException("Cannot initialize a ContactDetailLayoutController "
+                    + "without a non-null FragmentManager");
+        }
+
+        mFragmentManager = fragmentManager;
+        mViewPager = viewPager;
+        mTabCarousel = tabCarousel;
+        mContactDetailFragmentListener = contactDetailFragmentListener;
+
+        // Determine the layout based on whether the {@link ViewPager} is null or not. If the
+        // {@link ViewPager} is null, then this is a wide screen and the content can be displayed
+        // in 2 columns side by side. If the {@link ViewPager} is non-null, then this is a narrow
+        // screen and the user will need to swipe to see all the data.
+        mLayoutMode = (mViewPager == null) ? LayoutMode.TWO_COLUMN :
+                LayoutMode.VIEW_PAGER_AND_CAROUSEL;
+
+    }
+
+    public boolean isInitialized() {
+        return mIsInitialized;
+    }
+
+    public void initialize() {
+        mIsInitialized = true;
+        if (mDetailFragmentTag != null || mUpdatesFragmentTag != null) {
+            // Manually remove any {@link ViewPager} fragments if there was an orientation change
+            ContactDetailFragment oldDetailFragment = (ContactDetailFragment) mFragmentManager.
+                    findFragmentByTag(mDetailFragmentTag);
+            ContactDetailUpdatesFragment oldUpdatesFragment = (ContactDetailUpdatesFragment)
+                    mFragmentManager.findFragmentByTag(mUpdatesFragmentTag);
+
+            if (oldDetailFragment != null && oldUpdatesFragment != null) {
+                FragmentTransaction ft = mFragmentManager.beginTransaction();
+                ft.remove(oldDetailFragment);
+                ft.remove(oldUpdatesFragment);
+                ft.commit();
+            }
+        }
+        if (mViewPager != null) {
+            mViewPager.setAdapter(new ViewPagerAdapter(mFragmentManager));
+            mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+            mTabCarousel.setListener(mTabCarouselListener);
+        }
+    }
+
+    public void setContactDetailFragment(ContactDetailFragment contactDetailFragment) {
+        mContactDetailFragment = contactDetailFragment;
+    }
+
+    public void setContactDetailUpdatesFragment(ContactDetailUpdatesFragment updatesFragment) {
+        mContactDetailUpdatesFragment = updatesFragment;
+    }
+
+    public void setContactData(ContactLoader.Result data) {
+        mContactData = data;
+        if (mContactData.getSocialSnippet() != null) {
+            showContactWithUpdates();
+        } else {
+            showContactWithoutUpdates();
+        }
+    }
+
+    private void showContactWithUpdates() {
+        FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+        switch (mLayoutMode) {
+            case TWO_COLUMN: {
+                // Set the contact data
+                mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+                mContactDetailUpdatesFragment.setData(mContactData.getLookupUri(), mContactData);
+
+                // Update fragment visibility
+                ft.show(mContactDetailUpdatesFragment);
+                break;
+            }
+            case VIEW_PAGER_AND_CAROUSEL: {
+                // Set the contact data
+                mTabCarousel.loadData(mContactData);
+                mPagerContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+                mPagerContactDetailUpdatesFragment.setData(mContactData.getLookupUri(),
+                        mContactData);
+
+                // Update fragment and view visibility
+                mViewPager.setVisibility(View.VISIBLE);
+                mTabCarousel.setVisibility(View.VISIBLE);
+                ft.hide(mContactDetailFragment);
+                break;
+            }
+            default:
+                throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
+        }
+
+        ft.commit();
+    }
+
+    private void showContactWithoutUpdates() {
+        FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+        switch (mLayoutMode) {
+            case TWO_COLUMN:
+                mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+                ft.hide(mContactDetailUpdatesFragment);
+                break;
+            case VIEW_PAGER_AND_CAROUSEL:
+                mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+                ft.show(mContactDetailFragment);
+                mViewPager.setVisibility(View.GONE);
+                mTabCarousel.setVisibility(View.GONE);
+                break;
+            default:
+                throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
+        }
+
+        ft.commit();
+    }
+
+    public void onSaveInstanceState(Bundle outState) {
+        if (mPagerContactDetailFragment != null) {
+            outState.putString(KEY_DETAIL_FRAGMENT_TAG,
+                    mPagerContactDetailFragment.getTag());
+            outState.putString(KEY_UPDATES_FRAGMENT_TAG,
+                    mPagerContactDetailUpdatesFragment.getTag());
+        }
+    }
+
+    public void onRestoreInstanceState(Bundle savedState) {
+        mDetailFragmentTag = savedState.getString(KEY_DETAIL_FRAGMENT_TAG);
+        mUpdatesFragmentTag = savedState.getString(KEY_UPDATES_FRAGMENT_TAG);
+    }
+
+    public class ViewPagerAdapter extends FragmentPagerAdapter{
+
+        public ViewPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            switch (position) {
+                case 0:
+                    mPagerContactDetailFragment = new ContactDetailFragment();
+                    if (mContactData != null) {
+                        mPagerContactDetailFragment.setData(mContactData.getLookupUri(),
+                                mContactData);
+                    }
+                    mPagerContactDetailFragment.setListener(mContactDetailFragmentListener);
+                    mPagerContactDetailFragment.setVerticalScrollListener(mVerticalScrollListener);
+                    mPagerContactDetailFragment.setShowPhotoInHeader(false);
+                    return mPagerContactDetailFragment;
+                case 1:
+                    mPagerContactDetailUpdatesFragment = new ContactDetailUpdatesFragment();
+                    if (mContactData != null) {
+                        mPagerContactDetailUpdatesFragment.setData(mContactData.getLookupUri(),
+                                mContactData);
+                    }
+                    return mPagerContactDetailUpdatesFragment;
+            }
+            throw new IllegalStateException("No fragment at position " + position);
+        }
+
+        @Override
+        public int getCount() {
+            return FRAGMENT_COUNT;
+        }
+    }
+
+    private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            // The user is horizontally dragging the {@link ViewPager}, so send
+            // these scroll changes to the tab carousel. Ignore these events though if the carousel
+            // is actually controlling the {@link ViewPager} scrolls because it will already be
+            // in the correct position.
+            if (mViewPager.isFakeDragging()) {
+                return;
+            }
+            int x = (int) ((position + positionOffset) *
+                    mTabCarousel.getAllowedHorizontalScrollLength());
+            mTabCarousel.scrollTo(x, 0);
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            // Since a new page has been selected by the {@link ViewPager},
+            // update the tab selection in the carousel.
+            mTabCarousel.setCurrentTab(position);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {}
+
+    };
+
+    private ContactDetailTabCarousel.Listener mTabCarouselListener =
+            new ContactDetailTabCarousel.Listener() {
+
+        @Override
+        public void onTouchDown() {
+            // The user just started scrolling the carousel, so begin "fake dragging" the
+            // {@link ViewPager} if it's not already doing so.
+            if (mViewPager.isFakeDragging()) {
+                return;
+            }
+            mViewPager.beginFakeDrag();
+        }
+
+        @Override
+        public void onTouchUp() {
+            // The user just stopped scrolling the carousel, so stop "fake dragging" the
+            // {@link ViewPager} if was doing so before.
+            if (mViewPager.isFakeDragging()) {
+                mViewPager.endFakeDrag();
+            }
+        }
+
+        @Override
+        public void onScrollChanged(int l, int t, int oldl, int oldt) {
+            // The user is scrolling the carousel, so send the scroll deltas to the
+            // {@link ViewPager} so it can move in sync.
+            if (mViewPager.isFakeDragging()) {
+                mViewPager.fakeDragBy(oldl-l);
+            }
+        }
+
+        @Override
+        public void onTabSelected(int position) {
+            // The user selected a tab, so update the {@link ViewPager}
+            mViewPager.setCurrentItem(position);
+        }
+    };
+
+    private OnScrollListener mVerticalScrollListener = new OnScrollListener() {
+
+        @Override
+        public void onScroll(
+                AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+            if (mTabCarousel == null) {
+                return;
+            }
+            // If the FIRST item is not visible on the screen, then the carousel must be pinned
+            // at the top of the screen.
+            if (firstVisibleItem != 0) {
+                mTabCarousel.setY(-mTabCarousel.getAllowedVerticalScrollLength());
+                return;
+            }
+            View topView = view.getChildAt(firstVisibleItem);
+            if (topView == null) {
+                return;
+            }
+            int amtToScroll = Math.max((int) view.getChildAt(firstVisibleItem).getY(),
+                    -mTabCarousel.getAllowedVerticalScrollLength());
+            mTabCarousel.setY(amtToScroll);
+        }
+
+        @Override
+        public void onScrollStateChanged(AbsListView view, int scrollState) {}
+
+    };
+}
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 3ece0ce..5504473 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -17,36 +17,74 @@
 package com.android.contacts.detail;
 
 import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactOptionsActivity;
 import com.android.contacts.R;
+import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
+import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.internal.util.Objects;
 
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.Entity;
+import android.content.Intent;
 import android.content.Loader;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Toast;
 
 /**
  * This is an invisible worker {@link Fragment} that loads the contact details for the contact card.
  * The data is then passed to the listener, who can then pass the data to other {@link View}s.
  */
-public class ContactLoaderFragment extends Fragment {
+public class ContactLoaderFragment extends Fragment implements FragmentKeyListener {
 
     private static final String TAG = ContactLoaderFragment.class.getSimpleName();
 
+    private boolean mOptionsMenuOptions;
+    private boolean mOptionsMenuEditable;
+    private boolean mOptionsMenuShareable;
+
     /**
      * This is a listener to the {@link ContactLoaderFragment} and will be notified when the
-     * contact details have finished loading.
+     * contact details have finished loading or if the user selects any menu options.
      */
     public static interface ContactLoaderFragmentListener {
+        /**
+         * Contact was not found, so somehow close this fragment. This is raised after a contact
+         * is removed via Menu/Delete
+         */
+        public void onContactNotFound();
+
+        /**
+         * Contact details have finished loading.
+         */
         public void onDetailsLoaded(ContactLoader.Result result);
+
+        /**
+         * User decided to go to Edit-Mode
+         */
+        public void onEditRequested(Uri lookupUri);
+
+        /**
+         * User decided to delete the contact
+         */
+        public void onDeleteRequested(Uri lookupUri);
+
     }
 
     private static final int LOADER_DETAILS = 1;
@@ -60,6 +98,8 @@
 
     private ContactLoader.Result mContactData;
 
+    private boolean mAllRestricted;
+
     public ContactLoaderFragment() {
     }
 
@@ -85,6 +125,7 @@
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        setHasOptionsMenu(true);
         // This is an empty view that is set to visibility gone.
         return inflater.inflate(R.layout.contact_detail_loader_fragment, container, false);
     }
@@ -148,8 +189,22 @@
                 mContactData = null;
             }
 
+            mAllRestricted = true;
+
+            for (Entity entity: mContactData.getEntities()) {
+                final ContentValues entValues = entity.getEntityValues();
+                // Mark when this contact has any unrestricted components
+                Integer restricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED);
+                final boolean isRestricted = restricted != null && restricted != 0;
+                if (!isRestricted) mAllRestricted = false;
+            }
+
             if (mListener != null) {
-                mListener.onDetailsLoaded(mContactData);
+                if (mContactData == null) {
+                    mListener.onContactNotFound();
+                } else {
+                    mListener.onDetailsLoaded(mContactData);
+                }
             }
         }
 
@@ -160,4 +215,105 @@
             }
         }
     };
+
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
+        inflater.inflate(R.menu.view_contact, menu);
+    }
+
+    public boolean isOptionsMenuChanged() {
+        return mOptionsMenuOptions != isContactOptionsChangeEnabled()
+                || mOptionsMenuEditable != isContactEditable()
+                || mOptionsMenuShareable != isContactShareable();
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        mOptionsMenuOptions = isContactOptionsChangeEnabled();
+        mOptionsMenuEditable = isContactEditable();
+        mOptionsMenuShareable = isContactShareable();
+
+        // 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);
+        optionsMenu.setVisible(mOptionsMenuOptions);
+
+        final MenuItem editMenu = menu.findItem(R.id.menu_edit);
+        editMenu.setVisible(mOptionsMenuEditable);
+
+        final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
+        deleteMenu.setVisible(mOptionsMenuEditable);
+
+        final MenuItem shareMenu = menu.findItem(R.id.menu_share);
+        shareMenu.setVisible(mOptionsMenuShareable);
+    }
+
+    public boolean isContactOptionsChangeEnabled() {
+        return mContactData != null && !mContactData.isDirectoryEntry()
+                && PhoneCapabilityTester.isPhone(mContext);
+    }
+
+    public boolean isContactEditable() {
+        return mContactData != null && !mContactData.isDirectoryEntry();
+    }
+
+    public boolean isContactShareable() {
+        return mContactData != null && !mContactData.isDirectoryEntry() && !mAllRestricted;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_edit: {
+                if (mListener != null) mListener.onEditRequested(mLookupUri);
+                break;
+            }
+            case R.id.menu_delete: {
+                if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+                return true;
+            }
+            case R.id.menu_options: {
+                if (mContactData == null) return false;
+                final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
+                intent.setData(mContactData.getLookupUri());
+                mContext.startActivity(intent);
+                return true;
+            }
+            case R.id.menu_share: {
+                if (mAllRestricted) return false;
+                if (mContactData == null) return false;
+
+                final String lookupKey = mContactData.getLookupKey();
+                final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+
+                final Intent intent = new Intent(Intent.ACTION_SEND);
+                intent.setType(Contacts.CONTENT_VCARD_TYPE);
+                intent.putExtra(Intent.EXTRA_STREAM, shareUri);
+
+                // Launch chooser to share contact via
+                final CharSequence chooseTitle = mContext.getText(R.string.share_via);
+                final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
+
+                try {
+                    mContext.startActivity(chooseIntent);
+                } catch (ActivityNotFoundException ex) {
+                    Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean handleKeyDown(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DEL: {
+                if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+                return true;
+            }
+        }
+        return false;
+    }
 }