Create Inline-Editors for every existing type, remove some intermediate code for modal editors

Change-Id: Ibd137797d09db0ca194c0468890290f56050d83c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 24dfe86..29fcedc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -397,7 +397,7 @@
             </intent-filter>
         </activity>
 
-        <!-- Edit and insert details for a contact -->
+        <!-- Edit an existing contact -->
         <activity
             android:name=".activities.ContactEditorActivity"
             android:theme="@style/TallTitleBarTheme"
@@ -412,7 +412,7 @@
             </intent-filter>
         </activity>
 
-        <!-- Insert details for a contact (for now this is seperate) -->
+        <!-- Create contact (for now this is seperate) -->
         <activity
             android:name=".ui.EditContactActivity"
             android:theme="@style/TallTitleBarTheme">
@@ -426,25 +426,6 @@
             </intent-filter>
         </activity>
 
-        <!-- Activity to edit a single field of a contact, like an E-Mail address or phone -->
-        <activity
-            android:name=".activities.ContactFieldEditorActivity"
-            android:windowSoftInputMode="stateAlwaysVisible|adjustResize"
-            android:theme="@android:style/Theme.Dialog">
-
-            <intent-filter>
-                <action android:name="android.intent.action.EDIT" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/name" android:host="com.android.contacts" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.EDIT" />
-                <action android:name="android.intent.action.INSERT" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/email_v2" android:host="com.android.contacts" />
-            </intent-filter>
-        </activity>
-
         <!-- Stub service used to keep our process alive long enough for
              background threads to finish their operations. -->
         <service
diff --git a/res/layout/list_edit_item_organization.xml b/res/layout/list_edit_item_organization.xml
new file mode 100644
index 0000000..588a2a2
--- /dev/null
+++ b/res/layout/list_edit_item_organization.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<com.android.contacts.views.editor.view.OrganizationView
+        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="horizontal"
+        android:paddingLeft="9dip"
+        android:gravity="center_vertical"
+        android:background="@drawable/edit_rawcontact_bg">
+    <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+        <TextView android:id="@+id/caption"
+                android:layout_width="60dip"
+                android:layout_height="wrap_content" />
+        <Button android:id="@+id/type"
+                android:layout_width="100dip"
+                android:layout_height="wrap_content" />
+    </LinearLayout>
+    <LinearLayout
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical">
+        <EditText android:id="@+id/company_field"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        <EditText android:id="@+id/title_field"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+    </LinearLayout>
+</com.android.contacts.views.editor.view.OrganizationView>
diff --git a/res/layout/list_edit_item_simple_or_structured.xml b/res/layout/list_edit_item_simple_or_structured.xml
new file mode 100644
index 0000000..240f26d
--- /dev/null
+++ b/res/layout/list_edit_item_simple_or_structured.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<com.android.contacts.views.editor.view.SimpleOrStructuredView
+    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="horizontal"
+    android:paddingLeft="9dip"
+    android:gravity="center_vertical"
+    android:background="@drawable/edit_rawcontact_bg"
+>
+
+    <TextView android:id="@+id/caption"
+        android:layout_width="60dip"
+        android:layout_height="wrap_content"
+    />
+
+    <EditText android:id="@+id/field"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+    />
+
+    <Button android:id="@+id/structuredEditorButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/edit_structured_editor_button"
+    />
+
+</com.android.contacts.views.editor.view.SimpleOrStructuredView>
diff --git a/res/layout/list_edit_item_single_field.xml b/res/layout/list_edit_item_single_field.xml
new file mode 100644
index 0000000..4ffc0d7
--- /dev/null
+++ b/res/layout/list_edit_item_single_field.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<com.android.contacts.views.editor.view.SingleFieldView
+    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="horizontal"
+    android:paddingLeft="9dip"
+    android:gravity="center_vertical"
+    android:background="@drawable/edit_rawcontact_bg"
+>
+
+    <TextView android:id="@+id/caption"
+        android:layout_width="60dip"
+        android:layout_height="wrap_content"
+    />
+
+    <EditText android:id="@+id/field"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+    />
+
+</com.android.contacts.views.editor.view.SingleFieldView>
diff --git a/res/layout/list_edit_item_text_icons.xml b/res/layout/list_edit_item_text_icons.xml
deleted file mode 100644
index 03868b7..0000000
--- a/res/layout/list_edit_item_text_icons.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2009, 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.views.editor.view.DataView
-    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="horizontal"
-    android:paddingLeft="9dip"
-    android:gravity="center_vertical"
-    android:background="@drawable/edit_rawcontact_bg"
->
-
-    <LinearLayout
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_marginLeft="5dip"
-        android:orientation="vertical"
-        android:paddingTop="5dip"
-        android:paddingBottom="7dip"
-        android:gravity="center_vertical"
-    >
-
-        <TextView android:id="@android:id/text1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-        />
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-        >
-            <TextView android:id="@android:id/text2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-            />
-
-            <ImageView android:id="@+id/primary_icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:paddingLeft="3dip"
-                android:src="@drawable/ic_default_number"
-            />
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <ImageView android:id="@+id/action_icon"
-        android:layout_width="30dip"
-        android:layout_height="30dip"
-        android:layout_marginLeft="14dip"
-        android:layout_marginRight="14dip"
-        android:gravity="center"
-        android:scaleType="centerInside"
-    />
-
-    <View android:id="@+id/divider"
-        android:layout_width="1px"
-        android:layout_height="match_parent"
-        android:layout_marginTop="5dip"
-        android:layout_marginBottom="5dip"
-        android:background="@drawable/divider_vertical_dark"
-    />
-
-    <ImageView android:id="@+id/secondary_action_button"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_centerVertical="true"
-        android:paddingLeft="14dip"
-        android:paddingRight="14dip"
-        android:gravity="center"
-        android:scaleType="center"
-        android:background="@android:drawable/list_selector_background"
-    />
-
-</com.android.contacts.views.editor.view.DataView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1b07bff..8969228 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1237,4 +1237,7 @@
 
     <!-- Shown in a Toast to indicate an error while trying to save the Data -->
     <string name="edit_error_saving">Error saving</string>
+
+    <!-- Text in the editor to show the structured editor -->
+    <string name="edit_structured_editor_button">...</string>
 </resources>
diff --git a/src/com/android/contacts/activities/ContactFieldEditorActivity.java b/src/com/android/contacts/activities/ContactFieldEditorActivity.java
deleted file mode 100644
index 6f958f6..0000000
--- a/src/com/android/contacts/activities/ContactFieldEditorActivity.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.contacts.activities;
-
-import com.android.contacts.views.editor.ContactFieldEditorBaseFragment;
-import com.android.contacts.views.editor.ContactFieldEditorEmailFragment;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-public class ContactFieldEditorActivity extends Activity {
-    public final static String BUNDLE_RAW_CONTACT_URI = "RawContactUri";
-
-    private ContactFieldEditorBaseFragment mFragment;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mFragment = new ContactFieldEditorEmailFragment();
-        mFragment.setListener(mFragmentListener);
-
-        openFragmentTransaction()
-            .add(android.R.id.content, mFragment)
-            .commit();
-
-        final Intent intent = getIntent();
-        final Uri rawContactUri = Uri.parse(intent.getStringExtra(BUNDLE_RAW_CONTACT_URI));
-        final boolean isInsert;
-        if (Intent.ACTION_EDIT.equals(intent.getAction())) {
-            isInsert = false;
-        } else if (Intent.ACTION_INSERT.equals(intent.getAction())) {
-            isInsert = true;
-        } else throw new IllegalArgumentException("Action is neither EDIT nor INSERT");
-
-        if (isInsert) {
-            mFragment.setupInsert(intent.getType(), rawContactUri);
-        } else {
-            mFragment.setupEdit(rawContactUri, intent.getData());
-        }
-    }
-
-    private ContactFieldEditorBaseFragment.Listener mFragmentListener =
-            new ContactFieldEditorBaseFragment.Listener() {
-        public void onCancel() {
-            setResult(RESULT_CANCELED);
-            finish();
-        }
-
-        public void onContactNotFound() {
-            setResult(RESULT_CANCELED);
-            finish();
-        }
-
-        public void onDataNotFound() {
-            setResult(RESULT_CANCELED);
-            finish();
-        }
-
-        public void onSaved() {
-            setResult(RESULT_OK);
-            finish();
-        }
-    };
-}
diff --git a/src/com/android/contacts/activities/TwoPaneActivity.java b/src/com/android/contacts/activities/TwoPaneActivity.java
index 63283ad..3f8a139 100644
--- a/src/com/android/contacts/activities/TwoPaneActivity.java
+++ b/src/com/android/contacts/activities/TwoPaneActivity.java
@@ -29,9 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.TextUtils;
-import android.widget.LinearLayout;
 import android.widget.Toast;
