Merge "Make ImportVCardActivity tolerate screen orientation during caching vCard to be imported."
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 823c033..afb6f70 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -23,19 +23,15 @@
 import com.android.contacts.model.ContactsSource.DataKind;
 import com.android.contacts.ui.EditContactActivity;
 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.EmailViewModel;
 import com.android.contacts.views.editor.viewModel.FooterViewModel;
+import com.android.contacts.views.editor.viewModel.GenericViewModel;
 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;
@@ -52,7 +48,6 @@
 import android.content.Entity.NamedContentValues;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
@@ -67,17 +62,13 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.util.Log;
-import android.view.ContextMenu;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.View.OnCreateContextMenuListener;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.Toast;
@@ -257,11 +248,9 @@
                 if (mimeType == null) continue;
 
                 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, mContext,
-                        ContactsSource.LEVEL_MIMETYPES);
+                        ContactsSource.LEVEL_CONSTRAINTS);
                 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,
@@ -278,13 +267,6 @@
                     continue;
                 }
 
-                if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    final PhoneViewModel itemEditor = PhoneViewModel.createForExisting(mContext,
-                            rawContact, dataId, entryValues, kind.titleRes);
-                    rawContact.getFields().add(itemEditor);
-                    continue;
-                }
-
                 if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
                     final EmailViewModel itemEditor = EmailViewModel.createForExisting(mContext,
                             rawContact, dataId, entryValues, kind.titleRes);
@@ -299,23 +281,12 @@
                     continue;
                 }
 
-                if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    final NicknameViewModel itemEditor = NicknameViewModel.createForExisting(
-                            mContext, rawContact, dataId, entryValues, kind.titleRes);
-                    rawContact.getFields().add(itemEditor);
-                    continue;
-                }
-
-                if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    final NoteViewModel itemEditor = NoteViewModel.createForExisting(mContext,
-                            rawContact, dataId, entryValues, kind.titleRes);
-                    rawContact.getFields().add(itemEditor);
-                    continue;
-                }
-
-                if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    final WebsiteViewModel itemEditor = WebsiteViewModel.createForExisting(
-                            mContext, rawContact, dataId, entryValues, kind.titleRes);
+                if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) ||
+                        Note.CONTENT_ITEM_TYPE.equals(mimeType) ||
+                        Website.CONTENT_ITEM_TYPE.equals(mimeType) ||
+                        Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final GenericViewModel itemEditor = GenericViewModel.fromDataKind(
+                            mContext, rawContact, dataId, entryValues, kind);
                     rawContact.getFields().add(itemEditor);
                     continue;
                 }
@@ -345,19 +316,16 @@
         for (int i = 0; i < mRawContacts.size(); i++) {
             final DisplayRawContact rawContact = mRawContacts.get(i);
             // Header
-            mFieldContainer.addView(
-                            rawContact.getHeader().getView(mInflater, mFieldContainer));
+            rawContact.getHeader().createAndAddView(mInflater, mFieldContainer);
 
             // Data items
             final ArrayList<BaseViewModel> fields = rawContact.getFields();
             for (int j = 0; j < fields.size(); j++) {
-                final BaseViewModel field = fields.get(j);
-                mFieldContainer.addView(field.getView(mInflater, mFieldContainer));
+                fields.get(j).createAndAddView(mInflater, mFieldContainer);
             }
 
             // Footer
-            mFieldContainer.addView(
-                    rawContact.getFooter().getView(mInflater, mFieldContainer));
+            rawContact.getFooter().createAndAddView(mInflater, mFieldContainer);
         }
     }
 
