Merge "Making call/sms interaction preserve dialog state"
diff --git a/proguard.flags b/proguard.flags
index 6cc704c..577144b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -12,3 +12,8 @@
 -keep class com.android.contacts.model.EntityDelta$ValuesDelta {
   public android.content.ContentValues getAfter();
 }
+
+# Any methods whose name is '*ForTest' are preserved.
+-keep class ** {
+  *** *ForTest(...);
+}
\ No newline at end of file
diff --git a/res/layout/contact_detail_activity.xml b/res/layout/contact_detail_activity.xml
index 9b63fec..c13603f 100644
--- a/res/layout/contact_detail_activity.xml
+++ b/res/layout/contact_detail_activity.xml
@@ -14,14 +14,12 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     <fragment android:name="com.android.contacts.views.detail.ContactDetailFragment"
             android:id="@+id/contact_detail_fragment"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1" />
-</LinearLayout>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout/contact_editor_activity.xml b/res/layout/contact_editor_activity.xml
index 3768f6a..a2cdead 100644
--- a/res/layout/contact_editor_activity.xml
+++ b/res/layout/contact_editor_activity.xml
@@ -14,14 +14,12 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     <fragment android:name="com.android.contacts.views.editor.ContactEditorFragment"
             android:id="@+id/contact_editor_fragment"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1" />
-</LinearLayout>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout/contact_editor_fragment.xml b/res/layout/contact_editor_fragment.xml
index 6a360ac..602f7f9 100644
--- a/res/layout/contact_editor_fragment.xml
+++ b/res/layout/contact_editor_fragment.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
 >
 
     <ScrollView
         android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:layout_weight="1"
+        android:layout_height="match_parent"
         android:fillViewport="true"
     >
 
@@ -34,28 +32,4 @@
         />
 
     </ScrollView>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        style="@android:style/ButtonBar"
-    >
-
-        <Button android:id="@+id/btn_done"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/menu_done"
-        />
-
-        <Button android:id="@+id/btn_discard"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/menu_doNotSave"
-        />
-
-    </LinearLayout>
-
-</LinearLayout>
+</FrameLayout>
diff --git a/res/layout/item_contact_editor.xml b/res/layout/item_contact_editor.xml
index 57d641f..bcbb3ba 100644
--- a/res/layout/item_contact_editor.xml
+++ b/res/layout/item_contact_editor.xml
@@ -19,117 +19,108 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
+    android:orientation="vertical"
 >
 
-    <!-- The content -->
-    <LinearLayout
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical"
+    <!-- Account info header -->
+    <RelativeLayout android:id="@+id/header"
+        android:layout_height="64dip"
+        android:layout_width="match_parent"
     >
 
-        <!-- Account info header -->
-        <RelativeLayout android:id="@+id/header"
-            android:layout_height="64dip"
+        <ImageView android:id="@+id/header_color_bar"
             android:layout_width="match_parent"
-        >
+            android:layout_height="4dip"
+            android:layout_marginBottom="5dip"
+            android:background="@color/edit_divider"
+        />
 
-            <ImageView android:id="@+id/header_color_bar"
-                android:layout_width="match_parent"
-                android:layout_height="4dip"
-                android:layout_marginBottom="5dip"
-                android:background="@color/edit_divider"
-            />
-
-            <ImageView android:id="@+id/header_icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="7dip"
-                android:layout_marginRight="7dip"
-                android:layout_centerVertical="true"
-                android:layout_below="@id/header_color_bar"
-            />
-
-            <TextView android:id="@+id/header_account_type"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@+id/header_icon"
-                android:layout_alignTop="@id/header_icon"
-                android:layout_marginTop="-4dip"
-
-                android:textSize="24sp"
-                android:textColor="?android:attr/textColorPrimary"
-                android:singleLine="true"
-            />
-
-            <TextView android:id="@+id/header_account_name"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@+id/header_icon"
-                android:layout_alignBottom="@+id/header_icon"
-                android:layout_marginBottom="2dip"
-
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorPrimary"
-                android:singleLine="true"
-            />
-
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1px"
-                android:layout_alignParentBottom="true"
-
-                android:background="?android:attr/listDivider"
-            />
-
-        </RelativeLayout>
-
-        <FrameLayout
-            android:id="@+id/stub_photo"
-            android:layout_width="match_parent"
+        <ImageView android:id="@+id/header_icon"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingLeft="12dip"
-            android:paddingTop="10dip">
+            android:layout_marginLeft="7dip"
+            android:layout_marginRight="7dip"
+            android:layout_centerVertical="true"
+            android:layout_below="@id/header_color_bar"
+        />
 