-import android.widget.LinearLayout.LayoutParams;
 
 public class TwoPaneActivity extends Activity {
     private final static String TAG = "TwoPaneActivity";
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 58f56ff..4f65f6d 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -18,19 +18,24 @@
 
 import com.android.contacts.ContactOptionsActivity;
 import com.android.contacts.R;
-import com.android.contacts.activities.ContactFieldEditorActivity;
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.DataKind;
 import com.android.contacts.ui.EditContactActivity;
-import com.android.contacts.util.DataStatus;
 import com.android.contacts.views.ContactLoader;
 import com.android.contacts.views.editor.view.ViewTypes;
 import com.android.contacts.views.editor.viewModel.BaseViewModel;
-import com.android.contacts.views.editor.viewModel.DataViewModel;
 import com.android.contacts.views.editor.viewModel.EmailViewModel;
 import com.android.contacts.views.editor.viewModel.FooterViewModel;
+import com.android.contacts.views.editor.viewModel.ImViewModel;
+import com.android.contacts.views.editor.viewModel.NicknameViewModel;
+import com.android.contacts.views.editor.viewModel.NoteViewModel;
+import com.android.contacts.views.editor.viewModel.OrganizationViewModel;
 import com.android.contacts.views.editor.viewModel.PhoneViewModel;
+import com.android.contacts.views.editor.viewModel.PhotoViewModel;
+import com.android.contacts.views.editor.viewModel.StructuredNameViewModel;
+import com.android.contacts.views.editor.viewModel.StructuredPostalViewModel;
+import com.android.contacts.views.editor.viewModel.WebsiteViewModel;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -61,7 +66,6 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -265,6 +269,24 @@
                         ContactsSource.LEVEL_MIMETYPES);
                 if (kind == null) continue;
 
+                // TODO: This surely can be written more nicely. Think about a factory once
+                // all editors are done
+                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final StructuredNameViewModel itemEditor =
+                            StructuredNameViewModel.createForExisting(mContext, rawContact, dataId,
+                            entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
+
+                if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final StructuredPostalViewModel itemEditor =
+                            StructuredPostalViewModel.createForExisting(mContext, rawContact,
+                            dataId, entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
+
                 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
                     final PhoneViewModel itemEditor = PhoneViewModel.createForExisting(mContext,
                             rawContact, dataId, entryValues, kind.titleRes);
@@ -279,70 +301,48 @@
                     continue;
                 }
 
+                if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final ImViewModel itemEditor = ImViewModel.createForExisting(mContext,
+                            rawContact, dataId, entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
 
-                final DataViewModel entry = new DataViewModel(mContext, mimeType, kind,
-                        rawContact, dataId, entryValues);
+                if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final NicknameViewModel itemEditor = NicknameViewModel.createForExisting(
+                            mContext, rawContact, dataId, entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
 
-                final boolean hasData = !TextUtils.isEmpty(entry.data);
-                final boolean isSuperPrimary = entryValues.getAsInteger(
-                        Data.IS_SUPER_PRIMARY) != 0;
+                if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final NoteViewModel itemEditor = NoteViewModel.createForExisting(mContext,
+                            rawContact, dataId, entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
 
-                final Intent itemEditIntent = new Intent(Intent.ACTION_EDIT, entry.uri);
-                itemEditIntent.putExtra(ContactFieldEditorActivity.BUNDLE_RAW_CONTACT_URI,
-                        rawContactUriString);
-                entry.intent = itemEditIntent;
-                entry.actionIcon = R.drawable.edit;
+                if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final WebsiteViewModel itemEditor = WebsiteViewModel.createForExisting(
+                            mContext, rawContact, dataId, entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
 
-                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    rawContact.getFields().add(entry);
-                } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    rawContact.getFields().add(entry);
-                } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    // Remember super-primary phone
-                    entry.isPrimary = isSuperPrimary;
-                    rawContact.getFields().add(entry);
-                } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    // Build email entries
-                    entry.isPrimary = isSuperPrimary;
-                    rawContact.getFields().add(entry);
-                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    // Build postal entries
-                    rawContact.getFields().add(entry);
-                } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    // Build IM entries
-                    if (TextUtils.isEmpty(entry.label)) {
-                        entry.label = mContext.getString(R.string.chat).toLowerCase();
-                    }
-                    rawContact.getFields().add(entry);
-                } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
-                        (hasData || !TextUtils.isEmpty(entry.label))) {
-                    entry.uri = null;
+                if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final OrganizationViewModel itemEditor =
+                            OrganizationViewModel.createForExisting(mContext, rawContact, dataId,
+                            entryValues, kind.titleRes);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
+                }
 
-                    if (TextUtils.isEmpty(entry.label)) {
-                        entry.label = entry.data;
-                        entry.data = "";
-                    }
-
-                    rawContact.getFields().add(entry);
-                } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    entry.uri = null;
-                    rawContact.getFields().add(entry);
-                } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    entry.uri = null;
-                    entry.maxLines = 100;
-                    rawContact.getFields().add(entry);
-                } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
-                    entry.uri = null;
-                    entry.maxLines = 10;
-                    rawContact.getFields().add(entry);
-                } else {
-                    // Use social summary when requested by external source
-                    final DataStatus status = mContactData.getStatuses().get(entry.id);
-                    final boolean hasSocial = kind.actionBodySocial && status != null;
-
-                    if (hasSocial || hasData) {
-                        rawContact.getFields().add(entry);
-                    }
+                if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final PhotoViewModel itemEditor =
+                            PhotoViewModel.createForExisting(mContext, rawContact, dataId,
+                            entryValues);
+                    rawContact.getFields().add(itemEditor);
+                    continue;
                 }
             }
         }
@@ -492,43 +492,6 @@
         if (mListener != null) mListener.onDialogRequested(dialogId, null);
     }
 
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-        AdapterView.AdapterContextMenuInfo info;
-        try {
-             info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-        } catch (ClassCastException e) {
-            Log.e(TAG, "bad menuInfo", e);
-            return;
-        }
-
-        // This can be null sometimes, don't crash...
-        if (info == null) {
-            Log.e(TAG, "bad menuInfo");
-            return;
-        }
-
-        final BaseViewModel baseEntry = mAdapter.getEntry(info.position);
-        if (baseEntry instanceof DataViewModel) {
-            final DataViewModel entry = (DataViewModel) baseEntry;
-            menu.setHeaderTitle(R.string.contactOptionsTitle);
-            if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
-                menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
-                menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
-                if (!entry.isPrimary) {
-                    menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
-                }
-            } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
-                menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
-                if (!entry.isPrimary) {
-                    menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
-                }
-            } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
-                menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
-            }
-        }
-    }
-
     // This was the ListView based code to expand/collapse sections.
 //    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 //        if (mListener == null) return;
@@ -619,12 +582,13 @@
                         final String rawContactUriString = ContentUris.withAppendedId(
                                 RawContacts.CONTENT_URI, rawContactId).toString();
                         final DataKind dataKind = filteredDataKinds.get(which);
-                        final Intent intent = new Intent();
-                        intent.setType(dataKind.mimeType);
-                        intent.setAction(Intent.ACTION_INSERT);
-                        intent.putExtra(ContactFieldEditorActivity.BUNDLE_RAW_CONTACT_URI,
-                                rawContactUriString);
-                        if (mListener != null) mListener.onEditorRequested(intent);
+                        // TODO: Add new row
+//                        final Intent intent = new Intent();
+//                        intent.setType(dataKind.mimeType);
+//                        intent.setAction(Intent.ACTION_INSERT);
+//                        intent.putExtra(ContactFieldEditorActivity.BUNDLE_RAW_CONTACT_URI,
+//                                rawContactUriString);
+//                        if (mListener != null) mListener.onEditorRequested(intent);
                     }
                 };
                 return new AlertDialog.Builder(mContext)
