Refactor to inflate views from XML

- This is part 1 of getting the contact editor up to par with
the UI mocks, there are some known issues that will be fixed
in coming CLs (i.e. add button functionality)
- This way, the field editors can be vertically stacked
on the phone and horizontally stacked on the tablet.
- Get rid of duplicate labels by using the hint text in the field
or spinner as the title

Change-Id: Ia00a1a65ad0bbd7a4bc05ccb93dd1b1496bca5df
diff --git a/res/layout-xlarge/event_field_editor_view.xml b/res/layout-xlarge/event_field_editor_view.xml
new file mode 100644
index 0000000..2deb551
--- /dev/null
+++ b/res/layout-xlarge/event_field_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/date_view"
+            layout="@layout/edit_date_picker" />
+
+        <include
+            android:id="@+id/spinner"
+            layout="@layout/edit_spinner"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout-xlarge/item_kind_section.xml b/res/layout-xlarge/item_kind_section.xml
index 57de26d..a4c874e 100644
--- a/res/layout-xlarge/item_kind_section.xml
+++ b/res/layout-xlarge/item_kind_section.xml
@@ -22,16 +22,6 @@
     android:layout_height="wrap_content"
     android:orientation="horizontal">
 
-    <TextView
-        android:id="@+id/kind_title"
-        android:layout_width="150dip"
-        android:layout_height="@dimen/editor_min_line_item_height"
-        android:gravity="center_vertical"
-        android:textColor="#7F7F7F"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:singleLine="true"
-        android:ellipsize="marquee" />
-
     <LinearLayout
         android:id="@+id/kind_editors"
         android:layout_width="0dip"
diff --git a/res/layout-xlarge/phonetic_name_editor_view.xml b/res/layout-xlarge/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..0ce560e
--- /dev/null
+++ b/res/layout-xlarge/phonetic_name_editor_view.xml
@@ -0,0 +1,67 @@
+<?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.editor.PhoneticNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize"
+    android:layout_marginLeft="52dip"
+    android:layout_marginRight="48dip"
+    android:layout_marginTop="6dip"
+    android:layout_marginBottom="4dip">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout-xlarge/raw_contact_editor_view.xml b/res/layout-xlarge/raw_contact_editor_view.xml
index 3c3b9d0..abf2463 100644
--- a/res/layout-xlarge/raw_contact_editor_view.xml
+++ b/res/layout-xlarge/raw_contact_editor_view.xml
@@ -67,25 +67,13 @@
                     android:layout_height="1px"
                     android:background="@color/contact_detail_header_divider_color" />
 
-                <com.android.contacts.editor.StructuredNameEditorView
+                <include
                     android:id="@+id/edit_name"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingRight="?android:attr/scrollbarSize"
-                    android:layout_marginLeft="52dip"
-                    android:layout_marginRight="48dip"
-                    android:layout_marginTop="6dip"
-                    android:layout_marginBottom="4dip" />
+                    layout="@layout/structured_name_editor_view" />
 
-                <com.android.contacts.editor.PhoneticNameEditorView
+                <include
                     android:id="@+id/edit_phonetic_name"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingRight="?android:attr/scrollbarSize"
-                    android:layout_marginLeft="52dip"
-                    android:layout_marginRight="48dip"
-                    android:layout_marginTop="6dip"
-                    android:layout_marginBottom="4dip" />
+                    layout="@layout/phonetic_name_editor_view" />
 
                 <FrameLayout
                     android:layout_width="match_parent"
diff --git a/res/layout-xlarge/structured_name_editor_view.xml b/res/layout-xlarge/structured_name_editor_view.xml
new file mode 100644
index 0000000..048d1db
--- /dev/null
+++ b/res/layout-xlarge/structured_name_editor_view.xml
@@ -0,0 +1,66 @@
+<?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.editor.StructuredNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize"
+    android:layout_marginLeft="52dip"
+    android:layout_marginRight="48dip"
+    android:layout_marginTop="6dip"
+    android:layout_marginBottom="4dip">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout-xlarge/text_fields_editor_view.xml b/res/layout-xlarge/text_fields_editor_view.xml
new file mode 100644
index 0000000..927654b
--- /dev/null
+++ b/res/layout-xlarge/text_fields_editor_view.xml
@@ -0,0 +1,62 @@
+<?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.editor.TextFieldsEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="top"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/spinner"
+            layout="@layout/edit_spinner"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/layout/edit_date_picker.xml b/res/layout/edit_date_picker.xml
new file mode 100644
index 0000000..a957bd9
--- /dev/null
+++ b/res/layout/edit_date_picker.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<!-- Button to select a date in the contact editor. -->
+
+<Button
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/date_view"
+    android:layout_width="0dip"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    style="@android:style/Widget.Holo.Spinner" />
\ No newline at end of file
diff --git a/res/layout/edit_delete_button.xml b/res/layout/edit_delete_button.xml
new file mode 100644
index 0000000..c0a895e
--- /dev/null
+++ b/res/layout/edit_delete_button.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<!-- "Delete field" button in the contact editor. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/editor_min_line_item_height"
+    android:layout_gravity="top">
+    <ImageButton
+        android:id="@+id/delete_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:duplicateParentState="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:src="@drawable/ic_menu_remove_field_holo_light"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:paddingTop="@dimen/editor_round_button_padding_top"
+        android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+        android:contentDescription="@string/description_minus_button" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_divider.xml b/res/layout/edit_divider.xml
index eb2a49a..786bfca 100644
--- a/res/layout/edit_divider.xml
+++ b/res/layout/edit_divider.xml
@@ -16,6 +16,6 @@
 
 <View xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="1dip"
     android:background="?android:attr/listDivider"
     />
