Delegating context menu handling

Change-Id: I4fa407f92e7f0b30215084b9b8c05596e3862b07
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index b51c1af..ffb2bc9 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -21,6 +21,7 @@
 import com.android.contacts.list.ContactItemListAdapter;
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.DefaultContactListFragment;
+import com.android.contacts.list.LightContactBrowserContextMenuAdapter;
 import com.android.contacts.list.LightContactBrowser;
 import com.android.contacts.list.MultiplePhonePickerFragment;
 import com.android.contacts.list.OnContactBrowserActionListener;
@@ -31,7 +32,7 @@
 import com.android.contacts.ui.ContactsPreferencesActivity.Prefs;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.Constants;
-import com.android.contacts.widget.TextWithHighlighting;
+import com.android.contacts.widget.ContextMenuAdapter;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -49,7 +50,6 @@
 import android.content.SharedPreferences;
 import android.content.UriMatcher;
 import android.content.res.Resources;
-import android.database.CharArrayBuffer;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.MatrixCursor;
@@ -371,8 +371,10 @@
     static final String KEY_PICKER_MODE = "picker_mode";
 
     public ContactEntryListAdapter mAdapter;
+    private ContextMenuAdapter mContextMenuAdapter;
     public ContactListEmptyView mEmptyView;
 
+
     public int mMode = MODE_DEFAULT;
     private boolean mRunQueriesSynchronously;
     protected QueryHandler mQueryHandler;
@@ -568,7 +570,32 @@
                         Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
                         startActivityAndForwardResult(intent);
                     }
+
+                    public void onAddToFavoritesAction(Uri contactUri) {
+                        ContentValues values = new ContentValues(1);
+                        values.put(Contacts.STARRED, 1);
+                        getContentResolver().update(contactUri, values, null, null);
+                    }
+
+                    public void onRemoveFromFavoritesAction(Uri contactUri) {
+                        ContentValues values = new ContentValues(1);
+                        values.put(Contacts.STARRED, 0);
+                        getContentResolver().update(contactUri, values, null, null);
+                    }
+
+                    public void onCallContactAction(Uri contactUri) {
+                        // TODO
+                    }
+
+                    public void onSmsContactAction(Uri contactUri) {
+                        // TODO
+                    }
+
+                    public void onDeleteContactAction(Uri contactUri) {
+                        doContactDelete(contactUri);
+                    }
                 });
+                mContextMenuAdapter = new LightContactBrowserContextMenuAdapter(fragment);
                 mListFragment = fragment;
                 break;
             }
