Merge "Allow phone contact card to switch b/t having/not having social updates"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index aeec2b2..e255fd2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -117,7 +117,9 @@
             </intent-filter>
         </activity>
 
-        <!-- Tab container for all tabs -->
+        <!-- The entrance point for Phone UI.
+             stateAlwaysHidden is set to suppress keyboard show up on
+             dialpad screen. -->
         <activity android:name=".activities.DialtactsActivity"
             android:label="@string/launcherDialer"
             android:theme="@style/DialtactsTheme"
@@ -128,7 +130,7 @@
             android:screenOrientation="nosensor"
             android:enabled="@*android:bool/config_voice_capable"
             android:taskAffinity="android.task.contacts.phone"
-        >
+            android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.DIAL" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/res/layout-sw580dp/contact_detail_fragment.xml b/res/layout-sw580dp/contact_detail_fragment.xml
index c9dad2a..f05dc56 100644
--- a/res/layout-sw580dp/contact_detail_fragment.xml
+++ b/res/layout-sw580dp/contact_detail_fragment.xml
@@ -30,8 +30,9 @@
 
     <!-- Real list -->
     <ListView android:id="@android:id/list"
+        android:layout_weight="1"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="0dip"
         android:cacheColorHint="#00000000"
         android:divider="@null"
     />
diff --git a/res/layout/contact_tile_frequent.xml b/res/layout/contact_tile_frequent.xml
index 2e79ba6..52b7126 100644
--- a/res/layout/contact_tile_frequent.xml
+++ b/res/layout/contact_tile_frequent.xml
@@ -17,7 +17,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     class="com.android.contacts.list.ContactTileView"
     android:focusable="true"
-    android:background="@null"
+    android:background="?android:attr/selectableItemBackground"
+    android:nextFocusRight="@+id/contact_tile_quick"
     android:paddingRight="16dip"
     android:paddingLeft="16dip" >
 
@@ -30,6 +31,7 @@
             android:layout_width="64dip"
             android:layout_height="64dip"
             android:scaleType="centerCrop"
+            android:focusable="true"
             android:layout_alignParentRight="true" />
 
         <LinearLayout
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
index 563dea0..7d24cef 100644
--- a/res/layout/contact_tile_frequent_phone.xml
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -14,10 +14,12 @@
      limitations under the License.
 -->
 <view
+    android:id="@+id/contact_tile_frequent_phone"
     xmlns:android="http://schemas.android.com/apk/res/android"
     class="com.android.contacts.list.ContactTileView"
     android:focusable="true"
-    android:background="@null"
+    android:background="?android:attr/selectableItemBackground"
+    android:nextFocusLeft="@+id/contact_tile_quick"
     android:paddingRight="16dip"
     android:paddingLeft="16dip" >
 
@@ -26,10 +28,12 @@
         android:layout_height="match_parent" >
 
         <QuickContactBadge
-            android:id="@+id/contact_tile_quick"
+            android:id="@id/contact_tile_quick"
+            android:nextFocusRight="@id/contact_tile_frequent_phone"
             android:layout_width="64dip"
             android:layout_height="64dip"
             android:scaleType="centerCrop"
+            android:focusable="true"
             android:layout_alignParentLeft="true" />
 
         <TextView
diff --git a/res/layout/contact_tile_starred.xml b/res/layout/contact_tile_starred.xml
index 5fdfe65..757abea 100644
--- a/res/layout/contact_tile_starred.xml
+++ b/res/layout/contact_tile_starred.xml
@@ -70,6 +70,7 @@
             android:id="@+id/contact_tile_push_state"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:focusable="true"
             android:background="?android:attr/selectableItemBackground" />
 
     </RelativeLayout>
diff --git a/res/layout/contact_tile_starred_quick_contact.xml b/res/layout/contact_tile_starred_quick_contact.xml
index 9bb4abe..3f072d1 100644
--- a/res/layout/contact_tile_starred_quick_contact.xml
+++ b/res/layout/contact_tile_starred_quick_contact.xml
@@ -69,6 +69,7 @@
             android:id="@+id/contact_tile_quick"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:focusable="true"
             android:background="@null" />
 
     </RelativeLayout>