diff --git a/res/layout/edit_expansion_button.xml b/res/layout/edit_expansion_button.xml
new file mode 100644
index 0000000..27e6102
--- /dev/null
+++ b/res/layout/edit_expansion_button.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<!-- "More" or "less" expansion button in the contact editor. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/editor_min_line_item_height"
+    android:layout_gravity="top">
+    <ImageButton
+        android:id="@+id/expansion_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:duplicateParentState="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:paddingTop="@dimen/editor_round_button_padding_top"
+        android:paddingBottom="@dimen/editor_round_button_padding_bottom" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_field_list.xml b/res/layout/edit_field_list.xml
new file mode 100644
index 0000000..448a1fd
--- /dev/null
+++ b/res/layout/edit_field_list.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Layout to contain a list of fields in the contact editor. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/editors"
+    android:layout_width="0dip"
+    android:layout_weight="1"
+    android:layout_height="wrap_content"
+    android:paddingBottom="6dip"
+    android:orientation="vertical" />
\ No newline at end of file
diff --git a/res/layout/edit_field_title.xml b/res/layout/edit_field_title.xml
new file mode 100644
index 0000000..d8f6808
--- /dev/null
+++ b/res/layout/edit_field_title.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<!-- Title of a field in the contact editor. -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/title"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/editor_field_title_padding"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:singleLine="true"
+    android:ellipsize="marquee"
+    android:fadingEdge="horizontal"
+    android:textSize="@dimen/editor_field_title_text_size" />
\ No newline at end of file
diff --git a/res/layout/edit_spinner.xml b/res/layout/edit_spinner.xml
new file mode 100644
index 0000000..1511c8c
--- /dev/null
+++ b/res/layout/edit_spinner.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<!-- Spinner for a field in the contact editor. -->
+
+<Spinner
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/spinner"
+    android:layout_width="@dimen/editor_type_label_width"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="@color/kind_title"
+    android:singleLine="true"
+    android:ellipsize="marquee"
+    android:fadingEdge="horizontal" />
\ No newline at end of file
diff --git a/res/layout/event_field_editor_view.xml b/res/layout/event_field_editor_view.xml
new file mode 100644
index 0000000..45fece9
--- /dev/null
+++ b/res/layout/event_field_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/date_view"
+            layout="@layout/edit_date_picker" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
index c0e42a0..2cfb2a5 100644
--- a/res/layout/item_kind_section.xml
+++ b/res/layout/item_kind_section.xml
@@ -25,7 +25,7 @@
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="1px"
+        android:layout_height="15dip"
         android:background="?android:attr/listDivider" />
 
     <LinearLayout
@@ -40,7 +40,8 @@
         android:orientation="horizontal"
         android:gravity="center_vertical"
         android:focusable="true"
-        android:clickable="true">
+        android:clickable="true"
+        android:visibility="gone">
 
         <TextView
             android:id="@+id/kind_title"
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 642908e..3590963 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -27,7 +27,7 @@
         android:src="@drawable/ic_contact_picture"
         android:cropToPadding="true"
         android:scaleType="centerCrop"
-        android:gravity="center"
+        android:gravity="left"
     />
     <View
         android:id="@+id/frame"
diff --git a/res/layout/phonetic_name_editor_view.xml b/res/layout/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..3a3987d
--- /dev/null
+++ b/res/layout/phonetic_name_editor_view.xml
@@ -0,0 +1,63 @@
+<?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.editor.PhoneticNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index ac30a6b..16b868e 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -86,9 +86,7 @@
         <FrameLayout
             android:id="@+id/stub_photo"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingLeft="12dip"
-            android:paddingTop="10dip">
+            android:layout_height="wrap_content">
 
             <include
                 android:id="@+id/edit_photo"
@@ -96,21 +94,13 @@
 
         </FrameLayout>
 
-        <com.android.contacts.editor.StructuredNameEditorView
+        <include
             android:id="@+id/edit_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingRight="?android:attr/scrollbarSize"