-            <include
-                android:id="@+id/edit_photo"
-                layout="@layout/item_photo_editor" />
-
-        </FrameLayout>
-
-        <include
-            android:id="@+id/edit_name"
-            android:layout_width="match_parent"
+        <TextView android:id="@+id/header_account_type"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_below="@id/stub_photo"
-            android:layout_marginTop="6dip"
-            android:layout_marginBottom="4dip"
-            layout="@layout/item_generic_editor" />
+            android:layout_toRightOf="@+id/header_icon"
+            android:layout_alignTop="@id/header_icon"
+            android:layout_marginTop="-4dip"
 
-        <LinearLayout
-            android:id="@+id/sect_fields"
-            android:layout_width="match_parent"
+            android:textSize="24sp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:singleLine="true"
+        />
+
+        <TextView android:id="@+id/header_account_name"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
+            android:layout_toRightOf="@+id/header_icon"
+            android:layout_alignBottom="@+id/header_icon"
+            android:layout_marginBottom="2dip"
+
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary"
+            android:singleLine="true"
         />
 
         <View
             android:layout_width="match_parent"
             android:layout_height="1px"
             android:layout_alignParentBottom="true"
+
             android:background="?android:attr/listDivider"
         />
 
-        <Button
-            android:id="@+id/button_add_field"
-            android:text="@string/add_field"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="right"
-            android:layout_marginTop="10dip"
-        />
-    </LinearLayout>
+    </RelativeLayout>
+
+    <FrameLayout
+        android:id="@+id/stub_photo"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="12dip"
+        android:paddingTop="10dip">
+
+        <include
+            android:id="@+id/edit_photo"
+            layout="@layout/item_photo_editor" />
+
+    </FrameLayout>
+
+    <include
+        android:id="@+id/edit_name"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/stub_photo"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="4dip"
+        layout="@layout/item_generic_editor" />
+
+    <LinearLayout
+        android:id="@+id/sect_fields"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+    />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1px"
+        android:layout_alignParentBottom="true"
+        android:background="?android:attr/listDivider"
+    />
+
+    <Button
+        android:id="@+id/button_add_field"
+        android:text="@string/add_field"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right"
+        android:layout_marginTop="10dip"
+    />
 
 </com.android.contacts.ui.widget.ContactEditorView>
diff --git a/res/layout/item_editor_field.xml b/res/layout/item_editor_field.xml
deleted file mode 100644
index 9ad8689..0000000
--- a/res/layout/item_editor_field.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<EditText
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" />
diff --git a/res/layout/item_generic_editor.xml b/res/layout/item_generic_editor.xml
index 329ad28..e62612f 100644
--- a/res/layout/item_generic_editor.xml
+++ b/res/layout/item_generic_editor.xml
@@ -35,7 +35,7 @@
         android:layout_alignParentRight="true"
         style="@style/MinusButton" />
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/edit_fields"
         android:layout_width="0dip"
         android:layout_height="wrap_content"
@@ -43,7 +43,6 @@
         android:layout_alignWithParentIfMissing="true"
         android:layout_toRightOf="@id/edit_label"
         android:layout_toLeftOf="@id/edit_delete"
-        android:orientation="vertical"
         android:baselineAligned="false"
         android:gravity="center_vertical" />
 
diff --git a/res/layout/status_bar_ongoing_event_progress_bar.xml b/res/layout/status_bar_ongoing_event_progress_bar.xml
index d1bce1a..5eb4e9d 100644
--- a/res/layout/status_bar_ongoing_event_progress_bar.xml
+++ b/res/layout/status_bar_ongoing_event_progress_bar.xml
@@ -31,7 +31,6 @@
         android:layout_height="match_parent"
         android:orientation="horizontal"
         >
