Merge "Several renames for consistency. Should help with fragmentizing afterwards"
diff --git a/res/layout-xlarge-land/contact_detail_list_item.xml b/res/layout-xlarge-land/contact_detail_list_item.xml
index 9749f2a..326e7b6 100644
--- a/res/layout-xlarge-land/contact_detail_list_item.xml
+++ b/res/layout-xlarge-land/contact_detail_list_item.xml
@@ -23,11 +23,6 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <!-- Separating line (between kinds) -->
-    <include
-        android:id="@+id/kind_divider"
-        layout="@layout/contact_detail_divider" />
-
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -118,12 +113,7 @@
 
              </FrameLayout>
         </LinearLayout>
-    </LinearLayout>
 
-    <!-- Separating line (only for the last row) -->
-    <include
-        android:id="@+id/line_below_last"
-        android:layout_marginBottom="8dip"
-        layout="@layout/contact_detail_divider" />
+    </LinearLayout>
 
 </LinearLayout>
diff --git a/res/layout-xlarge/contact_detail_fragment.xml b/res/layout-xlarge/contact_detail_fragment.xml
index 0a4c53c..4d6a900 100644
--- a/res/layout-xlarge/contact_detail_fragment.xml
+++ b/res/layout-xlarge/contact_detail_fragment.xml
@@ -23,25 +23,6 @@
     android:layout_height="match_parent"
     android:background="@drawable/panel_content">
 
-    <!-- Header View (including social status) -->
-    <com.android.contacts.widget.InterpolatingLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        >
-        <com.android.contacts.detail.ContactDetailHeaderView
-            android:id="@+id/contact_header_widget"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="28dip"
-            ex:layout_wideParentWidth="800dip"
-            ex:layout_wideMarginLeft="64dip"
-            ex:layout_widePaddingRight="48dip"
-            ex:layout_narrowParentWidth="500dip"
-            ex:layout_narrowMarginLeft="32dip"
-            ex:layout_narrowPaddingRight="16dip"
-        />
-    </com.android.contacts.widget.InterpolatingLayout>
-
     <!-- Placeholder for empty list -->
     <com.android.contacts.widget.InterpolatingLayout
         android:id="@android:id/empty"
diff --git a/res/layout/contact_detail_fragment.xml b/res/layout/contact_detail_fragment.xml
index da37323..72a62db 100644
--- a/res/layout/contact_detail_fragment.xml
+++ b/res/layout/contact_detail_fragment.xml
@@ -20,13 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <com.android.contacts.detail.ContactDetailHeaderView
-        android:id="@+id/contact_header_widget"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
-
     <ListView android:id="@android:id/list"
-        android:paddingTop="5dip"
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="1"
diff --git a/res/layout/contact_detail_header_view_list_item.xml b/res/layout/contact_detail_header_view_list_item.xml
new file mode 100644
index 0000000..cf149df
--- /dev/null
+++ b/res/layout/contact_detail_header_view_list_item.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<com.android.contacts.detail.ContactDetailHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/contact_header_widget"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
index b504fe0..c9067a6 100644
--- a/res/layout/contact_detail_list_item.xml
+++ b/res/layout/contact_detail_list_item.xml
@@ -21,92 +21,71 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <!-- Separating line (between kinds) -->
-    <include
-        android:id="@+id/kind_divider"
-        layout="@layout/contact_detail_divider" />
+    android:paddingLeft="@dimen/detail_item_side_margin"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
 
     <LinearLayout
-        android:layout_width="match_parent"
+        android:layout_width="0dip"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="@dimen/detail_item_side_margin"
-        android:layout_marginBottom="7dip"
-        android:minHeight="@dimen/detail_min_line_item_height"
-        android:orientation="horizontal"
+        android:layout_weight="1"
+        android:orientation="vertical"
         android:gravity="center_vertical">
 
-        <LinearLayout
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:orientation="vertical"
-            android:gravity="center_vertical">
+        <TextView
+            android:id="@+id/kind"
+            style="@style/ContactDetailItemType" />
 