-            android:layout_marginTop="6dip"
-            android:layout_marginBottom="4dip" />
+            layout="@layout/structured_name_editor_view" />
 
-        <com.android.contacts.editor.PhoneticNameEditorView
+        <include
             android:id="@+id/edit_phonetic_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingRight="?android:attr/scrollbarSize"
-            android:layout_marginTop="6dip"
-            android:layout_marginBottom="4dip" />
+            layout="@layout/phonetic_name_editor_view" />
 
         <ViewStub android:id="@+id/aggregation_suggestion_stub"
             android:inflatedId="@+id/aggregation_suggestion"
diff --git a/res/layout/structured_name_editor_view.xml b/res/layout/structured_name_editor_view.xml
new file mode 100644
index 0000000..8820c56
--- /dev/null
+++ b/res/layout/structured_name_editor_view.xml
@@ -0,0 +1,62 @@
+<?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.editor.StructuredNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout/text_fields_editor_view.xml b/res/layout/text_fields_editor_view.xml
new file mode 100644
index 0000000..f9e6a29
--- /dev/null
+++ b/res/layout/text_fields_editor_view.xml
@@ -0,0 +1,62 @@
+<?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.editor.TextFieldsEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_button_container"
+            layout="@layout/edit_expansion_button"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
index d18b789..7a9b742 100644
--- a/res/values-xlarge/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -16,6 +16,8 @@
 <resources>
     <dimen name="edit_photo_size">96dip</dimen>
     <dimen name="aggregation_suggestion_icon_size">64dip</dimen>
+    <dimen name="editor_type_label_width">180dip</dimen>
+    <dimen name="editor_field_spinner_text_size">15sp</dimen>
     <dimen name="quick_contact_width">356dip</dimen>
     <dimen name="contact_name_text_size">26sp</dimen>
     <dimen name="action_bar_filter_min_width">120dip</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2255cd8..38b836a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="edit_photo_size">76dip</dimen>
+    <dimen name="edit_photo_size">120dip</dimen>
 
     <!-- The height of the ScrollingTabWidget -->
     <dimen name="tab_height">40dip</dimen>
@@ -41,11 +41,23 @@
     <dimen name="editor_round_button_padding_bottom">8dip</dimen>
 
     <!-- Width of the Type-Label in the Editor -->
-    <dimen name="editor_type_label_width">180dip</dimen>
-    
+    <dimen name="editor_type_label_width">120dip</dimen>
+
+    <!-- Padding of the title of a field in the Editor -->
+    <dimen name="editor_field_title_padding">10dip</dimen>
+
     <!-- Minimum height of a row in the Editor -->
     <dimen name="editor_min_line_item_height">48dip</dimen>
 
+    <!-- Font size used for the value of a field in the contact editor. -->
+    <dimen name="editor_field_text_size">18sp</dimen>
+
+    <!-- Font size used for the title of a field in the contact editor. -->
+    <dimen name="editor_field_title_text_size">15sp</dimen>
+
+    <!-- Font size for the entries in a spinner in the contact editor. -->
+    <dimen name="editor_field_spinner_text_size">10sp</dimen>
+
     <!-- Minimum height of a row in the contact detail -->
     <dimen name="detail_min_line_item_height">48dip</dimen>
 
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index f021deb..f757b54 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -32,10 +32,8 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.View;
 import android.widget.Button;
-import android.widget.LinearLayout;
 
 import java.text.ParsePosition;
 import java.util.Calendar;
@@ -65,57 +63,30 @@
         super(context, attrs, defStyle);
     }
 
+    /** {@inheritDoc} */
     @Override
-    public int getBaseline(int row) {
-        int baseline = super.getBaseline(row);
-        if (mDateView != null) {
-            // The date view will be centered vertically in the corresponding line item
-            int lineItemHeight = getLineItemHeight(row);
-            int offset = (lineItemHeight - mDateView.getMeasuredHeight()) / 2;
-            baseline = Math.max(baseline, offset + mDateView.getBaseline());
-        }
-        return baseline;
-    }
+    protected void onFinishInflate() {
+        super.onFinishInflate();
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        int l1 = getPaddingLeft();
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-
-        // Fields
-        // Subtract buttons left and right if necessary
-        final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
-        final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
-        final int r2 = r1 - deleteWidth - labelWidth;
-        if (mDateView != null) {
-            int height = mDateView.getMeasuredHeight();
-            int baseline = getBaseline(0);
-            int top = t1 + baseline - mDateView.getBaseline();
-            mDateView.layout(
-                    l1, top,
-                    r2, top + height);
-        }
-    }
-
-    @Override
-    protected int getLineItemHeight(int row) {
-        int height = mDateView == null ? 0 : mDateView.getMeasuredHeight();
-        return Math.max(height, super.getLineItemHeight(row));
+        mDateView = (Button) findViewById(R.id.date_view);
+        mDateView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showDialog(R.id.dialog_event_date_picker);
+            }
+        });
     }
 
     @Override
     protected void requestFocusForFirstEditField() {
-        if (mDateView != null) mDateView.requestFocus();
+        mDateView.requestFocus();
     }
 
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
 
