New edit UI, now without tabs.

Make ExternalSource derive from FallbackSource
and add photo and name to the list of sources
since they're not optional.

Change-Id: I043db076a001a711e56dd6e5e6ee32c4c0c9477a
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 6e993ba..ab8e931 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -17,8 +17,6 @@
 package com.android.contacts;
 
 import com.android.contacts.Collapser.Collapsible;
-import com.android.contacts.ScrollingTabWidget.OnTabSelectionChangedListener;
-import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.DataKind;
@@ -40,7 +38,6 @@
 import android.content.Entity;
 import android.content.EntityIterator;
 import android.content.Intent;
-import android.content.DialogInterface.OnClickListener;
 import android.content.Entity.NamedContentValues;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -62,9 +59,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.ContextMenu;
-import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -74,9 +69,6 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
-import android.view.animation.Animation.AnimationListener;
 import android.widget.AdapterView;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -551,7 +543,7 @@
         } else if (requestCode == REQUEST_EDIT_CONTACT) {
             if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
                 finish();
-            } else {
+            } else if (resultCode == Activity.RESULT_OK) {
                 mLookupUri = intent.getData();
                 if (mLookupUri == null) {
                     finish();
diff --git a/src/com/android/contacts/model/ContactsSource.java b/src/com/android/contacts/model/ContactsSource.java
index f0c21e3..f82f8a1 100644
--- a/src/com/android/contacts/model/ContactsSource.java
+++ b/src/com/android/contacts/model/ContactsSource.java
@@ -138,6 +138,10 @@
         }
     }
 
+    abstract public int getHeaderColor(Context context);
+
+    abstract public int getSideBarColor(Context context);
+    
     /**
      * {@link Comparator} to sort by {@link DataKind#weight}.
      */
diff --git a/src/com/android/contacts/model/ExchangeSource.java b/src/com/android/contacts/model/ExchangeSource.java
index 6d4e357..5161499 100644
--- a/src/com/android/contacts/model/ExchangeSource.java
+++ b/src/com/android/contacts/model/ExchangeSource.java
@@ -272,4 +272,14 @@
 
         return kind;
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff876b47;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffc6ab8c;
+    }
 }
diff --git a/src/com/android/contacts/model/ExternalSource.java b/src/com/android/contacts/model/ExternalSource.java
index 0786e28..11e06c3 100644
--- a/src/com/android/contacts/model/ExternalSource.java
+++ b/src/com/android/contacts/model/ExternalSource.java
@@ -77,7 +77,7 @@
  * <p>
  * In the future this may be inflated from XML defined by a data source.
  */
-public class ExternalSource extends ContactsSource {
+public class ExternalSource extends FallbackSource {
     private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
 
@@ -110,6 +110,10 @@
             inflate(context, parser);
         }
 
+        // Bring in name and photo from fallback source, which are non-optional
+        inflateStructuredName(inflateLevel);
+        inflatePhoto(inflateLevel);
+
         setInflatedLevel(inflateLevel);
     }
 
@@ -260,4 +264,14 @@
             return mPublishedMode ? inflatePublished(status.published) : status.title;
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff7f93bc;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffbdc7d8;
+    }
 }
diff --git a/src/com/android/contacts/model/FallbackSource.java b/src/com/android/contacts/model/FallbackSource.java
index ebef42b..ca405eb 100644
--- a/src/com/android/contacts/model/FallbackSource.java
+++ b/src/com/android/contacts/model/FallbackSource.java
@@ -621,4 +621,14 @@
             }
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff7f93bc;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffbdc7b8;
+    }
 }
diff --git a/src/com/android/contacts/model/GoogleSource.java b/src/com/android/contacts/model/GoogleSource.java
index 010982c..e8f7f46 100644
--- a/src/com/android/contacts/model/GoogleSource.java
+++ b/src/com/android/contacts/model/GoogleSource.java
@@ -260,4 +260,14 @@
             cursor.close();
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff000000;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffffffff;
+    }
 }
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index e6bb4cf..50532a3 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -16,53 +16,6 @@
 
 package com.android.contacts.ui;
 
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Entity;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.content.ContentProviderOperation.Builder;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.Contacts.Data;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.ContextThemeWrapper;
-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.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
 import com.android.contacts.ContactsListActivity;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
@@ -82,15 +35,57 @@
 import com.android.internal.widget.ContactHeaderWidget;
 import com.google.android.collect.Lists;
 
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Entity;
+import android.content.Intent;
+import android.content.OperationApplicationException;
+import android.content.ContentProviderOperation.Builder;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts.Data;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+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.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
  * Activity for editing or inserting a contact.
  */