diff --git a/res/layout/contact_tile_starred_secondary_target.xml b/res/layout/contact_tile_starred_secondary_target.xml
index e72575e..6543dc1 100644
--- a/res/layout/contact_tile_starred_secondary_target.xml
+++ b/res/layout/contact_tile_starred_secondary_target.xml
@@ -51,10 +51,12 @@
             android:id="@+id/contact_tile_push_state"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:focusable="true"
+            android:nextFocusRight="@+id/contact_tile_secondary_button"
             android:background="?android:attr/selectableItemBackground" />
 
         <ImageButton
-            android:id="@+id/contact_tile_secondary_button"
+            android:id="@id/contact_tile_secondary_button"
             android:src="@drawable/ic_contacts_holo_dark"
             android:background="?android:attr/selectableItemBackground"
             android:layout_height="@dimen/contact_tile_shadowbox_height"
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index 90d2593..9c5099f 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -35,7 +35,8 @@
              in the java code.
 
              Background drawable can be controlled programatically. -->
-        <EditText android:id="@+id/digits"
+        <com.android.contacts.dialpad.DigitsEditText
+            android:id="@+id/digits"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_alignParentLeft="true"
diff --git a/res/layout/dialtacts_custom_action_bar.xml b/res/layout/dialtacts_custom_action_bar.xml
new file mode 100644
index 0000000..2be66f7
--- /dev/null
+++ b/res/layout/dialtacts_custom_action_bar.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.
+-->
+
+<!-- Dimensions are set at runtime in ActionBarAdapter -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:layout_height="0dip"
+    android:orientation="horizontal">
+
+    <SearchView
+        android:id="@+id/search_view"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:iconifiedByDefault="false" />
+
+    <ImageButton
+        android:id="@+id/search_option"
+        android:layout_width="wrap_content"
+        android:paddingRight="8dip"
+        android:layout_height="match_parent"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/ic_menu_overflow"
+        android:background="@android:color/transparent"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
index 77da9cb..4dc62a8 100644
--- a/res/menu/dialpad_options.xml
+++ b/res/menu/dialpad_options.xml
@@ -30,4 +30,10 @@
         android:icon="@drawable/ic_menu_wait"
         android:title="@string/add_wait"
         android:showAsAction="withText" />
+
+    <item
+        android:id="@+id/menu_call_settings_dialpad"
+        android:title="@string/call_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:showAsAction="withText" />
 </menu>
diff --git a/res/menu/dialtacts_search_options.xml b/res/menu/dialtacts_search_options.xml
new file mode 100644
index 0000000..f2e0c67
--- /dev/null
+++ b/res/menu/dialtacts_search_options.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<!-- Used with DialtactsActivity's search mode. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/filter_option"
+        android:title="@string/menu_contacts_filter"
+        android:showAsAction="withText" />
+</menu>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2f74c32..a67aa6b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -256,6 +256,6 @@
     <dimen name="directory_header_height">24dip</dimen>
 
     <!--  Vertical and horizontal padding in between contact tiles -->
-    <dimen name="contact_tile_divider_padding">2dip</dimen>
+    <dimen name="contact_tile_divider_padding">1dip</dimen>
 
 </resources>