-        if (mDateView != null) mDateView.setEnabled(!isReadOnly() && enabled);
+        mDateView.setEnabled(!isReadOnly() && enabled);
     }
 
     @Override
@@ -124,20 +95,7 @@
         if (kind.fieldList.size() != 1) throw new IllegalStateException("kind must have 1 field");
         super.setValues(kind, entry, state, readOnly, vig);
 
-        if (mDateView == null) {
-
-            mDateView = new Button(getContext(), null, android.R.attr.spinnerStyle);
-            mDateView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
-                    LayoutParams.WRAP_CONTENT));
-            mDateView.setEnabled(isEnabled() && !readOnly);
-            mDateView.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    showDialog(R.id.dialog_event_date_picker);
-                }
-            });
-            addView(mDateView);
-        }
+        mDateView.setEnabled(isEnabled() && !readOnly);
 
         rebuildDateView();
     }
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
index bc76ba8..ce5c96a 100644
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ b/src/com/android/contacts/editor/KindSectionView.java
@@ -28,11 +28,11 @@
 import android.provider.ContactsContract.Data;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import java.util.ArrayList;
 
@@ -47,7 +47,6 @@
     private ViewGroup mEditors;
     private View mAddPlusButtonContainer;
     private ImageButton mAddPlusButton;
-    private TextView mTitle;
     private String mTitleString;
 
     private DataKind mKind;
@@ -56,7 +55,7 @@
 
     private ViewIdGenerator mViewIdGenerator;
 
-    private int mMinLineItemHeight;
+    private LayoutInflater mInflater;
 
     public KindSectionView(Context context) {
         this(context, null);
@@ -64,8 +63,6 @@
 
     public KindSectionView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMinLineItemHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.editor_min_line_item_height);
     }
 
     @Override
@@ -108,6 +105,8 @@
         setDrawingCacheEnabled(true);
         setAlwaysDrawnWithCacheEnabled(true);
 
+        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
         mEditors = (ViewGroup)findViewById(R.id.kind_editors);
 
         mAddPlusButtonContainer = findViewById(R.id.kind_plus_container);
@@ -124,8 +123,6 @@
                 });
             }
         });
-
-        mTitle = (TextView)findViewById(R.id.kind_title);
     }
 
     /** {@inheritDoc} */
@@ -153,7 +150,6 @@
         mTitleString = (kind.titleRes == -1 || kind.titleRes == 0)
                 ? ""
                 : getResources().getString(kind.titleRes);
-        mTitle.setText(mTitleString);
 
         rebuildFromState();
         updateAddVisible();
@@ -193,16 +189,12 @@
      */
     private View createEditorView(ValuesDelta entry) {
         final View view;
-        if (mKind.editorClass == null) {
-            view = new TextFieldsEditorView(mContext);
-        } else {
-            try {
-                view = mKind.editorClass.getConstructor(Context.class).newInstance(
-                        mContext);
-            } catch (Exception e) {
-                throw new RuntimeException(
-                        "Cannot allocate editor for " + mKind.editorClass);
-            }
+        try {
+            view = mInflater.inflate(mKind.editorLayoutResourceId, mEditors, false);
+        } catch (Exception e) {
+            throw new RuntimeException(
+                    "Cannot allocate editor with layout resource ID " +
+                    mKind.editorLayoutResourceId);
         }
 
         view.setEnabled(isEnabled());
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index 96f234b..9d418d0 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -25,14 +25,12 @@
 import com.android.contacts.model.EntityModifier;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.DialogManager.DialogShowingView;
-import com.android.contacts.util.ThemeUtils;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Entity;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
@@ -46,25 +44,28 @@
 import android.widget.ArrayAdapter;
 import android.widget.EditText;
 import android.widget.ImageButton;
+import android.widget.LinearLayout;
 import android.widget.Spinner;
 import android.widget.TextView;
 
 import java.util.List;
 
 /**
- * Base class for editors that handles labels and values.
- * Uses {@link ValuesDelta} to read any existing
- * {@link Entity} values, and to correctly write any changes values.
+ * Base class for editors that handles labels and values. Uses
+ * {@link ValuesDelta} to read any existing {@link Entity} values, and to
+ * correctly write any changes values.
  */
-public abstract class LabeledEditorView extends ViewGroup implements Editor, DialogShowingView {
+public abstract class LabeledEditorView extends LinearLayout implements Editor, DialogShowingView {
     protected static final String DIALOG_ID_KEY = "dialog_id";
     private static final int DIALOG_ID_CUSTOM = 1;
 
     private static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
             | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
 
+    private TextView mTitle;
     private Spinner mLabel;
     private EditTypeAdapter mEditTypeAdapter;
+    private View mDeleteContainer;
     private ImageButton mDelete;
 
     private DataKind mKind;
@@ -117,6 +118,39 @@
                 R.dimen.editor_min_line_item_height);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    protected void onFinishInflate() {
+
+        mTitle = (TextView) findViewById(R.id.title);
+
+        mLabel = (Spinner) findViewById(R.id.spinner);
+        mLabel.setOnItemSelectedListener(mSpinnerListener);
+
+        mDeleteContainer = findViewById(R.id.delete_button_container);
+        mDelete = (ImageButton) findViewById(R.id.delete_button);
+        mDelete.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // defer removal of this button so that the pressed state is visible shortly
+                new Handler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Keep around in model, but mark as deleted
+                        mEntry.markDeleted();
+
+                        ((ViewGroup) getParent()).removeView(LabeledEditorView.this);
+
+                        if (mListener != null) {
+                            // Notify listener when present
+                            mListener.onDeleted(LabeledEditorView.this);
+                        }
+                    }
+                });
+            }
+        });
+    }
+
     public boolean isReadOnly() {
         return mReadOnly;
     }
