Fixed VERSION check, persist changed values, IM changes.

Moved the buildDiff() VERSION assertion to the first
operation, since otherwise the value has already changed
when we reach it.

Added the few lines needed to start persisting any edited
data.  Still need to write the back-off and retry when we
encounter a VERSION failure.

Cleaned up our hard-coded data types, added support for
StructuredPostal and IM editors.  Standardized the EditText
flags for IME between the sources.  Based on a pending
framework change, moved to using two fields for IM to mirror
how we handle TYPE values.
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 825ded0..156f487 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -123,31 +123,6 @@
         return display;
     }
 
-    public static String encodePredefinedImProtocol(int protocol) {
-        return "pre:" + protocol;
-    }
-
-    public static String encodeCustomImProtocol(String protocolString) {
-        return "custom:" + protocolString;
-    }
-
-    public static Object decodeImProtocol(String encodedString) {
-        if (encodedString == null) {
-            return null;
-        }
-
-        if (encodedString.startsWith("pre:")) {
-            return Integer.parseInt(encodedString.substring(4));
-        }
-
-        if (encodedString.startsWith("custom:")) {
-            return encodedString.substring(7);
-        }
-
-        throw new IllegalArgumentException(
-                "the value is not a valid encoded protocol, " + encodedString);
-    }
-
     /**
      * Opens an InputStream for the person's photo and returns the photo as a Bitmap.
      * If the person's photo isn't present returns null.
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index e549da7..751ef75 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -776,25 +776,27 @@
                     } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
                         String[] protocolStrings = getResources().getStringArray(
                                 android.R.array.imProtocols);
-                        Object protocolObj = ContactsUtils.decodeImProtocol(
-                                aggCursor.getString(DATA_5_COLUMN));
+                        Object protocolObj = aggCursor.getString(DATA_5_COLUMN);
                         String host = null;
-                        if (protocolObj instanceof Number) {
-                            int protocol = ((Number) protocolObj).intValue();
-                            entry.label = buildActionString(R.string.actionChat,
-                                    protocolStrings[protocol], false);
-                            host = ContactsUtils.lookupProviderNameFromId(
-                                    protocol).toLowerCase();
-                            if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
-                                    || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
-                                entry.maxLabelLines = 2;
-                            }
-                        } else if (protocolObj != null) {
+                        // TODO: fix by moving to contactssource-based rendering rules
+//                      Object protocolObj = ContactsUtils.decodeImProtocol(
+//                      aggCursor.getString(DATA_5_COLUMN));
+//                        if (protocolObj instanceof Number) {
+//                            int protocol = ((Number) protocolObj).intValue();
+//                            entry.label = buildActionString(R.string.actionChat,
+//                                    protocolStrings[protocol], false);
+//                            host = ContactsUtils.lookupProviderNameFromId(
+//                                    protocol).toLowerCase();
+//                            if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
+//                                    || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
+//                                entry.maxLabelLines = 2;
+//                            }
+//                        } else if (protocolObj != null) {
                             String providerName = (String) protocolObj;
                             entry.label = buildActionString(R.string.actionChat,
                                     providerName, false);
                             host = providerName.toLowerCase();
-                        }
+//                        }
 
                         // Only add the intent if there is a valid host
                         if (!TextUtils.isEmpty(host)) {
diff --git a/src/com/android/contacts/model/AugmentedEntity.java b/src/com/android/contacts/model/AugmentedEntity.java
index 755c394..2cded4e 100644
--- a/src/com/android/contacts/model/AugmentedEntity.java
+++ b/src/com/android/contacts/model/AugmentedEntity.java
@@ -24,7 +24,6 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.provider.BaseColumns;
-import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 
@@ -40,7 +39,7 @@
  * changes still cleanly applied.
  * <p>
  * One benefit of this approach is that we can build changes entirely on an
- * empty {@link Entity}, which then becomes an insert {@link Contacts} case.
+ * empty {@link Entity}, which then becomes an insert {@link RawContacts} case.
  * <p>
  * When applying modifications over an {@link Entity}, we try finding the
  * original {@link Data#_ID} rows where the modifications took place. If those
@@ -77,7 +76,7 @@
     public static AugmentedEntity fromBefore(Entity before) {
         final AugmentedEntity entity = new AugmentedEntity();
         entity.mValues = AugmentedValues.fromBefore(before.getEntityValues());
-        entity.mValues.setIdColumn(Contacts._ID);
+        entity.mValues.setIdColumn(RawContacts._ID);
         for (NamedContentValues namedValues : before.getSubValues()) {
             entity.addEntry(AugmentedValues.fromBefore(namedValues.values));
         }
@@ -311,11 +310,13 @@
         }
 
         // If any operations, assert that version is identical so we bail if changed
-        if (diff.size() > 0 && beforeVersion != null) {
-            builder = ContentProviderOperation.newCountQuery(Contacts.CONTENT_URI);
-            builder.withSelection(RawContacts.VERSION + "=" + beforeVersion, null);
+        if (diff.size() > 0 && beforeVersion != null && beforeId != null) {
+            builder = ContentProviderOperation.newCountQuery(RawContacts.CONTENT_URI);
+            builder.withSelection(RawContacts._ID + "=" + beforeId + " AND " + RawContacts.VERSION
+                    + "=" + beforeVersion, null);
             builder.withExpectedCount(1);
-            possibleAdd(diff, builder);
+            // Sneak version check at beginning of list
+            diff.add(0, builder.build());
         }
 
         return diff;
diff --git a/src/com/android/contacts/model/ContactsSource.java b/src/com/android/contacts/model/ContactsSource.java
index 1e0811e..c344711 100644
--- a/src/com/android/contacts/model/ContactsSource.java
+++ b/src/com/android/contacts/model/ContactsSource.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.model;
 
+import android.content.ContentValues;
 import android.database.Cursor;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
@@ -141,6 +142,8 @@
         public List<EditType> typeList;
         public List<EditField> fieldList;
 
+        public ContentValues defaultValues;
+
         public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
             this.mimeType = mimeType;
             this.titleRes = titleRes;
@@ -206,9 +209,9 @@
             this.inputType = inputType;
         }
 
-        public EditField(String column, int titleRes, int inputType, int minLines) {
+        public EditField(String column, int titleRes, int inputType, boolean optional) {
             this(column, titleRes, inputType);
-            this.minLines = minLines;
+            this.optional = optional;
         }
     }
 
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 147b28f..e68d0dc 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -84,10 +84,19 @@
     public static void insertChild(AugmentedEntity state, DataKind kind) {
         final ContentValues after = new ContentValues();
 
-        // TODO: add the best-kind of entry based on current state machine
-        // TODO: fill in other default values
+        // Our parent CONTACT_ID is provided later
         after.put(Data.MIMETYPE, kind.mimeType);
-//        after.put(Data.CONTACT_ID, state.values.getAsLong(Contacts._ID));
+
+        // Fill-in with any requested default values
+        if (kind.defaultValues != null) {
+            after.putAll(kind.defaultValues);
+        }
+
+        if (kind.typeColumn != null) {
+            // TODO: add the best-kind of entry based on current state machine
+            final EditType firstType = kind.typeList.get(0);
+            after.put(kind.typeColumn, firstType.rawValue);
+        }
 
         state.addEntry(AugmentedValues.fromAfter(after));
     }
diff --git a/src/com/android/contacts/model/Sources.java b/src/com/android/contacts/model/Sources.java
index b642064..e77a7dd 100644
--- a/src/com/android/contacts/model/Sources.java
+++ b/src/com/android/contacts/model/Sources.java
@@ -18,9 +18,12 @@
 
 import com.android.contacts.R;
 
+import android.content.ContentValues;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Nickname;
@@ -77,6 +80,23 @@
         return mSources.get(accountType);
     }
 
+    private static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE;
+    private static final int FLAGS_EMAIL = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+    private static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
+    private static final int FLAGS_PHONETIC = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC;
+    private static final int FLAGS_GENERIC_NAME = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
+    private static final int FLAGS_NOTE = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+    private static final int FLAGS_WEBSITE = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_VARIATION_URI;
+    private static final int FLAGS_POSTAL = EditorInfo.TYPE_CLASS_TEXT
+            | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+            | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+
     /**
      * Hard-coded instance of {@link ContactsSource} for Google Contacts.
      */