-        
         <LinearLayout
             android:layout_width="40dp"
             android:layout_height="match_parent"
@@ -40,14 +39,14 @@
             android:focusable="true"
             android:clickable="true"
             >
-            <com.android.server.status.AnimatedImageView 
-                android:id="@+id/appIcon"
+            <com.android.contacts.vcard.AnimatedImageView
+                android:id="@+id/status_icon"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center"
                 android:src="@android:drawable/sym_def_app_icon"
                 />
-            <TextView android:id="@+id/progress_text" 
+            <TextView android:id="@+id/status_progress_text"
                 android:layout_width="wrap_content" 
                 android:layout_height="wrap_content"
                 android:textColor="#ff000000"
@@ -73,7 +72,7 @@
                 android:layout_alignParentTop="true"
                 android:paddingTop="10dp"
                 >
-                <TextView android:id="@+id/title"
+                <TextView android:id="@+id/status_title"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:singleLine="true"
@@ -81,7 +80,7 @@
                     android:textColor="#ff000000"
                     android:paddingLeft="2dp"
                     />
-                <TextView android:id="@+id/description"
+                <TextView android:id="@+id/status_description"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:textColor="#ff000000"
@@ -90,7 +89,7 @@
                     android:paddingLeft="5dp"
                     />
             </LinearLayout>
-            <ProgressBar android:id="@+id/progress_bar"
+            <ProgressBar android:id="@+id/status_progress_bar"
                 style="?android:attr/progressBarStyleHorizontal"
                 android:layout_width="match_parent" 
                 android:layout_height="wrap_content"
@@ -101,7 +100,7 @@
         </RelativeLayout>
     </LinearLayout>
         
-    <com.android.server.status.AnimatedImageView 
+    <com.android.contacts.vcard.AnimatedImageView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:src="@android:drawable/divider_horizontal_bright"
diff --git a/res/layout/two_pane_activity.xml b/res/layout/two_pane_activity.xml
index 407360b..dbac12f 100644
--- a/res/layout/two_pane_activity.xml
+++ b/res/layout/two_pane_activity.xml
@@ -35,7 +35,7 @@
                 android:layout_weight="1" />
 
         <!--  Holder for detail- or editor-fragment. -->
-        <LinearLayout
+        <FrameLayout
             android:id="@+id/two_pane_right_view"
             android:orientation="horizontal"
             android:layout_width="0px"
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
index 758a3b5..882a4fb 100644
--- a/src/com/android/contacts/DialtactsActivity.java
+++ b/src/com/android/contacts/DialtactsActivity.java
@@ -102,7 +102,7 @@
 
         setCurrentTab(intent);
 
-        if (intent.getAction().equals(UI.FILTER_CONTACTS_ACTION)
+        if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
                 && icicle == null) {
             setupFilterText(intent);
         }
@@ -275,7 +275,7 @@
         fixIntent(newIntent);
         setCurrentTab(newIntent);
         final String action = newIntent.getAction();
-        if (action.equals(UI.FILTER_CONTACTS_ACTION)) {
+        if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
             setupFilterText(newIntent);
         } else if (isDialIntent(newIntent)) {
             setupDialUri(newIntent);
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
index ccd1ce3..e3213a3 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -1070,8 +1070,8 @@
             // most recent entry.
             position = 0;
         }