@@ -129,96 +163,18 @@
     }
 
     /**
-     * Returns the number of rows in this editor, including the invisible ones.
-     */
-    protected int getLineItemCount() {
-        return 1;
-    }
-
-    protected boolean isLineItemVisible(int row) {
-        return true;
-    }
-
-    protected int getLineItemHeight(int row) {
-        int fieldHeight = 0;
-        int buttonHeight = 0;
-        if (row == 0) {
-            // summarize the EditText heights
-            if (mLabel != null) {
-                fieldHeight = mLabel.getMeasuredHeight();
-            }
-
-            // Ensure there is enough space for the minus button
-            View deleteButton = getDelete();
-            final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
-            buttonHeight += deleteHeight;
-        }
-
-        return Math.max(Math.max(buttonHeight, fieldHeight), mMinLineItemHeight);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-
-        int height = 0;
-        height += getPaddingTop() + getPaddingBottom();
-
-        int count = getLineItemCount();
-        for (int i = 0; i < count; i++) {
-            if (isLineItemVisible(i)) {
-                height += getLineItemHeight(i);
-            }
-        }
-
-        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
-                resolveSize(height, heightMeasureSpec));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        // Subtract padding from the borders ==> x1 variables
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-        int b1 = getMeasuredHeight() - getPaddingBottom();
-
-        final int r2;
-        if (mDelete != null) {
-            r2 = r1 - mDelete.getMeasuredWidth();
-            // Vertically center the delete button in the first line item
-            int height = mDelete.getMeasuredHeight();
-            int top = t1 + (mMinLineItemHeight - height) / 2;
-            mDelete.layout(
-                    r2, top,
-                    r1, top + height);
-        } else {
-            r2 = r1;
-        }
-
-        if (mLabel != null) {
-            int baseline = getBaseline(0);
-            int y = t1 + baseline - mLabel.getBaseline();
-            mLabel.layout(
-                    r2 - mLabel.getMeasuredWidth(), y,
-                    r2, y + mLabel.getMeasuredHeight());
-        }
-    }
-
-    /**
      * Creates or removes the type/label button. Doesn't do anything if already correctly configured
      */
     private void setupLabelButton(boolean shouldExist) {
-        if (shouldExist && mLabel == null) {
-            mLabel = new Spinner(mContext);
-            final int width =
-                    mContext.getResources().getDimensionPixelSize(R.dimen.editor_type_label_width);
-            mLabel.setLayoutParams(new LayoutParams(width, LayoutParams.WRAP_CONTENT));
-            mLabel.setOnItemSelectedListener(mSpinnerListener);
+        if (shouldExist) {
             mLabel.setEnabled(!mReadOnly && isEnabled());
-            addView(mLabel);
-        } else if (!shouldExist && mLabel != null) {
-            removeView(mLabel);
-            mLabel = null;
+            mLabel.setVisibility(View.VISIBLE);
+
+            // Since there's a spinner for this editor, use this as the title
+            // instead of the title TextView.
+            mTitle.setVisibility(View.GONE);
+        } else {
+            mLabel.setVisibility(View.GONE);
         }
     }
 
@@ -226,46 +182,11 @@
      * Creates or removes the remove button. Doesn't do anything if already correctly configured
      */
     private void setupDeleteButton(boolean shouldExist) {
-        if (shouldExist && mDelete == null) {
-            mDelete = new ImageButton(mContext);
-            mDelete.setImageResource(R.drawable.ic_menu_remove_field_holo_light);
-            mDelete.setBackgroundResource(
-                    ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
-            final Resources resources = mContext.getResources();
-            mDelete.setPadding(
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_left),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_top),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_right),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_bottom));
-            mDelete.setContentDescription(
-                    getResources().getText(R.string.description_minus_button));
-            mDelete.setLayoutParams(
-                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-            mDelete.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    // defer removal of this button so that the pressed state is visible shortly
-                    new Handler().post(new Runnable() {
-                        @Override
-                        public void run() {
-                            // Keep around in model, but mark as deleted
-                            mEntry.markDeleted();
-
-                            ((ViewGroup) getParent()).removeView(LabeledEditorView.this);
-
-                            if (mListener != null) {
-                                // Notify listener when present
-                                mListener.onDeleted(LabeledEditorView.this);
-                            }
-                        }
-                    });
-                }
-            });
+        if (shouldExist) {
+            mDeleteContainer.setVisibility(View.VISIBLE);
             mDelete.setEnabled(!mReadOnly && isEnabled());
-            addView(mDelete);
-        } else if (!shouldExist && mDelete != null) {
-            removeView(mDelete);
-            mDelete = null;
+        } else {
+            mDeleteContainer.setVisibility(View.GONE);
         }
     }
 