@@ -643,48 +607,6 @@
         return null;
     }
 
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case MENU_ITEM_MAKE_DEFAULT: {
-                if (makeItemDefault(item)) {
-                    return true;
-                }
-                break;
-            }
-        }
-
-        return false;
-    }
-
-    private boolean makeItemDefault(MenuItem item) {
-        final BaseViewModel baseEntry = getViewEntryForMenuItem(item);
-        if (baseEntry == null || !(baseEntry instanceof DataViewModel)) {
-            return false;
-        }
-        final DataViewModel entry = (DataViewModel) baseEntry;
-
-        // Update the primary values in the data record.
-        ContentValues values = new ContentValues(1);
-        values.put(Data.IS_SUPER_PRIMARY, 1);
-
-        mContext.getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
-                values, null, null);
-        return true;
-    }
-
-    private BaseViewModel getViewEntryForMenuItem(MenuItem item) {
-        final AdapterView.AdapterContextMenuInfo info;
-        try {
-             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
-        } catch (ClassCastException e) {
-            Log.e(TAG, "bad menuInfo", e);
-            return null;
-        }
-
-        return mAdapter.getEntry(info.position);
-    }
-
     private FooterViewModel.Listener mRawContactFooterListener =
             new FooterViewModel.Listener() {
         public void onAddClicked(DisplayRawContact rawContact) {
diff --git a/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java b/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java
deleted file mode 100644
index afc0673..0000000
--- a/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.contacts.views.editor;
-
-import com.android.contacts.views.ContactLoader;
-import com.android.contacts.views.ContactSaveService;
-import com.android.internal.util.ArrayUtils;
-
-import android.app.Activity;
-import android.app.LoaderManagingFragment;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Entity;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.ContentProviderOperation.Builder;
-import android.content.Entity.NamedContentValues;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-public abstract class ContactFieldEditorBaseFragment
-        extends LoaderManagingFragment<ContactLoader.Result> {
-    private static final String TAG = "ContactFieldEditorBaseFragment";
-
-    private static final int LOADER_DETAILS = 1;
-
-    private String mMimeType;
-    private long mRawContactId;
-    private Uri mRawContactUri;
-    private long mDataId;
-    private Uri mDataUri;
-    private Context mContext;
-    private Listener mListener;
-    private ContactLoader.Result mContactData;
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        mContext = activity;
-    }
-
-    public ContactLoader.Result getContactData() {
-        return mContactData;
-    }
-
-    /**
-     * Sets up the Fragment for Insert-Mode. Neither mimeType nor rawContactUri must be null
-     */
-    public void setupInsert(String mimeType, Uri rawContactUri) {
-        mMimeType = mimeType;
-        mRawContactUri = rawContactUri;
-        mRawContactId = Long.parseLong(rawContactUri.getLastPathSegment());
-        mDataUri = null;
-        mDataId = 0;
-    }
-
-    /**
-     * Sets up the Fragment for Edit-Mode. Neither rawContactUri nor dataUri must be null
-     * and dataUri must reference a data item that is associated with the given rawContactUri
-     */
-    public void setupEdit(Uri rawContactUri, Uri dataUri) {
-        mMimeType = null;
-        mRawContactUri = rawContactUri;
-        mRawContactId = Long.parseLong(rawContactUri.getLastPathSegment());
-        mDataUri = dataUri;
-        mDataId = Long.parseLong(dataUri.getLastPathSegment());
-    }
-
-    @Override
-    protected void onInitializeLoaders() {
-        startLoading(LOADER_DETAILS, null);
-    }
-
-    @Override
-    protected Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
-        switch (id) {
-            case LOADER_DETAILS: {
-                return new ContactLoader(mContext,
-                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
-            }
-            default: {
-                Log.wtf(TAG, "Unknown ID in onCreateLoader: " + id);
-            }
-        }
-        return null;
-    }
-
-    @Override
-    protected void onLoadFinished(Loader<ContactLoader.Result> loader,
-            ContactLoader.Result data) {
-        final int id = loader.getId();
-        switch (id) {
-            case LOADER_DETAILS:
-                if (data == ContactLoader.Result.NOT_FOUND) {
-                    // Item has been deleted
-                    Log.i(TAG, "No contact found. Closing activity");
-                    if (mListener != null) mListener.onContactNotFound();
-                    return;
-                }
-                if (data == ContactLoader.Result.ERROR) {
-                    throw new IllegalStateException("Error during data loading");
-                }
-                mContactData = data;
-
-                // Find the correct RawContact
-                for (Entity entity : mContactData.getEntities()) {
-                    final ContentValues rawContactEntity = entity.getEntityValues();
-                    final long rawContactId = rawContactEntity.getAsLong(RawContacts._ID);
-                    if (rawContactId == mRawContactId) {
-                        if (mDataId == 0) {
-                            // Do an INSERT
-                            setupEmpty(rawContactEntity);
-                            return;
-                        }
-                        // Do an EDIT. Find the correct item
-                        for (NamedContentValues subValue : entity.getSubValues()) {
-                            final long dataId = subValue.values.getAsLong(Data._ID);
-                            if (dataId == mDataId) {
-                                loadData(subValue);
-                                return;
-                            }
-                        }
-                    }
-                }
-
-                // Item could not be found
-                Log.i(TAG, "Data item not found. Closing activity");
-                if (mListener != null) mListener.onDataNotFound();
-                return;
-            default: {
-                Log.wtf(TAG, "Unknown ID in onLoadFinished: " + id);
-            }
-        }
-    }
-
-    protected void saveData() {
-        final ContentResolver resolver = getActivity().getContentResolver();
-
-        final ArrayList<ContentProviderOperation> operations =
-            new ArrayList<ContentProviderOperation>();
-
-        final Builder builder;
-        if (getDataUri() == null) {
-            // INSERT
-            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-            builder.withValue(Data.MIMETYPE, mMimeType);
-            builder.withValue(Data.RAW_CONTACT_ID, getRawContactId());
-            saveData(builder);
-        } else {
-            // UPDATE
-            builder = ContentProviderOperation.newUpdate(getDataUri());
-            saveData(builder);
-        }
-        operations.add(builder.build());
-
-        // Tell the Service to save
-        final Intent serviceIntent = new Intent();
-        final ContentProviderOperation[] operationsArray =
-                operations.toArray(ArrayUtils.emptyArray(ContentProviderOperation.class));
-        serviceIntent.putExtra(ContactSaveService.EXTRA_OPERATIONS, operationsArray);
-        serviceIntent.setClass(getActivity().getApplicationContext(), ContactSaveService.class);
-
-        getActivity().startService(serviceIntent);
-    }
-
-    protected abstract void saveData(final Builder builder);
-    protected abstract void setupEmpty(ContentValues rawContactEntity);
-    protected abstract void loadData(NamedContentValues contentValues);
-
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
-
-    public Listener getListener() {
-        return mListener;
-    }
-
-    public Uri getRawContactUri() {
-        return mRawContactUri;
-    }
-
-    public long getRawContactId() {
-        return mRawContactId;
-    }
-
-    public Uri getDataUri() {
-        return mDataUri;
-    }
-
-    public long getDataId() {
-        return mDataId;
-    }
-
-    public static interface Listener {
-        void onContactNotFound();
-        void onDataNotFound();
-        void onCancel();
-        void onSaved();
-    }
-}
diff --git a/src/com/android/contacts/views/editor/ContactFieldEditorEmailFragment.java b/src/com/android/contacts/views/editor/ContactFieldEditorEmailFragment.java
deleted file mode 100644
index 7760b64..0000000
--- a/src/com/android/contacts/views/editor/ContactFieldEditorEmailFragment.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.contacts.views.editor;
-
-import com.android.contacts.R;
-import com.android.contacts.util.ViewGroupAnimator;
-
-import android.content.ContentValues;
-import android.content.ContentProviderOperation.Builder;
-import android.content.Entity.NamedContentValues;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-
-public class ContactFieldEditorEmailFragment extends ContactFieldEditorBaseFragment {
-    private EditText mEditText;
-    private View mCustomTypeContainer;
-    private EditText mCustomTypeTextView;
-    private SparseArray<Button> mTypeButtons = new SparseArray<Button>();
-    private int mSelectedType;
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        final View view = inflater.inflate(R.layout.contact_field_editor_email_fragment, container,
-                false);
-
-        mEditText = (EditText) view.findViewById(R.id.email_edit_text);
-        mTypeButtons.append(Email.TYPE_HOME, (Button) view.findViewById(R.id.email_type_home));
-        mTypeButtons.append(Email.TYPE_WORK, (Button) view.findViewById(R.id.email_type_work));
-        mTypeButtons.append(Email.TYPE_OTHER, (Button) view.findViewById(R.id.email_type_other));
-        mTypeButtons.append(Email.TYPE_CUSTOM, (Button) view.findViewById(R.id.email_type_custom));
-
-        mCustomTypeContainer = view.findViewById(R.id.email_edit_custom_type);
-        mCustomTypeTextView = (EditText) view.findViewById(R.id.email_edit_type_text);
-
-        for (int i = 0; i < mTypeButtons.size(); i++) {
-            final int type = mTypeButtons.keyAt(i);
-            final Button button = mTypeButtons.get(type);
-            button.setText(Email.getTypeLabelResource(type));
-            button.setOnClickListener(mTypeButtonOnClickListener);
-        }
-
-        final Button okButton = (Button) view.findViewById(R.id.btn_ok);
-        final Button cancelButton = (Button) view.findViewById(R.id.btn_cancel);
-        okButton.setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                saveData();
-
-                if (getListener() != null) getListener().onSaved();
-            }
-        });
-        cancelButton.setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                if (getListener() != null) getListener().onCancel();
-            }
-        });
-        return view;
-    }
-
-    private void pushTypeButton(int newType, boolean animate) {
-        boolean doAnimation =
-                animate && ((mSelectedType == Email.TYPE_CUSTOM) ^ (newType == Email.TYPE_CUSTOM));
-
-        final ViewGroupAnimator animator;
-        if (doAnimation) {
-            animator = ViewGroupAnimator.captureView(getView().getRootView());
-        } else {
-            animator = null;
-        }
-        for (int i = 0; i < mTypeButtons.size(); i++) {
-            final int type = mTypeButtons.keyAt(i);
-            final Button button = mTypeButtons.get(type);
-            button.setTextColor(newType == type ? Color.BLACK : Color.GRAY);
-        }
-
-        mCustomTypeContainer.setVisibility(newType == Email.TYPE_CUSTOM ? View.VISIBLE : View.GONE);
-
-        if (doAnimation) animator.animate();
-
-        mSelectedType = newType;
-    }
-
-    private OnClickListener mTypeButtonOnClickListener = new OnClickListener() {
-        public void onClick(View v) {
-            final Button b = (Button) v;
-            final int index = mTypeButtons.indexOfValue(b);
-            final int type = mTypeButtons.keyAt(index);
-            pushTypeButton(type, true);
-        }
-    };
-
-    @Override
-    protected void setupEmpty(ContentValues rawContactEntity) {
-        pushTypeButton(Email.TYPE_HOME, false);
-    }
-
-    @Override
-    protected void loadData(NamedContentValues contentValues) {
-        mEditText.setText(contentValues.values.getAsString(Email.ADDRESS));
-        pushTypeButton(contentValues.values.getAsInteger(Email.TYPE).intValue(), false);
-        mCustomTypeTextView.setText(contentValues.values.getAsString(Email.LABEL));
-    }
-
-    @Override
-    protected void saveData(Builder builder) {
-        builder.withValue(Email.ADDRESS, mEditText.getText().toString());
-        builder.withValue(Email.TYPE, mSelectedType);
-        builder.withValue(Email.LABEL, mCustomTypeTextView.getText().toString());
-    }
-}
diff --git a/src/com/android/contacts/views/editor/view/DataView.java b/src/com/android/contacts/views/editor/view/DataView.java
deleted file mode 100644
index e4cd1de..0000000
--- a/src/com/android/contacts/views/editor/view/DataView.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.contacts.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class DataView extends LinearLayout {
-    public TextView mLabelTextView;
-    public TextView mDataTextView;
-    public ImageView mActionIconImageView;
-    public ImageView mPrimaryIconImageView;
-    public ImageView mSecondaryActionButtonImageView;
-    public View mSecondaryActionDividerView;
-
-    public DataView(Context context) {
-        super(context);
-    }
-
-    public DataView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public DataView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public static DataView inflate(LayoutInflater inflater, ViewGroup parent,
-            boolean attachToRoot) {
-        return (DataView) inflater.inflate(R.layout.list_edit_item_text_icons, parent,
-                attachToRoot);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mLabelTextView = (TextView) findViewById(android.R.id.text1);
-        mDataTextView = (TextView) findViewById(android.R.id.text2);
-        mActionIconImageView = (ImageView) findViewById(R.id.action_icon);
-        mPrimaryIconImageView = (ImageView) findViewById(R.id.primary_icon);
-        mSecondaryActionButtonImageView = (ImageView) findViewById(R.id.secondary_action_button);
-        mSecondaryActionDividerView = findViewById(R.id.divider);
-    }
-
-    public void setLabelText(String value, int maxLines) {
-        mLabelTextView.setText(value);
-        setMaxLines(mLabelTextView, maxLines);
-    }
-
-    public void setDataText(String value, int maxLines) {
-        mDataTextView.setText(value);
-        setMaxLines(mDataTextView, maxLines);
-    }
-
-    private static void setMaxLines(TextView textView, int maxLines) {
-        if (maxLines == 1) {
-            textView.setSingleLine(true);
-            textView.setEllipsize(TextUtils.TruncateAt.END);
-        } else {
-            textView.setSingleLine(false);
-            textView.setMaxLines(maxLines);
-            textView.setEllipsize(null);
-        }
-    }
-
-    public void setPrimary(boolean value) {
-        mPrimaryIconImageView.setVisibility(value ? View.VISIBLE : View.GONE);
-    }
-
-    public void setPrimaryIntent(Intent intent, Resources resources, int actionIcon) {
-        if (intent != null) {
-            mActionIconImageView.setImageDrawable(resources.getDrawable(actionIcon));
-            mActionIconImageView.setVisibility(View.VISIBLE);
-        } else {
-            mActionIconImageView.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    public void setSecondaryIntent(Intent intent, Resources resources, int actionIcon) {
-        if (intent != null) {
-            mSecondaryActionButtonImageView.setImageDrawable(resources.getDrawable(actionIcon));
-            mSecondaryActionButtonImageView.setVisibility(View.VISIBLE);
-            mSecondaryActionDividerView.setVisibility(View.VISIBLE);
-        } else {
-            mSecondaryActionButtonImageView.setVisibility(View.INVISIBLE);
-            mSecondaryActionDividerView.setVisibility(View.INVISIBLE);
-        }
-    }
-}
diff --git a/src/com/android/contacts/views/editor/view/OrganizationView.java b/src/com/android/contacts/views/editor/view/OrganizationView.java
new file mode 100644
index 0000000..53b400f
--- /dev/null
+++ b/src/com/android/contacts/views/editor/view/OrganizationView.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.view;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class OrganizationView extends LinearLayout {
+    private TextView mCaptionTextView;
+    private EditText mCompanyFieldEditText;
+    private EditText mTitleFieldEditText;
+    private Button mTypeButton;
+    private Listener mListener;
+    private boolean mHasFocus;
+
+    public OrganizationView(Context context) {
+        super(context);
+    }
+
+    public OrganizationView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public OrganizationView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static OrganizationView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (OrganizationView) inflater.inflate(R.layout.list_edit_item_organization, parent,
+                attachToRoot);
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mCaptionTextView = (TextView) findViewById(R.id.caption);
+        mCompanyFieldEditText = (EditText) findViewById(R.id.company_field);
+        mCompanyFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
+        mTitleFieldEditText = (EditText) findViewById(R.id.title_field);
+        mTitleFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
+        mTypeButton = (Button) findViewById(R.id.type);
+    }
+
+    public void setLabelText(int resId) {
+        mCaptionTextView.setText(resId);
+    }
+
+    public void setFieldValues(CharSequence company, CharSequence title) {
+        mCompanyFieldEditText.setText(company);
+        mTitleFieldEditText.setText(title);
+    }
+
+    public CharSequence getCompanyFieldValue() {
+        return mCompanyFieldEditText.getText();
+    }
+
+    public CharSequence getTitleFieldValue() {
+        return mTitleFieldEditText.getText();
+    }
+
+    public void setTypeDisplayLabel(CharSequence type) {
+        mTypeButton.setText(type);
+    }
+
+    private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            boolean anyHasFocus = mCompanyFieldEditText.hasFocus()
+                    || mTitleFieldEditText.hasFocus();
+            if (mHasFocus && !anyHasFocus && mListener != null) {
+                mListener.onFocusLost(OrganizationView.this);
+            }
+            mHasFocus = anyHasFocus;
+        }
+    };
+
+    public interface Listener {
+        void onFocusLost(OrganizationView view);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java b/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java
new file mode 100644
index 0000000..2bd0bd0
--- /dev/null
+++ b/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.view;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class SimpleOrStructuredView extends LinearLayout {
+    private TextView mCaptionTextView;
+    private EditText mFieldEditText;
+    private Button mStructuredEditorButton;
+    private Listener mListener;
+    private boolean mHasFocus;
+
+    public SimpleOrStructuredView(Context context) {
+        super(context);
+    }
+
+    public SimpleOrStructuredView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SimpleOrStructuredView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static SimpleOrStructuredView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (SimpleOrStructuredView) inflater.inflate(
+                R.layout.list_edit_item_simple_or_structured, parent, attachToRoot);
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mCaptionTextView = (TextView) findViewById(R.id.caption);
+        mFieldEditText = (EditText) findViewById(R.id.field);
+        mFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
+        mStructuredEditorButton = (Button) findViewById(R.id.structuredEditorButton);
+        mStructuredEditorButton.setOnClickListener(mFullEditorClickListener);
+    }
+
+    public void setLabelText(int resId) {
+        mCaptionTextView.setText(resId);
+    }
+
+    public void setDisplayName(CharSequence value) {
+        mFieldEditText.setText(value);
+    }
+
+    public CharSequence getDisplayName() {
+        return mFieldEditText.getText();
+    }
+
+    private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (mHasFocus && !hasFocus && mListener != null) {
+                mListener.onFocusLost(SimpleOrStructuredView.this);
+            }
+            mHasFocus = hasFocus;
+        }
+    };
+
+    private OnClickListener mFullEditorClickListener = new OnClickListener() {
+        public void onClick(View v) {
+            if (mListener != null) {
+                mListener.onStructuredEditorRequested(SimpleOrStructuredView.this);
+            }
+        }
+    };
+
+    public interface Listener {
+        void onFocusLost(SimpleOrStructuredView view);
+        void onStructuredEditorRequested(SimpleOrStructuredView view);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/view/SingleFieldView.java b/src/com/android/contacts/views/editor/view/SingleFieldView.java
new file mode 100644
index 0000000..2d4695e
--- /dev/null
+++ b/src/com/android/contacts/views/editor/view/SingleFieldView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.view;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class SingleFieldView extends LinearLayout {
+    private TextView mCaptionTextView;
+    private EditText mFieldEditText;
+    private Listener mListener;
+    private boolean mHasFocus;
+
+    public SingleFieldView(Context context) {
+        super(context);
+    }
+
+    public SingleFieldView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SingleFieldView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static SingleFieldView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (SingleFieldView) inflater.inflate(R.layout.list_edit_item_single_field,
+                parent, attachToRoot);
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mCaptionTextView = (TextView) findViewById(R.id.caption);
+        mFieldEditText = (EditText) findViewById(R.id.field);
+        mFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
+    }
+
+    public void setLabelText(int resId) {
+        mCaptionTextView.setText(resId);
+    }
+
+    public void setFieldValue(CharSequence value) {
+        mFieldEditText.setText(value);
+    }
+
+    public CharSequence getFieldValue() {
+        return mFieldEditText.getText();
+    }
+
+    private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (mHasFocus && !hasFocus && mListener != null) {
+                mListener.onFocusLost(SingleFieldView.this);
+            }
+            mHasFocus = hasFocus;
+        }
+    };
+
+    public interface Listener {
+        void onFocusLost(SingleFieldView view);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/view/ViewTypes.java b/src/com/android/contacts/views/editor/view/ViewTypes.java
index 7cd2702..9795e40 100644
--- a/src/com/android/contacts/views/editor/view/ViewTypes.java
+++ b/src/com/android/contacts/views/editor/view/ViewTypes.java
@@ -18,9 +18,12 @@
 
 public interface ViewTypes {
     public static final int DATA = 0;
-    public static final int FIELD_AND_TYPE = 1;
-    public static final int PHOTO = 2;
-    public static final int RAW_CONTACT_HEADER = 3;
-    public static final int RAW_CONTACT_FOOTER = 4;
-    public static final int _COUNT = 5;
+    public static final int SINGLE_FIELD = 1;
+    public static final int FIELD_AND_TYPE = 2;
+    public static final int SIMPLE_OR_STRUCTURED = 3;
+    public static final int PHOTO = 4;
+    public static final int ORGANIZATION = 5;
+    public static final int RAW_CONTACT_HEADER = 6;
+    public static final int RAW_CONTACT_FOOTER = 7;
+    public static final int _COUNT = 8;
 }
diff --git a/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java b/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
index 0a324ca..6bef3ca 100644
--- a/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
@@ -24,7 +24,6 @@
 import android.view.ViewGroup;
 
 public abstract class BaseViewModel {
-    // TODO: Consider just storing the Id of the RawContact. Would prevent some cyclic references
     private final DisplayRawContact mRawContact;
     private final Context mContext;
 
diff --git a/src/com/android/contacts/views/editor/viewModel/DataViewModel.java b/src/com/android/contacts/views/editor/viewModel/DataViewModel.java
index fb1c4c5..23e98e4 100644
--- a/src/com/android/contacts/views/editor/viewModel/DataViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/DataViewModel.java
@@ -16,128 +16,86 @@
 
 package com.android.contacts.views.editor.viewModel;
 
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.util.Constants;
+import com.android.contacts.views.ContactSaveService;
 import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.DataView;
-import com.android.contacts.views.editor.view.PhotoView;
-import com.android.contacts.views.editor.view.ViewTypes;
+import com.android.internal.util.ArrayUtils;
 
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.content.ContentProviderOperation.Builder;
 import android.net.Uri;
 import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.telephony.PhoneNumberUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 
-public class DataViewModel extends BaseViewModel {
-    public String label;
-    public String data;
-    public Uri uri;
-    public long id = 0;
-    public int maxLines = 1;
-    public String mimetype;
+import java.util.ArrayList;
 
-    public int actionIcon = -1;
-    public boolean isPrimary = false;
-    public Intent intent;
-    public Intent secondaryIntent = null;
-    public int maxLabelLines = 1;
-    public byte[] binaryData = null;
+public abstract class DataViewModel extends BaseViewModel {
+    private final long mDataId;
+    private final ContentValues mContentValues;
+    private final String mMimeType;
+    private final Uri mDataUri;
+
+    protected DataViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, String mimeType) {
+        super(context, rawContact);
+        mDataId = dataId;
+        mContentValues = contentValues;
+        mMimeType = mimeType;
+        mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId);
+    }
+
+    public long getDataId() {
+        return mDataId;
+    }
+
+    public Uri getDataUri() {
+        return mDataUri;
+    }
+
+    protected ContentValues getContentValues() {
+        return mContentValues;
+    }
+
+    public String getMimeType() {
+        return mMimeType;
+    }
 
     /**
-     * Build new {@link DataViewModel} and populate from the given values.
+     * Uncoditionally saves the current state to the database. No difference analysis is performed
      */
-    public DataViewModel(Context context, String mimeType, DataKind kind,
-            DisplayRawContact rawContact, long dataId, ContentValues values) {
-        super(context, rawContact);
-        id = dataId;
-        uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
-        mimetype = mimeType;
-        label = buildActionString(kind, values, false, context);
-        data = buildDataString(kind, values, context);
-        binaryData = values.getAsByteArray(Data.DATA15);
-    }
+    public void saveData() {
+        final ContentResolver resolver = getContext().getContentResolver();
 
-    @Override
-    public int getEntryType() {
-        return Photo.CONTENT_ITEM_TYPE.equals(mimetype) ? ViewTypes.PHOTO : ViewTypes.DATA;
-    }
+        final ArrayList<ContentProviderOperation> operations =
+            new ArrayList<ContentProviderOperation>();
 
-    @Override
-    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
-        // Special Case: Photo
-        if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
-            final PhotoView result = convertView != null
-                    ? (PhotoView) convertView
-                    : PhotoView.inflate(inflater, parent, false);
-
-            final Bitmap bitmap = binaryData != null
-                    ? BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length)
-                    : null;
-            result.setPhoto(bitmap);
-            return result;
-        }
-
-        // All other cases
-        final DataView result = convertView != null
-                ? (DataView) convertView
-                : DataView.inflate(inflater, parent, false);
-
-        // Set the label
-        result.setLabelText(label, maxLabelLines);
-
-        // Set data
-        if (data != null) {
-            if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)
-                    || Constants.MIME_SMS_ADDRESS.equals(mimetype)) {
-                result.setDataText(PhoneNumberUtils.formatNumber(data), maxLines);
-            } else {
-                result.setDataText(data, maxLines);
-            }
+        final Builder builder;
+        if (getDataUri() == null) {
+            // INSERT
+            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+            builder.withValue(Data.MIMETYPE, getMimeType());
+            builder.withValue(Data.RAW_CONTACT_ID, getRawContact().getId());
+            writeToBuilder(builder, true);
         } else {
-            result.setDataText("", maxLines);
+            // UPDATE
+            builder = ContentProviderOperation.newUpdate(getDataUri());
+            writeToBuilder(builder, false);
         }