-            <TextView
-                android:id="@+id/kind"
-                style="@style/ContactDetailItemType" />
+        <TextView
+            android:id="@+id/type"
+            style="@style/ContactDetailItemType" />
 
-            <TextView
-                android:id="@+id/type"
-                style="@style/ContactDetailItemType" />
-
-            <TextView
-                android:id="@+id/data"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceLarge" />
-
-            <TextView
-                android:id="@+id/footer"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:visibility="gone" />
-
-        </LinearLayout>
-
-        <ImageView
-            android:id="@+id/presence_icon"
-            android:layout_width="32dip"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="5dip"
-            android:gravity="center"
-            android:scaleType="centerInside" />
-
-        <View
-            android:id="@+id/divider"
-            android:layout_width="1px"
-            android:layout_height="match_parent"
-            android:layout_marginTop="15dip"
-            android:layout_marginBottom="8dip"
-            android:background="?android:attr/dividerVertical" />
-
-        <FrameLayout
-            android:id="@+id/secondary_action_button_container"
+        <TextView
+            android:id="@+id/data"
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginTop="10dip"
-            android:paddingLeft="@dimen/detail_item_icon_margin"
-            android:paddingRight="@dimen/detail_item_icon_margin"
-            android:duplicateParentState="false"
-            android:background="?android:attr/selectableItemBackground">
-            <ImageView
-                android:id="@+id/secondary_action_button"
-                android:layout_width="32dip"
-                android:layout_height="match_parent"
-                android:duplicateParentState="false" />
-        </FrameLayout>
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:paddingBottom="5dip"
+            android:textAppearance="?android:attr/textAppearanceLarge" />
 
+        <TextView
+            android:id="@+id/footer"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:visibility="gone" />
     </LinearLayout>
 
-    <!-- Separating line (only for the last row) -->
-    <include
-        android:id="@+id/line_below_last"
-        android:layout_marginBottom="8dip"
-        layout="@layout/contact_detail_divider" />
+    <ImageView
+        android:id="@+id/presence_icon"
+        android:layout_width="32dip"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="5dip"
+        android:gravity="center"
+        android:scaleType="centerInside" />
 
+    <View
+        android:id="@+id/divider"
+        android:layout_width="1px"
+        android:layout_height="match_parent"
+        android:layout_marginTop="15dip"
+        android:layout_marginBottom="10dip"
+        android:background="?android:attr/dividerVertical" />
+
+    <FrameLayout
+        android:id="@+id/secondary_action_button_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginTop="5dip"
+        android:paddingLeft="@dimen/detail_item_icon_margin"
+        android:paddingRight="@dimen/detail_item_icon_margin"
+        android:duplicateParentState="false"
+        android:background="?android:attr/selectableItemBackground">
+        <ImageView
+            android:id="@+id/secondary_action_button"
+            android:layout_width="32dip"
+            android:layout_height="match_parent"
+            android:duplicateParentState="false" />
+    </FrameLayout>
 </LinearLayout>
diff --git a/res/layout/contact_detail_divider.xml b/res/layout/contact_detail_separator_list_item.xml
similarity index 100%
rename from res/layout/contact_detail_divider.xml
rename to res/layout/contact_detail_separator_list_item.xml
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index dd7b906..88008a3 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -164,34 +164,26 @@
      * A list of distinct contact IDs included in the current contact.
      */
     private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
-    private ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mNicknameEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mRelationEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
+    private ArrayList<DetailViewEntry> mPhoneEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mSmsEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mEmailEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mPostalEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mImEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mNicknameEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mGroupEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mRelationEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mNoteEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mWebsiteEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mSipEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mEventEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mOtherEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<ViewEntry> mAllEntries = new ArrayList<ViewEntry>();
     private LayoutInflater mInflater;
 
     private boolean mTransitionAnimationRequested;
 
     public ContactDetailFragment() {
         // Explicit constructor for inflation
-
-        // Build the list of sections. The order they're added to mSections dictates the
-        // order they are displayed in the list.
-        mSections.add(mPhoneEntries);
-        mSections.add(mSmsEntries);
-        mSections.add(mEmailEntries);
-        mSections.add(mImEntries);
-        mSections.add(mPostalEntries);
-        mSections.add(mNicknameEntries);
-        mSections.add(mOtherEntries);
-        mSections.add(mRelationEntries);
-        mSections.add(mGroupEntries);
     }
 
     @Override