@@ -288,8 +209,8 @@
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
-        if (mLabel != null) mLabel.setEnabled(!mReadOnly && enabled);
-        if (mDelete != null) mDelete.setEnabled(!mReadOnly && enabled);
+        mLabel.setEnabled(!mReadOnly && enabled);
+        mDelete.setEnabled(!mReadOnly && enabled);
     }
 
     public Spinner getLabel() {
@@ -317,7 +238,6 @@
      * possible custom label string.
      */
     private void rebuildLabel() {
-        if (mLabel == null) return;
         mEditTypeAdapter = new EditTypeAdapter(mContext);
         mLabel.setAdapter(mEditTypeAdapter);
         if (mEditTypeAdapter.hasCustomSelection()) {
@@ -374,10 +294,18 @@
         }
         setVisibility(View.VISIBLE);
 
+        // TODO: handle resources from remote packages
+        String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
+                ? ""
+                : getResources().getString(kind.titleRes);
+
+        // Setup title (may not be shown if there is a Spinner setup later).
+        mTitle.setText(titleString.toUpperCase());
+
         // Display label selector if multiple types available
         final boolean hasTypes = EntityModifier.hasEditTypes(kind);
         setupLabelButton(hasTypes);
-        if (mLabel != null) mLabel.setEnabled(!readOnly && isEnabled());
+        mLabel.setEnabled(!readOnly && isEnabled());
         if (hasTypes) {
             mType = EntityModifier.getCurrentType(entry, kind);
             rebuildLabel();
@@ -502,10 +430,13 @@
     private class EditTypeAdapter extends ArrayAdapter<EditType> {
         private final LayoutInflater mInflater;
         private boolean mHasCustomSelection;
+        private int mTextSize;
 
         public EditTypeAdapter(Context context) {
             super(context, 0);
             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            mTextSize = getResources().getDimensionPixelSize(
+                    R.dimen.editor_field_spinner_text_size);
 
             if (mType != null && mType.customColumn != null) {
 
@@ -548,6 +479,7 @@
             }
 
             textView = (TextView) view;
+            textView.setTextSize(mTextSize);
 
             EditType type = getItem(position);
             String text;
@@ -556,7 +488,7 @@
             } else {
                 text = getContext().getString(type.labelRes);
             }
-            textView.setText(text);
+            textView.setText(text.toUpperCase());
             return view;
         }
     }
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index 9a44cce..a1b3476 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -22,11 +22,9 @@
 import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.util.ThemeUtils;
 
 import android.content.Context;
 import android.content.Entity;
-import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,18 +35,21 @@
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.EditText;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
 /**
- * Simple editor that handles labels and any {@link EditField} defined for
- * the entry. Uses {@link ValuesDelta} to read any existing
- * {@link Entity} values, and to correctly write any changes values.
+ * Simple editor that handles labels and any {@link EditField} defined for the
+ * entry. Uses {@link ValuesDelta} to read any existing {@link Entity} values,
+ * and to correctly write any changes values.
  */
 public class TextFieldsEditorView extends LabeledEditorView {
     private EditText[] mFieldEditTexts = null;
-    private ImageButton mMoreOrLess;
+    private ViewGroup mFields = null;
+    private View mExpansionButtonContainer;
+    private ImageButton mExpansionButton;
     private boolean mHideOptional = true;
     private boolean mHasShortAndLongForms;
     private int mEditorTextSize;
@@ -65,105 +66,46 @@
         super(context, attrs, defStyle);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        setDrawingCacheEnabled(true);
+        setAlwaysDrawnWithCacheEnabled(true);
+
+        mEditorTextSize = getResources().getDimensionPixelSize(R.dimen.editor_field_text_size);
+        mFields = (ViewGroup) findViewById(R.id.editors);
+        mExpansionButtonContainer = findViewById(R.id.expansion_button_container);
+        mExpansionButton = (ImageButton) findViewById(R.id.expansion_button);
+        mExpansionButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Save focus
+                final View focusedChild = getFocusedChild();
+                final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
+
+                // Reconfigure GUI
+                mHideOptional = !mHideOptional;
+                onOptionalFieldVisibilityChange();
+                rebuildValues();
+
+                // Restore focus
+                View newFocusView = findViewById(focusedViewId);
+                if (newFocusView == null || newFocusView.getVisibility() == GONE) {
+                    // find first visible child
+                    newFocusView = TextFieldsEditorView.this;
+                }
+                newFocusView.requestFocus();
+            }
+        });
+    }
+
     public void setEditorTextSize(int textSize) {
         this.mEditorTextSize = textSize;
     }
 
     @Override