diff --git a/src/com/android/contacts/views/editor/view/EditorItemView.java b/src/com/android/contacts/views/editor/view/EditorItemView.java
new file mode 100644
index 0000000..5d22d62
--- /dev/null
+++ b/src/com/android/contacts/views/editor/view/EditorItemView.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class EditorItemView extends LinearLayout implements OnClickListener, OnFocusChangeListener {
+    private final int CAPTION_WIDTH_DIP = 70;
+    private final int TYPE_BUTTON_WIDTH_DIP = 50;
+
+    private TextView mCaptionTextView;
+    private LinearLayout mFieldContainer;
+    private Button mTypeButton;
+
+    private Listener mListener;
+
+    private int[] mTypeResIds;
+    private int mCustomTypeIndex;
+
+    private boolean mHasFocus;
+
+    public EditorItemView(Context context) {
+        super(context);
+        createEmptyLayout();
+    }
+
+    public EditorItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        createEmptyLayout();
+    }
+
+    public EditorItemView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        createEmptyLayout();
+    }
+
+    private void createEmptyLayout() {
+        final int captionWidthPixels =
+                (int) (getResources().getDisplayMetrics().density * CAPTION_WIDTH_DIP);
+
+        // Caption
+        mCaptionTextView = new TextView(getContext());
+        mCaptionTextView.setLayoutParams(new LinearLayout.LayoutParams(captionWidthPixels,
+                LayoutParams.WRAP_CONTENT));
+        addView(mCaptionTextView);
+
+        // Text Fields
+        mFieldContainer = new LinearLayout(getContext());
+        mFieldContainer.setOrientation(LinearLayout.VERTICAL);
+        mFieldContainer.setLayoutParams(new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT,
+                1.0f));
+        addView(mFieldContainer);
+    }
+
+    /**
+     * Configures the View. This function must only be called once after construction
+     * @param captionResId
+     *         The caption of this item
+     * @param fieldResIds
+     *         Ressource Ids of the editable fields
+     * @param typeResIds
+     *         A list of user selectable type-ressource-ids. If this parameter is null,
+     *         no type can be selected
+     * @param customTypeIndex
+     *         The index of the type representing "Custom" (allowing the user to type
+     *         a custom type. If this is -1, no type is custom. If types is null, this
+     *         parameter is ignored.
+     */
+    public void configure(int captionResId, int[] fieldResIds, int[] typeResIds,
+            int customTypeIndex) {
+        mCaptionTextView.setText(captionResId);
+
+        // Create Fields
+        for (int fieldResId : fieldResIds) {
+            final EditText fieldEditText = new EditText(getContext());
+            fieldEditText.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT));
+            fieldEditText.setContentDescription(getResources().getString(fieldResId));
+            fieldEditText.setOnFocusChangeListener(this);
+            mFieldContainer.addView(fieldEditText);
+        }
+
+        // Configure Type Button
+        mCustomTypeIndex = customTypeIndex;
+        mTypeResIds = typeResIds;
+        if (typeResIds != null) {
+            final int typeButtonWidthPixels =
+                (int) (getResources().getDisplayMetrics().density * TYPE_BUTTON_WIDTH_DIP);
+            mTypeButton = new Button(getContext());
+            mTypeButton.setLayoutParams(new LinearLayout.LayoutParams(typeButtonWidthPixels,
+                    LayoutParams.WRAP_CONTENT));
+        }
+    }
+
+    public void setFieldValue(int index, CharSequence value) {
+        final EditText editText = (EditText) mFieldContainer.getChildAt(index);
+        editText.setText(value);
+    }
+
+    public String getFieldValue(int index) {
+        final EditText editText = (EditText) mFieldContainer.getChildAt(index);
+        final CharSequence resultCharSequence = editText.getText();
+        if (resultCharSequence == null) return "";
+        return resultCharSequence.toString();
+    }
+
+    public void setType(int typeValue, String labelValue) {
+        if (mTypeButton == null) {
+            return;
+        }
+        if (typeValue == mCustomTypeIndex) {
+            mTypeButton.setText(labelValue);
+        } else {
+            final int typeResId = mTypeResIds[typeValue];
+            mTypeButton.setText(getResources().getString(typeResId));
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mTypeButton) {
+
+            return;
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        // Focus was gained, lost etc. We should only fire an event if we lost focus to another
+        // control
+        if (mHasFocus && !hasFocus && mListener != null) {
+            mListener.onFocusLost();
+        }
+        mHasFocus = hasFocus;
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    public interface Listener {
+        /**
+         * Called when the user has changed the Type.
+         * @param newIndex
+         *         The index of the newly selected type (corresponds to the array passed as
+         *         typeResIds in configure.
+         * @param customText
+         *         If this is the type "Custom", this field contains the user-entered text.
+         *         Otherwise this parameter is null
+         */
+        void onTypeChanged(int newIndex, String customText);
+
+        /**
+         * Called when the user has navigated away from this editor (this is not raised if
+         * the focus switches between fields of the same editor).
+         */
+        void onFocusLost();
+    }
+}
diff --git a/src/com/android/contacts/views/editor/view/SingleFieldView.java b/src/com/android/contacts/views/editor/view/SingleFieldView.java
deleted file mode 100644
index 2d4695e..0000000
--- a/src/com/android/contacts/views/editor/view/SingleFieldView.java
+++ /dev/null
@@ -1,91 +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.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
deleted file mode 100644
index 9795e40..0000000
--- a/src/com/android/contacts/views/editor/view/ViewTypes.java
+++ /dev/null
@@ -1,29 +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;
-
-public interface ViewTypes {
-    public static final int DATA = 0;
-    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 8a2f78a..3e5a4f4 100644
--- a/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
@@ -42,6 +42,5 @@
         return mContext;
     }
 
-    public abstract int getEntryType();
-    public abstract View getView(LayoutInflater inflater, ViewGroup parent);
+    public abstract View createAndAddView(LayoutInflater inflater, ViewGroup parent);
 }