-public final class EditContactActivity extends Activity implements View.OnClickListener,
-        ScrollingTabWidget.OnTabSelectionChangedListener,
-        ContactHeaderWidget.ContactHeaderListener, EditorListener {
+public final class EditContactActivity extends Activity
+        implements View.OnClickListener, EditorListener {
     private static final String TAG = "EditContactActivity";
 
     /** The launch code when picking a photo and the raw data is returned */
@@ -100,7 +95,6 @@
     private static final int REQUEST_JOIN_CONTACT = 3022;
 
     private static final String KEY_EDIT_STATE = "state";
-    private static final String KEY_SELECTED_RAW_CONTACT = "selected";
 
     /** The result code when view activity should close after edit returns */
     public static final int RESULT_CLOSE_VIEW_ACTIVITY = 777;
@@ -110,46 +104,27 @@
     public static final int SAVE_MODE_JOIN = 2;
 
 
-    private String mQuerySelection;
+    String mQuerySelection;
 
-    private ScrollingTabWidget mTabWidget;
-    private ContactHeaderWidget mHeader;
-    private TextView mAccountName;
-
-    private ContactEditorView mEditor;
-
-    private EntitySet mState;
     private long mContactIdForJoin;
+    EntitySet mState;
 
+    /** The linear layout holding the ContactEditorViews */
+    LinearLayout mContent;
+    
     private ArrayList<Dialog> mManagedDialogs = Lists.newArrayList();
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        final Context context = this;
-        final LayoutInflater inflater = this.getLayoutInflater();
-
         final Intent intent = getIntent();
         final String action = intent.getAction();
-        final Bundle extras = intent.getExtras();
 
         setContentView(R.layout.act_edit);
 
-        // Header bar is filled later after queries finish
-        mHeader = (ContactHeaderWidget)this.findViewById(R.id.contact_header_widget);
-        mHeader.setContactHeaderListener(this);
-        mHeader.showStar(false);
-        mHeader.enableClickListeners();
-
-        mTabWidget = (ScrollingTabWidget)this.findViewById(R.id.tab_widget);
-        mTabWidget.setTabSelectionListener(this);
-        mAccountName = (TextView)mTabWidget.findViewById(R.id.account_name);
-
         // Build editor and listen for photo requests
-        mEditor = (ContactEditorView)this.findViewById(android.R.id.tabcontent);
-        mEditor.getPhotoEditor().setEditorListener(this);
-        mEditor.setNameEditorListener(this);
+        mContent = (LinearLayout) findViewById(R.id.editors);
 
         findViewById(R.id.btn_done).setOnClickListener(this);
         findViewById(R.id.btn_discard).setOnClickListener(this);
@@ -160,12 +135,9 @@
         if (Intent.ACTION_EDIT.equals(action) && !hasIncomingState) {
             // Read initial state from database
             new QueryEntitiesTask(this).execute(intent);
-            mHeader.showStar(true);
-            mHeader.setContactUri(intent.getData(), false);
         } else if (Intent.ACTION_INSERT.equals(action) && !hasIncomingState) {
             // Trigger dialog to pick account type
             doAddAction();
-            mHeader.showStar(false);
         }
     }
 
@@ -227,8 +199,7 @@
         @Override
         protected void onPostExecute(EditContactActivity target, Void result) {
             // Bind UI to new background state
-            target.bindTabs();
-            target.bindHeader();
+            target.bindEditors();
         }
     }
 
@@ -239,7 +210,6 @@
         if (hasValidState()) {
             // Store entities with modifications
             outState.putParcelable(KEY_EDIT_STATE, mState);
-            outState.putLong(KEY_SELECTED_RAW_CONTACT, getSelectedRawContactId());
         }
 
         super.onSaveInstanceState(outState);
@@ -250,15 +220,8 @@
         // Read modifications from instance
         mState = savedInstanceState.<EntitySet> getParcelable(KEY_EDIT_STATE);
 
-        bindTabs();
-        bindHeader();
+        bindEditors();
 
-        if (hasValidState()) {
-            final Long selectedId = savedInstanceState.getLong(KEY_SELECTED_RAW_CONTACT);
-            setSelectedRawContactId(selectedId);
-        }
-
-        // Restore selected tab and any focus
         super.onRestoreInstanceState(savedInstanceState);
     }
 
@@ -285,47 +248,12 @@
     /**
      * Show this {@link Dialog} and manage with the {@link Activity}.
      */