-    protected int getLineItemCount() {
-        int count = mFieldEditTexts == null ? 0 : mFieldEditTexts.length;
-        return Math.max(count, super.getLineItemCount());
-    }
-
-    @Override
-    protected boolean isLineItemVisible(int row) {
-        return mFieldEditTexts != null && mFieldEditTexts[row].getVisibility() != View.GONE;
-    }
-
-    @Override
-    public int getBaseline(int row) {
-        int baseline = super.getBaseline(row);
-        if (mFieldEditTexts != null) {
-            EditText editText = mFieldEditTexts[row];
-            // The text field will be centered vertically in the corresponding line item
-            int lineItemHeight = getLineItemHeight(row);
-            int offset = (lineItemHeight - editText.getMeasuredHeight()) / 2;
-            baseline = Math.max(baseline, offset + editText.getBaseline());
-        }
-        return baseline;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        int l1 = getPaddingLeft();
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-
-        if ((mMoreOrLess != null)) {
-            // Ensure that the more-or-less button does not overlap the delete button.
-            int moreOrLessTop = t1;
-            if (getDelete() != null) {
-                moreOrLessTop = getDelete().getBottom() + getPaddingTop();
-            }
-            mMoreOrLess.layout(
-                    r1 - mMoreOrLess.getMeasuredWidth(), moreOrLessTop,
-                    r1, moreOrLessTop + mMoreOrLess.getMeasuredHeight());
-        }
-
-        // Subtract buttons if necessary
-        final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
-        final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
-        final int moreOrLessWidth = mMoreOrLess != null ? mMoreOrLess.getMeasuredWidth() : 0;
-        final int r2 = r1 - Math.max(deleteWidth, moreOrLessWidth) - labelWidth;
-
-        // Layout text fields
-        int y = t1;
-        if (mFieldEditTexts != null) {
-            for (int i = 0; i < mFieldEditTexts.length; i++) {
-                int baseline = getBaseline(i);
-                EditText editText = mFieldEditTexts[i];
-                if (editText.getVisibility() != View.GONE) {
-                    int height = editText.getMeasuredHeight();
-                    int top = t1 + y + baseline - editText.getBaseline();
-                    editText.layout(
-                            l1, top,
-                            r2, top + height);
-                    y += getLineItemHeight(i);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected int getLineItemHeight(int row) {
-        int fieldHeight = 0;
-        int buttonHeight = 0;
-
-        boolean lastLineItem = true;
-        if (mFieldEditTexts != null) {
-            fieldHeight = mFieldEditTexts[row].getMeasuredHeight();
-            lastLineItem = (row == mFieldEditTexts.length - 1);
-        }
-
-        // Ensure there is enough space for the more/less button
-        if (row == 0) {
-            final int moreOrLessHeight = mMoreOrLess != null ? mMoreOrLess.getMeasuredHeight() : 0;
-            buttonHeight += moreOrLessHeight;
-        }
-
-        // Ensure there is enough space for the minus button
-        if (lastLineItem) {
-            View deleteButton = getDelete();
-            final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
-            buttonHeight += deleteHeight;
-        }
-
-        return Math.max(Math.max(buttonHeight, fieldHeight), super.getLineItemHeight(row));
-    }
-
-    @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
 
@@ -172,7 +114,7 @@
                 mFieldEditTexts[index].setEnabled(!isReadOnly() && enabled);
             }
         }
-        if (mMoreOrLess != null) mMoreOrLess.setEnabled(!isReadOnly() && enabled);
+        mExpansionButton.setEnabled(!isReadOnly() && enabled);
     }
 
     /**
@@ -180,51 +122,12 @@
      */
     private void setupMoreOrLessButton(boolean shouldExist, boolean collapsed) {
         if (shouldExist) {
-            if (mMoreOrLess == null) {
-                mMoreOrLess = new ImageButton(mContext);
-                mMoreOrLess.setBackgroundResource(
-                        ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
-                final Resources resources = mContext.getResources();
-                mMoreOrLess.setPadding(
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_left),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_top),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_right),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_bottom));
-                mMoreOrLess.setLayoutParams(
-                        new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-                mMoreOrLess.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        // Save focus
-                        final View focusedChild = getFocusedChild();
-                        final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
-
-                        // Reconfigure GUI
-                        mHideOptional = !mHideOptional;
-                        onOptionalFieldVisibilityChange();
-                        rebuildValues();
-
-                        // Restore focus
-                        View newFocusView = findViewById(focusedViewId);
-                        if (newFocusView == null || newFocusView.getVisibility() == GONE) {
-                            // find first visible child
-                            newFocusView = TextFieldsEditorView.this;
-                        }
-                        newFocusView.requestFocus();
-                    }
-                });
-                addView(mMoreOrLess);
-            }
-            mMoreOrLess.setImageResource(collapsed
+            mExpansionButtonContainer.setVisibility(View.VISIBLE);
+            mExpansionButton.setImageResource(collapsed
                     ? R.drawable.ic_menu_expander_minimized_holo_light
                     : R.drawable.ic_menu_expander_maximized_holo_light);
-        } else if (mMoreOrLess != null) {
-            removeView(mMoreOrLess);
-            mMoreOrLess = null;
+        } else {
+            mExpansionButtonContainer.setVisibility(View.GONE);
         }
     }
 