+        operations.add(builder.build());
 
-        // Set the primary icon
-        result.setPrimary(isPrimary);
+        // Tell the Service to save
+        // TODO: Handle the case where the data element has been removed in the background
+        final Intent serviceIntent = new Intent();
+        final ContentProviderOperation[] operationsArray =
+                operations.toArray(ArrayUtils.emptyArray(ContentProviderOperation.class));
+        serviceIntent.putExtra(ContactSaveService.EXTRA_OPERATIONS, operationsArray);
+        serviceIntent.setClass(getContext().getApplicationContext(), ContactSaveService.class);
 
-        // Set the action icon
-        result.setPrimaryIntent(intent, getContext().getResources(), actionIcon);
-
-        // Set the secondary action button
-        // TODO: Change this to our new form
-        result.setSecondaryIntent(null, null, 0);
-        return result;
+        getContext().startService(serviceIntent);
     }
 
-    private static String buildActionString(DataKind kind, ContentValues values,
-            boolean lowerCase, Context context) {
-        if (kind.actionHeader == null) {
-            return null;
-        }
-        CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
-        if (actionHeader == null) {
-            return null;
-        }
-        return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
-    }
-
-    private static String buildDataString(DataKind kind, ContentValues values,
-            Context context) {
-        if (kind.actionBody == null) {
-            return null;
-        }
-        CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
-        return actionBody == null ? null : actionBody.toString();
-    }
-
+    protected abstract void writeToBuilder(final Builder builder, boolean isInsert);
 }
