Refactor list-caches into Custom Views with Listeners

Change-Id: Ie8e1c3713bc609d64725506e2d5cd8141d15c3e7
diff --git a/res/layout/list_edit_item_footer.xml b/res/layout/list_edit_item_footer.xml
index 03195f5..cf53ceb 100644
--- a/res/layout/list_edit_item_footer.xml
+++ b/res/layout/list_edit_item_footer.xml
@@ -17,7 +17,8 @@
  */
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.contacts.views.editor.typeViews.FooterView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
@@ -45,4 +46,4 @@
         android:text="@string/edit_delete_rawcontact"
     />
 
-</LinearLayout>
+</com.android.contacts.views.editor.typeViews.FooterView>
diff --git a/res/layout/list_edit_item_header.xml b/res/layout/list_edit_item_header.xml
index 8a29d75..d2f9317 100644
--- a/res/layout/list_edit_item_header.xml
+++ b/res/layout/list_edit_item_header.xml
@@ -17,7 +17,8 @@
  */
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.contacts.views.editor.typeViews.HeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
@@ -41,4 +42,4 @@
         android:scaleType="center"
         android:background="@android:drawable/list_selector_background"
     />
-</LinearLayout>
+</com.android.contacts.views.editor.typeViews.HeaderView>
diff --git a/res/layout/list_edit_item_photo.xml b/res/layout/list_edit_item_photo.xml
index a366671..c8f0cdf 100644
--- a/res/layout/list_edit_item_photo.xml
+++ b/res/layout/list_edit_item_photo.xml
@@ -17,7 +17,8 @@
  */
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.contacts.views.editor.typeViews.PhotoView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
@@ -68,5 +69,4 @@
         android:scaleType="center"
         android:background="@android:drawable/list_selector_background"
     />
-
-</LinearLayout>
+</com.android.contacts.views.editor.typeViews.PhotoView>
diff --git a/res/layout/list_edit_item_text_icons.xml b/res/layout/list_edit_item_text_icons.xml
index e632424..ab7b149 100644
--- a/res/layout/list_edit_item_text_icons.xml
+++ b/res/layout/list_edit_item_text_icons.xml
@@ -17,7 +17,8 @@
  */
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.contacts.views.editor.typeViews.DataView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
@@ -95,4 +96,4 @@
         android:background="@android:drawable/list_selector_background"
     />
 
-</LinearLayout>
+</com.android.contacts.views.editor.typeViews.DataView>
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 28a1854..4f57fb7 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -26,6 +26,10 @@
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.views.ContactLoader;
+import com.android.contacts.views.editor.typeViews.DataView;
+import com.android.contacts.views.editor.typeViews.FooterView;
+import com.android.contacts.views.editor.typeViews.HeaderView;
+import com.android.contacts.views.editor.typeViews.PhotoView;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -40,7 +44,6 @@
 import android.content.Intent;
 import android.content.Loader;
 import android.content.Entity.NamedContentValues;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
@@ -70,14 +73,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnClickListener;
 import android.view.View.OnCreateContextMenuListener;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.ImageView;
 import android.widget.ListView;
-import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.AdapterView.OnItemClickListener;
 
@@ -128,7 +127,7 @@
         final View view = inflater.inflate(R.layout.contact_editor_fragment, container, false);
 
         setHasOptionsMenu(true);
-        
+
         mInflater = inflater;
 
         mHeaderView =
@@ -391,18 +390,9 @@
 
         @Override
         public View getView(View convertView, ViewGroup parent) {
-            final View result;
-            final HeaderItemViewCache viewCache;
-            if (convertView != null) {
-                result = convertView;
-                viewCache = (HeaderItemViewCache) result.getTag();
-            } else {
-                result = mInflater.inflate(R.layout.list_edit_item_header, parent, false);
-                viewCache = new HeaderItemViewCache();
-                result.setTag(viewCache);
-                viewCache.logo = (ImageView) result.findViewById(R.id.logo);
-                viewCache.caption = (TextView) result.findViewById(R.id.caption);
-            }
+            final HeaderView result = convertView != null
+                    ? (HeaderView) convertView
+                    : HeaderView.inflate(mInflater, parent, false);
 
             CharSequence accountType = getRawContact().getSource().getDisplayLabel(mContext);
             if (TextUtils.isEmpty(accountType)) {
@@ -419,14 +409,14 @@
                         accountType, accountName);
             }
 