-    private void showAndManageDialog(Dialog dialog) {
+    void showAndManageDialog(Dialog dialog) {
         startManagingDialog(dialog);
         dialog.show();
     }
 
     /**
-     * Return the {@link RawContacts#_ID} of the currently selected tab.
-     */
-    protected Long getSelectedRawContactId() {
-        final int tabIndex = mTabWidget.getCurrentTab();
-        return this.mTabRawContacts.get(tabIndex);
-    }
-
-    /**
-     * Return the {@link EntityDelta} for the currently selected tab.
-     */
-    protected EntityDelta getSelectedEntityDelta() {
-        final Long rawContactId = getSelectedRawContactId();
-        return mState.getByRawContactId(rawContactId);
-    }
-
-    /**
-     * Set the selected tab based on the given {@link RawContacts#_ID}.
-     */
-    protected void setSelectedRawContactId(Long rawContactId) {
-        int tabIndex = 0;
-
-        // Find index of requested contact
-        final int size = mTabRawContacts.size();
-        for (int i = 0; i < size; i++) {
-            if (mTabRawContacts.valueAt(i) == rawContactId) {
-                tabIndex = i;
-                break;
-            }
-        }
-
-        mTabWidget.setCurrentTab(tabIndex);
-        this.onTabSelectionChanged(tabIndex, false);
-    }
-
-    /**
      * Check if our internal {@link #mState} is valid, usually checked before
      * performing user actions.
      */
@@ -336,128 +264,46 @@
 
 
     /**
-     * Map from {@link #mTabWidget} indexes to {@link RawContacts#_ID}, usually
-     * used when mapping to {@link #mState}.
+     * An array of the raw contacts in the order they appear in the list.
      */
-    private SparseArray<Long> mTabRawContacts = new SparseArray<Long>();
-
+    private EntityDelta[] mEntities;
 
     /**
-     * Rebuild tabs to match our underlying {@link #mState} object, usually
+     * Rebuild the editors to match our underlying {@link #mState} object, usually
      * called once we've parsed {@link Entity} data or have inserted a new
      * {@link RawContacts}.
      */
-    protected void bindTabs() {
+    protected void bindEditors() {
         if (!hasValidState()) return;
 
+        final LayoutInflater inflater = (LayoutInflater) getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
         final Sources sources = Sources.getInstance(this);
-        final Long selectedRawContactId = this.getSelectedRawContactId();
 
-        // Remove any existing tabs and rebuild any visible
-        mTabWidget.removeAllTabs();
-        mTabRawContacts.clear();
-        for (EntityDelta entity : mState) {
+        // Remove any existing editors and rebuild any visible
+
+        mContent.removeAllViews();
+        mEntities = new EntityDelta[mState.size()];
+        int size = mState.size();
+        for (int i = 0; i < size; i++) {
+            // TODO ensure proper ordering of entities in the list
+            EntityDelta entity = mState.get(i);
             final ValuesDelta values = entity.getValues();
             if (!values.isVisible()) continue;
 
             final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
-            final Long rawContactId = values.getAsLong(RawContacts._ID);
             final ContactsSource source = sources.getInflatedSource(accountType,
                     ContactsSource.LEVEL_CONSTRAINTS);
 
-            final int tabIndex = mTabWidget.getTabCount();
-            final View tabView = ContactsUtils.createTabIndicatorView(
-                    mTabWidget.getTabParent(), source);
-            mTabWidget.addTab(tabView);
-            mTabRawContacts.put(tabIndex, rawContactId);
-        }
-
-        final boolean hasActiveTabs = mTabWidget.getTabCount() > 0;
-        if (hasActiveTabs) {
-            // Focus on last selected contact
-            this.setSelectedRawContactId(selectedRawContactId);
-        } else {
-            // Nothing remains to edit, save and bail entirely
-            this.doSaveAction(SAVE_MODE_DEFAULT);
+            ContactEditorView editor = (ContactEditorView) inflater.inflate(
+                    R.layout.item_contact_editor, mContent, false);
+            mContent.addView(editor);
+            editor.setState(entity, source);
+            mEntities[i] = entity;
         }
 
         // Show editor now that we've loaded state
-        mEditor.setVisibility(View.VISIBLE);
-    }
-
-    /**
-     * Bind our header based on {@link #mState}, which include any edits.
-     * Usually called once {@link Entity} data has been loaded, or after a
-     * primary {@link Data} change.
-     */
-    protected void bindHeader() {
-        if (!hasValidState()) return;
-
-        boolean starred = false;
-
-        ValuesDelta photoDelta = mState.getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
-        if (photoDelta != null) {
-            final byte[] photoBytes = photoDelta.getAsByteArray(Photo.PHOTO);
-            if (photoBytes != null) {
-                Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
-                        photoBytes.length);
-                mHeader.setPhoto(photo);
-            }
-        }
-
-        ValuesDelta nameDelta = mState.getSuperPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
-        if (nameDelta != null) {
-            String visibleName = getVisibleName(nameDelta);
-            if (visibleName != null) {
-                mHeader.setDisplayName(visibleName, null);
-            }
-        }
-
-        for (EntityDelta delta : mState) {
-            Long isCurrStarred = delta.getValues().getAsLong(RawContacts.STARRED);
-            starred = starred || (isCurrStarred != null && isCurrStarred != 0);
-        }
-        mHeader.setStared(starred);
-    }
-
-    private static String getVisibleName(ValuesDelta nameDelta) {
-        final String givenName = nameDelta.getAsString(StructuredName.GIVEN_NAME);
-        final String familyName = nameDelta.getAsString(StructuredName.FAMILY_NAME);
-        final boolean hasGiven = !TextUtils.isEmpty(givenName);
-        final boolean hasFamily = !TextUtils.isEmpty(familyName);
-
-        if (hasGiven && hasFamily) {
-            return givenName + " " + familyName;
-        } else if (hasFamily) {
-            return familyName;
-        } else if (hasGiven) {
-            return givenName;
-        } else {
-            return null;
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onTabSelectionChanged(int tabIndex, boolean clicked) {
-        if (!hasValidState()) return;
-
-        // Find entity and source for selected tab
-        final EntityDelta entity = this.getSelectedEntityDelta();
-        if (entity == null) return;
-
-        final Sources sources = Sources.getInstance(this);
-        final ValuesDelta values = entity.getValues();
-        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
-        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
-        final ContactsSource source = sources.getInflatedSource(accountType,
-                ContactsSource.LEVEL_CONSTRAINTS);
-
-        mAccountName.setText(getString(R.string.account_name_format,
-                source.getDisplayLabel(this), accountName));
-        mAccountName.setVisibility(View.VISIBLE);
-
-        // Assign editor state based on entity and source
-        mEditor.setState(entity, source);
+        mContent.setVisibility(View.VISIBLE);
     }
 
     /** {@inheritDoc} */
@@ -498,12 +344,13 @@
 
         switch (requestCode) {
             case PHOTO_PICKED_WITH_DATA: {
+/*
                 // When reaching this point, we've already inflated our tab
                 // state and returned to the last-visible tab.
                 final Bitmap photo = data.getParcelableExtra("data");
                 mEditor.setPhotoBitmap(photo);
-                bindHeader();
                 break;
+*/
             }
 
             case REQUEST_JOIN_CONTACT: {
@@ -525,17 +372,6 @@
     }
 
     @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        final boolean hasPhotoEditor = mEditor.hasPhotoEditor();
-        final boolean hasSetPhoto = mEditor.hasSetPhoto();
-
-        menu.findItem(R.id.menu_photo_add).setVisible(hasPhotoEditor);
-        menu.findItem(R.id.menu_photo_remove).setVisible(hasSetPhoto);
-
-        return true;
-    }
-
-    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case R.id.menu_done:
@@ -546,10 +382,6 @@
                 return doAddAction();
             case R.id.menu_delete:
                 return doDeleteAction();
-            case R.id.menu_photo_add:
-                return doPickPhotoAction();
-            case R.id.menu_photo_remove:
-                return doRemovePhotoAction();
             case R.id.menu_split:
                 return doSplitContactAction();
             case R.id.menu_join:
@@ -692,7 +524,7 @@
      * Saves or creates the contact based on the mode, and if successful
      * finishes the activity.
      */
-    private boolean doSaveAction(int saveMode) {
+    boolean doSaveAction(int saveMode) {
         if (!hasValidState()) return false;
 
         final PersistTask task = new PersistTask(this, saveMode);
@@ -858,17 +690,6 @@
         return true;
     }
 
-    /**
-     * Clear any existing photo under the currently selected tab.
-     */
-    public boolean doRemovePhotoAction() {
-        if (!hasValidState()) return false;
-
-        // Remove photo from selected contact
-        mEditor.setPhotoBitmap(null);
-        return true;
-    }
-
     /** {@inheritDoc} */
     public void onDeleted(Editor editor) {
         // Ignore any editor deletes
@@ -884,7 +705,6 @@
                 break;
             }
             case EditorListener.FIELD_CHANGED: {
-                bindHeader();
                 break;
             }
         }
@@ -976,8 +796,7 @@
                     // Update the UI.
                     EditContactActivity target = mTarget.get();
                     if (target != null) {
-                        target.bindTabs();
-                        target.bindHeader();
+                        target.bindEditors();
                     }
                 }
             };
@@ -1061,8 +880,7 @@
                 // Account was auto-selected on the background thread,
                 // but we need to update the UI still in the
                 // now-current UI thread.
-                target.bindTabs();
-                target.bindHeader();
+                target.bindEditors();
             }
         }
     }