-        final Cursor cursor = mAdapter.getCursor();
-        if (cursor != null && cursor.moveToPosition(position)) {
+        final Cursor cursor = (Cursor)mAdapter.getItem(position);
+        if (cursor != null) {
             String number = cursor.getString(NUMBER_COLUMN_INDEX);
             if (TextUtils.isEmpty(number)
                     || number.equals(CallerInfo.UNKNOWN_NUMBER)
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 2bac145..4369160 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -25,7 +25,6 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.MenuItem;
 import android.widget.Toast;
 
 public class ContactEditorActivity extends Activity {
@@ -59,14 +58,6 @@
     }
 
     @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        // TODO: This is too hardwired.
-        if (mFragment.onContextItemSelected(item)) return true;
-
-        return super.onContextItemSelected(item);
-    }
-
-    @Override
     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
             boolean globalSearch) {
         if (globalSearch) {
diff --git a/src/com/android/contacts/ui/widget/GenericEditorView.java b/src/com/android/contacts/ui/widget/GenericEditorView.java
index 5c2f9b7..4656fe3 100644
--- a/src/com/android/contacts/ui/widget/GenericEditorView.java
+++ b/src/com/android/contacts/ui/widget/GenericEditorView.java
@@ -26,7 +26,6 @@
 import com.android.contacts.model.ContactsSource.EditType;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.ui.ViewIdGenerator;
-import com.android.contacts.util.ViewGroupAnimator;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.DialogManager.DialogShowingView;
 
@@ -51,7 +50,9 @@
 import android.view.inputmethod.EditorInfo;
 import android.widget.ArrayAdapter;
 import android.widget.EditText;
+import android.widget.FrameLayout;
 import android.widget.ImageButton;
+import android.widget.LinearLayout;
 import android.widget.ListAdapter;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -65,7 +66,6 @@
  */
 public class GenericEditorView extends RelativeLayout implements Editor, View.OnClickListener,
         DialogShowingView {
-    protected static final int RES_FIELD = R.layout.item_editor_field;
     protected static final int RES_LABEL_ITEM = android.R.layout.simple_list_item_1;
 
     private static final String DIALOG_ID_KEY = "dialog_id";
@@ -78,7 +78,7 @@
             | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
 
     protected TextView mLabel;
-    protected ViewGroup mFields;
+    protected FrameLayout mFields;
     protected View mDelete;
     protected ImageButton mMoreOrLess;
 
@@ -86,6 +86,7 @@
     protected ValuesDelta mEntry;
     protected EntityDelta mState;
     protected boolean mReadOnly;
+    private EditText[] mFieldEditTexts = null;
 
     protected boolean mHideOptional = true;
 
@@ -113,7 +114,7 @@
         mLabel = (TextView)findViewById(R.id.edit_label);
         mLabel.setOnClickListener(this);
 
-        mFields = (ViewGroup)findViewById(R.id.edit_fields);
+        mFields = (FrameLayout)findViewById(R.id.edit_fields);
 
         mDelete = findViewById(R.id.edit_delete);
         mDelete.setOnClickListener(this);
@@ -135,10 +136,12 @@
     @Override
     public void setEnabled(boolean enabled) {
         mLabel.setEnabled(enabled);
-        final int count = mFields.getChildCount();
-        for (int pos = 0; pos < count; pos++) {
-            final View v = mFields.getChildAt(pos);
-            v.setEnabled(enabled);
+        if (mFields.getChildCount() == 0) return;
+
+        if (mFieldEditTexts != null) {
+            for (int index = 0; index < mFieldEditTexts.length; index++) {
+                mFieldEditTexts[index].setEnabled(enabled);
+            }
         }
         mMoreOrLess.setEnabled(enabled);
     }
@@ -177,11 +180,12 @@
     }
 
     public boolean isAnyFieldFilledOut() {
-        int childCount = mFields.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            EditText editorView = (EditText) mFields.getChildAt(i);
-            if (!TextUtils.isEmpty(editorView.getText())) {
-                return true;
+        if (mFieldEditTexts != null) {
+            for (int index = 0; index < mFieldEditTexts.length; index++) {
+                final EditText editText = mFieldEditTexts[index];
+                if (!TextUtils.isEmpty(editText.getText())) {
+                    return true;
+                }
             }
         }
         return false;
@@ -226,11 +230,28 @@
         // Build out set of fields
         mFields.removeAllViews();
         boolean hidePossible = false;
-        int n = 0;
-        for (EditField field : kind.fieldList) {
-            // Inflate field from definition
-            EditText fieldView = (EditText)mInflater.inflate(RES_FIELD, mFields, false);
-            fieldView.setId(vig.getId(state, kind, entry, n++));
+
+        // If there is one field, put it directly into the FrameLayout mFields. If there are
+        // several or 0, put them into a LinearLayout
+        final ViewGroup container;
+        if (kind.fieldList.size() == 1) {
+            container = mFields;
+        } else {
+            final LinearLayout linearLayout = new LinearLayout(mContext);
+            linearLayout.setOrientation(LinearLayout.VERTICAL);
+            linearLayout.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT));
+            mFields.addView(linearLayout);
+            container = linearLayout;
+        }
+        mFieldEditTexts = new EditText[kind.fieldList.size()];
+        for (int index = 0; index < kind.fieldList.size(); index++) {
+            final EditField field = kind.fieldList.get(index);
+            final EditText fieldView = new EditText(mContext);
+            fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT));
+            mFieldEditTexts[index] = fieldView;
+            fieldView.setId(vig.getId(state, kind, entry, index));
             if (field.titleRes > 0) {
                 fieldView.setHint(field.titleRes);
             }
@@ -267,7 +288,7 @@
             fieldView.setEnabled(enabled);
             hidePossible = hidePossible || couldHide;
 
-            mFields.addView(fieldView);
+            container.addView(fieldView);
         }
 
         // When hiding fields, place expandable
diff --git a/src/com/android/contacts/vcard/AnimatedImageView.java b/src/com/android/contacts/vcard/AnimatedImageView.java
new file mode 100644
index 0000000..8911b52
--- /dev/null
+++ b/src/com/android/contacts/vcard/AnimatedImageView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.vcard;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews.RemoteView;
+
+/**
+ * Just a bare copy from SystemUI. Do not edit. 
+ */
+public class AnimatedImageView extends ImageView {
+    AnimationDrawable mAnim;
+    boolean mAttached;
+
+    public AnimatedImageView(Context context) {
+        super(context);
+    }
+
+    public AnimatedImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void updateAnim() {
+        Drawable drawable = getDrawable();
+        if (mAttached && mAnim != null) {
+            mAnim.stop();
+        }
+        if (drawable instanceof AnimationDrawable) {
+            mAnim = (AnimationDrawable)drawable;
+            if (mAttached) {
+                mAnim.start();
+            }
+        } else {
+            mAnim = null;
+        }
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+        updateAnim();
+    }
+
+    @Override
+    @android.view.RemotableViewMethod
+    public void setImageResource(int resid) {
+        super.setImageResource(resid);
+        updateAnim();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mAnim != null) {
+            mAnim.start();
+        }
+        mAttached = true;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAnim != null) {
+            mAnim.stop();
+        }
+        mAttached = false;
+    }
+}
+
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index 7e7c7c8..fd3498a 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -224,13 +224,13 @@
 
         final RemoteViews remoteViews = new RemoteViews(mService.getPackageName(),
                 R.layout.status_bar_ongoing_event_progress_bar);
-        remoteViews.setTextViewText(R.id.description, message);
-        remoteViews.setProgressBar(R.id.progress_bar, total, current, (total == -1));
+        remoteViews.setTextViewText(R.id.status_description, message);
+        remoteViews.setProgressBar(R.id.status_progress_bar, total, current, (total == -1));
 
         final String percentage = mService.getString(R.string.percentage,
                 String.valueOf((current * 100)/total));
-        remoteViews.setTextViewText(R.id.progress_text, percentage);
-        remoteViews.setImageViewResource(R.id.appIcon, android.R.drawable.stat_sys_upload);
+        remoteViews.setTextViewText(R.id.status_progress_text, percentage);
+        remoteViews.setImageViewResource(R.id.status_icon, android.R.drawable.stat_sys_upload);
 
         final Notification notification = new Notification();
         notification.icon = android.R.drawable.stat_sys_upload;
diff --git a/src/com/android/contacts/vcard/ExportVCardActivity.java b/src/com/android/contacts/vcard/ExportVCardActivity.java
index 43ae858..fbe6d48 100644
--- a/src/com/android/contacts/vcard/ExportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ExportVCardActivity.java
@@ -18,9 +18,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.ProgressDialog;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -28,23 +26,17 @@
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.contacts.R;
 import com.android.vcard.VCardComposer;
-import com.android.vcard.VCardConfig;
 
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Queue;
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 6f765f9..427d62d 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -101,7 +101,9 @@
             return new VCardEntryCommitter(mResolver);
         }
     }