-            viewCache.caption.setText(accountTypeDisplay);
-            viewCache.logo.setImageDrawable(getRawContact().getSource().getDisplayIcon(mContext));
+            result.setCaptionText(accountTypeDisplay);
+            result.setLogo(getRawContact().getSource().getDisplayIcon(mContext));
 
             return result;
         }
     }
 
-    private class FooterViewEntry extends BaseViewEntry {
+    public class FooterViewEntry extends BaseViewEntry {
         public FooterViewEntry(RawContact rawContact) {
             super(rawContact);
         }
@@ -438,28 +428,28 @@
 
         @Override
         public View getView(View convertView, ViewGroup parent) {
-            final View result;
-            final FooterItemViewCache viewCache;
-            if (convertView != null) {
-                result = convertView;
-                viewCache = (FooterItemViewCache) result.getTag();
-            } else {
-                result = mInflater.inflate(R.layout.list_edit_item_footer, parent, false);
-                viewCache = new FooterItemViewCache();
-                result.setTag(viewCache);
-                viewCache.addInformationButton =
-                    (Button) result.findViewById(R.id.add_information);
-                viewCache.separateButton =
-                    (Button) result.findViewById(R.id.separate);
-                viewCache.deleteButton =
-                    (Button) result.findViewById(R.id.deleteButton);
-                viewCache.addInformationButton.setOnClickListener(
-                        mAddInformationButtonClickListener);
-            }
+            final FooterView result = convertView != null
+                    ? (FooterView) convertView
+                    : FooterView.inflate(mInflater, parent, false);
 
-            viewCache.viewEntry = this;
+            result.setListener(mViewListener);
             return result;
         }
+
+        private FooterView.Listener mViewListener = new FooterView.Listener() {
+            public void onAddClicked() {
+              // Create a bundle to show the Dialog
+              final Bundle bundle = new Bundle();
+              bundle.putLong(BUNDLE_RAW_CONTACT_ID, getRawContact().getId());
+              if (mListener != null) {
+                  mListener.onDialogRequested(R.id.edit_dialog_add_information, bundle);
+              }
+            }
+            public void onSeparateClicked() {
+            }
+            public void onDeleteClicked() {
+            }
+        };
     }
 
     private class DataViewEntry extends BaseViewEntry {
@@ -498,128 +488,50 @@
 
         @Override
         public View getView(View convertView, ViewGroup parent) {
-            final View result;
+            // Special Case: Photo
             if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                final PhotoItemViewCache viewCache;
-                if (convertView != null) {
-                    result = convertView;
-                    viewCache = (PhotoItemViewCache) result.getTag();
-                } else {
-                    // Create a new view if needed
-                    result = mInflater.inflate(R.layout.list_edit_item_photo, parent, false);
+                final PhotoView result = convertView != null
+                        ? (PhotoView) convertView
+                        : PhotoView.inflate(mInflater, parent, false);
 
-                    // Cache the children
-                    viewCache = new PhotoItemViewCache();
-                    viewCache.photo = (ImageView) result.findViewById(R.id.photo);
-                    viewCache.galleryActionButton =
-                            (ImageView) result.findViewById(R.id.action_icon);
-                    viewCache.takePhotoActionButton =
-                            (ImageView) result.findViewById(R.id.secondary_action_button);
-                    result.setTag(viewCache);
-                }
                 final Bitmap bitmap = binaryData != null
                         ? BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length)
                         : null;
-                viewCache.photo.setImageBitmap(bitmap);
-            } else {
-                final DataItemViewCache viewCache;
-                if (convertView != null) {
-                    result = convertView;
-                    viewCache = (DataItemViewCache) result.getTag();
-                } else {
-                    // Create a new view if needed
-                    result = mInflater.inflate(R.layout.list_edit_item_text_icons, parent,
-                            false);
-
-                    // Cache the children
-                    viewCache = new DataItemViewCache();
-                    viewCache.label = (TextView) result.findViewById(android.R.id.text1);
-                    viewCache.data = (TextView) result.findViewById(android.R.id.text2);
-                    viewCache.actionIcon = (ImageView) result.findViewById(R.id.action_icon);
-                    viewCache.primaryIcon = (ImageView) result.findViewById(R.id.primary_icon);
-                    viewCache.secondaryActionButton = (ImageView) result.findViewById(
-                            R.id.secondary_action_button);
-                    viewCache.secondaryActionDivider = result.findViewById(R.id.divider);
-                    result.setTag(viewCache);
-                }
-                final Resources resources = mContext.getResources();
-
-                // Set the label
-                setMaxLines(viewCache.label, maxLabelLines);
-                viewCache.label.setText(label);
-
-                if (data != null) {
-                    if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)
-                            || Constants.MIME_SMS_ADDRESS.equals(mimetype)) {
-                        viewCache.data.setText(PhoneNumberUtils.formatNumber(data));
-                    } else {
-                        viewCache.data.setText(data);
-                    }
-                    setMaxLines(viewCache.data, maxLines);
-                }
-
-                // Set the primary icon
-                viewCache.primaryIcon.setVisibility(isPrimary ? View.VISIBLE : View.GONE);
-
-                // Set the action icon
-                final ImageView action = viewCache.actionIcon;
-                if (intent != null) {
-                    action.setImageDrawable(resources.getDrawable(actionIcon));
-                    action.setVisibility(View.VISIBLE);
-                } else {
-                    action.setVisibility(View.INVISIBLE);
-                }
-
-                // Set the secondary action button
-                final ImageView secondaryActionView = viewCache.secondaryActionButton;
-                secondaryActionView.setVisibility(View.GONE);
-                viewCache.secondaryActionDivider.setVisibility(View.GONE);
+                result.setPhoto(bitmap);
+                return result;
             }