@@ -1264,6 +1291,11 @@
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        if (mContextMenuAdapter != null) {
+            mContextMenuAdapter.onCreateContextMenu(menu, view, menuInfo);
+            return;
+        }
+
         // If Contacts was invoked by another Activity simply as a way of
         // picking a contact, don't show the context menu
         if ((mMode & MODE_MASK_PICKER) == MODE_MASK_PICKER) {
@@ -1319,6 +1351,10 @@
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
+        if (mContextMenuAdapter != null) {
+            return mContextMenuAdapter.onContextItemSelected(item);
+        }
+
         AdapterView.AdapterContextMenuInfo info;
         try {
              info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
@@ -2083,6 +2119,10 @@
         mSortOrder = mContactsPrefs.getSortOrder();
         mDisplayOrder = mContactsPrefs.getDisplayOrder();
 
+        if (mListFragment != null) {
+            mListFragment.setContactNameDisplayOrder(mDisplayOrder);
+        }
+
         if (mListView instanceof ContactEntryListView) {
             ContactEntryListView listView = (ContactEntryListView)mListView;
 
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index 86c7ba5..80f8113 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -19,6 +19,12 @@
 import com.android.contacts.widget.TextWithHighlightingFactory;
 
 import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.widget.ListView;
 
 /**
  * Common base class for various contact-related lists, e.g. contact list, phone number list
@@ -26,11 +32,20 @@
  */
 public abstract class ContactEntryListAdapter extends PinnedHeaderListAdapter {
 
+    // TODO move to a type-specific adapter
+    public static final int SUMMARY_ID_COLUMN_INDEX = 0;
+    public static final int SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 1;
+    public static final int SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX = 2;
+    public static final int SUMMARY_STARRED_COLUMN_INDEX = 4;
+    public static final int SUMMARY_LOOKUP_KEY_COLUMN_INDEX = 8;
+    public static final int SUMMARY_HAS_PHONE_COLUMN_INDEX = 10;
+
     /**
      * The animation is used here to allocate animated name text views.
      */
     private TextWithHighlightingFactory mTextWithHighlightingFactory;
 
+    private int mDisplayOrder;
     private boolean mNameHighlightingEnabled;
 
     public ContactEntryListAdapter(Context context) {
@@ -41,6 +56,14 @@
         return mContext;
     }
 
+    public void setContactNameDisplayOrder(int displayOrder) {
+        mDisplayOrder = displayOrder;
+    }
+
+    public int getContactNameDisplayOrder() {
+        return mDisplayOrder;
+    }
+
     public void setNameHighlightingEnabled(boolean flag) {
         mNameHighlightingEnabled = flag;
     }
@@ -65,4 +88,41 @@
     public void onContentChanged() {
         super.onContentChanged();
     }
+
+    public void moveToPosition(int position) {
+        // For side-effect
+        getItem(position);
+        DatabaseUtils.dumpCurrentRow(getCursor());
+    }
+
+    public boolean getHasPhoneNumber() {
+        return getCursor().getInt(SUMMARY_HAS_PHONE_COLUMN_INDEX) != 0;
+    }
+
+    public boolean isContactStarred() {
+        return getCursor().getInt(SUMMARY_STARRED_COLUMN_INDEX) != 0;
+    }
+
+    public String getContactDisplayName() {
+        return getCursor().getString(getSummaryDisplayNameColumnIndex());
+    }
+
+    public int getSummaryDisplayNameColumnIndex() {
+        if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+            return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
+        } else {
+            return SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX;
+        }
+    }
+
+    /**
+     * Builds the {@link Contacts#CONTENT_LOOKUP_URI} for the given
+     * {@link ListView} position.
+     */
+    public Uri getContactUri() {
+        Cursor cursor = getCursor();
+        long contactId = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
+        String lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
+        return Contacts.getLookupUri(contactId, lookupKey);
+    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 4bbb16f..fd38599 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -46,16 +46,17 @@
     private String mQueryString;
 
     private ContactsApplicationController mAppController;
-    private ListAdapter mAdapter;
+    private ContactEntryListAdapter mAdapter;
     private ListView mListView;
 
     private boolean mLegacyCompatibility;
+    private int mDisplayOrder;
 
     protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
-    protected abstract ListAdapter createListAdapter();
+    protected abstract ContactEntryListAdapter createListAdapter();
     protected abstract void onItemClick(int position, long id);
 
-    public ListAdapter getAdapter() {
+    public ContactEntryListAdapter getAdapter() {
         return mAdapter;
     }
 
@@ -107,6 +108,14 @@
         mLegacyCompatibility = flag;
     }
 
+    public void setContactNameDisplayOrder(int displayOrder) {
+        mDisplayOrder = displayOrder;
+        if (mAdapter != null) {
+            mAdapter.setContactNameDisplayOrder(displayOrder);
+        }
+    }
+
+
     @Deprecated
     public void setContactsApplicationController(ContactsApplicationController controller) {
         mAppController = controller;
@@ -121,11 +130,11 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container) {
         View view = inflateView(inflater, container);
         mAdapter = createListAdapter();
-        configureView(view, mAdapter);
+        configureView(view);
         return view;
     }
 
-    protected void configureView(View view, ListAdapter adapter) {
+    protected void configureView(View view) {
         mListView = (ListView)view.findViewById(android.R.id.list);
         if (mListView == null) {
             throw new RuntimeException(
@@ -138,12 +147,14 @@
             mListView.setEmptyView(emptyView);
         }
 
-        mListView.setAdapter(adapter);
+        mListView.setAdapter(mAdapter);
         mListView.setOnItemClickListener(this);
 
-        ((ContactsListActivity)getActivity()).setupListView(adapter, mListView);
+        mAdapter.setContactNameDisplayOrder(mDisplayOrder);
 
-        configurePinnedHeader(mListView, adapter);
+        ((ContactsListActivity)getActivity()).setupListView(mAdapter, mListView);
+
+        configurePinnedHeader();
 
         if (isSearchResultsMode()) {
             TextView titleText = (TextView)view.findViewById(R.id.search_results_for);
@@ -154,16 +165,14 @@
         }
     }
 
-    private void configurePinnedHeader(ListView listView, ListAdapter adapter) {
+    private void configurePinnedHeader() {
         if (!mSectionHeaderDisplayEnabled) {
             return;
         }
 
-        if (listView instanceof PinnedHeaderListView
-                && adapter instanceof PinnedHeaderListAdapter) {
-            PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)listView;
-            PinnedHeaderListAdapter pinnedHeaderListAdapter = (PinnedHeaderListAdapter)adapter;
-            View headerView = pinnedHeaderListAdapter.createPinnedHeaderView(pinnedHeaderList);
+        if (mListView instanceof PinnedHeaderListView) {
+            PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)mListView;
+            View headerView = mAdapter.createPinnedHeaderView(pinnedHeaderList);
             pinnedHeaderList.setPinnedHeaderView(headerView);
         }
     }
diff --git a/src/com/android/contacts/list/DefaultContactListFragment.java b/src/com/android/contacts/list/DefaultContactListFragment.java
index 51ee2cc..1f02432 100644
--- a/src/com/android/contacts/list/DefaultContactListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactListFragment.java
@@ -22,7 +22,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ListAdapter;
 
 /**
  * Fragment for the default contact list.
@@ -37,7 +36,7 @@
     }
 
     @Override
-    protected ListAdapter createListAdapter() {
+    protected ContactEntryListAdapter createListAdapter() {
         ContactItemListAdapter adapter =
                 new ContactItemListAdapter((ContactsListActivity)getActivity());
         adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
index 52beb4a..3693070 100644
--- a/src/com/android/contacts/list/JoinContactListFragment.java
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -21,7 +21,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ListAdapter;
 
 /**
  * Fragment for the Join Contact list.
@@ -29,7 +28,7 @@
 public class JoinContactListFragment extends ContactEntryListFragment {
 
     @Override
-    public ListAdapter createListAdapter() {
+    public ContactEntryListAdapter createListAdapter() {
         JoinContactListAdapter adapter =
                 new JoinContactListAdapter((JoinContactActivity)getActivity());
         adapter.setSectionHeaderDisplayEnabled(true);
diff --git a/src/com/android/contacts/list/LightContactBrowser.java b/src/com/android/contacts/list/LightContactBrowser.java
index 02d9f00..2c17d32 100644
--- a/src/com/android/contacts/list/LightContactBrowser.java
+++ b/src/com/android/contacts/list/LightContactBrowser.java
@@ -19,24 +19,16 @@
 import com.android.contacts.R;
 
 import android.content.Intent;
-import android.database.Cursor;
 import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ListAdapter;
-import android.widget.ListView;
 
 /**
  * Fragment for the light-weight contact list.
  */
 public class LightContactBrowser extends ContactEntryListFragment {
 
-    // TODO move these constants to the "loader"
-    public static final int SUMMARY_ID_COLUMN_INDEX = 0;
-    public static final int SUMMARY_LOOKUP_KEY_COLUMN_INDEX = 8;
-
     private OnContactBrowserActionListener mListener;
     private boolean mEditMode;
     private boolean mCreateContactEnabled;
@@ -58,15 +50,19 @@
             if (position == 0 && !isSearchMode() && isCreateContactEnabled()) {
                 mListener.onCreateNewContactAction();
             } else {
-                mListener.onEditContactAction(getContactUri(position));
+                ContactEntryListAdapter adapter = getAdapter();
+                adapter.moveToPosition(position);
+                mListener.onEditContactAction(adapter.getContactUri());
             }
         } else {
-            mListener.onViewContactAction(getContactUri(position));
+            ContactEntryListAdapter adapter = getAdapter();
+            adapter.moveToPosition(position);
+            mListener.onViewContactAction(adapter.getContactUri());
         }
     }
 
     @Override
-    protected ListAdapter createListAdapter() {
+    protected ContactEntryListAdapter createListAdapter() {
         ContactItemListAdapter adapter =
                 new ContactItemListAdapter((ContactsListActivity)getActivity());
         adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
@@ -101,19 +97,31 @@
         return mCreateContactEnabled;
     }
 
-    /**
-     * Build the {@link Contacts#CONTENT_LOOKUP_URI} for the given
-     * {@link ListView} position.
-     */
-    private Uri getContactUri(int position) {
-        final Cursor cursor = (Cursor)getAdapter().getItem(position);
-        if (cursor == null) {
-            return null;
-        }
+    public void viewContact(Uri contactUri) {
+        mListener.onViewContactAction(contactUri);
+    }
 
-        // Build and return soft, lookup reference
-        long contactId = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
-        String lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
-        return Contacts.getLookupUri(contactId, lookupKey);
+    public void editContact(Uri contactUri) {
+        mListener.onEditContactAction(contactUri);
+    }
+
+    public void deleteContact(Uri contactUri) {
+        mListener.onDeleteContactAction(contactUri);
+    }
+
+    public void addToFavorites(Uri contactUri) {
+        mListener.onAddToFavoritesAction(contactUri);
+    }
+
+    public void removeFromFavorites(Uri contactUri) {
+        mListener.onRemoveFromFavoritesAction(contactUri);
+    }
+
+    public void callContact(Uri contactUri) {
+        mListener.onCallContactAction(contactUri);
+    }
+
+    public void smsContact(Uri contactUri) {
+        mListener.onSmsContactAction(contactUri);
     }
 }
diff --git a/src/com/android/contacts/list/LightContactBrowserContextMenuAdapter.java b/src/com/android/contacts/list/LightContactBrowserContextMenuAdapter.java
new file mode 100644
index 0000000..7e7a336
--- /dev/null
+++ b/src/com/android/contacts/list/LightContactBrowserContextMenuAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.widget.ContextMenuAdapter;
+
+import android.net.Uri;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+
+/**
+ * A contextual menu adapter for the light version of the contact browser.
+ */
+public class LightContactBrowserContextMenuAdapter implements ContextMenuAdapter {
+
+    private static final int MENU_ITEM_VIEW_CONTACT = 1;
+    private static final int MENU_ITEM_CALL = 2;
+    private static final int MENU_ITEM_SEND_SMS = 3;
+    private static final int MENU_ITEM_EDIT = 4;
+    private static final int MENU_ITEM_DELETE = 5;
+    private static final int MENU_ITEM_TOGGLE_STAR = 6;
+
+    private static final String TAG = "LightContactBrowserContextMenuAdapter";
+
+    private final LightContactBrowser mBrowser;
+
+    public LightContactBrowserContextMenuAdapter(LightContactBrowser browser) {
+        this.mBrowser = browser;
+    }
+
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+        } catch (ClassCastException e) {
+            Log.wtf(TAG, "Bad menuInfo", e);
+            return;
+        }
+
+        ContactEntryListAdapter adapter = mBrowser.getAdapter();
+        adapter.moveToPosition(info.position);
+
+        // Setup the menu header
+        menu.setHeaderTitle(adapter.getContactDisplayName());
+
+        // View contact details
+        menu.add(0, MENU_ITEM_VIEW_CONTACT, 0, R.string.menu_viewContact);
+
+        if (adapter.getHasPhoneNumber()) {
+            // Calling contact
+            menu.add(0, MENU_ITEM_CALL, 0, R.string.menu_call);
+            // Send SMS item
+            menu.add(0, MENU_ITEM_SEND_SMS, 0, R.string.menu_sendSMS);
+        }
+
+        // Star toggling
+        if (!adapter.isContactStarred()) {
+            menu.add(0, MENU_ITEM_TOGGLE_STAR, 0, R.string.menu_addStar);
+        } else {
+            menu.add(0, MENU_ITEM_TOGGLE_STAR, 0, R.string.menu_removeStar);
+        }
+
+        // Contact editing
+        menu.add(0, MENU_ITEM_EDIT, 0, R.string.menu_editContact);
+        menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact);
+    }
+
+    public boolean onContextItemSelected(MenuItem item) {
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+        } catch (ClassCastException e) {
+            Log.wtf(TAG, "Bad menuInfo", e);
+            return false;
+        }
+
+        ContactEntryListAdapter adapter = mBrowser.getAdapter();
+        adapter.moveToPosition(info.position);
+        final Uri contactUri = adapter.getContactUri();
+
+        switch (item.getItemId()) {
+            case MENU_ITEM_VIEW_CONTACT: {
+                mBrowser.viewContact(contactUri);
+                return true;
+            }
+
+            case MENU_ITEM_TOGGLE_STAR: {
+                if (adapter.isContactStarred()) {
+                    mBrowser.removeFromFavorites(contactUri);
+                } else {
+                    mBrowser.addToFavorites(contactUri);
+                }
+                return true;
+            }
+
+            case MENU_ITEM_CALL: {
+                mBrowser.callContact(contactUri);
+                return true;
+            }
+
+            case MENU_ITEM_SEND_SMS: {
+                mBrowser.smsContact(contactUri);
+                return true;
+            }
+
+            case MENU_ITEM_EDIT: {
+                mBrowser.editContact(contactUri);
+                return true;
+            }
+
+            case MENU_ITEM_DELETE: {
+                mBrowser.deleteContact(contactUri);
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/src/com/android/contacts/list/MultiplePhonePickerFragment.java b/src/com/android/contacts/list/MultiplePhonePickerFragment.java
index fd3b517..a38c0ec 100644
--- a/src/com/android/contacts/list/MultiplePhonePickerFragment.java
+++ b/src/com/android/contacts/list/MultiplePhonePickerFragment.java
@@ -21,7 +21,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ListAdapter;
 
 /**
  * Fragment for the multiple phone picker.
@@ -29,7 +28,7 @@
 public class MultiplePhonePickerFragment extends ContactEntryListFragment {
 
     @Override
-    public ListAdapter createListAdapter() {
+    public ContactEntryListAdapter createListAdapter() {
         MultiplePhonePickerAdapter adapter =
                 new MultiplePhonePickerAdapter((MultiplePhonePickerActivity)getActivity());
         adapter.setSectionHeaderDisplayEnabled(true);
diff --git a/src/com/android/contacts/list/OnContactBrowserActionListener.java b/src/com/android/contacts/list/OnContactBrowserActionListener.java
index 8893695..c6fe102 100644
--- a/src/com/android/contacts/list/OnContactBrowserActionListener.java
+++ b/src/com/android/contacts/list/OnContactBrowserActionListener.java
@@ -23,22 +23,48 @@
 public interface OnContactBrowserActionListener  {
 
     /**
-     * Search all contacts for the specified string an show results for browsing.
+     * Searches all contacts for the specified string an show results for browsing.
      */
     void onSearchAllContactsAction(String string);
 
     /**
-     * Open the specified contact for viewing.
+     * Opens the specified contact for viewing.
      */
     void onViewContactAction(Uri contactLookupUri);
 
     /**
-     * Create a new contact.
+     * Creates a new contact.
      */
     void onCreateNewContactAction();
 
     /**
-     * Edit the specified contact.
+     * Opens the specified contact for editing.
      */
     void onEditContactAction(Uri contactLookupUri);
+
+    /**
+     * Initiates the contact deletion process.
+     */
+    void onDeleteContactAction(Uri contactUri);
+
+    /**
+     * Adds the specified contact to favorites
+     */
+    void onAddToFavoritesAction(Uri contactUri);
+
+    /**
+     * Removes the specified contact from favorites.
+     */
+    void onRemoveFromFavoritesAction(Uri contactUri);
+
+    /**
+     * Places a call to the specified contact.
+     */
+    void onCallContactAction(Uri contactUri);
+
+    /**
+     * Initiates a text message to the specified contact.
+     */
+    void onSmsContactAction(Uri contactUri);
+
 }
diff --git a/src/com/android/contacts/widget/ContextMenuAdapter.java b/src/com/android/contacts/widget/ContextMenuAdapter.java
new file mode 100644
index 0000000..3aa7ca7
--- /dev/null
+++ b/src/com/android/contacts/widget/ContextMenuAdapter.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+
+/**
+ * An adapter for the contextual menu.
+ */
+public interface ContextMenuAdapter {
+
+    /**
+     * See {@link android.app.Activity#onCreateContextMenu}.
+     */
+    void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo);
+
+    /**
+     * See {@link android.app.Activity#onContextItemSelected}.
+     */
+    boolean onContextItemSelected(MenuItem item);
+}