@@ -1076,13 +894,14 @@
         builder.setMessage(R.string.deleteConfirmation);
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int which) {
-                // Mark the currently selected contact for deletion
-                final EntityDelta delta = getSelectedEntityDelta();
-                if (delta == null) return;
-                delta.markDeleted();
+                // Mark all raw contacts for deletion
+                for (EntityDelta delta : mState) {
+                    delta.markDeleted();
+                }
 
-                bindTabs();
-                bindHeader();
+                // Save the deletes
+                doSaveAction(SAVE_MODE_DEFAULT);
+                finish();
             }
         });
         builder.setNegativeButton(android.R.string.cancel, null);
@@ -1138,9 +957,6 @@
                 final ValuesDelta structuredName = allNames.get(which);
                 structuredName.put(Data.IS_PRIMARY, 1);
                 structuredName.put(Data.IS_SUPER_PRIMARY, 1);
-
-                // Update header based on edited values
-                bindHeader();
             }
         };
 
diff --git a/src/com/android/contacts/ui/widget/ContactEditorView.java b/src/com/android/contacts/ui/widget/ContactEditorView.java
index c723f6f..f89ee31 100644
--- a/src/com/android/contacts/ui/widget/ContactEditorView.java
+++ b/src/com/android/contacts/ui/widget/ContactEditorView.java
@@ -35,12 +35,14 @@
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
-import android.widget.RelativeLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
@@ -54,7 +56,7 @@
  * adding {@link Data} rows or changing {@link EditType}, are performed through
  * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced.
  */