+
+            // All other cases
+            final DataView result = convertView != null
+                    ? (DataView) convertView
+                    : DataView.inflate(mInflater, parent, false);
+
+            // Set the label
+            result.setLabelText(label, maxLabelLines);
+
+            // Set data
+            if (data != null) {
+                if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)
+                        || Constants.MIME_SMS_ADDRESS.equals(mimetype)) {
+                    result.setDataText(PhoneNumberUtils.formatNumber(data), maxLines);
+                } else {
+                    result.setDataText(data, maxLines);
+                }
+            } else {
+                result.setDataText("", maxLines);
+            }
+
+            // Set the primary icon
+            result.setPrimary(isPrimary);
+
+            // Set the action icon
+            result.setPrimaryIntent(intent, mContext.getResources(), actionIcon);
+
+            // Set the secondary action button
+            // TODO: Change this to our new form
+            result.setSecondaryIntent(null, null, 0);
             return result;
         }
-
-        private void setMaxLines(TextView textView, int maxLines) {
-            if (maxLines == 1) {
-                textView.setSingleLine(true);
-                textView.setEllipsize(TextUtils.TruncateAt.END);
-            } else {
-                textView.setSingleLine(false);
-                textView.setMaxLines(maxLines);
-                textView.setEllipsize(null);
-            }
-        }
-    }
-
-    /** Cache of the header of a raw contact */
-    private static class HeaderItemViewCache {
-        public ImageView logo;
-        public TextView caption;
-    }
-
-    /** Cache of the footer of a raw contact */
-    private static class FooterItemViewCache {
-        public Button addInformationButton;
-        public Button separateButton;
-        public Button deleteButton;
-
-        public FooterViewEntry viewEntry;
-    }
-
-    /** Cache of the children views of a row */
-    private static class DataItemViewCache {
-        public TextView label;
-        public TextView data;
-        public ImageView actionIcon;
-        public ImageView primaryIcon;
-        public ImageView secondaryActionButton;
-        public View secondaryActionDivider;
-    }
-
-    /** Cache of the children views of a row */
-    private static class PhotoItemViewCache {
-        public ImageView photo;
-        public ImageView takePhotoActionButton;
-        public ImageView galleryActionButton;
     }
 
     /** Possible Item Types */
@@ -967,22 +879,6 @@
         return mAdapter.getEntry(info.position);
     }
 