@@ -223,9 +215,6 @@
 
         mInflater = inflater;
 
-        mHeaderView = (ContactDetailHeaderView) mView.findViewById(R.id.contact_header_widget);
-        mHeaderView.setListener(mHeaderViewListener);
-
         mListView = (ListView) mView.findViewById(android.R.id.list);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
         mListView.setOnItemClickListener(this);
@@ -304,19 +293,22 @@
             return;
         }
 
-        // Set the header
-        mHeaderView.loadData(mContactData);
+        // Clear old header
+        mHeaderView = null;
 
         // Build up the contact entries
         buildEntries();
 
-        // Collapse similar data items in select sections.
+        // Collapse similar data items for select {@link DataKind}s.
         Collapser.collapseList(mPhoneEntries);
         Collapser.collapseList(mSmsEntries);
         Collapser.collapseList(mEmailEntries);
         Collapser.collapseList(mPostalEntries);
         Collapser.collapseList(mImEntries);
 
+        // Make one aggregated list of all entries for display to the user.
+        flattenAllLists();
+
         if (mAdapter == null) {
             mAdapter = new ViewAdapter();
             mListView.setAdapter(mAdapter);
@@ -350,10 +342,7 @@
         mHasSip = PhoneCapabilityTester.isSipPhone(mContext);
 
         // Clear out the old entries
-        final int numSections = mSections.size();
-        for (int i = 0; i < numSections; i++) {
-            mSections.get(i).clear();
-        }
+        mAllEntries.clear();
 
         mRawContactIds.clear();
 
@@ -409,8 +398,8 @@
                         accountType, mimeType);
                 if (kind == null) continue;
 
-                final ViewEntry entry = ViewEntry.fromValues(mContext, mimeType, kind, dataId,
-                        entryValues, mContactData.isDirectoryEntry(),
+                final DetailViewEntry entry = DetailViewEntry.fromValues(mContext, mimeType, kind,
+                        dataId, entryValues, mContactData.isDirectoryEntry(),
                         mContactData.getDirectoryId());
 
                 final boolean hasData = !TextUtils.isEmpty(entry.data);
@@ -464,8 +453,8 @@
                         final String imMime = Im.CONTENT_ITEM_TYPE;
                         final DataKind imKind = accountTypes.getKindOrFallback(accountType,
                                 imMime);
-                        final ViewEntry imEntry = ViewEntry.fromValues(mContext, imMime, imKind,
-                                dataId, entryValues, mContactData.isDirectoryEntry(),
+                        final DetailViewEntry imEntry = DetailViewEntry.fromValues(mContext, imMime,
+                                imKind, dataId, entryValues, mContactData.isDirectoryEntry(),
                                 mContactData.getDirectoryId());
                         buildImActions(imEntry, entryValues);
                         imEntry.applyStatus(status, false);
@@ -507,7 +496,7 @@
                     // Build note entries
                     entry.uri = null;
                     entry.maxLines = 100;
-                    mOtherEntries.add(entry);
+                    mNoteEntries.add(entry);
                 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build Website entries
                     entry.uri = null;
@@ -519,7 +508,7 @@
                     } catch (ParseException e) {
                         Log.e(TAG, "Couldn't parse website: " + entry.data);
                     }
-                    mOtherEntries.add(entry);
+                    mWebsiteEntries.add(entry);
                 } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build SipAddress entries
                     entry.uri = null;
@@ -531,17 +520,17 @@
                         entry.intent = null;
                         entry.actionIcon = -1;
                     }