diff --git a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java b/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
index c4cc931..3ca11ae 100644
--- a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
@@ -18,7 +18,6 @@
 
 import com.android.contacts.views.editor.DisplayRawContact;
 import com.android.contacts.views.editor.view.FieldAndTypeView;
-import com.android.contacts.views.editor.view.ViewTypes;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -26,7 +25,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 
 public abstract class FieldAndTypeViewModel extends DataViewModel {
@@ -49,12 +47,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.FIELD_AND_TYPE;
-    }
-
-    @Override
-    public FieldAndTypeView getView(LayoutInflater inflater, ViewGroup parent) {
+    public FieldAndTypeView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final FieldAndTypeView result = FieldAndTypeView.inflate(inflater, parent, false);
 
         result.setListener(mViewListener);
@@ -62,6 +55,7 @@
         result.setFieldValue(getFieldValue());
         result.setTypeDisplayLabel(getTypeDisplayLabel());
 
+        parent.addView(result);
         return result;
     }
 
diff --git a/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java b/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java
index 28610ef..df254fc 100644
--- a/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java
@@ -18,7 +18,6 @@
 
 import com.android.contacts.views.editor.DisplayRawContact;
 import com.android.contacts.views.editor.view.FooterView;
-import com.android.contacts.views.editor.view.ViewTypes;
 
 import android.content.Context;
 import android.view.LayoutInflater;
@@ -35,15 +34,11 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.RAW_CONTACT_FOOTER;
-    }
-
-    @Override
-    public View getView(LayoutInflater inflater, ViewGroup parent) {
+    public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final FooterView result = FooterView.inflate(inflater, parent, false);
 
         result.setListener(mViewListener);
+        parent.addView(result);
         return result;
     }
 