-    public OnClickListener mAddInformationButtonClickListener = new OnClickListener() {
-        public void onClick(View v) {
-            // The parent of the Button allows identifying the section
-            final View parentView = (View) v.getParent();
-            final FooterItemViewCache viewCache = (FooterItemViewCache) parentView.getTag();
-            final FooterViewEntry entry = viewCache.viewEntry;
-            final RawContact rawContact = entry.getRawContact();
-
-            // Create a bundle to show the Dialog
-            final Bundle bundle = new Bundle();
-            bundle.putLong(BUNDLE_RAW_CONTACT_ID, rawContact.getId());
-            if (mListener != null) mListener.onDialogRequested(R.id.edit_dialog_add_information,
-                    bundle);
-        }
-    };
-
     private class RawContact {
         private final ContactsSource mSource;
         private String mAccountName;
diff --git a/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java b/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java
index 295b984..afc0673 100644
--- a/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java
+++ b/src/com/android/contacts/views/editor/ContactFieldEditorBaseFragment.java
@@ -16,7 +16,6 @@
 
 package com.android.contacts.views.editor;
 
-import com.android.contacts.R;
 import com.android.contacts.views.ContactLoader;
 import com.android.contacts.views.ContactSaveService;
 import com.android.internal.util.ArrayUtils;
@@ -31,18 +30,13 @@
 import android.content.Entity;
 import android.content.Intent;
 import android.content.Loader;
-import android.content.OperationApplicationException;
 import android.content.ContentProviderOperation.Builder;
 import android.content.Entity.NamedContentValues;
-import android.content.pm.PackageParser.ServiceIntentInfo;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
-import android.widget.Toast;
 
 import java.util.ArrayList;
 
diff --git a/src/com/android/contacts/views/editor/typeViews/DataView.java b/src/com/android/contacts/views/editor/typeViews/DataView.java
new file mode 100644
index 0000000..f146e36
--- /dev/null
+++ b/src/com/android/contacts/views/editor/typeViews/DataView.java
@@ -0,0 +1,114 @@
+/*
+ * 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.typeViews;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class DataView extends LinearLayout {
+    public TextView mLabelTextView;
+    public TextView mDataTextView;
+    public ImageView mActionIconImageView;
+    public ImageView mPrimaryIconImageView;
+    public ImageView mSecondaryActionButtonImageView;
+    public View mSecondaryActionDividerView;
+
+    public DataView(Context context) {
+        super(context);
+    }
+
+    public DataView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DataView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static DataView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (DataView) inflater.inflate(R.layout.list_edit_item_text_icons, parent, attachToRoot);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mLabelTextView = (TextView) findViewById(android.R.id.text1);
+        mDataTextView = (TextView) findViewById(android.R.id.text2);
+        mActionIconImageView = (ImageView) findViewById(R.id.action_icon);
+        mPrimaryIconImageView = (ImageView) findViewById(R.id.primary_icon);
+        mSecondaryActionButtonImageView = (ImageView) findViewById(R.id.secondary_action_button);
+        mSecondaryActionDividerView = findViewById(R.id.divider);
+    }
+
+    public void setLabelText(String value, int maxLines) {
+        mLabelTextView.setText(value);
+        setMaxLines(mLabelTextView, maxLines);
+    }
+
+    public void setDataText(String value, int maxLines) {
+        mDataTextView.setText(value);
+        setMaxLines(mDataTextView, maxLines);
+    }
+
+    private static void setMaxLines(TextView textView, int maxLines) {
+        if (maxLines == 1) {
+            textView.setSingleLine(true);
+            textView.setEllipsize(TextUtils.TruncateAt.END);
+        } else {
+            textView.setSingleLine(false);
+            textView.setMaxLines(maxLines);
+            textView.setEllipsize(null);
+        }
+    }
+
+    public void setPrimary(boolean value) {
+        mPrimaryIconImageView.setVisibility(value ? View.VISIBLE : View.GONE);
+    }
+
+    public void setPrimaryIntent(Intent intent, Resources resources, int actionIcon) {
+        if (intent != null) {
+            mActionIconImageView.setImageDrawable(resources.getDrawable(actionIcon));
+            mActionIconImageView.setVisibility(View.VISIBLE);
+        } else {
+            mActionIconImageView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    public void setSecondaryIntent(Intent intent, Resources resources, int actionIcon) {
+        if (intent != null) {
+            mSecondaryActionButtonImageView.setImageDrawable(resources.getDrawable(actionIcon));
+            mSecondaryActionButtonImageView.setVisibility(View.VISIBLE);
+            mSecondaryActionDividerView.setVisibility(View.VISIBLE);
+        } else {
+            mSecondaryActionButtonImageView.setVisibility(View.INVISIBLE);
+            mSecondaryActionDividerView.setVisibility(View.INVISIBLE);
+        }
+    }
+}
diff --git a/src/com/android/contacts/views/editor/typeViews/FooterView.java b/src/com/android/contacts/views/editor/typeViews/FooterView.java
new file mode 100644
index 0000000..d8ae66d
--- /dev/null
+++ b/src/com/android/contacts/views/editor/typeViews/FooterView.java
@@ -0,0 +1,92 @@
+/*
+ * 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.typeViews;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+public class FooterView extends LinearLayout {
+    private Button mAddInformationButton;
+    private Button mSeparateButton;
+    private Button mDeleteButton;
+    private Listener mListener;
+
+    public FooterView(Context context) {
+        super(context);
+    }
+
+    public FooterView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FooterView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static FooterView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (FooterView) inflater.inflate(R.layout.list_edit_item_footer, parent, attachToRoot);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mAddInformationButton = (Button) findViewById(R.id.add_information);
+        mAddInformationButton.setOnClickListener(mClickListener);
+
+        mSeparateButton = (Button) findViewById(R.id.separate);
+        mSeparateButton.setOnClickListener(mClickListener);
+
+        mDeleteButton = (Button) findViewById(R.id.delete);
+        mDeleteButton.setOnClickListener(mClickListener);
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    private OnClickListener mClickListener = new OnClickListener() {
+        public void onClick(View v) {
+            if (mListener == null) return;
+            switch (v.getId()) {
+                case R.id.add_information:
+                    mListener.onAddClicked();
+                    break;
+                case R.id.separate:
+                    mListener.onSeparateClicked();
+                    break;
+                case R.id.delete:
+                    mListener.onDeleteClicked();
+                    break;
+            }
+        }
+    };
+
+    public static interface Listener {
+        void onAddClicked();
+        void onSeparateClicked();
+        void onDeleteClicked();
+    }
+}
diff --git a/src/com/android/contacts/views/editor/typeViews/HeaderView.java b/src/com/android/contacts/views/editor/typeViews/HeaderView.java
new file mode 100644
index 0000000..bd69c50
--- /dev/null
+++ b/src/com/android/contacts/views/editor/typeViews/HeaderView.java
@@ -0,0 +1,66 @@
+/*
+ * 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.typeViews;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class HeaderView extends LinearLayout {
+    private ImageView mLogoImageView;
+    private TextView mCaptionTextView;
+
+    public HeaderView(Context context) {
+        super(context);
+    }
+
+    public HeaderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HeaderView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static HeaderView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (HeaderView) inflater.inflate(R.layout.list_edit_item_header, parent, attachToRoot);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mLogoImageView = (ImageView) findViewById(R.id.logo);
+        mCaptionTextView = (TextView) findViewById(R.id.caption);
+    }
+
+    public void setCaptionText(String value) {
+        mCaptionTextView.setText(value);
+    }
+
+    public void setLogo(Drawable value) {
+        mLogoImageView.setImageDrawable(value);
+    }
+}
diff --git a/src/com/android/contacts/views/editor/typeViews/PhotoView.java b/src/com/android/contacts/views/editor/typeViews/PhotoView.java
new file mode 100644
index 0000000..9e63a79
--- /dev/null
+++ b/src/com/android/contacts/views/editor/typeViews/PhotoView.java
@@ -0,0 +1,92 @@
+/*
+ * 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.typeViews;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+public class PhotoView extends LinearLayout {
+    private ImageView mPhotoImageView;
+    private ImageView mTakePhotoActionButton;
+    private ImageView mGalleryActionButton;
+    private Listener mListener;
+
+    public PhotoView(Context context) {
+        super(context);
+    }
+
+    public PhotoView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PhotoView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public static PhotoView inflate(LayoutInflater inflater, ViewGroup parent,
+            boolean attachToRoot) {
+        return (PhotoView) inflater.inflate(R.layout.list_edit_item_photo, parent, attachToRoot);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mPhotoImageView = (ImageView) findViewById(R.id.photo);
+
+        mTakePhotoActionButton = (ImageView) findViewById(R.id.action_icon);
+        mTakePhotoActionButton.setOnClickListener(mClickListener);
+
+        mGalleryActionButton = (ImageView) findViewById(R.id.secondary_action_button);
+        mGalleryActionButton.setOnClickListener(mClickListener);
+    }
+
+    public void setListener(Listener value) {
+        mListener = value;
+    }
+
+    public void setPhoto(Bitmap value) {
+        mPhotoImageView.setImageBitmap(value);
+    }
+
+    private OnClickListener mClickListener = new OnClickListener() {
+        public void onClick(View v) {
+            if (mListener == null) return;
+            switch (v.getId()) {
+                case R.id.action_icon:
+                    mListener.onTakePhotoClicked();
+                    break;
+                case R.id.secondary_action_button:
+                    mListener.onChooseFromGalleryClicked();
+                    break;
+            }
+        }
+    };
+
+    public static interface Listener {
+        void onTakePhotoClicked();
+        void onChooseFromGalleryClicked();
+    }
+}