-                    mOtherEntries.add(entry);
-                    // TODO: Consider moving the SipAddress into its own
-                    // section (rather than lumping it in with mOtherEntries)
-                    // so that we can reposition it right under the phone number.
+                    mSipEntries.add(entry);
+                    // TODO: Now that SipAddress is in its own list of entries
+                    // (instead of grouped in mOtherEntries), consider
+                    // repositioning it right under the phone number.
                     // (Then, we'd also update FallbackAccountType.java to set
                     // secondary=false for this field, and tweak the weight
                     // of its DataKind.)
                 } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     entry.data = DateUtils.formatDate(mContext, entry.data);
                     entry.uri = null;
-                    mOtherEntries.add(entry);
+                    mEventEntries.add(entry);
                 } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     entry.intent = new Intent(Intent.ACTION_SEARCH);
                     entry.intent.putExtra(SearchManager.QUERY, entry.data);
@@ -567,7 +556,7 @@
         }
 
         if (!groups.isEmpty()) {
-            ViewEntry entry = new ViewEntry();
+            DetailViewEntry entry = new DetailViewEntry();
             Collections.sort(groups);
             StringBuilder sb = new StringBuilder();
             int size = groups.size();
@@ -585,6 +574,49 @@
     }
 
     /**
+     * Collapse all contact detail entries into one aggregated list with a {@link HeaderViewEntry}
+     * at the top.
+     */
+    private void flattenAllLists() {
+        // All contacts should have a header view (even if there is no data for the contact).
+        mAllEntries.add(new HeaderViewEntry());
+
+        flattenList(mPhoneEntries);
+        flattenList(mSmsEntries);
+        flattenList(mEmailEntries);
+        flattenList(mImEntries);
+        flattenList(mPostalEntries);
+        flattenList(mNicknameEntries);
+        flattenList(mNoteEntries);
+        flattenList(mWebsiteEntries);
+        flattenList(mSipEntries);
+        flattenList(mEventEntries);
+        flattenList(mOtherEntries);
+        flattenList(mRelationEntries);
+        flattenList(mGroupEntries);
+    }
+
+    /**
+     * Iterate through {@link DetailViewEntry} in the given list and add it to a list of all
+     * entries. Add a {@link SeparatorViewEntry} at the end if the length of the list was not 0.
+     * Clear the original list.
+     */
+    private void flattenList(ArrayList<DetailViewEntry> entries) {
+        int count = entries.size();
+
+        for (int i = 0; i < count; i++) {
+            mAllEntries.add(entries.get(i));
+        }
+
+        if (count > 0) {
+            mAllEntries.add(new SeparatorViewEntry());
+        }
+
+        // Clear old list because it's not needed anymore.
+        entries.clear();
+    }
+
+    /**
      * Maps group ID to the corresponding group name, collapses all synonymous groups.
      * Ignores default groups (e.g. My Contacts) and favorites groups.
      */
@@ -621,7 +653,7 @@
      * {@link Email} row. If the result is non-null, it either contains one or two Intents
      * (e.g. [Text, Videochat] or just [Text])
      */