diff --git a/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java b/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java
new file mode 100644
index 0000000..5817876
--- /dev/null
+++ b/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java
@@ -0,0 +1,188 @@
+/*
+ * 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.model.ContactsSource.DataKind;
+import com.android.contacts.model.ContactsSource.EditField;
+import com.android.contacts.model.ContactsSource.EditType;
+import com.android.contacts.views.editor.DisplayRawContact;
+import com.android.contacts.views.editor.view.EditorItemView;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentProviderOperation.Builder;
+import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+public class GenericViewModel extends DataViewModel {
+    private static final String TAG = "GenericViewModel";
+
+    private final int mLabelResId;
+    private final Field[] mFields;
+    private final int[] mTypeResIds;
+    private final int mCustomTypeIndex;
+    private final String mTypeColumn;
+    private final String mLabelColumn;
+
+    private EditorItemView mEditorItemView;
+
+    /**
+     * Overload without custom types
+     */
+    protected GenericViewModel(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, String mimeType, int labelResId,
+            Field[] fields) {
+        this(context, rawContact, dataId, contentValues, mimeType, labelResId, fields,
+                null, -1, null, null);
+    }
+
+    protected GenericViewModel(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, String mimeType, int labelResId,
+            Field[] fields, int[] typeResIds, int customTypeIndex,
+            String typeColumn, String labelColumn) {
+        super(context, rawContact, dataId, contentValues, mimeType);
+        mLabelResId = labelResId;
+        mFields = fields;
+        mTypeResIds = typeResIds;
+        mCustomTypeIndex = customTypeIndex;
+        mTypeColumn = typeColumn;
+        mLabelColumn = labelColumn;
+    }
+
+    public static GenericViewModel fromDataKind(Context context, DisplayRawContact rawContact,
+            long dataId, ContentValues contentValues, DataKind dataKind) {
+        final Field[] fields = new Field[dataKind.fieldList.size()];
+        for (int i = 0; i < fields.length; i++) {
+            final EditField editField = dataKind.fieldList.get(i);
+            fields[i] = new Field(editField.titleRes, editField.column);
+        }
+        final int[] typeResIds;
+        if (dataKind.typeList == null) {
+            typeResIds = null;
+        } else {
+            typeResIds = new int[dataKind.typeList.size()];
+            for (int i = 0; i < typeResIds.length; i++) {
+                final EditType editType = dataKind.typeList.get(i);
+                typeResIds[i] = editType.labelRes;
+            }
+        }
+        return new GenericViewModel(context, rawContact, dataId, contentValues, dataKind.mimeType,
+                dataKind.titleRes, fields, typeResIds, BaseTypes.TYPE_CUSTOM,
+                dataKind.typeColumn, "");
+    }
+
+    @Override
+    public EditorItemView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
+        if (mEditorItemView == null) {
+            final EditorItemView result = new EditorItemView(getContext());
+            result.setListener(mViewListener);
+
+            final int[] fieldResIds = new int[mFields.length];
+            for (int i = 0; i < mFields.length; i++) {
+                fieldResIds[i] = mFields[i].getResId();
+            }
+
+            result.configure(mLabelResId, fieldResIds, mTypeResIds, mCustomTypeIndex);
+
+            parent.addView(result);
+
+            mEditorItemView = result;
+        }
+
+        // Set fields
+        final ContentValues contentValues = getContentValues();
+        for (int i = 0; i < mFields.length; i++) {
+            mEditorItemView.setFieldValue(i, contentValues.getAsString(mFields[i].getName()));
+        }
+
+        // Set type if required
+        if (mTypeColumn != null) {
+            mEditorItemView.setType(getTypeValue(), getLabelValue());
+        }
+
+        return mEditorItemView;
+    }
+
+    @Override
+    protected void writeToBuilder(Builder builder, boolean isInsert) {
+        for (int i = 0; i < mFields.length; i++) {
+            final String fieldName = mFields[i].getName();
+            builder.withValue(fieldName, getContentValues().getAsString(fieldName));
+        }
+        if (mTypeColumn != null) {
+            builder.withValue(mTypeColumn, getTypeValue());
+            builder.withValue(mLabelColumn, getLabelValue());
+        }
+    }
+
+    private int getTypeValue() {
+        return getContentValues().getAsInteger(mTypeColumn);
+    }
+
+    private String getLabelValue() {
+        return getContentValues().getAsString(mLabelColumn);
+    }
+
+    private EditorItemView.Listener mViewListener = new EditorItemView.Listener() {
+        public void onFocusLost() {
+            Log.v(TAG, "Received FocusLost. Checking for changes");
+            boolean hasChanged = false;
+
+            final ContentValues contentValues = getContentValues();
+
+            for (int i = 0; i < mFields.length; i++) {
+                final String oldValue = contentValues.getAsString(mFields[i].getName());
+                final String newValue = mEditorItemView.getFieldValue(i);
+                if (!TextUtils.equals(oldValue, newValue)) {
+                    contentValues.put(mFields[i].getName(), newValue);
+                    hasChanged = true;
+                }
+            }
+            if (hasChanged) {
+                Log.v(TAG, "Found changes. Updating DB");
+                saveData();
+            }
+        }
+
+        @Override
+        public void onTypeChanged(int newIndex, String customText) {
+            // TODO Auto-generated method stub
+
+        }
+    };
+
+    /* package */ static class Field {
+        private int mResId;
+        private String mName;
+
+        public int getResId() {
+            return mResId;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public Field(int resourceId, String databaseField) {
+            mResId = resourceId;
+            mName = databaseField;
+        }
+    }
+}
diff --git a/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java b/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java
index 15b5226..901452c 100644
--- a/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java
@@ -19,7 +19,6 @@
 import com.android.contacts.R;
 import com.android.contacts.views.editor.DisplayRawContact;
 import com.android.contacts.views.editor.view.HeaderView;
-import com.android.contacts.views.editor.view.ViewTypes;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -43,12 +42,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.RAW_CONTACT_HEADER;
-    }
-
-    @Override
-    public View getView(LayoutInflater inflater, ViewGroup parent) {
+    public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final HeaderView result = HeaderView.inflate(inflater, parent, false);
 
         CharSequence accountType = getRawContact().getSource().getDisplayLabel(getContext());
@@ -69,6 +63,7 @@
         result.setCaptionText(accountTypeDisplay);
         result.setLogo(getRawContact().getSource().getDisplayIcon(getContext()));
 
+        parent.addView(result);
         return result;
     }
 }