-public class ContactEditorView extends RelativeLayout implements OnClickListener {
+public class ContactEditorView extends LinearLayout implements OnClickListener {
     private LayoutInflater mInflater;
 
     private TextView mReadOnly;
@@ -72,6 +74,12 @@
     private Drawable mSecondaryOpen;
     private Drawable mSecondaryClosed;
 
+    private View mHeader;
+    private View mSideBar;
+    private ImageView mHeaderIcon;
+    private TextView mHeaderAccountType;
+    private TextView mHeaderAccountName;
+    
     public ContactEditorView(Context context) {
         super(context);
     }
@@ -101,6 +109,12 @@
         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
         mSecondary = (ViewGroup)findViewById(R.id.sect_secondary);
 
+        mHeader = findViewById(R.id.header);
+        mSideBar = findViewById(R.id.color_bar);
+        mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
+        mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
+        mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+        
         mSecondaryHeader = (TextView)findViewById(R.id.head_secondary);
         mSecondaryHeader.setOnClickListener(this);
 
@@ -170,6 +184,19 @@
         // Make sure we have StructuredName
         EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
 
+        // Fill in the header info
+        mHeader.setBackgroundColor(source.getHeaderColor(mContext));
+        mSideBar.setBackgroundColor(source.getSideBarColor(mContext));
+        ValuesDelta values = state.getValues();
+        String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+        if (TextUtils.isEmpty(accountName)) {
+            // TODO get from resource
+            accountName = "Local contact";
+        }
+        mHeaderAccountName.setText(accountName);
+        mHeaderAccountType.setText(source.getDisplayLabel(mContext));
+        mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
+
         // Show photo editor when supported
         EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE);
         mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null);
@@ -177,8 +204,15 @@
         mPhoto.setEnabled(!source.readOnly);
         mName.setEnabled(!source.readOnly);
 
-        mReadOnly.setVisibility(source.readOnly ? View.VISIBLE : View.GONE);
-
+        boolean readOnly = source.readOnly;
+        if (readOnly) {
+            mGeneral.setVisibility(View.GONE);
+            mSecondary.setVisibility(View.GONE);
+            mSecondaryHeader.setVisibility(View.GONE);
+        } else {
+            mReadOnly.setVisibility(View.GONE);
+        }
+    
         // Create editor sections for each possible data kind
         for (DataKind kind : source.getSortedDataKinds()) {
             // Skip kind of not editable
@@ -193,7 +227,7 @@
                 // Handle special case editor for photos
                 final ValuesDelta primary = state.getPrimaryEntry(mimeType);
                 mPhoto.setValues(kind, primary, state, source.readOnly);
-            } else {
+            } else if (!readOnly) {
                 // Otherwise use generic section-based editors
                 if (kind.fieldList == null) continue;
                 final ViewGroup parent = kind.secondary ? mSecondary : mGeneral;