-    public static void buildImActions(ViewEntry entry, ContentValues values) {
+    public static void buildImActions(DetailViewEntry entry, ContentValues values) {
         final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
 
         if (!isEmail && !isProtocolValid(values)) {
@@ -690,15 +722,65 @@
     }
 
     /**
-     * A basic structure with the data for a contact entry in the list.
+     * Base class for an item in the {@link ViewAdapter} list of data, which is
+     * supplied to the {@link ListView}.
      */
-    static class ViewEntry implements Collapsible<ViewEntry> {
+    static class ViewEntry {
+        private final int viewTypeForAdapter;
+        protected long id = -1;
+        /** Whether or not the entry can be focused on or not. */
+        protected boolean isEnabled = false;
+
+        ViewEntry(int viewType) {
+            viewTypeForAdapter = viewType;
+        }
+
+        int getViewType() {
+            return viewTypeForAdapter;
+        }
+
+        long getId() {
+            return id;
+        }
+
+        boolean isEnabled(){
+            return isEnabled;
+        }
+    }
+
+    /**
+     * Header item in the {@link ViewAdapter} list of data.
+     */
+    static class HeaderViewEntry extends ViewEntry {
+
+        HeaderViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_HEADER_ENTRY);
+        }
+
+    }
+
+    /**
+     * Separator between items of the same {@link DataKind} in the
+     * {@link ViewAdapter} list of data.
+     */
+    static class SeparatorViewEntry extends ViewEntry {
+
+        SeparatorViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_SEPARATOR_ENTRY);
+        }
+
+    }
+
+    /**
+     * An item with a single detail for a contact in the {@link ViewAdapter}
+     * list of data.
+     */
+    static class DetailViewEntry extends ViewEntry implements Collapsible<DetailViewEntry> {
         public int type = -1;
         public String kind;
         public String typeString;
         public String data;
         public Uri uri;
-        public long id = 0;
         public int maxLines = 1;
         public String mimetype;
 
@@ -717,17 +799,19 @@
 
         public CharSequence footerLine = null;
 
-        ViewEntry() {
+        DetailViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_DETAIL_ENTRY);
+            isEnabled = true;
         }
 
         /**
-         * Build new {@link ViewEntry} and populate from the given values.
+         * Build new {@link DetailViewEntry} and populate from the given values.
          */