diff --git a/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java b/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java
deleted file mode 100644
index 55c7976..0000000
--- a/src/com/android/contacts/views/editor/viewModel/NicknameViewModel.java
+++ /dev/null
@@ -1,36 +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.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
deleted file mode 100644
index 80e45d8..0000000
--- a/src/com/android/contacts/views/editor/viewModel/NoteViewModel.java
+++ /dev/null
@@ -1,36 +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.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
index 1ccfb79..bfa296d 100644
--- a/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
@@ -18,7 +18,6 @@
 
 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;
@@ -27,7 +26,6 @@
 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 {
@@ -57,12 +55,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.ORGANIZATION;
-    }
-
-    @Override
-    public OrganizationView getView(LayoutInflater inflater, ViewGroup parent) {
+    public OrganizationView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final OrganizationView result = OrganizationView.inflate(inflater, parent, false);
 
         result.setListener(mViewListener);
@@ -70,6 +63,7 @@
         result.setFieldValues(getCompanyFieldValue(), getTitleFieldValue());
         result.setTypeDisplayLabel(getTypeDisplayLabel());
 
+        parent.addView(result);
         return result;
     }
 
diff --git a/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java b/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java
deleted file mode 100644
index 9ea033b..0000000
--- a/src/com/android/contacts/views/editor/viewModel/PhoneViewModel.java
+++ /dev/null
@@ -1,41 +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.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-
-public class PhoneViewModel extends FieldAndTypeViewModel {
-    private PhoneViewModel(Context context, DisplayRawContact rawContact, long dataId,
-            ContentValues contentValues, int titleResId) {
-        super(context, rawContact, dataId, contentValues, Phone.CONTENT_ITEM_TYPE, titleResId,
-                Phone.NUMBER, Phone.TYPE, Phone.LABEL);
-    }
-
-    public static PhoneViewModel createForExisting(Context context, DisplayRawContact rawContact,
-            long dataId, ContentValues contentValues, int titleResId) {
-        return new PhoneViewModel(context, rawContact, dataId, contentValues, titleResId);
-    }
-
-    @Override
-    protected CharSequence getTypeDisplayLabel() {
-        return Phone.getTypeLabel(getContext().getResources(), getType(), getLabel());
-    }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java b/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
index ae04933..682545c 100644
--- a/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
@@ -18,7 +18,6 @@
 
 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;
@@ -51,12 +50,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.PHOTO;
-    }
-
-    @Override
-    public View getView(LayoutInflater inflater, ViewGroup parent) {
+    public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final PhotoView result = PhotoView.inflate(inflater, parent, false);
 
         final byte[] binaryData = getContentValues().getAsByteArray(Photo.PHOTO);
@@ -65,6 +59,7 @@
                 ? BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length)
                 : null;
         result.setPhoto(bitmap);
+        parent.addView(result);
         return result;
     }
 }
diff --git a/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java b/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java
deleted file mode 100644
index a5ef528..0000000
--- a/src/com/android/contacts/views/editor/viewModel/SingleFieldViewModel.java
+++ /dev/null
@@ -1,92 +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.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, ViewGroup parent) {
-        final SingleFieldView result = 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
index fb92b83..3351f4e 100644
--- a/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
@@ -18,7 +18,6 @@
 
 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;
@@ -64,12 +63,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.SIMPLE_OR_STRUCTURED;
-    }
-
-    @Override
-    public View getView(LayoutInflater inflater, ViewGroup parent) {
+    public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final SimpleOrStructuredView result =
                 SimpleOrStructuredView.inflate(inflater, parent, false);
 
@@ -77,6 +71,7 @@
         result.setLabelText(mLabelResId);
         result.setDisplayName(getDisplayName());
 
+        parent.addView(result);
         return result;
     }
 
diff --git a/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java b/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
index ef46bdb..f1a1559 100644
--- a/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
+++ b/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
@@ -18,7 +18,6 @@
 
 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;
@@ -73,12 +72,7 @@
     }
 
     @Override
-    public int getEntryType() {
-        return ViewTypes.SIMPLE_OR_STRUCTURED;
-    }
-
-    @Override
-    public View getView(LayoutInflater inflater, ViewGroup parent) {
+    public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
         final SimpleOrStructuredView result =
                 SimpleOrStructuredView.inflate(inflater, parent, false);
 
@@ -86,6 +80,7 @@
         result.setLabelText(mLabelResId);
         result.setDisplayName(getFormattedAddress());
 
+        parent.addView(result);
         return result;
     }
 
diff --git a/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java b/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java
deleted file mode 100644
index 5aabb29..0000000
--- a/src/com/android/contacts/views/editor/viewModel/WebsiteViewModel.java
+++ /dev/null
@@ -1,36 +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.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);
-    }
-}