@@ -255,7 +158,7 @@
         // Remove edit texts that we currently have
         if (mFieldEditTexts != null) {
             for (EditText fieldEditText : mFieldEditTexts) {
-                removeView(fieldEditText);
+                mFields.removeView(fieldEditText);
             }
         }
         boolean hidePossible = false;
@@ -268,9 +171,7 @@
             fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                     LayoutParams.WRAP_CONTENT));
             fieldView.setGravity(Gravity.TOP);
-            if (mEditorTextSize != 0) {
-                fieldView.setTextSize(mEditorTextSize);
-            }
+            fieldView.setTextSize(mEditorTextSize);
             mFieldEditTexts[index] = fieldView;
             fieldView.setId(vig.getId(state, kind, entry, index));
             if (field.titleRes > 0) {
@@ -324,12 +225,12 @@
                 hidePossible = hidePossible || couldHide;
             }
 
-            addView(fieldView);
+            mFields.addView(fieldView);
         }
 
         // When hiding fields, place expandable
         setupMoreOrLessButton(hidePossible, mHideOptional);
-        if (mMoreOrLess != null) mMoreOrLess.setEnabled(!readOnly && isEnabled());
+        mExpansionButton.setEnabled(!readOnly && isEnabled());
     }
 
     /**
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index 837dafe..f17bc5a 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -1,12 +1,12 @@
 package com.android.contacts.model;
 
+import com.android.contacts.R;
 import com.android.contacts.model.AccountType.EditField;
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.AccountType.StringInflater;
 
 import android.content.ContentValues;
 import android.provider.ContactsContract.Data;
-import android.view.View;
 
 import java.text.SimpleDateFormat;
 import java.util.List;
@@ -56,7 +56,12 @@
 
     public ContentValues defaultValues;
 
-    public Class<? extends View> editorClass;
+    /**
+     * Layout resource id for an editor {@link View} to edit this
+     * {@link DataKind}. The default is a text editor, but this can be
+     * overridden when a more appropriate XML layout is available.
+     */
+    public int editorLayoutResourceId = R.layout.text_fields_editor_view;
 
     /**
      * If this is a date field, this specifies the format of the date when saving. The
@@ -76,11 +81,11 @@
     }
 
     public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
-        this(mimeType, titleRes, iconRes, weight, editable, null);
+        this(mimeType, titleRes, iconRes, weight, editable, R.layout.text_fields_editor_view);
     }
 
     public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
-            Class<? extends View> editorClass) {
+            int editorLayoutResourceId) {
         this.mimeType = mimeType;
         this.titleRes = titleRes;
         this.iconRes = iconRes;
@@ -88,6 +93,6 @@
         this.editable = editable;
         this.isList = true;
         this.typeOverallMax = -1;
-        this.editorClass = editorClass;
+        this.editorLayoutResourceId = editorLayoutResourceId;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index 2b1a520..b81eb50 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -303,7 +303,7 @@
         kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, false).setSpecificMax(1));
 
         kind.dateFormatWithYear = DateUtils.DATE_AND_TIME_FORMAT;
-        kind.editorClass = EventFieldEditorView.class;
+        kind.editorLayoutResourceId = R.layout.event_field_editor_view;
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index 7983a65..699f679 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -138,7 +138,7 @@
                     R.string.eventLabelsGroup, -1, 150, true));
         kind.actionHeader = new EventActionInflater();
         kind.actionBody = new SimpleInflater(Event.START_DATE);
-        kind.editorClass = EventFieldEditorView.class;
+        kind.editorLayoutResourceId = R.layout.event_field_editor_view;
 
         kind.typeColumn = Event.TYPE;
         kind.typeList = Lists.newArrayList();