-    /* package */ CommitterGenerator mCommitterGenerator = new DefaultCommitterGenerator();
+
+    private CommitterGenerator mCommitterGenerator =
+        new DefaultCommitterGenerator();
 
     public ImportProcessor(final Context context) {
         mContext = context;
@@ -124,7 +126,7 @@
         }
     }
 
-    public synchronized void pushRequest(ImportRequest parameter) {
+    public synchronized void pushRequest(final ImportRequest request) {
         ensureInit();
 
         final boolean needThreadStart;
@@ -137,11 +139,11 @@
         } else {
             needThreadStart = false;
         }
-        final int count = parameter.entryCount;
+        final int count = request.entryCount;
         if (count > 0) {
             mNotifier.addTotalCount(count);
         }
-        mPendingRequests.add(parameter);
+        mPendingRequests.add(request);
         if (needThreadStart) {
             mThreadStarter.start();
         }
@@ -188,14 +190,14 @@
     /**
      * Would be run inside synchronized block.
      */
-    /* package */ boolean handleOneRequest(final ImportRequest parameter) {
+    /* package */ boolean handleOneRequest(final ImportRequest request) {
         if (mCanceled) {
             Log.i(LOG_TAG, "Canceled before actually handling parameter ("
-                    + parameter.uri + ")");
+                    + request.uri + ")");
             return false;
         }
         final int[] possibleVCardVersions;
-        if (parameter.vcardVersion == ImportVCardActivity.VCARD_VERSION_AUTO_DETECT) {
+        if (request.vcardVersion == ImportVCardActivity.VCARD_VERSION_AUTO_DETECT) {
             /**
              * Note: this code assumes that a given Uri is able to be opened more than once,
              * which may not be true in certain conditions.
@@ -206,14 +208,14 @@
             };
         } else {
             possibleVCardVersions = new int[] {
-                    parameter.vcardVersion
+                    request.vcardVersion
             };
         }
 
-        final Uri uri = parameter.uri;
-        final Account account = parameter.account;
-        final int estimatedVCardType = parameter.estimatedVCardType;
-        final String estimatedCharset = parameter.estimatedCharset;
+        final Uri uri = request.uri;
+        final Account account = request.account;
+        final int estimatedVCardType = request.estimatedVCardType;
+        final String estimatedCharset = request.estimatedCharset;
 
         final VCardEntryConstructor constructor =
                 new VCardEntryConstructor(estimatedVCardType, account, estimatedCharset);
@@ -360,11 +362,20 @@
         }
     }
 
-    public List<Uri> getCreatedUris() {
+    public List<Uri> getCreatedUrisForTest() {
         return mCreatedUris;
     }
 
-    public List<Uri> getFailedUris() {
+    public List<Uri> getFailedUrisForTest() {
         return mFailedUris;
     }
-}
\ No newline at end of file
+
+    public void injectCommitterGeneratorForTest(
+            final CommitterGenerator generator) {
+        mCommitterGenerator = generator;
+    }
+
+    public void initNotifierForTest() {
+        mNotifier.init(mContext, mNotificationManager);
+    }
+}
diff --git a/src/com/android/contacts/vcard/ImportProgressNotifier.java b/src/com/android/contacts/vcard/ImportProgressNotifier.java
index e6f1037..afd6c3a 100644
--- a/src/com/android/contacts/vcard/ImportProgressNotifier.java
+++ b/src/com/android/contacts/vcard/ImportProgressNotifier.java
@@ -20,7 +20,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.widget.RemoteViews;
 
 import com.android.contacts.ContactsListActivity;
 import com.android.contacts.R;
@@ -57,10 +56,6 @@
         // - There's high probability where name comes soon after the beginning of entry, so
         //   we don't need to hurry to show something.
 
-        // TODO: should not create this every time?
-        final RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(),
-                R.layout.status_bar_ongoing_event_progress_bar);
-
         final String title = mContext.getString(R.string.reading_vcard_title);
 
         String totalCountString;
@@ -72,25 +67,47 @@
                 totalCountString,
                 contactStruct.getDisplayName());
 
-        remoteViews.setTextViewText(R.id.title, title);
-        remoteViews.setTextViewText(R.id.description, description);
-        remoteViews.setProgressBar(R.id.progress_bar, mTotalCount, mCurrentCount,
-                mTotalCount == -1);
-        final String percentage =
-                mContext.getString(R.string.percentage,
-                        String.valueOf(mCurrentCount * 100/mTotalCount));
+        final Context context = mContext.getApplicationContext();
 
-        remoteViews.setTextViewText(R.id.progress_text, percentage);
-        remoteViews.setImageViewResource(R.id.appIcon, android.R.drawable.stat_sys_download);
+        /* TODO: fix this.
+        final RemoteViews remoteViews =
+                new RemoteViews(context.getPackageName(),
+                R.layout.status_bar_ongoing_event_progress_bar);
+        remoteViews.setTextViewText(R.id.status_title, title);
+        remoteViews.setTextViewText(R.id.status_description, description);
+        remoteViews.setProgressBar(R.id.status_progress_bar, mTotalCount, mCurrentCount,
+                mTotalCount == -1);
+        final String percentage;
+        if (mTotalCount > 0) {
+            percentage = context.getString(R.string.percentage,
+                    String.valueOf(mCurrentCount * 100/mTotalCount));
+        } else {
+            percentage = "";
+        }
+        remoteViews.setTextViewText(R.id.status_progress_text, percentage);
+        remoteViews.setImageViewResource(R.id.status_icon, android.R.drawable.stat_sys_download);
 
         final Notification notification = new Notification();
         notification.icon = android.R.drawable.stat_sys_download;
+        notification.tickerText = description;
         notification.flags |= Notification.FLAG_ONGOING_EVENT;
         notification.tickerText = description;
-        notification.contentView = remoteViews;
-        notification.contentIntent =
-                PendingIntent.getActivity(mContext, 0,
-                        new Intent(mContext, ContactsListActivity.class), 0);
+        notification.contentView = remoteViews;*/
+
+        final long when = System.currentTimeMillis();
+        final Notification notification = new Notification(
+                android.R.drawable.stat_sys_download,
+                description,
+                when);
+
+        final PendingIntent pendingIntent =
+            PendingIntent.getActivity(context, 0,
+                    new Intent(context, ContactsListActivity.class),
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // notification.contentIntent = pendingIntent;
+        notification.setLatestEventInfo(context, title, description, pendingIntent);
+
         mNotificationManager.notify(VCardService.IMPORT_NOTIFICATION_ID, notification);
     }
 
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index a35285f..cb6eba6 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -796,10 +796,12 @@
         }
     }
 
+    @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
         inflater.inflate(R.menu.view, menu);
     }
 
+    @Override
     public void onPrepareOptionsMenu(Menu menu) {
         // Only allow edit when we have at least one raw_contact id
         final boolean hasRawContact = (mRawContactIds.size() > 0);
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 5b281fc..d6baa9c 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -77,7 +77,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.View.OnClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
@@ -164,16 +163,7 @@
 
         mContent = (LinearLayout) view.findViewById(R.id.editors);
 
-        view.findViewById(R.id.btn_done).setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                doSaveAction(SAVE_MODE_DEFAULT);
-            }
-        });
-        view.findViewById(R.id.btn_discard).setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                doRevertAction();
-            }
-        });
+        setHasOptionsMenu(true);
 
         return view;
     }