@@ -117,8 +137,7 @@
                     Phone.LABEL));
 
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup,
-                    EditorInfo.TYPE_CLASS_PHONE));
+            kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
 
             list.add(kind);
         }
@@ -140,8 +159,40 @@
                     Email.LABEL));
 
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup,
-                    EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS));
+            kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+            list.add(kind);
+        }
+
+        {
+            // GOOGLE: IM
+            DataKind kind = new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
+                    android.R.drawable.sym_action_chat, 20, true);
+
+            kind.actionHeader = new ActionLabelInflater(R.string.actionChat, kind);
+            kind.actionBody = new ColumnInflater(Im.DATA);
+
+            // NOTE: even though a traditional "type" exists, for editing
+            // purposes we're using the network to pick labels
+
+            kind.defaultValues = new ContentValues();
+            kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+            kind.typeColumn = Im.PROTOCOL;
+            kind.typeList = new ArrayList<EditType>();
+            kind.typeList.add(new EditType(Im.PROTOCOL_AIM, R.string.type_im_aim));
+            kind.typeList.add(new EditType(Im.PROTOCOL_MSN, R.string.type_im_msn));
+            kind.typeList.add(new EditType(Im.PROTOCOL_YAHOO, R.string.type_im_yahoo));
+            kind.typeList.add(new EditType(Im.PROTOCOL_SKYPE, R.string.type_im_skype));
+            kind.typeList.add(new EditType(Im.PROTOCOL_QQ, R.string.type_im_qq));
+            kind.typeList.add(new EditType(Im.PROTOCOL_GOOGLE_TALK, R.string.type_im_google_talk));
+            kind.typeList.add(new EditType(Im.PROTOCOL_ICQ, R.string.type_im_icq));
+            kind.typeList.add(new EditType(Im.PROTOCOL_JABBER, R.string.type_im_jabber));
+            kind.typeList.add(new EditType(Im.PROTOCOL_CUSTOM, R.string.type_custom, true, -1,
+                    Im.CUSTOM_PROTOCOL));
+
+            kind.fieldList = new ArrayList<EditField>();
+            kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
 
             list.add(kind);
         }