diff --git a/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java b/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java
index 70bb8ea..bcc4397 100644
--- a/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java
@@ -25,8 +25,8 @@
 public class EmailViewModel extends FieldAndTypeViewModel {
     private EmailViewModel(Context context, DisplayRawContact rawContact, long dataId,
             ContentValues contentValues, int titleResId) {
-        super(context, rawContact, dataId, contentValues, titleResId, Email.ADDRESS, Email.TYPE,
-                Email.LABEL);
+        super(context, rawContact, dataId, contentValues, Email.CONTENT_ITEM_TYPE, titleResId,
+                Email.ADDRESS, Email.TYPE, Email.LABEL);
     }
 
     public static EmailViewModel createForExisting(Context context, DisplayRawContact rawContact,
diff --git a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java b/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
index e6468c6..c754e92 100644
--- a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
@@ -16,46 +16,31 @@
 
 package com.android.contacts.views.editor.viewModel;
 
-import com.android.contacts.views.ContactSaveService;
 import com.android.contacts.views.editor.DisplayRawContact;
 import com.android.contacts.views.editor.view.FieldAndTypeView;
 import com.android.contacts.views.editor.view.ViewTypes;
-import com.android.internal.util.ArrayUtils;
 
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.Intent;
 import android.content.ContentProviderOperation.Builder;
-import android.net.Uri;
-import android.provider.ContactsContract.Data;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.util.ArrayList;
-
-public abstract class FieldAndTypeViewModel extends BaseViewModel {
+public abstract class FieldAndTypeViewModel extends DataViewModel {
     private static final String TAG = "FieldAndTypeViewModel";
 
-    private final long mDataId;
-    private final ContentValues mContentValues;
     private final int mLabelResId;
-    private final Uri mDataUri;
     private final String mFieldColumn;
     private final String mLabelColumn;
     private final String mTypeColumn;
 
     protected FieldAndTypeViewModel(Context context, DisplayRawContact rawContact,
-            long dataId, ContentValues contentValues, int labelResId, String fieldColumn,
-            String typeColumn, String labelColumn) {
-        super(context, rawContact);
-        mDataId = dataId;
-        mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId);
-        mContentValues = contentValues;
+            long dataId, ContentValues contentValues, String mimeType, int labelResId,
+            String fieldColumn, String typeColumn, String labelColumn) {
+        super(context, rawContact, dataId, contentValues, mimeType);
         mLabelResId = labelResId;
 
         mFieldColumn = fieldColumn;
@@ -82,45 +67,8 @@
         return result;
     }
 
-    public long getDataId() {
-        return mDataId;
-    }
-
-    public ContentValues getContentValues() {
-        return mContentValues;
-    }
-
-    private void saveData() {
-        final ContentResolver resolver = getContext().getContentResolver();
-
-        final ArrayList<ContentProviderOperation> operations =
-            new ArrayList<ContentProviderOperation>();
-
-        final Builder builder;
-//        if (getDataUri() == null) {
-//            // INSERT
-//            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-//            builder.withValue(Data.MIMETYPE, mMimeType);
-//            builder.withValue(Data.RAW_CONTACT_ID, getRawContactId());
-//            writeToBuilder(builder);
-//        } else {
-            // UPDATE
-            builder = ContentProviderOperation.newUpdate(mDataUri);
-            writeToBuilder(builder);
-//        }
-        operations.add(builder.build());
-
-        // Tell the Service to save
-        final Intent serviceIntent = new Intent();
-        final ContentProviderOperation[] operationsArray =
-                operations.toArray(ArrayUtils.emptyArray(ContentProviderOperation.class));
-        serviceIntent.putExtra(ContactSaveService.EXTRA_OPERATIONS, operationsArray);
-        serviceIntent.setClass(getContext().getApplicationContext(), ContactSaveService.class);
-
-        getContext().startService(serviceIntent);
-    }
-
-    private void writeToBuilder(final Builder builder) {
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
         builder.withValue(mFieldColumn, getFieldValue());
         builder.withValue(mTypeColumn, getType());
         builder.withValue(mLabelColumn, getLabel());
@@ -130,23 +78,23 @@
         return getContentValues().getAsString(mFieldColumn);
     }
 
-    private void putFieldValue(String value) {
+    protected void putFieldValue(String value) {
         getContentValues().put(mFieldColumn, value);
     }
 
-    public int getType() {
+    protected int getType() {
         return getContentValues().getAsInteger(mTypeColumn).intValue();
     }
 
-    private void putType(int value) {
+    protected void putType(int value) {
         getContentValues().put(mTypeColumn, value);
     }
 
-    public String getLabel() {
+    protected String getLabel() {
         return getContentValues().getAsString(mLabelColumn);
     }
 
-    private void putLabel(String value) {
+    protected void putLabel(String value) {
         getContentValues().put(mLabelColumn, value);
     }
 
@@ -159,7 +107,7 @@
 
             final String oldValue = getFieldValue();
             final String newValue = view.getFieldValue().toString();
-            if (!oldValue.equals(newValue)) {
+            if (!TextUtils.equals(oldValue, newValue)) {
                 putFieldValue(newValue);
                 hasChanged = true;
             }
diff --git a/src/com/android/contacts/views/editor/viewModel/ImViewModel.java b/src/com/android/contacts/views/editor/viewModel/ImViewModel.java
new file mode 100644
index 0000000..7ee3b21
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/ImViewModel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+
+/**
+ * Editor for Instant Messaging fields. The Type (HOME, WORK, OTHER, CUSTOM) is not shown but
+ * instead the same field is used for showing the Protocol (AIM, YAHOO etc). When
+ * creating new Im rows, the Type is set to OTHER
+ */
+public class ImViewModel extends FieldAndTypeViewModel {
+    private ImViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int titleResId) {
+        super(context, rawContact, dataId, contentValues, Im.CONTENT_ITEM_TYPE, titleResId, Im.DATA,
+                Im.PROTOCOL, Im.CUSTOM_PROTOCOL);
+    }
+
+    public static ImViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int titleResId) {
+        return new ImViewModel(context, rawContact, dataId, contentValues, titleResId);
+    }
+
+    @Override
+    protected CharSequence getTypeDisplayLabel() {
+        return Im.getProtocolLabel(getContext().getResources(), getType(), getLabel());
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        // The Type field is not exposed in the UI. Write OTHER for Insert but don't change it
+        // for updates
+        if (isInsert) {
+            builder.withValue(Im.TYPE, Im.TYPE_OTHER);
+            builder.withValue(Im.LABEL, "");
+        }
+        super.writeToBuilder(builder, isInsert);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java b/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java
new file mode 100644
index 0000000..55c7976
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+
+public class NicknameViewModel extends SingleFieldViewModel {
+    private NicknameViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int titleResId) {
+        super(context, rawContact, dataId, contentValues, Nickname.CONTENT_ITEM_TYPE, titleResId,
+                Nickname.NAME);
+    }
+
+    public static NicknameViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int titleResId) {
+        return new NicknameViewModel(context, rawContact, dataId, contentValues, titleResId);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/NoteViewModel.java b/src/com/android/contacts/views/editor/viewModel/NoteViewModel.java
new file mode 100644
index 0000000..80e45d8
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/NoteViewModel.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+
+public class NoteViewModel extends SingleFieldViewModel {
+    private NoteViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int titleResId) {
+        super(context, rawContact, dataId, contentValues, Note.CONTENT_ITEM_TYPE, titleResId,
+                Note.NOTE);
+    }
+
+    public static NoteViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int titleResId) {
+        return new NoteViewModel(context, rawContact, dataId, contentValues, titleResId);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java b/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
new file mode 100644
index 0000000..0f40532
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.OrganizationView;
+import com.android.contacts.views.editor.view.ViewTypes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class OrganizationViewModel extends DataViewModel {
+    private static final String TAG = "OrganizationViewModel";
+
+    private final int mLabelResId;
+    private final String mCompanyFieldColumn;
+    private final String mTitleFieldColumn;
+    private final String mLabelColumn;
+    private final String mTypeColumn;
+
+    private OrganizationViewModel(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int labelResId) {
+        super(context, rawContact, dataId, contentValues, Organization.CONTENT_ITEM_TYPE);
+        mLabelResId = labelResId;
+
+        mCompanyFieldColumn = Organization.COMPANY;
+        mTitleFieldColumn = Organization.TITLE;
+        mTypeColumn = Organization.TYPE;
+        mLabelColumn = Organization.LABEL;
+    }
+
+    public static OrganizationViewModel createForExisting(Context context,
+            DisplayRawContact rawContact, long dataId, ContentValues contentValues,
+            int titleResId) {
+        return new OrganizationViewModel(context, rawContact, dataId, contentValues, titleResId);
+    }
+
+    @Override
+    public int getEntryType() {
+        return ViewTypes.ORGANIZATION;
+    }
+
+    @Override
+    public OrganizationView getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        final OrganizationView result = convertView != null
+                ? (OrganizationView) convertView
+                : OrganizationView.inflate(inflater, parent, false);
+
+        result.setListener(mViewListener);
+        result.setLabelText(mLabelResId);
+        result.setFieldValues(getCompanyFieldValue(), getTitleFieldValue());
+        result.setTypeDisplayLabel(getTypeDisplayLabel());
+
+        return result;
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        builder.withValue(mCompanyFieldColumn, getCompanyFieldValue());
+        builder.withValue(mTitleFieldColumn, getTitleFieldValue());
+        builder.withValue(mTypeColumn, getType());
+        builder.withValue(mLabelColumn, getLabel());
+    }
+
+    protected String getCompanyFieldValue() {
+        return getContentValues().getAsString(mCompanyFieldColumn);
+    }
+
+    protected String getTitleFieldValue() {
+        return getContentValues().getAsString(mTitleFieldColumn);
+    }
+
+    private void putCompanyFieldValue(String value) {
+        getContentValues().put(mCompanyFieldColumn, value);
+    }
+
+    private void putTitleFieldValue(String value) {
+        getContentValues().put(mTitleFieldColumn, value);
+    }
+
+    private int getType() {
+        return getContentValues().getAsInteger(mTypeColumn).intValue();
+    }
+
+    private void putType(int value) {
+        getContentValues().put(mTypeColumn, value);
+    }
+
+    private String getLabel() {
+        return getContentValues().getAsString(mLabelColumn);
+    }
+
+    private void putLabel(String value) {
+        getContentValues().put(mLabelColumn, value);
+    }
+
+    private CharSequence getTypeDisplayLabel() {
+        return Organization.getTypeLabel(getContext().getResources(), getType(), getLabel());
+    }
+
+    private OrganizationView.Listener mViewListener = new OrganizationView.Listener() {
+        public void onFocusLost(OrganizationView view) {
+            Log.v(TAG, "Received FocusLost. Checking for changes");
+            boolean hasChanged = false;
+
+            final String oldCompanyValue = getCompanyFieldValue();
+            final String newCompanyValue = view.getCompanyFieldValue().toString();
+            if (!TextUtils.equals(oldCompanyValue, newCompanyValue)) {
+                putCompanyFieldValue(newCompanyValue);
+                hasChanged = true;
+            }
+
+            final String oldTitleValue = getTitleFieldValue();
+            final String newTitleValue = view.getTitleFieldValue().toString();
+            if (!TextUtils.equals(oldTitleValue, newTitleValue)) {
+                putTitleFieldValue(newTitleValue);
+                hasChanged = true;
+            }
+            if (hasChanged) {
+                Log.v(TAG, "Found changes. Updating DB");
+                saveData();
+            }
+        }
+    };
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java b/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java
index 496bf2a..9ea033b 100644
--- a/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java
@@ -25,8 +25,8 @@
 public class PhoneViewModel extends FieldAndTypeViewModel {
     private PhoneViewModel(Context context, DisplayRawContact rawContact, long dataId,
             ContentValues contentValues, int titleResId) {
-        super(context, rawContact, dataId, contentValues, titleResId, Phone.NUMBER, Phone.TYPE,
-                Phone.LABEL);
+        super(context, rawContact, dataId, contentValues, Phone.CONTENT_ITEM_TYPE, titleResId,
+                Phone.NUMBER, Phone.TYPE, Phone.LABEL);
     }
 
     public static PhoneViewModel createForExisting(Context context, DisplayRawContact rawContact,
diff --git a/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java b/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
new file mode 100644
index 0000000..05adf57
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.PhotoView;
+import com.android.contacts.views.editor.view.ViewTypes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Editor for the contact photo.
+ */
+public class PhotoViewModel extends DataViewModel {
+    private PhotoViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues) {
+        super(context, rawContact, dataId, contentValues, Im.CONTENT_ITEM_TYPE);
+    }
+
+    public static PhotoViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues) {
+        return new PhotoViewModel(context, rawContact, dataId, contentValues);
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        // TODO
+    }
+
+    @Override
+    public int getEntryType() {
+        return ViewTypes.PHOTO;
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        final PhotoView result = convertView != null
+                ? (PhotoView) convertView
+                : PhotoView.inflate(inflater, parent, false);
+
+        final byte[] binaryData = getContentValues().getAsByteArray(Photo.PHOTO);
+
+        final Bitmap bitmap = binaryData != null
+                ? BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length)
+                : null;
+        result.setPhoto(bitmap);
+        return result;
+    }
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java b/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java
new file mode 100644
index 0000000..16a9769
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.SingleFieldView;
+import com.android.contacts.views.editor.view.ViewTypes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public abstract class SingleFieldViewModel extends DataViewModel {
+    private static final String TAG = "SingleFieldViewModel";
+
+    private final int mLabelResId;
+    private final String mFieldColumn;
+
+    protected SingleFieldViewModel(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, String mimeType, int labelResId,
+            String fieldColumn) {
+        super(context, rawContact, dataId, contentValues, mimeType);
+        mLabelResId = labelResId;
+        mFieldColumn = fieldColumn;
+    }
+
+    @Override
+    public int getEntryType() {
+        return ViewTypes.SINGLE_FIELD;
+    }
+
+    @Override
+    public SingleFieldView getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        final SingleFieldView result = convertView != null
+                ? (SingleFieldView) convertView
+                : SingleFieldView.inflate(inflater, parent, false);
+
+        result.setListener(mViewListener);
+        result.setLabelText(mLabelResId);
+        result.setFieldValue(getFieldValue());
+
+        return result;
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        builder.withValue(mFieldColumn, getFieldValue());
+    }
+
+    protected String getFieldValue() {
+        return getContentValues().getAsString(mFieldColumn);
+    }
+
+    protected void putFieldValue(String value) {
+        getContentValues().put(mFieldColumn, value);
+    }
+
+    private SingleFieldView.Listener mViewListener = new SingleFieldView.Listener() {
+        public void onFocusLost(SingleFieldView view) {
+            Log.v(TAG, "Received FocusLost. Checking for changes");
+            boolean hasChanged = false;
+
+            final String oldValue = getFieldValue();
+            final String newValue = view.getFieldValue().toString();
+            if (!TextUtils.equals(oldValue, newValue)) {
+                putFieldValue(newValue);
+                hasChanged = true;
+            }
+            if (hasChanged) {
+                Log.v(TAG, "Found changes. Updating DB");
+                saveData();
+            }
+        }
+    };
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java b/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
new file mode 100644
index 0000000..269bc2a
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.SimpleOrStructuredView;
+import com.android.contacts.views.editor.view.ViewTypes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Editor for the StructuredName. Handles both the structured representation as well the
+ * single field display
+ */
+public class StructuredNameViewModel extends DataViewModel {
+    protected static final String TAG = "StructuredNameViewModel";
+    private final int mLabelResId;
+
+    private StructuredNameViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int labelResId) {
+        super(context, rawContact, dataId, contentValues, StructuredName.CONTENT_ITEM_TYPE);
+        mLabelResId = labelResId;
+    }
+
+    public static StructuredNameViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int labelResId) {
+        return new StructuredNameViewModel(context, rawContact, dataId, contentValues, labelResId);
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        // TODO: Handle both structured and unstructured inputs.
+        // if (structuredEntered()) {
+        // } else {
+        builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName());
+        builder.withValue(StructuredName.GIVEN_NAME, null);
+        builder.withValue(StructuredName.FAMILY_NAME, null);
+        builder.withValue(StructuredName.PREFIX, null);
+        builder.withValue(StructuredName.MIDDLE_NAME, null);
+        builder.withValue(StructuredName.SUFFIX, null);
+        // }
+    }
+
+    @Override
+    public int getEntryType() {
+        return ViewTypes.SIMPLE_OR_STRUCTURED;
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        final SimpleOrStructuredView result = convertView != null
+                ? (SimpleOrStructuredView) convertView
+                : SimpleOrStructuredView.inflate(inflater, parent, false);
+
+        result.setListener(mViewListener);
+        result.setLabelText(mLabelResId);
+        result.setDisplayName(getDisplayName());
+
+        return result;
+    }
+
+    private String getDisplayName() {
+        return getContentValues().getAsString(StructuredName.DISPLAY_NAME);
+    }
+
+    private void putDisplayName(String value) {
+        getContentValues().put(StructuredName.DISPLAY_NAME, value);
+    }
+
+    private SimpleOrStructuredView.Listener mViewListener = new SimpleOrStructuredView.Listener() {
+        public void onFocusLost(SimpleOrStructuredView view) {
+            Log.v(TAG, "Received FocusLost. Checking for changes");
+            boolean hasChanged = false;
+
+            final String oldValue = getDisplayName();
+            final String newValue = view.getDisplayName().toString();
+            if (!TextUtils.equals(oldValue, newValue)) {
+                putDisplayName(newValue);
+                hasChanged = true;
+            }
+            if (hasChanged) {
+                Log.v(TAG, "Found changes. Updating DB");
+                saveData();
+            }
+        }
+
+        public void onStructuredEditorRequested(SimpleOrStructuredView view) {
+            // TODO
+        }
+    };
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java b/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
new file mode 100644
index 0000000..9f8fc46
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.SimpleOrStructuredView;
+import com.android.contacts.views.editor.view.ViewTypes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Editor for the StructuredPostal. Handles both the structured representation as well the
+ * single field display
+ */
+public class StructuredPostalViewModel extends DataViewModel {
+    protected static final String TAG = "StructuredPostalViewModel";
+    private final int mLabelResId;
+
+    private StructuredPostalViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int labelResId) {
+        super(context, rawContact, dataId, contentValues, StructuredPostal.CONTENT_ITEM_TYPE);
+        mLabelResId = labelResId;
+    }
+
+    public static StructuredPostalViewModel createForExisting(Context context,
+            DisplayRawContact rawContact, long dataId, ContentValues contentValues,
+            int labelResId) {
+        return new StructuredPostalViewModel(context, rawContact, dataId, contentValues,
+                labelResId);
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        // TODO: Writing the non-structured field works pretty badly as we
+        // currently can't parse it properly. This is also a problem when an address it taken
+        // from Maps
+
+        // TODO: Handle both structured and unstructured inputs.
+
+        // if (structuredEntered()) {
+        // } else {
+        builder.withValue(StructuredPostal.FORMATTED_ADDRESS, getFormattedAddress());
+        builder.withValue(StructuredPostal.STREET, null);
+        builder.withValue(StructuredPostal.POBOX, null);
+        builder.withValue(StructuredPostal.NEIGHBORHOOD, null);
+        builder.withValue(StructuredPostal.CITY, null);
+        builder.withValue(StructuredPostal.REGION, null);
+        builder.withValue(StructuredPostal.POSTCODE, null);
+        builder.withValue(StructuredPostal.COUNTRY, null);
+        // }
+    }
+
+    @Override
+    public int getEntryType() {
+        return ViewTypes.SIMPLE_OR_STRUCTURED;
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        final SimpleOrStructuredView result = convertView != null
+                ? (SimpleOrStructuredView) convertView
+                : SimpleOrStructuredView.inflate(inflater, parent, false);
+
+        result.setListener(mViewListener);
+        result.setLabelText(mLabelResId);
+        result.setDisplayName(getFormattedAddress());
+
+        return result;
+    }
+
+    private String getFormattedAddress() {
+        return getContentValues().getAsString(StructuredPostal.FORMATTED_ADDRESS);
+    }
+
+    private void putDisplayName(String value) {
+        getContentValues().put(StructuredPostal.FORMATTED_ADDRESS, value);
+    }
+
+    private SimpleOrStructuredView.Listener mViewListener = new SimpleOrStructuredView.Listener() {
+        public void onFocusLost(SimpleOrStructuredView view) {
+            Log.v(TAG, "Received FocusLost. Checking for changes");
+            boolean hasChanged = false;
+
+            final String oldValue = getFormattedAddress();
+            final String newValue = view.getDisplayName().toString();
+            if (!TextUtils.equals(oldValue, newValue)) {
+                putDisplayName(newValue);
+                hasChanged = true;
+            }
+            if (hasChanged) {
+                Log.v(TAG, "Found changes. Updating DB");
+                saveData();
+            }
+        }
+
+        public void onStructuredEditorRequested(SimpleOrStructuredView view) {
+            // TODO
+        }
+    };
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java b/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java
new file mode 100644
index 0000000..5aabb29
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.views.editor.viewModel;
+
+import com.android.contacts.views.editor.DisplayRawContact;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+
+public class WebsiteViewModel extends SingleFieldViewModel {
+    private WebsiteViewModel(Context context, DisplayRawContact rawContact, long dataId,
+            ContentValues contentValues, int titleResId) {
+        super(context, rawContact, dataId, contentValues, Website.CONTENT_ITEM_TYPE, titleResId,
+                Website.URL);
+    }
+
+    public static WebsiteViewModel createForExisting(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, int titleResId) {
+        return new WebsiteViewModel(context, rawContact, dataId, contentValues, titleResId);
+    }
+}