diff --git a/tests/assets/v30_simple.vcf b/tests/assets/v30_simple.vcf
new file mode 100644
index 0000000..418661f
--- /dev/null
+++ b/tests/assets/v30_simple.vcf
@@ -0,0 +1,13 @@
+BEGIN:VCARD

+VERSION:3.0

+FN:And Roid

+N:And;Roid;;;

+ORG:Open;Handset; Alliance

+SORT-STRING:android

+TEL;TYPE=PREF;TYPE=VOICE:0300000000

+CLASS:PUBLIC

+X-GNO:0

+X-GN:group0

+X-REDUCTION:0

+REV:20081031T065854Z

+END:VCARD

diff --git a/tests/src/com/android/contacts/vcard/ImportProcessorTest.java b/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
index 543dce9..72245ba 100644
--- a/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
+++ b/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
@@ -22,6 +22,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.contacts.vcard.ImportProcessor.CommitterGenerator;
+import com.android.vcard.VCardEntryCommitter;
 import com.android.vcard.VCardInterpreter;
 import com.android.vcard.VCardSourceDetector;
 
@@ -100,7 +102,7 @@
     }
 
     /**
-     * Confirm {@link ImportProcessor#readOneVCard(android.net.Uri, int, String,
+     * Confirms {@link ImportProcessor#readOneVCard(android.net.Uri, int, String,
      * com.android.vcard.VCardInterpreter, int[])} successfully handles correct input.
      */
     public void testProcessSimple() throws IOException {
@@ -115,39 +117,64 @@
         assertTrue(mImportProcessor.readOneVCard(
                 uri, vcardType, charset, interpreter, versions));
     }