@@ -149,9 +200,11 @@
         {
             // GOOGLE: POSTAL
             DataKind kind = new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
-                    R.string.postalLabelsGroup, R.drawable.sym_action_map, 20, true);
+                    R.string.postalLabelsGroup, R.drawable.sym_action_map, 25, true);
 
             kind.actionHeader = new ActionLabelInflater(R.string.actionMap, kind);
+            // TODO: build body from various structured fields
+            kind.actionBody = new ColumnInflater(StructuredPostal.FORMATTED_ADDRESS);
 
             kind.typeColumn = StructuredPostal.TYPE;
             kind.typeList = new ArrayList<EditType>();
@@ -161,28 +214,30 @@
             kind.typeList.add(new EditType(StructuredPostal.TYPE_CUSTOM, R.string.type_custom,
                     true, -1, StructuredPostal.LABEL));
 
-            // TODO: define editors for each field
-
-// EditorInfo.TYPE_CLASS_TEXT
-//          | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
-//          | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
-//          | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
-
-//          entry.maxLines = 4;
-//          entry.lines = 2;
+            kind.fieldList = new ArrayList<EditField>();
+            kind.fieldList.add(new EditField(StructuredPostal.AGENT, -1, FLAGS_POSTAL, true));
+            kind.fieldList.add(new EditField(StructuredPostal.HOUSENAME, -1, FLAGS_POSTAL, true));
+            kind.fieldList.add(new EditField(StructuredPostal.STREET, -1, FLAGS_POSTAL));
+            kind.fieldList.add(new EditField(StructuredPostal.POBOX, -1, FLAGS_POSTAL, true));
+            kind.fieldList.add(new EditField(StructuredPostal.NEIGHBORHOOD, -1, FLAGS_POSTAL, true));
+            kind.fieldList.add(new EditField(StructuredPostal.CITY, -1, FLAGS_POSTAL));
+            kind.fieldList.add(new EditField(StructuredPostal.SUBREGION, -1, FLAGS_POSTAL, true));
+            kind.fieldList.add(new EditField(StructuredPostal.REGION, -1, FLAGS_POSTAL));
+            kind.fieldList.add(new EditField(StructuredPostal.POSTCODE, -1, FLAGS_POSTAL));
+            kind.fieldList.add(new EditField(StructuredPostal.COUNTRY, -1, FLAGS_POSTAL, true));
 
             list.add(kind);
         }
 