diff --git a/src/com/android/contacts/ContactTileLoaderFactory.java b/src/com/android/contacts/ContactTileLoaderFactory.java
index 30bd7e4..28f2164 100644
--- a/src/com/android/contacts/ContactTileLoaderFactory.java
+++ b/src/com/android/contacts/ContactTileLoaderFactory.java
@@ -23,7 +23,6 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Contacts.Data;
 
 /**
  * Used to create {@link CursorLoader}s to load different groups of {@link ContactTileView}s
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 71140bf..b6dfbfa 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -57,8 +57,10 @@
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
+import android.view.View.OnClickListener;
+import android.view.ViewConfiguration;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.PopupMenu;
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
@@ -243,6 +245,18 @@
     private boolean mInSearchUi;
     private SearchView mSearchView;
 
+    private final OnClickListener mFilterOptionClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
+            final Menu menu = popupMenu.getMenu();
+            popupMenu.inflate(R.menu.dialtacts_search_options);
+            final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
+            filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
+            popupMenu.show();
+        }
+    };
+
     /**
      * The index of the Fragment (or, the tab) that has last been manually selected.
      * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
@@ -363,6 +377,8 @@
         mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
         mViewPager.setOnPageChangeListener(mPageChangeListener);
 
+        prepareSearchView();
+
         // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
         setupDialer();
         setupCallLog();
@@ -388,6 +404,33 @@
         }
     }
 
+    private void prepareSearchView() {
+        final View searchViewLayout =
+                getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
+        mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+        mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
+        mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
+        // Since we're using a custom layout for showing SearchView instead of letting the
+        // search menu icon do that job, we need to manually configure the View so it looks
+        // "shown via search menu".
+        // - it should be iconified by default
+        // - it should not be iconified at this time
+        // See also comments for onActionViewExpanded()/onActionViewCollapsed()
+        mSearchView.setIconifiedByDefault(true);
+        mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+        mSearchView.setIconified(false);
+
+        if (!ViewConfiguration.get(this).hasPermanentMenuKey()) {
+            // Filter option menu should be shown on the right side of SearchView.
+            final View filterOptionView = searchViewLayout.findViewById(R.id.search_option);
+            filterOptionView.setVisibility(View.VISIBLE);
+            filterOptionView.setOnClickListener(mFilterOptionClickListener);
+        }
+
+        getActionBar().setCustomView(searchViewLayout,
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+    }
+
     @Override
     public void onAttachFragment(Fragment fragment) {
         // This method can be called before onCreate(), at which point we cannot rely on ViewPager.
@@ -653,20 +696,35 @@
         Tab tab = getActionBar().getSelectedTab();
         if (mInSearchUi) {
             searchMenuItem.setVisible(false);
-            filterOptionMenuItem.setVisible(true);
-            filterOptionMenuItem.setOnMenuItemClickListener(
-                    mFilterOptionsMenuItemClickListener);
+            if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
+                filterOptionMenuItem.setVisible(true);
+                filterOptionMenuItem.setOnMenuItemClickListener(
+                        mFilterOptionsMenuItemClickListener);
+            } else {
+                // Filter option menu should be not be shown as a overflow menu.
+                filterOptionMenuItem.setVisible(false);
+            }
             callSettingsMenuItem.setVisible(false);
         } else {
+            final boolean showCallSettingsMenu;
             if (tab != null && tab.getPosition() == TAB_INDEX_DIALER) {
                 searchMenuItem.setVisible(false);
+                // When permanent menu key is _not_ available, the call settings menu should be
+                // available via DialpadFragment.
+                showCallSettingsMenu = ViewConfiguration.get(this).hasPermanentMenuKey();
             } else {
                 searchMenuItem.setVisible(true);
                 searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener);
+                showCallSettingsMenu = true;
             }
             filterOptionMenuItem.setVisible(false);
-            callSettingsMenuItem.setVisible(true);
-            callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+
+            if (showCallSettingsMenu) {
+                callSettingsMenuItem.setVisible(true);
+                callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+            } else {
+                callSettingsMenuItem.setVisible(false);
+            }
         }
 
         return true;
@@ -695,46 +753,7 @@
             mLastManuallySelectedFragment = tab.getPosition();
         }
 
-        // Instantiate or reset SearchView in ActionBar.
-        if (mSearchView == null) {
-            final View searchViewLayout =
-                    getLayoutInflater().inflate(R.layout.custom_action_bar, null);
-            mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
-            mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
-            mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
-            // Since we're using a custom layout for showing SearchView instead of letting the
-            // search menu icon do that job, we need to manually configure the View so it looks
-            // "shown via search menu".
-            // - it should be iconified by default
-            // - it should not be iconified at this time
-            // See also comments for onActionViewExpanded()/onActionViewCollapsed()
-            mSearchView.setIconifiedByDefault(true);
-            mSearchView.setQueryHint(getString(R.string.hint_findContacts));
-            mSearchView.setIconified(false);
-            mSearchView.requestFocus();
-            // Show soft keyboard when SearchView has a focus. Need to delay the request in order
-            // to let InputMethodManager handle it correctly.
-            mSearchView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                }
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    if (mSearchView.hasFocus()) {
-                        mSearchView.postDelayed(new Runnable() {
-                            public void run() {
-                                showInputMethod(mSearchView.findFocus());
-                            }
-                        }, 0);
-                    }
-                }
-            });
-            actionBar.setCustomView(searchViewLayout,
-                    new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        } else {
-            mSearchView.setQuery(null, true);
-        }
+        mSearchView.setQuery(null, true);
 
         actionBar.setDisplayShowCustomEnabled(true);
         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
@@ -753,6 +772,9 @@
         // layout instead of asking the search menu item to take care of SearchView.
         mSearchView.onActionViewExpanded();
         mInSearchUi = true;
+
+        // Clear focus and suppress keyboard show-up.
+        mSearchView.clearFocus();
     }
 
     private void showInputMethod(View view) {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1da1c97..68e9cbb 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -156,6 +156,7 @@
     /** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
     private ViewPager mTabPager;
     private TabPagerAdapter mTabPagerAdapter;
+    private final TabPagerListener mTabPagerListener = new TabPagerListener();
 
     private ContactDetailLayoutController mContactDetailLayoutController;
 
@@ -315,7 +316,7 @@
             mTabPager = getView(R.id.tab_pager);
             mTabPagerAdapter = new TabPagerAdapter();
             mTabPager.setAdapter(mTabPagerAdapter);
-            mTabPager.setOnPageChangeListener(new TabPagerListener());
+            mTabPager.setOnPageChangeListener(mTabPagerListener);
 
             final String FAVORITE_TAG = "tab-pager-favorite";
             final String ALL_TAG = "tab-pager-all";
@@ -450,6 +451,9 @@
         // Re-register the listener, which may have been cleared when onSaveInstanceState was
         // called.  See also: onSaveInstanceState
         mActionBarAdapter.setListener(this);
+        if (mTabPager != null) {
+            mTabPager.setOnPageChangeListener(mTabPagerListener);
+        }
         // Current tab may have changed since the last onSaveInstanceState().  Make sure
         // the actual contents match the tab.
         updateFragmentsVisibility();
@@ -1527,6 +1531,9 @@
         // in order to avoid doing fragment transactions after it.
         // TODO Figure out a better way to deal with the issue.
         mActionBarAdapter.setListener(null);
+        if (mTabPager != null) {
+            mTabPager.setOnPageChangeListener(null);
+        }
     }
 
     @Override
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 62ba95e..188c546 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -33,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
 import android.net.Uri;
@@ -298,12 +297,10 @@
         mDialpad = fragmentView.findViewById(R.id.dialpad);  // This is null in landscape mode.
 
         // In landscape we put the keyboard in phone mode.
-        // In portrait we prevent the soft keyboard to show since the
-        // dialpad acts as one already.
         if (null == mDialpad) {
             mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
         } else {
-            mDigits.setInputType(android.text.InputType.TYPE_NULL);
+            mDigits.setCursorVisible(false);
         }
 
         // Set up the "dialpad chooser" UI; see showDialpadChooser().
@@ -571,10 +568,20 @@
     }
 
     private void setupMenuItems(Menu menu) {
+        final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_dialpad);
         final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
         final MenuItem twoSecPauseMenuItem = menu.findItem(R.id.menu_2s_pause);
         final MenuItem waitMenuItem = menu.findItem(R.id.menu_add_wait);
 
+        final Activity activity = getActivity();
+        if (activity != null && ViewConfiguration.get(activity).hasPermanentMenuKey()) {
+            // Call settings should be available via its parent Activity.
+            callSettingsMenuItem.setVisible(false);
+        } else {
+            callSettingsMenuItem.setVisible(true);
+            callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+        }
+
         // We show "add to contacts", "2sec pause", and "add wait" menus only when the user is
         // seeing usual dialpads and has typed at least one digit.
         // We never show a menu if the "choose dialpad" UI is up.
diff --git a/src/com/android/contacts/dialpad/DigitsEditText.java b/src/com/android/contacts/dialpad/DigitsEditText.java
new file mode 100644
index 0000000..930717a
--- /dev/null
+++ b/src/com/android/contacts/dialpad/DigitsEditText.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dialpad;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+/**
+ * EditText which suppresses IME show up.
+ */
+public class DigitsEditText extends EditText {
+    public DigitsEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setSuggestionsEnabled(false);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        final boolean ret = super.onTouchEvent(event);
+        // Must be done after super.onTouchEvent()
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index cabf639..eb496f2 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -168,7 +168,8 @@
 
         // Name
         primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
-        mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
+        mName.setText(primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
+                mContext.getString(R.string.missing_name));
 
         if (type.readOnly) {
             mAccountContainer.setOnClickListener(new OnClickListener() {
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 51cc965..2e511bc 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -45,14 +45,13 @@
         Contacts.SORT_KEY_PRIMARY,              // 3
         Contacts.STARRED,                       // 4
         Contacts.CONTACT_PRESENCE,              // 5
-        Contacts.CONTACT_CHAT_CAPABILITY,       // 6
-        Contacts.CONTACT_STATUS,                // 7
-        Contacts.PHOTO_ID,                      // 8
-        Contacts.PHOTO_THUMBNAIL_URI,           // 9
-        Contacts.LOOKUP_KEY,                    // 10
-        Contacts.PHONETIC_NAME,                 // 11
-        Contacts.HAS_PHONE_NUMBER,              // 12
-        Contacts.IS_USER_PROFILE,               // 13
+        Contacts.CONTACT_STATUS,                // 6
+        Contacts.PHOTO_ID,                      // 7
+        Contacts.PHOTO_THUMBNAIL_URI,           // 8
+        Contacts.LOOKUP_KEY,                    // 9
+        Contacts.PHONETIC_NAME,                 // 10
+        Contacts.HAS_PHONE_NUMBER,              // 11
+        Contacts.IS_USER_PROFILE,               // 12
     };
 
     protected static final String[] PROJECTION_DATA = new String[] {
@@ -62,13 +61,12 @@
         Data.SORT_KEY_PRIMARY,                  // 3
         Data.STARRED,                           // 4
         Data.CONTACT_PRESENCE,                  // 5
-        Data.CONTACT_CHAT_CAPABILITY,           // 6
-        Data.CONTACT_STATUS,                    // 7
-        Data.PHOTO_ID,                          // 8
-        Data.PHOTO_THUMBNAIL_URI,               // 9
-        Data.LOOKUP_KEY,                        // 10
-        Data.PHONETIC_NAME,                     // 11
-        Data.HAS_PHONE_NUMBER,                  // 12
+        Data.CONTACT_STATUS,                    // 6
+        Data.PHOTO_ID,                          // 7
+        Data.PHOTO_THUMBNAIL_URI,               // 8
+        Data.LOOKUP_KEY,                        // 9
+        Data.PHONETIC_NAME,                     // 10
+        Data.HAS_PHONE_NUMBER,                  // 11
     };
 
     protected static final String[] FILTER_PROJECTION = new String[] {
@@ -78,15 +76,14 @@
         Contacts.SORT_KEY_PRIMARY,              // 3
         Contacts.STARRED,                       // 4
         Contacts.CONTACT_PRESENCE,              // 5
-        Contacts.CONTACT_CHAT_CAPABILITY,       // 6
-        Contacts.CONTACT_STATUS,                // 7
-        Contacts.PHOTO_ID,                      // 8
-        Contacts.PHOTO_THUMBNAIL_URI,           // 9
-        Contacts.LOOKUP_KEY,                    // 10
-        Contacts.PHONETIC_NAME,                 // 11
-        Contacts.HAS_PHONE_NUMBER,              // 12
-        Contacts.IS_USER_PROFILE,               // 13
-        SearchSnippetColumns.SNIPPET,           // 14
+        Contacts.CONTACT_STATUS,                // 6
+        Contacts.PHOTO_ID,                      // 7
+        Contacts.PHOTO_THUMBNAIL_URI,           // 8
+        Contacts.LOOKUP_KEY,                    // 9
+        Contacts.PHONETIC_NAME,                 // 10
+        Contacts.HAS_PHONE_NUMBER,              // 11
+        Contacts.IS_USER_PROFILE,               // 12
+        SearchSnippetColumns.SNIPPET,           // 13
     };
 
     protected static final int CONTACT_ID_COLUMN_INDEX = 0;
@@ -95,15 +92,14 @@
     protected static final int CONTACT_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
     protected static final int CONTACT_STARRED_COLUMN_INDEX = 4;
     protected static final int CONTACT_PRESENCE_STATUS_COLUMN_INDEX = 5;
-    protected static final int CONTACT_CHAT_CAPABILITY_COLUMN_INDEX = 6;
-    protected static final int CONTACT_CONTACT_STATUS_COLUMN_INDEX = 7;
-    protected static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 8;
-    protected static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 9;
-    protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 10;
-    protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 11;
-    protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 12;
-    protected static final int CONTACT_IS_USER_PROFILE = 13;
-    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 14;
+    protected static final int CONTACT_CONTACT_STATUS_COLUMN_INDEX = 6;
+    protected static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 7;
+    protected static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 8;
+    protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 9;
+    protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 10;
+    protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 11;
+    protected static final int CONTACT_IS_USER_PROFILE = 12;
+    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 13;
 
     private CharSequence mUnknownNameText;
     private int mDisplayNameColumnIndex;
@@ -281,7 +277,7 @@
 
     protected void bindPresenceAndStatusMessage(final ContactListItemView view, Cursor cursor) {
         view.showPresenceAndStatusMessage(cursor, CONTACT_PRESENCE_STATUS_COLUMN_INDEX,
-                CONTACT_CHAT_CAPABILITY_COLUMN_INDEX, CONTACT_CONTACT_STATUS_COLUMN_INDEX);
+                CONTACT_CONTACT_STATUS_COLUMN_INDEX);
     }
 
     protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 4c4b780..ce1b119 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -1050,17 +1050,12 @@
      * Sets the proper icon (star or presence or nothing) and/or status message.
      */
     public void showPresenceAndStatusMessage(Cursor cursor, int presenceColumnIndex,
-            int capabilityColumnIndex, int contactStatusColumnIndex) {
+            int contactStatusColumnIndex) {
         Drawable icon = null;
         int presence = 0;
-        int chatCapability = 0;
         if (!cursor.isNull(presenceColumnIndex)) {
             presence = cursor.getInt(presenceColumnIndex);
-            if (capabilityColumnIndex != 0 && !cursor.isNull(presenceColumnIndex)) {
-                chatCapability = cursor.getInt(capabilityColumnIndex);
-            }
-            icon = ContactPresenceIconUtil.getChatCapabilityIcon(
-                    getContext(), presence, chatCapability);
+            icon = ContactPresenceIconUtil.getPresenceIcon(getContext(), presence);
         }
         setPresence(icon);
 
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 37ccded..b7434b1 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -16,6 +16,8 @@
 package com.android.contacts.list;
 
 import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.ContactStatusUtil;
 import com.android.contacts.ContactTileLoaderFactory;
 import com.android.contacts.GroupMemberLoader;
 import com.android.contacts.R;
@@ -25,6 +27,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
@@ -238,8 +241,8 @@
         contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
         contact.lookupKey = ContentUris.withAppendedId(
                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
-        contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
 
+        // Set phone number and label
         if (mDisplayType == DisplayType.STREQUENT_PHONE_ONLY) {
             int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
             String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
@@ -247,8 +250,25 @@
                     phoneNumberCustomLabel);
             contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
         } else {
-            contact.status = cursor.getString(mStatusIndex);
-            contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
+            // Set presence icon and status message
+            Drawable icon = null;
+            int presence = 0;
+            if (!cursor.isNull(mPresenceIndex)) {
+                presence = cursor.getInt(mPresenceIndex);
+                icon = ContactPresenceIconUtil.getPresenceIcon(mContext, presence);
+            }
+            contact.presenceIcon = icon;
+
+            String statusMessage = null;
+            if (mStatusIndex != 0 && !cursor.isNull(mStatusIndex)) {
+                statusMessage = cursor.getString(mStatusIndex);
+            }
+            // If there is no status message from the contact, but there was a presence value,
+            // then use the default status message string
+            if (statusMessage == null && presence != 0) {
+                statusMessage = ContactStatusUtil.getStatusString(mContext, presence);
+            }
+            contact.status = statusMessage;
         }
 
         return contact;
@@ -500,7 +520,6 @@
             } else {
                 contactTile = (ContactTileView) getChildAt(childIndex);
             }
-            contactTile.setClickable(entry != null);
             contactTile.loadFromContact(entry);
 
             switch (mItemViewType) {
@@ -531,7 +550,7 @@
         public String phoneNumber;
         public Uri photoUri;
         public Uri lookupKey;
-        public Integer presence;
+        public Drawable presenceIcon;
     }
 
     private static class ViewTypes {
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 25edd7f..bfc4a2e 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -96,22 +96,12 @@
             mLookupUri = entry.lookupKey;
 
             if (mStatus != null) {
-                String statusText;
-                if (entry.presence == null) {
+                if (entry.status == null) {
                     mStatus.setVisibility(View.GONE);
                 } else {
-                    statusText =
-                          (entry.status != null ? entry.status :
-                          ContactStatusUtil.getStatusString(mContext, entry.presence));
-                    mStatus.setText(statusText);
-                    int presenceDrawableResId = (entry.presence == null ? 0 :
-                            StatusUpdates.getPresenceIconResourceId(entry.presence));
-                    if (presenceDrawableResId != 0) {
-                        Log.i(TAG, "iconId = " + presenceDrawableResId);
-                        mStatus.setCompoundDrawablesWithIntrinsicBounds(
-                                getResources().getDrawable(presenceDrawableResId),
-                                null, null, null);
-                    }
+                    mStatus.setText(entry.status);
+                    mStatus.setCompoundDrawablesWithIntrinsicBounds(entry.presenceIcon,
+                            null, null, null);
                     mStatus.setVisibility(View.VISIBLE);
                 }
             }
diff --git a/src/com/android/contacts/list/LegacyContactListAdapter.java b/src/com/android/contacts/list/LegacyContactListAdapter.java
index b3ab2af..b21d484 100644
--- a/src/com/android/contacts/list/LegacyContactListAdapter.java
+++ b/src/com/android/contacts/list/LegacyContactListAdapter.java
@@ -91,6 +91,6 @@
     }
 
     protected void bindPresence(final ContactListItemView view, Cursor cursor) {
-        view.showPresenceAndStatusMessage(cursor, PERSON_PRESENCE_STATUS_COLUMN_INDEX, 0, 0);
+        view.showPresenceAndStatusMessage(cursor, PERSON_PRESENCE_STATUS_COLUMN_INDEX, 0);
     }
 }
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 60a7357..2d821e6 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -338,6 +338,17 @@
                         // Skip external account types that couldn't be initialized.
                         continue;
                     }
+                    if (!accountType.hasContactsMetadata()) {
+                        Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+                                + " it doesn't have the CONTACTS_STRUCTURE metadata");
+                        continue;
+                    }
+                    if (TextUtils.isEmpty(accountType.accountType)) {
+                        Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+                                + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
+                                + " attribute");
+                        continue;
+                    }
                     Log.d(TAG, "Registering extension package account type="
                             + accountType.accountType + ", dataSet=" + accountType.dataSet
                             + ", packageName=" + extensionPackage);
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 791f0ae..b6649c9 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -22,11 +22,9 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -46,7 +44,6 @@
 public class ExternalAccountType extends BaseAccountType {
     private static final String TAG = "ExternalAccountType";
 
-    private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
 
     private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
@@ -85,6 +82,7 @@
     private String mAccountTypeLabelAttribute;
     private String mAccountTypeIconAttribute;
     private boolean mInitSuccessful;
+    private boolean mHasContactsMetadata;
 
     public ExternalAccountType(Context context, String resPackageName) {
         this.resPackageName = resPackageName;
@@ -137,6 +135,13 @@
         return mInitSuccessful;
     }
 
+    /**
+     * Whether this account type has the android.provider.CONTACTS_STRUCTURE metadata xml.
+     */
+    public boolean hasContactsMetadata() {
+        return mHasContactsMetadata;
+    }
+
     @Override
     public String getEditContactActivityClassName() {
         return mEditContactActivityClassName;
@@ -207,6 +212,8 @@
                         + TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
             }
 
+            mHasContactsMetadata = true;
+
             int attributeCount = parser.getAttributeCount();
             for (int i = 0; i < attributeCount; i++) {
                 String attr = parser.getAttributeName(i);
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
index 806264d..a89177a 100644
--- a/src/com/android/contacts/util/ContactBadgeUtil.java
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -57,10 +57,15 @@
 
         final String statusLabelRes = streamItem.getLabelRes();
         final String statusResPackage = streamItem.getResPackage();
+
+        // Package name used for resources.getIdentifier()
+        String identiferPackage = statusResPackage;
         if (statusLabelRes  != null) {
             Resources resources;
             if (TextUtils.isEmpty(statusResPackage)) {
                 resources = context.getResources();
+                // In this case, we're using the framework resources.
+                identiferPackage = "android";
             } else {
                 PackageManager pm = context.getPackageManager();
                 try {
@@ -74,7 +79,7 @@
 
             if (resources != null) {
                 final int resId = resources.getIdentifier(statusLabelRes, "string",
-                        statusResPackage);
+                        identiferPackage);
                 if (resId == 0) {
                     Log.w(TAG, "Contact status update resource not found: " + statusLabelRes +
                             " in " + statusResPackage);