+
+    /**
+     * Confirms {@link ImportProcessor#handleOneRequest(ImportRequest)} accepts
+     * one request and import it.
+     */
+    public void testHandleOneRequestSimple() throws IOException {
+        CommitterGenerator generator = new CommitterGenerator() {
+            public VCardEntryCommitter generate(ContentResolver resolver) {
+                return new MockVCardEntryCommitter();
+            }
+        };
+        mImportProcessor.injectCommitterGeneratorForTest(generator);
+        mImportProcessor.initNotifierForTest();
+
+        final ImportRequest request = new ImportRequest(
+                null,  // account
+                copyToLocal("v30_simple.vcf"),
+                VCardSourceDetector.PARSE_TYPE_UNKNOWN,
+                null,  // estimatedCharset
+                ImportVCardActivity.VCARD_VERSION_AUTO_DETECT,
+                1);
+        assertTrue(mImportProcessor.handleOneRequest(request));
+        assertEquals(1, mImportProcessor.getCreatedUrisForTest().size());
+    }
 }
 
 /* package */ class EmptyVCardInterpreter implements VCardInterpreter {
+    @Override
     public void end() {
     }
-
+    @Override
     public void endEntry() {
     }
-
+    @Override
     public void endProperty() {
     }
-
+    @Override
     public void propertyGroup(String group) {
     }
-
+    @Override
     public void propertyName(String name) {
     }
-
+    @Override
     public void propertyParamType(String type) {
     }
-
+    @Override
     public void propertyParamValue(String value) {
     }
-
+    @Override
     public void propertyValues(List<String> values) {
     }
-
+    @Override
     public void start() {
     }
-
+    @Override
     public void startEntry() {
     }
-
+    @Override
     public void startProperty() {
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java b/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java
new file mode 100644
index 0000000..4765b38
--- /dev/null
+++ b/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.vcard;
+
+import android.net.Uri;
+
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryCommitter;
+
+import java.util.ArrayList;
+
+public class MockVCardEntryCommitter extends VCardEntryCommitter {
+
+    private final ArrayList<Uri> mUris = new ArrayList<Uri>(); 
+
+    public MockVCardEntryCommitter() {
+        super(null);
+    }
+
+    /**
+     * Exists for forcing super class to do nothing.
+     */
+    @Override
+    public void onStart() {
+    }
+
+    /**
+     * Exists for forcing super class to do nothing.
+     */
+    @Override
+    public void onEnd() {
+    }
+
+    @Override
+    public void onEntryCreated(final VCardEntry vcardEntry) {
+        mUris.add(null);
+    }
+
+    @Override
+    public ArrayList<Uri> getCreatedUris() {
+        return mUris;
+    }
+}
\ No newline at end of file