-        public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
+        public static DetailViewEntry fromValues(Context context, String mimeType, DataKind kind,
                 long dataId, ContentValues values, boolean isDirectoryEntry, long directoryId) {
-            final ViewEntry entry = new ViewEntry();
-            entry.context = context;
+            final DetailViewEntry entry = new DetailViewEntry();
             entry.id = dataId;
+            entry.context = context;
             entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
             if (isDirectoryEntry) {
                 entry.uri = entry.uri.buildUpon().appendQueryParameter(
@@ -770,13 +854,13 @@
         }
 
         /**
-         * Apply given {@link DataStatus} values over this {@link ViewEntry}
+         * Apply given {@link DataStatus} values over this {@link DetailViewEntry}
          *
          * @param fillData When true, the given status replaces {@link #data}
          *            and {@link #footerLine}. Otherwise only {@link #presence}
          *            is updated.
          */
-        public ViewEntry applyStatus(DataStatus status, boolean fillData) {
+        public DetailViewEntry applyStatus(DataStatus status, boolean fillData) {
             presence = status.getPresence();
             if (fillData && status.isValid()) {
                 this.data = status.getStatus().toString();
@@ -787,7 +871,7 @@
         }
 
         @Override
-        public boolean collapseWith(ViewEntry entry) {
+        public boolean collapseWith(DetailViewEntry entry) {
             // assert equal collapse keys
             if (!shouldCollapseWith(entry)) {
                 return false;
@@ -816,13 +900,13 @@
             // uri, and contactdId, shouldn't make a difference. Just keep the original.
 
             // Keep track of all the ids that have been collapsed with this one.
-            ids.add(entry.id);
+            ids.add(entry.getId());
             collapseCount++;
             return true;
         }
 
         @Override
-        public boolean shouldCollapseWith(ViewEntry entry) {
+        public boolean shouldCollapseWith(DetailViewEntry entry) {
             if (entry == null) {
                 return false;
             }
@@ -845,8 +929,6 @@
 
     /** Cache of the children views of a row */
     private static class ViewCache {
-        public View kindDivider;
-        public View lineBelowLast;
         public TextView kind;
         public TextView type;
         public TextView data;
@@ -858,9 +940,53 @@
     }
 
     private final class ViewAdapter extends BaseAdapter {
+
+        public static final int VIEW_TYPE_DETAIL_ENTRY = 0;
+        public static final int VIEW_TYPE_HEADER_ENTRY = 1;
+        public static final int VIEW_TYPE_SEPARATOR_ENTRY = 2;
+        private static final int VIEW_TYPE_COUNT = 3;
+
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final ViewEntry entry = getEntry(position);
+            switch (getItemViewType(position)) {
+                case VIEW_TYPE_HEADER_ENTRY:
+                    return getHeaderEntryView(convertView, parent);
+                case VIEW_TYPE_SEPARATOR_ENTRY:
+                    return getSeparatorEntryView(convertView, parent);
+                case VIEW_TYPE_DETAIL_ENTRY:
+                    return getDetailEntryView(position, convertView, parent);
+                default:
+                    throw new IllegalStateException("Invalid view type ID " +
+                            getItemViewType(position));
+            }
+        }
+
+        private View getHeaderEntryView(View convertView, ViewGroup parent) {
+            // We don't want to rely on the recycled header view because it may
+            // have been left over from a previously viewed contact (since we
+            // reuse the adapter), so we would have to bind the data to the
+            // header each time. However, since there is only 1 header per list,
+            // just hold onto the original header view for this contact and
+            // return that each time.
+            if (mHeaderView != null) {
+                return mHeaderView;
+            }
+            mHeaderView = (ContactDetailHeaderView) mInflater.inflate(
+                    R.layout.contact_detail_header_view_list_item, parent, false);
+            mHeaderView.setListener(mHeaderViewListener);
+            mHeaderView.loadData(mContactData);
+            return mHeaderView;
+        }
+
+        private View getSeparatorEntryView(View convertView, ViewGroup parent) {
+            if (convertView != null) {
+                return convertView;
+            }
+            return mInflater.inflate(R.layout.contact_detail_separator_list_item, parent, false);
+        }
+
+        private View getDetailEntryView(int position, View convertView, ViewGroup parent) {
+            final DetailViewEntry entry = (DetailViewEntry) getItem(position);
             final View v;
             final ViewCache viewCache;
 
@@ -875,8 +1001,6 @@
                 // Cache the children
                 viewCache = new ViewCache();
                 viewCache.kind = (TextView) v.findViewById(R.id.kind);
-                viewCache.kindDivider = v.findViewById(R.id.kind_divider);
-                viewCache.lineBelowLast = v.findViewById(R.id.line_below_last);
                 viewCache.type = (TextView) v.findViewById(R.id.type);
                 viewCache.data = (TextView) v.findViewById(R.id.data);
                 viewCache.footer = (TextView) v.findViewById(R.id.footer);
@@ -891,17 +1015,16 @@
                 v.setTag(viewCache);
             }
 
-            final ViewEntry previousEntry = position == 0 ? null : getEntry(position - 1);
-            final boolean isFirstOfItsKind =
-                    previousEntry == null ? true : !previousEntry.kind.equals(entry.kind);
+            final ViewEntry previousEntry = position == 0 ? null : getItem(position - 1);
+            final boolean isFirstOfItsKind = (previousEntry == null) ? true :
+                    (previousEntry.getViewType() != VIEW_TYPE_DETAIL_ENTRY);
 
             // Bind the data to the view
-            bindView(v, entry, position, isFirstOfItsKind);
+            bindView(v, entry, isFirstOfItsKind);
             return v;
         }
 
-        private void bindView(View view, ViewEntry entry, int position,
-                boolean isFirstOfItsKind) {
+        private void bindView(View view, DetailViewEntry entry, boolean isFirstOfItsKind) {
             final Resources resources = mContext.getResources();
             ViewCache views = (ViewCache) view.getTag();
 
@@ -912,15 +1035,6 @@
                 views.kind.setVisibility(View.GONE);
             }
 
-            views.kindDivider.setVisibility(isFirstOfItsKind && position != 0 ?
-                    View.VISIBLE : View.GONE);
-
-            if (position == getCount() - 1) {
-                views.lineBelowLast.setVisibility(View.VISIBLE);
-            } else {
-                views.lineBelowLast.setVisibility(View.GONE);
-            }
-
             if (!TextUtils.isEmpty(entry.typeString)) {
                 views.type.setText(entry.typeString.toUpperCase());
                 views.type.setVisibility(View.VISIBLE);
@@ -994,8 +1108,9 @@
                 if (mListener == null) return;
                 if (v == null) return;
                 final ViewEntry entry = (ViewEntry) v.getTag();
-                if (entry == null) return;
-                final Intent intent = entry.secondaryIntent;
+                if (entry == null || !(entry instanceof DetailViewEntry)) return;
+                final DetailViewEntry detailViewEntry = (DetailViewEntry) entry;
+                final Intent intent = detailViewEntry.secondaryIntent;
                 if (intent == null) return;
                 mListener.onItemClicked(intent);
             }
@@ -1003,40 +1118,42 @@
 
         @Override
         public int getCount() {
-            int count = 0;
-            final int numSections = mSections.size();
-            for (int i = 0; i < numSections; i++) {
-                final ArrayList<ViewEntry> section = mSections.get(i);
-                count += section.size();
-            }
-            return count;
+            return mAllEntries.size();
         }
 
         @Override
-        public Object getItem(int position) {
-            return getEntry(position);
+        public ViewEntry getItem(int position) {
+            return mAllEntries.get(position);
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return mAllEntries.get(position).getViewType();
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return VIEW_TYPE_COUNT;
         }
 
         @Override
         public long getItemId(int position) {
-            final ViewEntry entry = getEntry(position);
+            final ViewEntry entry = mAllEntries.get(position);
             if (entry != null) {
-                return entry.id;
+                return entry.getId();
             }
             return -1;
         }
 
-        private ViewEntry getEntry(int position) {
-            final int numSections = mSections.size();
-            for (int i = 0; i < numSections; i++) {
-                final ArrayList<ViewEntry> section = mSections.get(i);
-                final int sectionSize = section.size();
-                if (position < sectionSize) {
-                    return section.get(position);
-                }
-                position -= sectionSize;
-            }
-            return null;
+        @Override
+        public boolean areAllItemsEnabled() {
+            // Header will always be an item that is not enabled.
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItem(position).isEnabled();
         }
     }
 
@@ -1182,9 +1299,9 @@
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         if (mListener == null) return;
-        final ViewEntry entry = mAdapter.getEntry(position);
-        if (entry == null) return;
-        final Intent intent = entry.intent;
+        final ViewEntry entry = mAdapter.getItem(position);
+        if (entry == null || !(entry instanceof DetailViewEntry)) return;
+        final Intent intent = ((DetailViewEntry) entry).intent;
         if (intent == null) return;
         mListener.onItemClicked(intent);
     }
@@ -1220,7 +1337,7 @@
 
                 int index = mListView.getSelectedItemPosition();
                 if (index != -1) {
-                    final ViewEntry entry = mAdapter.getEntry(index);
+                    final DetailViewEntry entry = (DetailViewEntry) mAdapter.getItem(index);
                     if (entry != null && entry.intent != null &&
                             entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
                         mContext.startActivity(entry.intent);
diff --git a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
index 4cee32c..cfab94a 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
@@ -16,7 +16,7 @@
 
 package com.android.contacts.detail;
 
-import com.android.contacts.detail.ContactDetailFragment.ViewEntry;
+import com.android.contacts.detail.ContactDetailFragment.DetailViewEntry;
 
 import android.content.ContentValues;
 import android.content.Intent;
@@ -42,7 +42,7 @@
         values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
         values.put(Im.DATA, TEST_ADDRESS);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -59,7 +59,7 @@
         values.put(Im.DATA, TEST_ADDRESS);
         values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -78,7 +78,7 @@
         values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
                 Im.CAPABILITY_HAS_VOICE);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -97,7 +97,7 @@
         values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
         values.put(Im.DATA, TEST_ADDRESS);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
 
@@ -120,7 +120,7 @@
         values.put(Email.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
                 Im.CAPABILITY_HAS_VOICE);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());