-        // TODO: GOOGLE: IM
-//      entry.contentType = EditorInfo.TYPE_CLASS_TEXT
-//      | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
-
         {
             // GOOGLE: ORGANIZATION
             DataKind kind = new DataKind(Organization.CONTENT_ITEM_TYPE,
                     R.string.organizationLabelsGroup, R.drawable.sym_action_organization, 30, true);
 
+            kind.actionHeader = new SimpleInflater(R.string.organizationLabelsGroup);
+            // TODO: build body from multiple fields
+            kind.actionBody = new ColumnInflater(Organization.TITLE);
+
             kind.typeColumn = Organization.TYPE;
             kind.typeList = new ArrayList<EditType>();
             kind.typeList.add(new EditType(Organization.TYPE_WORK, R.string.type_work));
@@ -192,9 +247,9 @@
 
             kind.fieldList = new ArrayList<EditField>();
             kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company,
-                    EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS));
+                    FLAGS_GENERIC_NAME));
             kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title,
-                    EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS));
+                    FLAGS_GENERIC_NAME));
 
             list.add(kind);
         }
@@ -205,13 +260,11 @@
                     R.string.label_notes, R.drawable.sym_note, 110, true);
             kind.secondary = true;
 
-//            kind.actionHeader = new ActionLabelInflater(R.string.ac, kind);
-            kind.actionBody = new ColumnInflater(Email.DATA);
+            kind.actionHeader = new SimpleInflater(R.string.label_notes);
+            kind.actionBody = new ColumnInflater(Note.NOTE);
 
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Email.DATA, R.string.label_notes,
-                    EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
-                            | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE));
+            kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
 
             list.add(kind);
         }
@@ -222,12 +275,19 @@
                     R.string.nicknameLabelsGroup, -1, 115, true);
             kind.secondary = true;
 
+            kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
+            kind.actionBody = new ColumnInflater(Nickname.NAME);
+
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup));
+            kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
+                    FLAGS_PERSON_NAME));
 
             list.add(kind);
         }
 
+        // TODO: GOOGLE: GROUPMEMBERSHIP
+        // TODO: GOOGLE: WEBSITE
+
         return list;
     }
 
@@ -297,7 +357,7 @@
                     Phone.LABEL));
 
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup));
+            kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
 
             list.add(kind);
         }
@@ -317,7 +377,27 @@
             kind.typeList.add(new EditType(TYPE_EMAIL3, R.string.type_email_3, false, 1));
 
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup));
+            kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+            list.add(kind);
+        }
+
+        {
+            // EXCHANGE: IM
+            DataKind kind = new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
+                    android.R.drawable.sym_action_chat, 20, true);
+
+            kind.actionHeader = new ActionLabelInflater(R.string.actionChat, kind);
+            kind.actionBody = new ColumnInflater(Im.DATA);
+
+            kind.typeColumn = Im.TYPE;
+            kind.typeList = new ArrayList<EditType>();
+            kind.typeList.add(new EditType(TYPE_IM1, R.string.type_im_1, false, 1));
+            kind.typeList.add(new EditType(TYPE_IM2, R.string.type_im_2, false, 1));
+            kind.typeList.add(new EditType(TYPE_IM3, R.string.type_im_3, false, 1));
+
+            kind.fieldList = new ArrayList<EditField>();
+            kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
 
             list.add(kind);
         }
@@ -327,11 +407,14 @@
             DataKind kind = new DataKind(Nickname.CONTENT_ITEM_TYPE,
                     R.string.nicknameLabelsGroup, -1, 115, true);
             kind.secondary = true;
-
             kind.typeOverallMax = 1;
 
+            kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
+            kind.actionBody = new ColumnInflater(Nickname.NAME);
+
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup));
+            kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
+                    FLAGS_PERSON_NAME));
 
             list.add(kind);
         }
@@ -341,11 +424,13 @@
             DataKind kind = new DataKind(Website.CONTENT_ITEM_TYPE,
                     R.string.websiteLabelsGroup, -1, 120, true);
             kind.secondary = true;
-
             kind.typeOverallMax = 1;
 
+            kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
+            kind.actionBody = new ColumnInflater(Website.URL);
+
             kind.fieldList = new ArrayList<EditField>();
-            kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup));
+            kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
 
             list.add(kind);
         }
@@ -360,6 +445,9 @@
     public static class SimpleInflater implements StringInflater {
         // TODO: implement this
 
+        public SimpleInflater(int stringRes) {
+        }
+
         public SimpleInflater(int stringRes, String columnName) {
         }
 
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index dcecb95..7555b20 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -24,12 +24,14 @@
 
 import android.app.Activity;
 import android.app.Dialog;
+import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Entity;
 import android.content.EntityIterator;
 import android.content.Intent;
+import android.content.OperationApplicationException;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -164,8 +166,10 @@
         mTabContent.removeAllViews();
         mTabContent.addView(mEditor.getView());
 
+//        final ContactsSource source = Sources.getInstance().getKindsForAccountType(
+//                Sources.ACCOUNT_TYPE_GOOGLE);
         final ContactsSource source = Sources.getInstance().getKindsForAccountType(
-                Sources.ACCOUNT_TYPE_GOOGLE);
+                Sources.ACCOUNT_TYPE_EXCHANGE);
         mEditor.setState(mEntities.get(0), source);
 
 
@@ -310,11 +314,11 @@
     public void onClick(View v) {
         switch (v.getId()) {
 
-            case R.id.saveButton:
+            case R.id.btn_done:
                 doSaveAction();
                 break;
 
-            case R.id.discardButton:
+            case R.id.btn_discard:
                 doRevertAction();
                 break;
 
@@ -548,6 +552,32 @@
      * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
      */
     private void doSaveAction() {
+
+        final ContentResolver resolver = this.getContentResolver();
+
+        for (AugmentedEntity entity : mEntities) {
+
+            Log.d(TAG, "about to persist " + entity.toString());
+
+            final ArrayList<ContentProviderOperation> diff = entity.buildDiff();
+
+            // TODO: handle failed operations by re-reading entity
+            // may also need backoff algorithm to give failed msg after n tries
+
+            try {
+                resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+            } catch (RemoteException e) {
+                Log.w(TAG, "problem writing rawcontact diff", e);
+            } catch (OperationApplicationException e) {
+                Log.w(TAG, "problem writing rawcontact diff", e);
+            }
+
+        }
+
+        this.finish();
+
+
+
         // Save or create the contact if needed
 //        switch (mState) {
 //            case STATE_EDIT:
diff --git a/src/com/android/contacts/ui/widget/ContactEditorView.java b/src/com/android/contacts/ui/widget/ContactEditorView.java
index 1b55c1c..924241f 100644
--- a/src/com/android/contacts/ui/widget/ContactEditorView.java
+++ b/src/com/android/contacts/ui/widget/ContactEditorView.java
@@ -36,6 +36,7 @@
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
@@ -222,6 +223,12 @@
          * possible custom label string.
          */
         private void rebuildLabel() {
+            // Handle undetected types
+            if (mType == null) {
+                mLabel.setText(R.string.unknown);
+                return;
+            }
+
             if (mType.customColumn != null) {
                 // Use custom label string when present
                 final String customText = mEntry.getAsString(mType.customColumn);
@@ -327,12 +334,17 @@
             // Build list of valid types, including the current value
             final List<EditType> validTypes = EntityModifier.getValidTypes(mState, mKind, mType);
 
+            // Wrap our context to inflate list items using correct theme
+            final Context dialogContext = new ContextThemeWrapper(mContext,
+                    android.R.style.Theme_Light);
+            final LayoutInflater dialogInflater = mInflater.cloneInContext(dialogContext);
+
             final ListAdapter typeAdapter = new ArrayAdapter<EditType>(mContext, RES_LABEL_ITEM,
                     validTypes) {
                 @Override
                 public View getView(int position, View convertView, ViewGroup parent) {
                     if (convertView == null) {
-                        convertView = mInflater.inflate(RES_LABEL_ITEM, parent, false);
+                        convertView = dialogInflater.inflate(RES_LABEL_ITEM, parent, false);
                     }
 
                     final EditType type = this.getItem(position);
@@ -359,10 +371,6 @@
                 }
             };
 
-            // Wrap our context to inflate list items using correct theme
-            final Context dialogContext = new ContextThemeWrapper(mContext,
-                    android.R.style.Theme_Black);
-
             new AlertDialog.Builder(mContext).setSingleChoiceItems(typeAdapter, 0, clickListener)
                     .setTitle(R.string.selectLabel).show();
         }