Merge ""ShowOrCreate" dialog -- handle back press"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 02c378a..b39d3f4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -307,16 +307,6 @@
             </intent-filter>
         </activity>
 
-        <!-- An activity for selecting multiple phone numbers -->
-        <activity android:name="MultiplePhonePickerActivity"
-            android:enabled="false">
-            <intent-filter>
-                <action android:name="com.android.contacts.action.GET_MULTIPLE_PHONES" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.dir/phone_v2" android:host="com.android.contacts" />
-            </intent-filter>
-        </activity>
-
         <!-- Used to set options -->
         <activity
             android:name=".preference.ContactsPreferenceActivity"
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_1.9.png b/res/drawable-hdpi/appointment_indicator_leftside_1.9.png
deleted file mode 100644
index b72652b..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_1.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_10.9.png b/res/drawable-hdpi/appointment_indicator_leftside_10.9.png
deleted file mode 100644
index ff09049..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_10.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_11.9.png b/res/drawable-hdpi/appointment_indicator_leftside_11.9.png
deleted file mode 100644
index 6a2e4f2..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_11.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_12.9.png b/res/drawable-hdpi/appointment_indicator_leftside_12.9.png
deleted file mode 100644
index 0f19c83..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_12.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_13.9.png b/res/drawable-hdpi/appointment_indicator_leftside_13.9.png
deleted file mode 100644
index 7501e35..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_13.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_14.9.png b/res/drawable-hdpi/appointment_indicator_leftside_14.9.png
deleted file mode 100644
index 53f97a6..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_14.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_15.9.png b/res/drawable-hdpi/appointment_indicator_leftside_15.9.png
deleted file mode 100644
index 846f6f8..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_15.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_16.9.png b/res/drawable-hdpi/appointment_indicator_leftside_16.9.png
deleted file mode 100644
index 1707540..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_16.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_17.9.png b/res/drawable-hdpi/appointment_indicator_leftside_17.9.png
deleted file mode 100644
index 7fd945d..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_17.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_18.9.png b/res/drawable-hdpi/appointment_indicator_leftside_18.9.png
deleted file mode 100644
index 8cf47ae..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_18.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_19.9.png b/res/drawable-hdpi/appointment_indicator_leftside_19.9.png
deleted file mode 100644
index 6831c01..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_19.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_2.9.png b/res/drawable-hdpi/appointment_indicator_leftside_2.9.png
deleted file mode 100644
index b4cee11..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_2.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_20.9.png b/res/drawable-hdpi/appointment_indicator_leftside_20.9.png
deleted file mode 100644
index d07d826..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_20.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_21.9.png b/res/drawable-hdpi/appointment_indicator_leftside_21.9.png
deleted file mode 100644
index f410269..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_21.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_3.9.png b/res/drawable-hdpi/appointment_indicator_leftside_3.9.png
deleted file mode 100644
index 69bd6a9..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_3.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_4.9.png b/res/drawable-hdpi/appointment_indicator_leftside_4.9.png
deleted file mode 100644
index d09ea90..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_4.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_5.9.png b/res/drawable-hdpi/appointment_indicator_leftside_5.9.png
deleted file mode 100644
index d27fc91..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_5.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_6.9.png b/res/drawable-hdpi/appointment_indicator_leftside_6.9.png
deleted file mode 100644
index c014633..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_6.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_7.9.png b/res/drawable-hdpi/appointment_indicator_leftside_7.9.png
deleted file mode 100644
index febb514..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_7.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_8.9.png b/res/drawable-hdpi/appointment_indicator_leftside_8.9.png
deleted file mode 100644
index 1415e44..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_8.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appointment_indicator_leftside_9.9.png b/res/drawable-hdpi/appointment_indicator_leftside_9.9.png
deleted file mode 100644
index d018fcf..0000000
--- a/res/drawable-hdpi/appointment_indicator_leftside_9.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_1.9.png b/res/drawable-mdpi/appointment_indicator_leftside_1.9.png
deleted file mode 100644
index 5e40235..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_1.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_10.9.png b/res/drawable-mdpi/appointment_indicator_leftside_10.9.png
deleted file mode 100644
index d0cb144..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_10.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_11.9.png b/res/drawable-mdpi/appointment_indicator_leftside_11.9.png
deleted file mode 100644
index 034f496..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_11.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_12.9.png b/res/drawable-mdpi/appointment_indicator_leftside_12.9.png
deleted file mode 100644
index 6371b3a..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_12.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_13.9.png b/res/drawable-mdpi/appointment_indicator_leftside_13.9.png
deleted file mode 100644
index a8b42c6..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_13.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_14.9.png b/res/drawable-mdpi/appointment_indicator_leftside_14.9.png
deleted file mode 100644
index a69e519..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_14.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_15.9.png b/res/drawable-mdpi/appointment_indicator_leftside_15.9.png
deleted file mode 100644
index 5d68470..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_15.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_16.9.png b/res/drawable-mdpi/appointment_indicator_leftside_16.9.png
deleted file mode 100644
index d9420c1..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_16.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_17.9.png b/res/drawable-mdpi/appointment_indicator_leftside_17.9.png
deleted file mode 100644
index d0875c4..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_17.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_18.9.png b/res/drawable-mdpi/appointment_indicator_leftside_18.9.png
deleted file mode 100644
index fc152f7..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_18.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_19.9.png b/res/drawable-mdpi/appointment_indicator_leftside_19.9.png
deleted file mode 100644
index 6506a94..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_19.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_2.9.png b/res/drawable-mdpi/appointment_indicator_leftside_2.9.png
deleted file mode 100644
index 3baf5cc..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_2.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_20.9.png b/res/drawable-mdpi/appointment_indicator_leftside_20.9.png
deleted file mode 100644
index 28340ba..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_20.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_21.9.png b/res/drawable-mdpi/appointment_indicator_leftside_21.9.png
deleted file mode 100644
index 5319f07..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_21.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_3.9.png b/res/drawable-mdpi/appointment_indicator_leftside_3.9.png
deleted file mode 100644
index 9850791..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_3.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_4.9.png b/res/drawable-mdpi/appointment_indicator_leftside_4.9.png
deleted file mode 100644
index e344ccb..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_4.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_5.9.png b/res/drawable-mdpi/appointment_indicator_leftside_5.9.png
deleted file mode 100644
index 11b4dfb..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_5.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_6.9.png b/res/drawable-mdpi/appointment_indicator_leftside_6.9.png
deleted file mode 100644
index 7419d47..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_6.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_7.9.png b/res/drawable-mdpi/appointment_indicator_leftside_7.9.png
deleted file mode 100644
index 0a3a272..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_7.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_8.9.png b/res/drawable-mdpi/appointment_indicator_leftside_8.9.png
deleted file mode 100644
index db18d27..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_8.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appointment_indicator_leftside_9.9.png b/res/drawable-mdpi/appointment_indicator_leftside_9.9.png
deleted file mode 100644
index 5037de8..0000000
--- a/res/drawable-mdpi/appointment_indicator_leftside_9.9.png
+++ /dev/null
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 07ea96c..7437427 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -806,11 +806,17 @@
          [CHAR LIMIT=40] -->
     <string name="importing_vcard_canceled_title">Reading vCard data was canceled</string>
 
-    <!-- The message shown when vCard importer started running. -->
-    <string name="vcard_importer_start_message">vCard importer started.</string>
+    <!-- The message shown when vCard import request is accepted. The system may start that work soon, or do it later
+         when there are already other import/export requests. [CHAR LIMIT=30] -->
+    <string name="vcard_import_will_start_message">vCard importer will start shortly.</string>
+    <!-- The message shown when a given vCard import request is rejected by the system. [CHAR LIMIT=NONE] -->
+    <string name="vcard_import_request_rejected_message">vCard import request is rejected. Please try later.</string>
+    <!-- The message shown when vCard export request is accepted. The system may start that work soon, or do it later
+         when there are already other import/export requests. [CHAR LIMIT=30] -->
+    <string name="vcard_export_will_start_message">vCard exporter will start shortly.</string>
+    <!-- The message shown when a given vCard export request is rejected by the system. [CHAR LIMIT=NONE] -->
+    <string name="vcard_export_request_rejected_message">vCard export request is rejected. Please try later.</string>
 
-    <!-- The message shown when additional vCard to be imported is given during the import for others -->
-    <string name="vcard_importer_will_start_message">vCard importer will import the vCard after a while.</string>
 
     <!-- The percentage, used for expressing the progress of vCard import/export. -->
     <string name="percentage">%s%%</string>
@@ -844,9 +850,6 @@
          mention it here. -->
     <string name="fail_reason_too_long_filename">Required filename is too long (\"<xliff:g id="filename">%s</xliff:g>\")</string>
 
-    <!-- The message shown when vCard importer started running. -->
-    <string name="vcard_exporter_start_message">vCard exporter started.</string>
-
     <!-- The title shown when reading vCard is canceled (probably by a user) -->
     <string name="exporting_vcard_finished_title">Finished exporting vCard</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2c1d250..914ebcc 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -110,12 +110,6 @@
         <attr name="list_item_header_text_size" format="dimension" />
     </declare-styleable>
 
-    <declare-styleable name="MultiplePhonePickerItemView">
-        <attr name="list_item_header_chip_width" format="dimension"/>
-        <attr name="list_item_header_chip_right_margin" format="dimension"/>
-        <attr name="list_item_header_checkbox_margin" format="dimension"/>
-    </declare-styleable>
-
     <style name="ContactBrowserTheme" parent="@android:Theme">
         <item name="pressedBackground">@*android:drawable/list_selector_background</item>
         <item name="section_header_background">@drawable/section_header</item>
@@ -151,9 +145,6 @@
         <item name="list_item_presence_icon_margin">5dip</item>
         <item name="list_item_photo_size">56dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
-        <item name="list_item_header_chip_width">4dip</item>
-        <item name="list_item_header_chip_right_margin">4dip</item>
-        <item name="list_item_header_checkbox_margin">5dip</item>
         <item name="list_item_header_text_indent">56dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
         <item name="list_item_header_text_size">14sp</item>
diff --git a/src/com/android/contacts/MultiplePhonePickerActivity.java b/src/com/android/contacts/MultiplePhonePickerActivity.java
deleted file mode 100644
index 572b4de..0000000
--- a/src/com/android/contacts/MultiplePhonePickerActivity.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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;
-
-import com.android.contacts.list.MultiplePhonePickerFragment;
-import com.android.contacts.list.OnMultiplePhoneNumberPickerActionListener;
-
-import android.app.Activity;
-import android.app.FragmentTransaction;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.provider.ContactsContract.Intents;
-import android.view.Menu;
-import android.view.MenuInflater;
-
-/**
- * Displays of phone numbers and allows selection of multiple numbers.
- */
-public class MultiplePhonePickerActivity extends Activity {
-
-    /**
-     * Display only selected recipients or not in MODE_PICK_MULTIPLE_PHONES mode
-     */
-    private boolean mShowSelectedOnly = false;
-
-    private MultiplePhonePickerFragment mListFragment;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mListFragment = new MultiplePhonePickerFragment();
-        mListFragment.setOnMultiplePhoneNumberPickerActionListener(
-                new OnMultiplePhoneNumberPickerActionListener() {
-
-            public void onPhoneNumbersSelectedAction(Uri[] dataUris) {
-                returnActivityResult(dataUris);
-            }
-
-            public void onFinishAction() {
-                finish();
-            }
-        });
-
-        Parcelable[] extras = getIntent().getParcelableArrayExtra(Intents.EXTRA_PHONE_URIS);
-        mListFragment.setSelectedUris(extras);
-        FragmentTransaction transaction = getFragmentManager().openTransaction();
-        transaction.add(android.R.id.content, mListFragment);
-        transaction.commit();
-    }
-
-    @Override
-    public void onBackPressed() {
-        returnActivityResult(mListFragment.getSelectedUris());
-        super.onBackPressed();
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle icicle) {
-        super.onSaveInstanceState(icicle);
-        mListFragment.onSaveInstanceState(icicle);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
-
-        final MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.pick, menu);
-        return true;
-    }
-
-    /*
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        if (mShowSelectedOnly) {
-            menu.findItem(R.id.menu_display_selected).setVisible(false);
-            menu.findItem(R.id.menu_display_all).setVisible(true);
-            menu.findItem(R.id.menu_select_all).setVisible(false);
-            menu.findItem(R.id.menu_select_none).setVisible(false);
-            return true;
-        }
-        menu.findItem(R.id.menu_display_all).setVisible(false);
-        menu.findItem(R.id.menu_display_selected).setVisible(true);
-        if (mUserSelection.isAllSelected()) {
-            menu.findItem(R.id.menu_select_all).setVisible(false);
-            menu.findItem(R.id.menu_select_none).setVisible(true);
-        } else {
-            menu.findItem(R.id.menu_select_all).setVisible(true);
-            menu.findItem(R.id.menu_select_none).setVisible(false);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.menu_select_all: {
-                mUserSelection.setAllPhonesSelected(true);
-                checkAll(true);
-                updateWidgets(true);
-                return true;
-            }
-            case R.id.menu_select_none: {
-                mUserSelection.setAllPhonesSelected(false);
-                checkAll(false);
-                updateWidgets(true);
-                return true;
-            }
-            case R.id.menu_display_selected: {
-                mShowSelectedOnly = true;
-                startQuery();
-                return true;
-            }
-            case R.id.menu_display_all: {
-                mShowSelectedOnly = false;
-                startQuery();
-                return true;
-            }
-        }
-        return super.onOptionsItemSelected(item);
-    }
-*/
-    @Override
-    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
-            boolean globalSearch) {
-        // TODO
-//        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
-//            return;
-//        }
-//
-//        if (globalSearch) {
-//            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
-//        } else {
-//            if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
-//                if ((mMode & MODE_MASK_PICKER) != 0) {
-//                    Bundle extras = getIntent().getExtras();
-//                    if (extras == null) {
-//                        extras = new Bundle();
-//                    }
-//                    mUserSelection.fillSelectionForSearchMode(extras);
-//                    ContactsSearchManager.startSearchForResult(this, initialQuery,
-//                            SUBACTIVITY_FILTER, extras);
-//                } else {
-//                    ContactsSearchManager.startSearch(this, initialQuery);
-//                }
-//            }
-//        }
-    }
-
-//    @Override
-//    protected void startQuery(Uri uri, String[] projection) {
-//        // Filter unknown phone numbers first.
-//        mPhoneNumberAdapter.doFilter(null, mShowSelectedOnly);
-//        if (mShowSelectedOnly) {
-//            StringBuilder idSetBuilder = new StringBuilder();
-//            Iterator<Long> itr = mUserSelection.getSelectedPhonIds();
-//            if (itr.hasNext()) {
-//                idSetBuilder.append(Long.toString(itr.next()));
-//            }
-//            while (itr.hasNext()) {
-//                idSetBuilder.append(',');
-//                idSetBuilder.append(Long.toString(itr.next()));
-//            }
-//            String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
-//            mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, whereClause, null,
-//                    getSortOrder(projection));
-//        } else {
-//            mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-//                    projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
-//        }
-//    }
-
-//    @Override
-//    public Cursor doFilter(String filter) {
-//        String[] projection = getProjectionForQuery();
-//        if (mSearchMode && TextUtils.isEmpty(mListFragment.getQueryString())) {
-//            return new MatrixCursor(projection);
-//        }
-//
-//        final ContentResolver resolver = getContentResolver();
-//        // Filter phone numbers as well.
-//        mPhoneNumberAdapter.doFilter(filter, mShowSelectedOnly);
-//
-//        Uri uri = getUriToQuery();
-//        if (!TextUtils.isEmpty(filter)) {
-//            uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
-//        }
-//        return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
-//    }
-
-    public void returnActivityResult(Uri[] dataUris) {
-        Intent intent = new Intent();
-        intent.putExtra(Intents.EXTRA_PHONE_URIS, dataUris);
-        setResult(RESULT_OK, intent);
-        finish();
-    }
-
-    private void checkAll(boolean checked) {
-        // TODO fix this. It should iterate over the cursor rather than the views in the list.
-        /*
-        final ListView listView = getListView();
-        int childCount = listView.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final ContactListItemView child = (ContactListItemView)listView.getChildAt(i);
-            child.getCheckBoxView().setChecked(checked);
-        }
-        */
-    }
-}
diff --git a/src/com/android/contacts/list/MultiplePhonePickerAdapter.java b/src/com/android/contacts/list/MultiplePhonePickerAdapter.java
deleted file mode 100644
index 1eba085..0000000
--- a/src/com/android/contacts/list/MultiplePhonePickerAdapter.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * 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.list;
-
-import com.android.contacts.R;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.text.TextUtils;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-
-/**
- * List adapter for the multiple phone picker.
- */
-public class MultiplePhonePickerAdapter extends PhoneNumberListAdapter {
-
-    public interface OnSelectionChangeListener {
-        void onSelectionChange();
-    }
-
-    private static final int[] CHIP_COLOR_ARRAY = {
-        R.drawable.appointment_indicator_leftside_1,
-        R.drawable.appointment_indicator_leftside_2,
-        R.drawable.appointment_indicator_leftside_3,
-        R.drawable.appointment_indicator_leftside_4,
-        R.drawable.appointment_indicator_leftside_5,
-        R.drawable.appointment_indicator_leftside_6,
-        R.drawable.appointment_indicator_leftside_7,
-        R.drawable.appointment_indicator_leftside_8,
-        R.drawable.appointment_indicator_leftside_9,
-        R.drawable.appointment_indicator_leftside_10,
-        R.drawable.appointment_indicator_leftside_11,
-        R.drawable.appointment_indicator_leftside_12,
-        R.drawable.appointment_indicator_leftside_13,
-        R.drawable.appointment_indicator_leftside_14,
-        R.drawable.appointment_indicator_leftside_15,
-        R.drawable.appointment_indicator_leftside_16,
-        R.drawable.appointment_indicator_leftside_17,
-        R.drawable.appointment_indicator_leftside_18,
-        R.drawable.appointment_indicator_leftside_19,
-        R.drawable.appointment_indicator_leftside_20,
-        R.drawable.appointment_indicator_leftside_21,
-    };
-
-    public static final long INVALID_PHONE_ID = -1;
-
-    /** The phone numbers */
-    private ArrayList<String> mPhoneNumbers = new ArrayList<String>();
-
-    /** The selected phone numbers in the PhoneNumberAdapter */
-    private HashSet<String> mSelectedPhoneNumbers = new HashSet<String>();
-
-    /** The phone numbers after the filtering */
-    private ArrayList<String> mFilteredPhoneNumbers = new ArrayList<String>();
-
-    /** The PHONE_ID of selected number in user contacts*/
-    private HashSet<Long> mSelectedPhoneIds = new HashSet<Long>();
-
-    private boolean mSelectionChanged;
-
-    private OnSelectionChangeListener mSelectionChangeListener;
-
-    /**
-     * This is a map from contact ID to color index. A colored chip is used to
-     * indicate the number of phone numbers belong to one contact
-     */
-    private SparseIntArray mContactColor = new SparseIntArray();
-
-    public MultiplePhonePickerAdapter(Context context) {
-        super(context);
-    }
-
-    public void setOnSelectionChangeListener(OnSelectionChangeListener listener) {
-        this.mSelectionChangeListener = listener;
-    }
-
-    public void setPhoneNumbers(ArrayList<String> phoneNumbers) {
-        mPhoneNumbers.clear();
-        mPhoneNumbers.addAll(phoneNumbers);
-    }
-
-    public int getSelectedCount() {
-        return mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size();
-    }
-
-    public Uri[] getSelectedUris() {
-        Uri[] uris = new Uri[mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size()];
-        int count = mPhoneNumbers.size();
-        int index = 0;
-        for (int i = 0; i < count; i++) {
-            String phoneNumber = mPhoneNumbers.get(i);
-            if (isSelected(phoneNumber)) {
-                uris[index++] = Uri.parse("tel:" + phoneNumber);
-            }
-        }
-        for (Long contactId : mSelectedPhoneIds) {
-            uris[index++] = ContentUris.withAppendedId(Phone.CONTENT_URI, contactId);
-        }
-        return uris;
-    }
-
-    public void setSelectedUris(Uri[] uris) {
-        mSelectedPhoneNumbers.clear();
-        mSelectedPhoneIds.clear();
-        if (uris != null) {
-            for (Uri uri : uris) {
-                String scheme = uri.getScheme();
-                if ("tel".equals(scheme)) {
-                    String phoneNumber = uri.getSchemeSpecificPart();
-                    if (!mPhoneNumbers.contains(phoneNumber)) {
-                        mPhoneNumbers.add(phoneNumber);
-                    }
-                    mSelectedPhoneNumbers.add(phoneNumber);
-            } else if ("content".equals(scheme)) {
-                mSelectedPhoneIds.add(ContentUris.parseId(uri));
-            }
-            }
-        }
-        mFilteredPhoneNumbers.clear();
-        mFilteredPhoneNumbers.addAll(mPhoneNumbers);
-    }
-
-    public void toggleSelection(int position) {
-        if (position < mFilteredPhoneNumbers.size()) {
-            String phoneNumber = mPhoneNumbers.get(position);
-            setPhoneSelected(phoneNumber, !isSelected(phoneNumber));
-        } else {
-            Cursor cursor = ((Cursor)getItem(position));
-            cursor.moveToPosition(position - mFilteredPhoneNumbers.size());
-            long phoneId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
-            setPhoneSelected(phoneId, !isSelected(phoneId));
-        }
-        notifyDataSetChanged();
-    }
-
-    public boolean isSelectionChanged() {
-        return mSelectionChanged;
-    }
-
-    public void setSelectionChanged(boolean flag) {
-        mSelectionChanged = flag;
-        if (mSelectionChangeListener != null) {
-            mSelectionChangeListener.onSelectionChange();
-        }
-    }
-
-    public void setPhoneSelected(final String phoneNumber, boolean selected) {
-        if (!TextUtils.isEmpty(phoneNumber)) {
-            if (selected) {
-                mSelectedPhoneNumbers.add(phoneNumber);
-            } else {
-                mSelectedPhoneNumbers.remove(phoneNumber);
-            }
-        }
-        setSelectionChanged(true);
-    }
-
-    public void setPhoneSelected(long phoneId, boolean selected) {
-        if (selected) {
-            mSelectedPhoneIds.add(phoneId);
-        } else {
-            mSelectedPhoneIds.remove(phoneId);
-        }
-        setSelectionChanged(true);
-    }
-
-    public boolean isSelected(long phoneId) {
-        return mSelectedPhoneIds.contains(phoneId);
-    }
-
-    public boolean isSelected(final String phoneNumber) {
-        return mSelectedPhoneNumbers.contains(phoneNumber);
-    }
-
-    public void setAllPhonesSelected(boolean selected) {
-//        if (selected) {
-//            Cursor cursor = this.mMultiplePhoneSelectionActivity.mAdapter.getCursor();
-//            if (cursor != null) {
-//                int backupPos = cursor.getPosition();
-//                cursor.moveToPosition(-1);
-//                while (cursor.moveToNext()) {
-//                    setPhoneSelected(cursor
-//                            .getLong(MultiplePhonePickerActivity.PHONE_ID_COLUMN_INDEX), true);
-//                }
-//                cursor.moveToPosition(backupPos);
-//            }
-//            for (String number : this.mMultiplePhoneSelectionActivity.mPhoneNumberAdapter
-//                    .getFilteredPhoneNumbers()) {
-//                setPhoneSelected(number, true);
-//            }
-//        } else {
-//            mSelectedPhoneIds.clear();
-//            mSelectedPhoneNumbers.clear();
-//        }
-    }
-
-    public boolean isAllSelected() {
-        return false;
-//        return selectedCount() == this.mMultiplePhoneSelectionActivity.mPhoneNumberAdapter
-//                .getFilteredPhoneNumbers().size()
-//                + this.mMultiplePhoneSelectionActivity.mAdapter.getCount();
-    }
-
-    public Iterator<Long> getSelectedPhoneIds() {
-        return mSelectedPhoneIds.iterator();
-    }
-
-    @Override
-    public int getItemViewTypeCount() {
-        return 2;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return position < mPhoneNumbers.size() ? 0 : 1;
-    }
-
-    // TODO redo as two separate partitions
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view = null;
-//        if (convertView == null || convertView.getTag() == null) {
-//            view = newView(getContext(), null, parent);
-//        } else {
-//            view = convertView;
-//        }
-//
-//        boolean showingSuggestion = false;
-//
-//        if (position < mFilteredPhoneNumbers.size()) {
-//            bindExtraPhoneView(view, position);
-//        } else {
-//            Cursor cursor = ((Cursor)getItem(position));
-//            cursor.moveToPosition(position - mFilteredPhoneNumbers.size());
-//            bindView(view, getContext(), cursor);
-//        }
-        return view;
-    }
-
-    @Override
-    protected View newView(Context context, int partition, Cursor cursor, int position,
-            ViewGroup parent) {
-        final MultiplePhonePickerItemView view = new MultiplePhonePickerItemView(context, null);
-        view.setUnknownNameText(getUnknownNameText());
-        view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
-        return view;
-    }
-
-    private void bindExtraPhoneView(View itemView, int position) {
-        final MultiplePhonePickerItemView view = (MultiplePhonePickerItemView)itemView;
-        String phoneNumber = mFilteredPhoneNumbers.get(position);
-        view.getNameTextView().setText(phoneNumber);
-        CheckBox checkBox = view.getCheckBoxView();
-        checkBox.setChecked(isSelected(phoneNumber));
-        view.phoneId = INVALID_PHONE_ID;
-        view.phoneNumber = phoneNumber;
-    }
-
-    @Override
-    protected void bindView(View itemView, int partition, Cursor cursor, int position) {
-        super.bindView(itemView, partition, cursor, position);
-
-        final MultiplePhonePickerItemView view = (MultiplePhonePickerItemView)itemView;
-        view.phoneId = Long.valueOf(cursor.getLong(PHONE_ID_COLUMN_INDEX));
-        CheckBox checkBox = view.getCheckBoxView();
-        checkBox.setChecked(isSelected(view.phoneId));
-
-        long contactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
-        view.getChipView().setBackgroundResource(getChipColor(contactId));
-    }
-
-//    @Override
-//    protected void prepareEmptyView() {
-//        mMultiplePhonePickerActivity.mEmptyView.show(mMultiplePhonePickerActivity.mSearchMode,
-//                true, false, false, false, true, mMultiplePhonePickerActivity.mShowSelectedOnly);
-//    }
-
-    /**
-     * Get assigned chip color resource id for a given contact, 0 is returned if there is no mapped
-     * resource.
-     */
-    public int getChipColor(long contactId) {
-        return mContactColor.get((int)contactId);
-    }
-
-    // TODO filtering
-//    public void doFilter(final String constraint, boolean selectedOnly) {
-//        if (mPhoneNumbers == null) {
-//            return;
-//        }
-//        mFilteredPhoneNumbers.clear();
-//        for (String number : mPhoneNumbers) {
-//            if (selectedOnly && !mSelection.isSelected(number) ||
-//                    !TextUtils.isEmpty(constraint) && !number.startsWith(constraint)) {
-//                continue;
-//            }
-//            mFilteredPhoneNumbers.add(number);
-//        }
-//    }
-
-    @Override
-    public int getCount() {
-        return super.getCount() + mFilteredPhoneNumbers.size();
-    }
-
-    @Override
-    public void changeCursor(Cursor cursor) {
-        super.changeCursor(cursor);
-        updateChipColor(cursor);
-    }
-
-    /**
-     * Go through the cursor and assign the chip color to contact who has more
-     * than one phone numbers. Assume the cursor is clustered by CONTACT_ID.
-     */
-    public void updateChipColor(Cursor cursor) {
-        if (cursor == null || cursor.getCount() == 0) {
-            return;
-        }
-        mContactColor.clear();
-        cursor.moveToFirst();
-        int colorIndex = 0;
-        long prevContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
-        while (cursor.moveToNext()) {
-            long contactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
-            if (prevContactId == contactId) {
-                if (mContactColor.indexOfKey((int)contactId) < 0) {
-                    mContactColor.put((int)contactId, CHIP_COLOR_ARRAY[colorIndex]);
-                    colorIndex++;
-                    if (colorIndex >= CHIP_COLOR_ARRAY.length) {
-                        colorIndex = 0;
-                    }
-                }
-            }
-            prevContactId = contactId;
-        }
-    }
-}
diff --git a/src/com/android/contacts/list/MultiplePhonePickerFragment.java b/src/com/android/contacts/list/MultiplePhonePickerFragment.java
deleted file mode 100644
index bf75a9f..0000000
--- a/src/com/android/contacts/list/MultiplePhonePickerFragment.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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.list;
-
-import com.android.contacts.R;
-import com.android.contacts.list.MultiplePhonePickerAdapter.OnSelectionChangeListener;
-
-import android.app.Activity;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.View.OnClickListener;
-import android.view.animation.AnimationUtils;
-import android.widget.Button;
-
-/**
- * Fragment for the multiple phone picker.
- */
-public class MultiplePhonePickerFragment
-        extends ContactEntryListFragment<MultiplePhonePickerAdapter>
-        implements OnClickListener, OnSelectionChangeListener {
-
-    private static final String SELECTION_EXTRA_KEY = "selection";
-    private static final String SELECTION_CHANGED_EXTRA_KEY = "selectionChanged";
-
-    private OnMultiplePhoneNumberPickerActionListener mListener;
-
-    /**
-     * UI control of action panel in MODE_PICK_MULTIPLE_PHONES mode.
-     */
-    private View mFooterView;
-
-    private Uri[] mSelectedUris;
-    private boolean mSelectionChanged;
-
-    public MultiplePhonePickerFragment() {
-        setSectionHeaderDisplayEnabled(false);
-        setPhotoLoaderEnabled(true);
-    }
-
-    public void setOnMultiplePhoneNumberPickerActionListener(
-            OnMultiplePhoneNumberPickerActionListener listener) {
-        mListener = listener;
-    }
-
-    public Uri[] getSelectedUris() {
-        return getAdapter().getSelectedUris();
-    }
-
-    public void setSelectedUris(Parcelable[] extras) {
-        Uri[] uris = new Uri[extras == null ? 0 : extras.length];
-        if (extras != null) {
-            for (int i = 0; i < extras.length; i++) {
-                uris[i] = (Uri)extras[i];
-            }
-        }
-        setSelectedUris(uris);
-    }
-
-    public void setSelectedUris(Uri[] uris) {
-        mSelectedUris = uris;
-        MultiplePhonePickerAdapter adapter = getAdapter();
-        if (adapter != null) {
-            adapter.setSelectedUris(uris);
-        }
-    }
-
-    @Override
-    protected MultiplePhonePickerAdapter createListAdapter() {
-        return new MultiplePhonePickerAdapter(getActivity());
-    }
-
-    @Override
-    protected void configureAdapter() {
-        super.configureAdapter();
-        MultiplePhonePickerAdapter adapter = getAdapter();
-        adapter.setSelectedUris(mSelectedUris);
-        adapter.setSelectionChanged(mSelectionChanged);
-        adapter.setOnSelectionChangeListener(this);
-    }
-
-    @Override
-    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        View view = inflater.inflate(R.layout.contacts_list_content, null);
-        ViewStub stub = (ViewStub)view.findViewById(R.id.footer_stub);
-        if (stub != null) {
-            View stubView = stub.inflate();
-            mFooterView = stubView.findViewById(R.id.footer);
-            mFooterView.setVisibility(View.GONE);
-            Button doneButton = (Button) stubView.findViewById(R.id.done);
-            doneButton.setOnClickListener(this);
-            Button revertButton = (Button) stubView.findViewById(R.id.revert);
-            revertButton.setOnClickListener(this);
-        }
-        return view;
-    }
-
-    @Override
-    protected void onItemClick(int position, long id) {
-        getAdapter().toggleSelection(position);
-    }
-
-    public void onClick(View v) {
-        int id = v.getId();
-        switch (id) {
-            case R.id.done:
-                mListener.onPhoneNumbersSelectedAction(getAdapter().getSelectedUris());
-                break;
-            case R.id.revert:
-                mListener.onFinishAction();
-                break;
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedState) {
-        super.onCreate(savedState);
-        if (savedState != null) {
-            setSelectedUris(savedState.getParcelableArray(SELECTION_EXTRA_KEY));
-            mSelectionChanged = savedState.getBoolean(SELECTION_CHANGED_EXTRA_KEY, false);
-            if (getAdapter() != null) {
-                getAdapter().setSelectionChanged(mSelectionChanged);
-            }
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateWidgets();
-    }
-
-    public void onSelectionChange() {
-        updateWidgets();
-    }
-
-    private void updateWidgets() {
-        int selected = getAdapter().getSelectedCount();
-
-        Activity context = getActivity();
-        if (selected >= 1) {
-            final String format = context.getResources().getQuantityString(
-                    R.plurals.multiple_picker_title, selected);
-
-            // TODO: turn this into a callback
-            context.setTitle(String.format(format, selected));
-        } else {
-            // TODO: turn this into a callback
-            context.setTitle(context.getString(R.string.contactsList));
-        }
-
-        if (getAdapter().isSelectionChanged() && mFooterView.getVisibility() == View.GONE) {
-            mFooterView.setVisibility(View.VISIBLE);
-            mFooterView.startAnimation(AnimationUtils.loadAnimation(context, R.anim.footer_appear));
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle icicle) {
-        super.onSaveInstanceState(icicle);
-        icicle.putParcelableArray(SELECTION_EXTRA_KEY, getAdapter().getSelectedUris());
-        icicle.putBoolean(SELECTION_CHANGED_EXTRA_KEY, getAdapter().isSelectionChanged());
-    }
-}
diff --git a/src/com/android/contacts/list/MultiplePhonePickerItemView.java b/src/com/android/contacts/list/MultiplePhonePickerItemView.java
deleted file mode 100644
index 87f53bf..0000000
--- a/src/com/android/contacts/list/MultiplePhonePickerItemView.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.list;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.CheckBox;
-
-/**
- * A custom view for an item in the phone multi-picker list.
- */
-public class MultiplePhonePickerItemView extends ContactListItemView {
-
-    // Used to indicate the sequence of phones belong to the same contact in multi-picker
-    private View mChipView;
-    // Used to select the phone in multi-picker
-    private CheckBox mCheckBox;
-
-    private int mChipWidth;
-    private int mChipRightMargin;
-    private int mCheckBoxMargin;
-
-    public long phoneId;
-    // phoneNumber only validates when phoneId = INVALID_PHONE_ID
-    public String phoneNumber;
-
-    public MultiplePhonePickerItemView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        TypedArray a = getContext().obtainStyledAttributes(
-                attrs, R.styleable.MultiplePhonePickerItemView);
-        mChipWidth = a.getDimensionPixelOffset(
-                R.styleable.MultiplePhonePickerItemView_list_item_header_chip_width, 0);
-        mChipRightMargin = a.getDimensionPixelOffset(
-                R.styleable.MultiplePhonePickerItemView_list_item_header_chip_right_margin, 0);
-        mCheckBoxMargin = a.getDimensionPixelOffset(
-                R.styleable.MultiplePhonePickerItemView_list_item_header_checkbox_margin, 0);
-        a.recycle();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (isVisible(mChipView)) {
-            mChipView.measure(0, 0);
-        }
-
-        if (isVisible(mCheckBox)) {
-            mCheckBox.measure(0, 0);
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected int layoutLeftSide(int height, int topBound, int bottomBound, int leftBound) {
-        if (mChipView != null) {
-            mChipView.layout(leftBound, topBound, leftBound + mChipWidth, height);
-            leftBound += mChipWidth + mChipRightMargin;
-        }
-
-        return super.layoutLeftSide(height, topBound, bottomBound, leftBound);
-    }
-
-    @Override
-    protected int layoutRightSide(int height, int topBound, int rightBound) {
-        rightBound = super.layoutRightSide(height, topBound, rightBound);
-
-        if (isVisible(mCheckBox)) {
-            int checkBoxWidth = mCheckBox.getMeasuredWidth();
-            int checkBoxHight = mCheckBox.getMeasuredHeight();
-            rightBound -= mCheckBoxMargin + checkBoxWidth;
-            int checkBoxTop = topBound + (height - topBound - checkBoxHight) / 2;
-            mCheckBox.layout(
-                    rightBound,
-                    checkBoxTop,
-                    rightBound + checkBoxWidth,
-                    checkBoxTop + checkBoxHight);
-        }
-
-        return rightBound;
-    }
-
-    /**
-     * Returns the chip view for the multipicker, creating it if necessary.
-     */
-    public View getChipView() {
-        if (mChipView == null) {
-            mChipView = new View(mContext);
-            addView(mChipView);
-        }
-        return mChipView;
-    }
-
-    /**
-     * Returns the CheckBox view for the multipicker, creating it if necessary.
-     */
-    public CheckBox getCheckBoxView() {
-        if (mCheckBox == null) {
-            mCheckBox = new CheckBox(mContext);
-            mCheckBox.setClickable(false);
-            mCheckBox.setFocusable(false);
-            addView(mCheckBox);
-        }
-        return mCheckBox;
-    }
-}
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index e62d0ba..e6d8987 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -271,6 +271,11 @@
         public int specificMax;
         public String customColumn;
 
+        /**
+         * True if this type may be shown as blank.
+         */
+        public boolean unspecifiedType;
+
         public EditType(int rawValue, int labelRes) {
             this.rawValue = rawValue;
             this.labelRes = labelRes;
@@ -292,6 +297,11 @@
             return this;
         }
 
+        public EditType setUnspecifiedType(boolean unspecifiedType) {
+            this.unspecifiedType = unspecifiedType;
+            return this;
+        }
+
         @Override
         public boolean equals(Object object) {
             if (object instanceof EditType) {
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 5e0d853..8b35242 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -141,8 +141,9 @@
             final int depth = parser.getDepth();
             while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
                     && type != XmlPullParser.END_DOCUMENT) {
+                String tag = parser.getName();
                 if (type == XmlPullParser.END_TAG
-                        || !InflateTags.CONTACTS_DATA_KIND.equals(rootTag)) {
+                        || !InflateTags.CONTACTS_DATA_KIND.equals(tag)) {
                     continue;
                 }
 
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index ff3f5c5..601e77e 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -89,23 +89,28 @@
     }
 
     protected EditType buildPhoneType(int type) {
-        return new EditType(type, Phone.getTypeLabelResource(type));
+        return new EditType(type, Phone.getTypeLabelResource(type))
+                .setUnspecifiedType(type == Phone.TYPE_OTHER);
     }
 
     protected EditType buildEmailType(int type) {
-        return new EditType(type, Email.getTypeLabelResource(type));
+        return new EditType(type, Email.getTypeLabelResource(type))
+                .setUnspecifiedType(type == Email.TYPE_OTHER);
     }
 
     protected EditType buildPostalType(int type) {
-        return new EditType(type, StructuredPostal.getTypeLabelResource(type));
+        return new EditType(type, StructuredPostal.getTypeLabelResource(type))
+                .setUnspecifiedType(type == StructuredPostal.TYPE_OTHER);
     }
 
     protected EditType buildImType(int type) {
-        return new EditType(type, Im.getProtocolLabelResource(type));
+        return new EditType(type, Im.getProtocolLabelResource(type))
+                .setUnspecifiedType(type == Im.TYPE_OTHER);
     }
 
-    protected EventEditType buildEventType(int type, boolean yearOptional) {
-        return new EventEditType(type, Event.getTypeResource(type)).setYearOptional(yearOptional);
+    protected EditType buildEventType(int type, boolean yearOptional) {
+        return new EventEditType(type, Event.getTypeResource(type)).setYearOptional(yearOptional)
+                .setUnspecifiedType(type == Event.TYPE_OTHER);
     }
 
     protected EditType buildRelationType(int type) {
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index 2532cb2..4634b40 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -15,6 +15,11 @@
  */
 package com.android.contacts.vcard;
 
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactBrowserActivity;
+import com.android.vcard.VCardComposer;
+import com.android.vcard.VCardConfig;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -26,126 +31,78 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.contacts.R;
-import com.android.contacts.activities.ContactBrowserActivity;
-import com.android.vcard.VCardComposer;
-import com.android.vcard.VCardConfig;
-
 import java.io.FileNotFoundException;
 import java.io.OutputStream;
-import java.util.LinkedList;
-import java.util.Queue;
 
-public class ExportProcessor {
-    private static final String LOG_TAG = "ExportProcessor";
+/**
+ * Class for processing one export request from a user. Dropped after exporting requested Uri(s).
+ * {@link VCardService} will create another object when there is another export request.
+ */
+public class ExportProcessor implements Runnable {
+    private static final String LOG_TAG = "VCardExport";
 
-    private final Context mContext;
+    private final VCardService mService;
 
     private ContentResolver mResolver;
     private NotificationManager mNotificationManager;
 
-    boolean mCanceled;
+    private volatile boolean mCanceled;
 
-    boolean mReadyForRequest;
-    private final Queue<ExportRequest> mPendingRequests =
-        new LinkedList<ExportRequest>();
+    private ExportRequest mExportRequest; 
+    private int mJobId;
 
-    public ExportProcessor(Context context) {
-        mContext = context;
+    public ExportProcessor(VCardService service, ExportRequest exportRequest, int jobId) {
+        mService = service;
+        mResolver = service.getContentResolver();
+        mNotificationManager =
+                (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE);
+        mExportRequest = exportRequest;
+        mJobId = jobId;
     }
 
-    /* package */ ThreadStarter mThreadStarter = new ThreadStarter() {
-        public void start() {
-            final Thread thread = new Thread(new Runnable() {
-                public void run() {
-                    process();
-                }
-            });
-            thread.start();
-        }
-    };
-
-    public synchronized void pushRequest(ExportRequest parameter) {
-        if (mResolver == null) {
-            // Service object may not ready at the construction time
-            // (e.g. ContentResolver may be null).
-            mResolver = mContext.getContentResolver();
-            mNotificationManager =
-                    (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        }
-
-        final boolean needThreadStart;
-        if (!mReadyForRequest) {
-            needThreadStart = true;
-        } else {
-            needThreadStart = false;
-        }
-        mPendingRequests.add(parameter);
-        if (needThreadStart) {
-            mThreadStarter.start();
-        }
-
-        mReadyForRequest = true;
-    }
-
-    /* package */ void process() {
-        if (!mReadyForRequest) {
-            throw new RuntimeException(
-                    "process() is called before request being pushed "
-                    + "or after this object's finishing its processing.");
-        }
-
+    @Override
+    public void run() {
+        // ExecutorService ignores RuntimeException, so we need to show it here.
         try {
-            while (!mCanceled) {
-                final ExportRequest parameter;
-                synchronized (this) {
-                    if (mPendingRequests.size() == 0) {
-                        mReadyForRequest = false;
-                        break;
-                    } else {
-                        parameter = mPendingRequests.poll();
-                    }
-                }  // synchronized (this)
-                handleOneRequest(parameter);
-            }
-
-            doFinishNotification(mContext.getString(R.string.exporting_vcard_finished_title),
-                    "");
-        } finally {
-            // Not thread safe. Just in case.
-            // TODO: verify this works fine.
-            mReadyForRequest = false;
+            runInternal();
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "RuntimeException thrown during export", e);
+            throw e;
         }
     }
 
-    /* package */ void handleOneRequest(ExportRequest request) {
-        boolean shouldCallFinish = true;
+    private void runInternal() {
+        Log.i(LOG_TAG, String.format("vCard export (id: %d) has started.", mJobId));
+        final ExportRequest request = mExportRequest;
         VCardComposer composer = null;
+        boolean successful = false;
         try {
             final Uri uri = request.destUri;
             final OutputStream outputStream;
             try {
                 outputStream = mResolver.openOutputStream(uri);
             } catch (FileNotFoundException e) {
+                Log.w(LOG_TAG, "FileNotFoundException thrown", e);
                 // Need concise title.
 
                 final String errorReason =
-                    mContext.getString(R.string.fail_reason_could_not_open_file,
+                    mService.getString(R.string.fail_reason_could_not_open_file,
                             uri, e.getMessage());
-                shouldCallFinish = false;
+                Log.i(LOG_TAG, "failed to export (could not open output stream)");
                 doFinishNotification(errorReason, "");
                 return;
             }
+
             final String exportType = request.exportType;
             final int vcardType;
             if (TextUtils.isEmpty(exportType)) {
                 vcardType = VCardConfig.getVCardTypeFromString(
-                        mContext.getString(R.string.config_export_vcard_type));
+                        mService.getString(R.string.config_export_vcard_type));
             } else {
                 vcardType = VCardConfig.getVCardTypeFromString(exportType);
             }
 
-            composer = new VCardComposer(mContext, vcardType, true);
+            composer = new VCardComposer(mService, vcardType, true);
 
             // for test
             // int vcardType = (VCardConfig.VCARD_TYPE_V21_GENERIC |
@@ -155,12 +112,13 @@
             composer.addHandler(composer.new HandlerForOutputStream(outputStream));
 
             if (!composer.init()) {
+                Log.w(LOG_TAG, "vCard composer init failed");
                 final String errorReason = composer.getErrorReason();
                 Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
                 final String translatedErrorReason =
                         translateComposerError(errorReason);
                 final String title =
-                        mContext.getString(R.string.fail_reason_could_not_initialize_exporter,
+                        mService.getString(R.string.fail_reason_could_not_initialize_exporter,
                                 translatedErrorReason);
                 doFinishNotification(title, "");
                 return;
@@ -169,7 +127,7 @@
             final int total = composer.getCount();
             if (total == 0) {
                 final String title =
-                        mContext.getString(R.string.fail_reason_no_exportable_contact);
+                        mService.getString(R.string.fail_reason_no_exportable_contact);
                 doFinishNotification(title, "");
                 return;
             }
@@ -185,7 +143,7 @@
                     final String translatedErrorReason =
                             translateComposerError(errorReason);
                     final String title =
-                            mContext.getString(R.string.fail_reason_error_occurred_during_export,
+                            mService.getString(R.string.fail_reason_error_occurred_during_export,
                                     translatedErrorReason);
                     doFinishNotification(title, "");
                     return;
@@ -193,15 +151,21 @@
                 doProgressNotification(uri, total, current);
                 current++;
             }
+            Log.i(LOG_TAG, "Successfully finished exporting vCard " + request.destUri);
+
+            successful = true;
+            // TODO: Show "successful"
         } finally {
             if (composer != null) {
                 composer.terminate();
             }
+
+            mService.handleFinishExportNotification(mJobId, successful);
         }
     }
 
     private String translateComposerError(String errorMessage) {
-        final Resources resources = mContext.getResources();
+        final Resources resources = mService.getResources();
         if (VCardComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO.equals(errorMessage)) {
             return resources.getString(R.string.composer_failed_to_get_database_infomation);
         } else if (VCardComposer.FAILURE_REASON_NO_ENTRY.equals(errorMessage)) {
@@ -214,11 +178,11 @@
     }
 
     private void doProgressNotification(Uri uri, int total, int current) {
-        final String title = mContext.getString(R.string.exporting_contact_list_title);
+        final String title = mService.getString(R.string.exporting_contact_list_title);
         final String description =
-                mContext.getString(R.string.exporting_contact_list_message, uri);
+                mService.getString(R.string.exporting_contact_list_message, uri);
 
-        /* TODO: fix this
+        /* TODO: we should show more informative Notification to users.
         final RemoteViews remoteViews = new RemoteViews(mService.getPackageName(),
                 R.layout.status_bar_ongoing_event_progress_bar);
         remoteViews.setTextViewText(R.id.status_description, message);
@@ -244,7 +208,7 @@
                 description,
                 when);
 
-        final Context context = mContext.getApplicationContext();
+        final Context context = mService.getApplicationContext();
         final PendingIntent pendingIntent =
                 PendingIntent.getActivity(context, 0,
                         new Intent(context, ContactBrowserActivity.class),
@@ -258,10 +222,15 @@
         final Notification notification = new Notification();
         notification.icon = android.R.drawable.stat_sys_upload_done;
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
-        notification.setLatestEventInfo(mContext, title, message, null);
-        final Intent intent = new Intent(mContext, ContactBrowserActivity.class);
+        notification.setLatestEventInfo(mService, title, message, null);
+        final Intent intent = new Intent(mService, ContactBrowserActivity.class);
         notification.contentIntent =
-                PendingIntent.getActivity(mContext, 0, intent, 0);
+                PendingIntent.getActivity(mService, 0, intent, 0);
         mNotificationManager.notify(VCardService.EXPORT_NOTIFICATION_ID, notification);
     }
+
+    public void cancel() {
+        Log.i(LOG_TAG, "received cancel request");
+        mCanceled = true;
+    }
 }
diff --git a/src/com/android/contacts/vcard/ExportVCardActivity.java b/src/com/android/contacts/vcard/ExportVCardActivity.java
index fbe6d48..d53d5b2 100644
--- a/src/com/android/contacts/vcard/ExportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ExportVCardActivity.java
@@ -51,7 +51,7 @@
  * dialogs stuffs (like how onCreateDialog() is used).
  */
 public class ExportVCardActivity extends Activity {
-    private static final String LOG_TAG = "ExportVCardActivity";
+    private static final String LOG_TAG = "VCardExport";
 
     // If true, VCardExporter is able to emits files longer than 8.3 format.
     private static final boolean ALLOW_LONG_FILE_NAME = false;
@@ -75,8 +75,7 @@
         private Queue<ExportRequest> mPendingRequests = new LinkedList<ExportRequest>();
 
         public void doBindService() {
-            bindService(new Intent(ExportVCardActivity.this,
-                    VCardService.class), this, Context.BIND_AUTO_CREATE);
+
         }
 
         public synchronized void requestSend(final ExportRequest parameter) {
@@ -174,7 +173,10 @@
 
         public void onClick(DialogInterface dialog, int which) {
             if (which == DialogInterface.BUTTON_POSITIVE) {
-                mConnection.doBindService();
+                // We don't want the service finishes itself just after this connection.
+                startService(new Intent(ExportVCardActivity.this, VCardService.class));
+                bindService(new Intent(ExportVCardActivity.this, VCardService.class),
+                        mConnection, Context.BIND_AUTO_CREATE);
 
                 final ExportRequest request = new ExportRequest(mDestUri);
 
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 0e6acee..ca7223d 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -15,25 +15,6 @@
  */
 package com.android.contacts.vcard;
 
-import android.accounts.Account;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
 import com.android.contacts.R;
 import com.android.vcard.VCardEntryCommitter;
 import com.android.vcard.VCardEntryConstructor;
@@ -46,210 +27,73 @@
 import com.android.vcard.exception.VCardNotSupportedException;
 import com.android.vcard.exception.VCardVersionException;
 
+import android.accounts.Account;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Queue;
 
 /**
- * Class for processing incoming import request from {@link ImportVCardActivity}.
- *
- * This class is designed so that a user ({@link Service}) does not need to (and should not)
- * recreate multiple instances, as this holds total count of vCard entries to be imported.
+ * Class for processing one import request from a user. Dropped after importing requested Uri(s).
+ * {@link VCardService} will create another object when there is another import request.
  */
-public class ImportProcessor {
-    private static final String LOG_TAG = "VCardImporter";
+public class ImportProcessor implements Runnable {
+    private static final String LOG_TAG = "VCardImport";
 
-    private class ImportProcessorConnection implements ServiceConnection {
-        private Messenger mMessenger;
-        private boolean mBound;
-
-        public synchronized void tryBind() {
-            mContext.startService(new Intent(mContext, VCardService.class));
-            if (!mContext.bindService(new Intent(mContext, VCardService.class),
-                    this, Context.BIND_AUTO_CREATE)) {
-                throw new RuntimeException("Failed to bind to VCardService.");
-            }
-            mBound = true;
-        }
-
-        public synchronized void tryUnbind() {
-            if (mBound) {
-                mContext.unbindService(this);
-                // TODO: This is not appropriate. It would be better to send some "stop" request
-                // to service and let the service stop itself.
-                mContext.stopService(new Intent(mContext, VCardService.class));
-            } else {
-                // TODO: Not graceful.
-                Log.w(LOG_TAG, "unbind() is tried while ServiceConnection is not bound yet");
-            }
-            mBound = false;
-        }
-
-        public void sendFinishNotification() {
-            try {
-                mMessenger.send(Message.obtain(null,
-                        VCardService.MSG_NOTIFY_IMPORT_FINISHED,
-                        null));
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
-            }
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mMessenger = new Messenger(service);
-        }
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mMessenger = null;
-        }
-    }
-
-    private final Context mContext;
-    private ContentResolver mResolver;
-    private NotificationManager mNotificationManager;
+    private final VCardService mService;
+    private final ContentResolver mResolver;
+    private final NotificationManager mNotificationManager;
 
     private final List<Uri> mFailedUris = new ArrayList<Uri>();
-    private final List<Uri> mCreatedUris = new ArrayList<Uri>();
     private final ImportProgressNotifier mNotifier = new ImportProgressNotifier();
 
     private VCardParser mVCardParser;
 
-    /**
-     * Meaning a controller of this object requests the operation should be canceled
-     * or not, which implies {@link #mReadyForRequest} should be set to false soon, but
-     * it does not meaning cancel request is able to immediately stop this object,
-     * so we have two variables.
-     */
+    private ImportRequest mImportRequest;
+    private int mJobId;
+
     private boolean mCanceled;
 
-    /**
-     * Meaning that this object is able to accept import requests.
-     */
-    private boolean mReadyForRequest;
-    private final Queue<ImportRequest> mPendingRequests =
-            new LinkedList<ImportRequest>();
-
-    // For testability.
-    /* package */ ThreadStarter mThreadStarter = new ThreadStarter() {
-        public void start() {
-            final Thread thread = new Thread(new Runnable() {
-                public void run() {
-                    process();
-                }
-            });
-            thread.start();
-        }
-    };
-    /* package */ interface CommitterGenerator {
-        public VCardEntryCommitter generate(ContentResolver resolver);
-    }
-    /* package */ class DefaultCommitterGenerator implements CommitterGenerator {
-        public VCardEntryCommitter generate(ContentResolver resolver) {
-            return new VCardEntryCommitter(mResolver);
-        }
+    public ImportProcessor(final VCardService service, final ImportRequest request,
+            int jobId) {
+        mService = service;
+        mResolver = mService.getContentResolver();
+        mNotificationManager = (NotificationManager)
+                mService.getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotifier.init(mService, mNotificationManager);
+        mImportRequest = request;
+        mJobId = jobId;
     }
 
-    private CommitterGenerator mCommitterGenerator =
-        new DefaultCommitterGenerator();
 
-    public ImportProcessor(final Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Checks this object and initialize it if not.
-     *
-     * This method is needed since {@link VCardService} is not ready when this object is
-     * created and we need to delay this initialization, while we want to initialize
-     * this object soon in tests.
-     */
-    /* package */ void ensureInit() {
-        if (mResolver == null) {
-            // Service object may not ready at the construction time
-            // (e.g. ContentResolver may be null).
-            mResolver = mContext.getContentResolver();
-            mNotificationManager =
-                    (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        }
-    }
-
-    public synchronized void pushRequest(final ImportRequest request) {
-        ensureInit();
-
-        final boolean needThreadStart;
-        if (!mReadyForRequest) {
-            mFailedUris.clear();
-            mCreatedUris.clear();
-
-            mNotifier.init(mContext, mNotificationManager);
-            needThreadStart = true;
-        } else {
-            needThreadStart = false;
-        }
-        final int count = request.entryCount;
-        if (count > 0) {
-            mNotifier.addTotalCount(count);
-        }
-        mPendingRequests.add(request);
-        if (needThreadStart) {
-            mThreadStarter.start();
-        }
-
-        mReadyForRequest = true;
-    }
-
-    /**
-     * Starts processing import requests. Never stops until all given requests are
-     * processed or some error happens, assuming this method is called from a
-     * {@link Thread} object.
-     */
-    private void process() {
-        if (!mReadyForRequest) {
-            throw new RuntimeException(
-                    "process() is called before request being pushed "
-                    + "or after this object's finishing its processing.");
-        }
-
-        final ImportProcessorConnection connection = new ImportProcessorConnection();
-        connection.tryBind();
+    @Override
+    public void run() {
+        // ExecutorService ignores RuntimeException, so we need to show it here.
         try {
-            while (!mCanceled) {
-                final ImportRequest parameter;
-                synchronized (this) {
-                    if (mPendingRequests.size() == 0) {
-                        mReadyForRequest = false;
-                        break;
-                    } else {
-                        parameter = mPendingRequests.poll();
-                    }
-                }  // synchronized (this)
-                handleOneRequest(parameter);
-            }
-
-            // Currenty we don't have an appropriate way to let users see all URIs imported.
-            // Instead, we show one only when there's just one created uri.
-            doFinishNotification(mCreatedUris.size() > 0 ? mCreatedUris.get(0) : null);
-            connection.sendFinishNotification();
-            Log.i(LOG_TAG, "Finished successfully importing all vCard");
-        } finally {
-            mReadyForRequest = false;  // Just in case.
-            mNotifier.resetTotalCount();
-            connection.tryUnbind();
+            runInternal();
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "RuntimeException thrown during import", e);
+            throw e;
         }
     }
 
-    /**
-     * Would be run inside synchronized block.
-     */
-    /* package */ boolean handleOneRequest(final ImportRequest request) {
+    private void runInternal() {
+        Log.i(LOG_TAG, String.format("vCard import (id: %d) has started.", mJobId));
+        final ImportRequest request = mImportRequest;
         if (mCanceled) {
-            Log.i(LOG_TAG, "Canceled before actually handling parameter ("
-                    + request.uri + ")");
-            return false;
+            Log.i(LOG_TAG, "Canceled before actually handling parameter (" + request.uri + ")");
+            return;
         }
         final int[] possibleVCardVersions;
         if (request.vcardVersion == ImportVCardActivity.VCARD_VERSION_AUTO_DETECT) {
@@ -274,29 +118,32 @@
 
         final VCardEntryConstructor constructor =
                 new VCardEntryConstructor(estimatedVCardType, account, estimatedCharset);
-        final VCardEntryCommitter committer = mCommitterGenerator.generate(mResolver);
+        final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
         constructor.addEntryHandler(committer);
         constructor.addEntryHandler(mNotifier);
 
         final boolean successful =
             readOneVCard(uri, estimatedVCardType, estimatedCharset,
                     constructor, possibleVCardVersions);
+
+        mService.handleFinishImportNotification(mJobId, successful);
+
         if (successful) {
-            Log.i(LOG_TAG, "Successfully finished reading one vCard file finished: " + uri);
+            Log.i(LOG_TAG, "Successfully finished importing one vCard file: " + uri);
             List<Uri> uris = committer.getCreatedUris();
-            if (uris != null) {
-                mCreatedUris.addAll(uris);
+            if (uris != null && uris.size() > 0) {
+                doFinishNotification(uris.get(0));
             } else {
                 // Not critical, but suspicious.
                 Log.w(LOG_TAG,
-                        "Created Uris is null while the creation itself is successful.");
+                        "Created Uris is null or 0 length " +
+                        "though the creation itself is successful.");
+                doFinishNotification(null);
             }
         } else {
             Log.w(LOG_TAG, "Failed to read one vCard file: " + uri);
             mFailedUris.add(uri);
         }
-
-        return successful;
     }
 
     /*
@@ -319,10 +166,10 @@
 
         if (isCanceled()) {
             notification.icon = android.R.drawable.stat_notify_error;
-            title = mContext.getString(R.string.importing_vcard_canceled_title);
+            title = mService.getString(R.string.importing_vcard_canceled_title);
         } else {
             notification.icon = android.R.drawable.stat_sys_download_done;
-            title = mContext.getString(R.string.importing_vcard_finished_title);
+            title = mService.getString(R.string.importing_vcard_finished_title);
         }
 
         final Intent intent;
@@ -336,8 +183,8 @@
             intent = null;
         }
 
-        notification.setLatestEventInfo(mContext, title, "",
-                PendingIntent.getActivity(mContext, 0, intent, 0));
+        notification.setLatestEventInfo(mService, title, "",
+                PendingIntent.getActivity(mService, 0, intent, 0));
         mNotificationManager.notify(VCardService.IMPORT_NOTIFICATION_ID, notification);
     }
 
@@ -411,10 +258,6 @@
         return successful;
     }
 
-    public synchronized boolean isReadyForRequest() {
-        return mReadyForRequest;
-    }
-
     public boolean isCanceled() {
         return mCanceled;
     }
@@ -428,21 +271,4 @@
             }
         }
     }
-
-    public List<Uri> getCreatedUrisForTest() {
-        return mCreatedUris;
-    }
-
-    public List<Uri> getFailedUrisForTest() {
-        return mFailedUris;
-    }
-
-    public void injectCommitterGeneratorForTest(
-            final CommitterGenerator generator) {
-        mCommitterGenerator = generator;
-    }
-
-    public void initNotifierForTest() {
-        mNotifier.init(mContext, mNotificationManager);
-    }
 }
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 24df063..7a72ca2 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -54,7 +54,6 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.TextUtils;
-import android.text.format.DateUtils;
 import android.text.style.RelativeSizeSpan;
 import android.util.Log;
 
@@ -71,9 +70,7 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Queue;
 import java.util.Set;
 import java.util.Vector;
 
@@ -87,7 +84,7 @@
  * dialogs stuffs (like how onCreateDialog() is used).
  */
 public class ImportVCardActivity extends Activity {
-    private static final String LOG_TAG = "VCardImporter";
+    private static final String LOG_TAG = "VCardImport";
 
     private static final int SELECT_ACCOUNT = 0;
 
@@ -113,116 +110,16 @@
     private Uri mUri;
 
     private ProgressDialog mProgressDialogForScanVCard;
-    private ProgressDialog mProgressDialogForCacheVCard;
+    private ProgressDialog mProgressDialogForCachingVCard;
 
     private List<VCardFile> mAllVCardFileList;
     private VCardScanThread mVCardScanThread;
 
     private VCardCacheThread mVCardCacheThread;
+    private ImportRequestConnection mConnection;
 
     private String mErrorMessage;
 
-    private class CustomConnection implements ServiceConnection {
-        private Messenger mMessenger;
-        /**
-         * Stores {@link ImportRequest} objects until actual connection is established.
-         */
-        private Queue<ImportRequest> mPendingRequests =
-                new LinkedList<ImportRequest>();
-
-        private boolean mConnected = false;
-        private boolean mNeedToCallFinish = false;
-        private boolean mDisconnectAndFinishDone = false;
-
-        /**
-         * Tries to unbind this connection and call {@link ImportVCardActivity#finish()}.
-         * When timing is not appropriate, this object remembers this call and
-         * call {@link ImportVCardActivity#unbindService(ServiceConnection)} and
-         * {@link ImportVCardActivity#finish()} afterwards.
-         */
-        public void tryDisconnectAndFinish() {
-            synchronized (this) {
-                if (!mDisconnectAndFinishDone) {
-                    mNeedToCallFinish = true;
-                    if (mConnected) {
-                        // onServiceConnected() is already finished and we need to
-                        // "manually" call unbindService() here.
-                        unbindService(this);
-                        mConnected = false;
-                        mDisconnectAndFinishDone = true;
-                        finish();
-                    } else {
-                        // If not connected, finish() must be called when connected, as
-                        // We cannot call finish() now.
-                    }
-                }
-            }
-        }
-
-        public void requestSend(final ImportRequest parameter) {
-            synchronized (mPendingRequests) {
-                if (mMessenger != null) {
-                    sendMessage(parameter);
-                } else {
-                    mPendingRequests.add(parameter);
-                }
-            }
-        }
-
-        private void sendMessage(final ImportRequest request) {
-            try {
-                mMessenger.send(Message.obtain(null,
-                        VCardService.MSG_IMPORT_REQUEST,
-                        request));
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
-                runOnUiThread(new DialogDisplayer(
-                        getString(R.string.fail_reason_unknown)));
-            }
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (mPendingRequests) {
-                mMessenger = new Messenger(service);
-
-                // Send pending requests thrown from this Activity before an actual connection
-                // is established.
-                while (!mPendingRequests.isEmpty()) {
-                    final ImportRequest parameter = mPendingRequests.poll();
-                    if (parameter == null) {
-                        throw new NullPointerException();
-                    }
-                    sendMessage(parameter);
-                }
-            }
-
-            synchronized (this) {
-                if (!mDisconnectAndFinishDone) {
-                    mConnected = true;
-                    if (mNeedToCallFinish) {
-                        mDisconnectAndFinishDone = true;
-                        finish();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            synchronized (mPendingRequests) {
-                if (!mPendingRequests.isEmpty()) {
-                    Log.w(LOG_TAG, "Some request(s) are dropped.");
-                }
-            }
-
-            // Set to null so that we can detect inappropriate re-connection toward
-            // the Service via NullPointerException;
-            mPendingRequests = null;
-            mMessenger = null;
-        }
-    }
-
     private static class VCardFile {
         private final String mName;
         private final String mCanonicalPath;
@@ -275,25 +172,53 @@
 
     private CancelListener mCancelListener = new CancelListener();
 
+    private class ImportRequestConnection implements ServiceConnection {
+        private Messenger mMessenger;
+
+        public void sendImportRequest(final ImportRequest request) {
+            Log.i(LOG_TAG, String.format("Send an import request (Uri: %s)", request.uri));
+            try {
+                mMessenger.send(Message.obtain(null,
+                        VCardService.MSG_IMPORT_REQUEST,
+                        request));
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
+                runOnUiThread(new DialogDisplayer(getString(R.string.fail_reason_unknown)));
+            }
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mMessenger = new Messenger(service);
+            Log.i(LOG_TAG,
+                    String.format("Connected to VCardService. Kick a vCard cache thread (uri: %s)",
+                            Arrays.toString(mVCardCacheThread.getSourceUris())));
+            mVCardCacheThread.start();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.i(LOG_TAG, "Disconnected from VCardService");
+        }
+    }
+
     /**
-     * Caches all vCard data into local data directory so that we allow
-     * {@link VCardService} to access all the contents in given Uris, some of
-     * which may not be accessible from other components due to permission problem.
-     * (Activity which gives the Uri may allow only this Activity to access that content,
-     * not the other components like {@link VCardService}.
+     * Caches given vCard files into a local directory, and sends actual import request to
+     * {@link VCardService}.
      *
-     * We also allow the Service to happen to exit during the vCard import procedure.
+     * We need to cache given files into local storage. One of reasons is that some data (as Uri)
+     * may have special permissions. Callers may allow only this Activity to access that content,
+     * not what this Activity launched (like {@link VCardService}).
      */
     private class VCardCacheThread extends Thread
             implements DialogInterface.OnCancelListener {
         private boolean mCanceled;
         private PowerManager.WakeLock mWakeLock;
         private VCardParser mVCardParser;
-        private final Uri[] mSourceUris;
+        private final Uri[] mSourceUris;  // Given from a caller.
 
         public VCardCacheThread(final Uri[] sourceUris) {
             mSourceUris = sourceUris;
-            final int length = sourceUris.length;
             final Context context = ImportVCardActivity.this;
             final PowerManager powerManager =
                     (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -305,21 +230,27 @@
         @Override
         public void finalize() {
             if (mWakeLock != null && mWakeLock.isHeld()) {
+                Log.w(LOG_TAG, "WakeLock is being held.");
                 mWakeLock.release();
             }
         }
 
         @Override
         public void run() {
-            final Context context = ImportVCardActivity.this;
-            final ContentResolver resolver = context.getContentResolver();
-            String errorMessage = null;
+            Log.i(LOG_TAG, "vCard cache thread starts running.");
+            if (mConnection == null) {
+                throw new NullPointerException("vCard cache thread must be launched "
+                        + "after a service connection is established");
+            }
+
             mWakeLock.acquire();
-            final CustomConnection connection = new CustomConnection();
-            startService(new Intent(ImportVCardActivity.this, VCardService.class));
-            bindService(new Intent(ImportVCardActivity.this,
-                    VCardService.class), connection, Context.BIND_AUTO_CREATE);
             try {
+                if (mCanceled == true) {
+                    Log.i(LOG_TAG, "vCard cache operation is canceled.");
+                    return;
+                }
+
+                final Context context = ImportVCardActivity.this;
                 // Uris given from caller applications may not be opened twice: consider when
                 // it is not from local storage (e.g. "file:///...") but from some special
                 // provider (e.g. "content://...").
@@ -346,31 +277,36 @@
                     }
                     final Uri localDataUri = copyTo(sourceUri, filename);
                     if (mCanceled) {
+                        Log.i(LOG_TAG, "vCard cache operation is canceled.");
                         break;
                     }
                     if (localDataUri == null) {
                         Log.w(LOG_TAG, "destUri is null");
                         break;
                     }
-                    final ImportRequest parameter = constructRequestParameter(localDataUri);
+                    final ImportRequest parameter = constructImportRequest(localDataUri);
                     if (mCanceled) {
+                        Log.i(LOG_TAG, "vCard cache operation is canceled.");
                         return;
                     }
-                    connection.requestSend(parameter);
+                    mConnection.sendImportRequest(parameter);
                 }
             } catch (OutOfMemoryError e) {
-                Log.e(LOG_TAG, "OutOfMemoryError");
+                Log.e(LOG_TAG, "OutOfMemoryError occured during caching vCard");
                 System.gc();
                 runOnUiThread(new DialogDisplayer(
                         getString(R.string.fail_reason_low_memory_during_import)));
             } catch (IOException e) {
-                Log.e(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "IOException during caching vCard", e);
                 runOnUiThread(new DialogDisplayer(
                         getString(R.string.fail_reason_io_error)));
             } finally {
+                Log.i(LOG_TAG, "Finished caching vCard.");
                 mWakeLock.release();
-                mProgressDialogForCacheVCard.dismiss();
-                connection.tryDisconnectAndFinish();
+                unbindService(mConnection);
+                mProgressDialogForCachingVCard.dismiss();
+                mProgressDialogForCachingVCard = null;
+                finish();
             }
         }
 
@@ -423,28 +359,27 @@
         }
 
         /**
-         * Reads the Uri once (or twice) and constructs {@link ImportRequest} from
+         * Reads the Uri (possibly multiple times) and constructs {@link ImportRequest} from
          * its content.
+         *
+         * Uri should be local one, as we cannot guarantee other types of Uris can be read
+         * multiple times.
          */
-        private ImportRequest constructRequestParameter(final Uri uri) {
-            final ContentResolver resolver =
-                    ImportVCardActivity.this.getContentResolver();
+        private ImportRequest constructImportRequest(final Uri localDataUri) {
+            final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
             VCardEntryCounter counter = null;
             VCardSourceDetector detector = null;
             VCardInterpreterCollection interpreter = null;
             int vcardVersion = VCARD_VERSION_V21;
             try {
                 boolean shouldUseV30 = false;
-                InputStream is;
-
-                is = resolver.openInputStream(uri);
+                InputStream is = resolver.openInputStream(localDataUri);
                 mVCardParser = new VCardParser_V21();
                 try {
                     counter = new VCardEntryCounter();
                     detector = new VCardSourceDetector();
-                    interpreter =
-                            new VCardInterpreterCollection(
-                                    Arrays.asList(counter, detector));
+                    interpreter = new VCardInterpreterCollection(
+                            Arrays.asList(counter, detector));
                     mVCardParser.parse(is, interpreter);
                 } catch (VCardVersionException e1) {
                     try {
@@ -453,14 +388,13 @@
                     }
 
                     shouldUseV30 = true;
-                    is = resolver.openInputStream(uri);
+                    is = resolver.openInputStream(localDataUri);
                     mVCardParser = new VCardParser_V30();
                     try {
                         counter = new VCardEntryCounter();
                         detector = new VCardSourceDetector();
-                        interpreter =
-                                new VCardInterpreterCollection(
-                                        Arrays.asList(counter, detector));
+                        interpreter = new VCardInterpreterCollection(
+                                Arrays.asList(counter, detector));
                         mVCardParser.parse(is, interpreter);
                     } catch (VCardVersionException e2) {
                         throw new VCardException("vCard with unspported version.");
@@ -479,13 +413,13 @@
                 Log.w(LOG_TAG, "Nested Exception is found (it may be false-positive).");
                 // Go through without returning null.
             } catch (VCardException e) {
-                Log.e(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "VCardException during constructing ImportRequest", e);
                 return null;
             } catch (IOException e) {
-                Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
+                Log.e(LOG_TAG, "IOException during constructing ImportRequest", e);
                 return null;
             }
-            return new ImportRequest(mAccount, uri,
+            return new ImportRequest(mAccount, localDataUri,
                     detector.getEstimatedType(),
                     detector.getEstimatedCharset(),
                     vcardVersion, counter.getCount());
@@ -502,7 +436,9 @@
             }
         }
 
+        @Override
         public void onCancel(DialogInterface dialog) {
+            Log.i(LOG_TAG, "Cancel request has come. Abort caching vCard.");
             cancel();
         }
     }
@@ -921,17 +857,23 @@
                 return getVCardFileSelectDialog(false);
             }
             case R.id.dialog_cache_vcard: {
-                if (mProgressDialogForCacheVCard == null) {
+                if (mProgressDialogForCachingVCard == null) {
                     final String title = getString(R.string.caching_vcard_title);
                     final String message = getString(R.string.caching_vcard_message);
-                    mProgressDialogForCacheVCard = new ProgressDialog(this);
-                    mProgressDialogForCacheVCard.setTitle(title);
-                    mProgressDialogForCacheVCard.setMessage(message);
-                    mProgressDialogForCacheVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER);
-                    mProgressDialogForCacheVCard.setOnCancelListener(mVCardCacheThread);
-                    mVCardCacheThread.start();
+                    mProgressDialogForCachingVCard = new ProgressDialog(this);
+                    mProgressDialogForCachingVCard.setTitle(title);
+                    mProgressDialogForCachingVCard.setMessage(message);
+                    mProgressDialogForCachingVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+                    mProgressDialogForCachingVCard.setOnCancelListener(mVCardCacheThread);
+                    mConnection = new ImportRequestConnection();
+
+                    Log.i(LOG_TAG, "Bind to VCardService.");
+                    // We don't want the service finishes itself just after this connection.
+                    startService(new Intent(this, VCardService.class));
+                    bindService(new Intent(this, VCardService.class),
+                            mConnection, Context.BIND_AUTO_CREATE);
                 }
-                return mProgressDialogForCacheVCard;
+                return mProgressDialogForCachingVCard;
             }
             case R.id.dialog_io_exception: {
                 String message = (getString(R.string.scanning_sdcard_failed_message,
@@ -964,31 +906,11 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        if (mVCardCacheThread != null) {
-            final Uri[] uris = mVCardCacheThread.getSourceUris();
-            final int length = uris.length;
-            final String[] uriStrings = new String[length];
-            for (int i = 0; i < length; i++) {
-                    uriStrings[i] = uris[i].toString();
-            }
-            outState.putStringArray(CACHED_URIS, uriStrings);
-
-            mVCardCacheThread.cancel();
-        }
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle inState) {
-        final String[] uriStrings = inState.getStringArray(CACHED_URIS);
-        if (uriStrings != null && uriStrings.length > 0) {
-            final int length = uriStrings.length;
-            final Uri[] uris = new Uri[length];
-            for (int i = 0; i < length; i++) {
-                uris[i] = Uri.parse(uriStrings[i]);
-            }
-
-            mVCardCacheThread = new VCardCacheThread(uris);
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        if (mProgressDialogForCachingVCard != null) {
+            Log.i(LOG_TAG, "Cache thread is still running. Show progress dialog again.");
+            showDialog(R.id.dialog_cache_vcard);
         }
     }
 
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index 475d465..77dc8bd 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -15,98 +15,63 @@
  */
 package com.android.contacts.vcard;
 
+import com.android.contacts.R;
+
 import android.app.Service;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
-import android.text.format.DateUtils;
 import android.util.Log;
 import android.widget.Toast;
 
 import java.io.File;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.Date;
-
-import com.android.contacts.R;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
 
 /**
- * The class responsible for importing vCard from one ore multiple Uris.
+ * The class responsible for handling vCard import/export requests.
+ *
+ * This Service creates one ImportRequest/ExportRequest object (as Runnable) per request and push
+ * it to {@link ExecutorService} with single thread executor. The executor handles each request
+ * one by one, and notifies users when needed.
  */
+// TODO: Using IntentService looks simpler than using Service + ServiceConnection though this
+// works fine enough. Investigate the feasibility.
 public class VCardService extends Service {
-    private final static String LOG_TAG = VCardService.class.getSimpleName();
+    private final static String LOG_TAG = "VCardService";
 
     /* package */ static final int MSG_IMPORT_REQUEST = 1;
     /* package */ static final int MSG_EXPORT_REQUEST = 2;
     /* package */ static final int MSG_CANCEL_IMPORT_REQUEST = 3;
-    /* package */ static final int MSG_NOTIFY_IMPORT_FINISHED = 5;
 
     /* package */ static final int IMPORT_NOTIFICATION_ID = 1000;
     /* package */ static final int EXPORT_NOTIFICATION_ID = 1001;
 
     /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_";
 
-    public class ImportRequestHandler extends Handler {
-        private ImportProcessor mImportProcessor;
-        private ExportProcessor mExportProcessor = new ExportProcessor(VCardService.this);
-        private boolean mDoDelayedCancel = false;
-
-        public ImportRequestHandler() {
-            super();
-        }
-
+    public class RequestHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_IMPORT_REQUEST: {
-                    Log.i(LOG_TAG, "Received vCard import request.");
-                    if (mDoDelayedCancel) {
-                        Log.i(LOG_TAG, "A cancel request came before import request. " +
-                                "Refrain current import once.");
-                        mDoDelayedCancel = false;
-                    } else {
-                        final ImportRequest parameter = (ImportRequest)msg.obj;
-
-                        if (mImportProcessor == null || !mImportProcessor.isReadyForRequest()) {
-                            mImportProcessor = new ImportProcessor(VCardService.this);
-                        } else if (mImportProcessor.isCanceled()) {
-                            Log.i(LOG_TAG,
-                                    "Existing ImporterProcessor is canceled. create another.");
-                            mImportProcessor = new ImportProcessor(VCardService.this);
-                        }
-
-                        mImportProcessor.pushRequest(parameter);
-                        Toast.makeText(VCardService.this,
-                                getString(R.string.vcard_importer_start_message),
-                                Toast.LENGTH_LONG).show();
-                    }
+                    handleImportRequest((ImportRequest)msg.obj);
                     break;
                 }
                 case MSG_EXPORT_REQUEST: {
-                    Log.i(LOG_TAG, "Received vCard export request.");
-                    final ExportRequest parameter = (ExportRequest)msg.obj;
-                    mExportProcessor.pushRequest(parameter);
-                    Toast.makeText(VCardService.this,
-                            getString(R.string.vcard_exporter_start_message),
-                            Toast.LENGTH_LONG).show();
+                    handleExportRequest((ExportRequest)msg.obj);
                     break;
                 }
                 case MSG_CANCEL_IMPORT_REQUEST: {
-                    Log.i(LOG_TAG, "Received cancel import request.");
-                    if (mImportProcessor != null) {
-                        mImportProcessor.cancel();
-                    } else {
-                        Log.w(LOG_TAG, "ImportProcessor isn't ready. Delay the cancel request.");
-                        mDoDelayedCancel = true;
-                    }
+                    handleCancelAllImportRequest();
                     break;
                 }
-                case MSG_NOTIFY_IMPORT_FINISHED: {
-                    Log.i(LOG_TAG, "Received vCard import finish notification.");
-                    break;
-                }
+                // TODO: add cancel capability for export..
                 default: {
                     Log.w(LOG_TAG, "Received unknown request, ignoring it.");
                     super.hasMessages(msg.what);
@@ -115,8 +80,17 @@
         }
     }
 
-    private ImportRequestHandler mHandler = new ImportRequestHandler();
-    private Messenger mMessenger = new Messenger(mHandler);
+    private final Handler mHandler = new RequestHandler();
+    private final Messenger mMessenger = new Messenger(mHandler);
+    // Should be single thread, as we don't want to simultaneously handle import and export
+    // requests.
+    private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+
+    private int mCurrentJobId;
+    private final Map<Integer, ImportProcessor> mRunningJobMapForImport =
+            new HashMap<Integer, ImportProcessor>();
+    private final Map<Integer, ExportProcessor> mRunningJobMapForExport =
+            new HashMap<Integer, ExportProcessor>();
 
     @Override
     public int onStartCommand(Intent intent, int flags, int id) {
@@ -130,10 +104,123 @@
 
     @Override
     public void onDestroy() {
+        Log.i(LOG_TAG, "VCardService is finishing()");
+        cancelRequestsAndshutdown();
         clearCache();
         super.onDestroy();
     }
 
+    private synchronized void handleImportRequest(ImportRequest request) {
+        Log.i(LOG_TAG, String.format("Received vCard import request. id: %d", mCurrentJobId));
+        final ImportProcessor importProcessor =
+                new ImportProcessor(this, request, mCurrentJobId);
+        try {
+            mExecutorService.submit(importProcessor);
+        } catch (RejectedExecutionException e) {
+            Log.w(LOG_TAG, "vCard import request is rejected.", e);
+            // TODO: a little unkind to show Toast in this case, which is shown just a moment.
+            // Ideally we should show some persistent something users can notice more easily.
+            Toast.makeText(this, getString(R.string.vcard_import_request_rejected_message),
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+        mRunningJobMapForImport.put(mCurrentJobId, importProcessor);
+        mCurrentJobId++;
+        // TODO: Ideally we should detect the current status of import/export and show "started"
+        // when we can import right now and show "will start" when we cannot.
+        Toast.makeText(this, getString(R.string.vcard_import_will_start_message),
+                Toast.LENGTH_LONG).show();
+    }
+
+    private synchronized void handleExportRequest(ExportRequest request) {
+        Log.i(LOG_TAG, String.format("Received vCard export request. id: %d", mCurrentJobId));
+        final ExportProcessor exportProcessor =
+                new ExportProcessor(this, request, mCurrentJobId);
+        try {
+            mExecutorService.submit(exportProcessor);
+        } catch (RejectedExecutionException e) {
+            Log.w(LOG_TAG, "vCard export request is rejected.", e);
+            Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+        mRunningJobMapForExport.put(mCurrentJobId, exportProcessor);
+        mCurrentJobId++;
+        // See the comment in handleImportRequest()
+        Toast.makeText(this, getString(R.string.vcard_export_will_start_message),
+                Toast.LENGTH_LONG).show();
+    }
+
+    private synchronized void handleCancelAllImportRequest() {
+        Log.i(LOG_TAG, "Received cancel import request.");
+        cancelAllImportRequest();
+        mRunningJobMapForImport.clear();
+    }
+
+    private void cancelAllImportRequest() {
+        for (final Map.Entry<Integer, ImportProcessor> entry :
+                mRunningJobMapForImport.entrySet()) {
+            final int jobId = entry.getKey();
+            final ImportProcessor importProcessor = entry.getValue();
+            importProcessor.cancel();
+            Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
+        }
+    }
+
+    private void cancelAllExportRequest() {
+        for (final Map.Entry<Integer, ExportProcessor> entry :
+                mRunningJobMapForExport.entrySet()) {
+            final int jobId = entry.getKey();
+            final ExportProcessor exportProcessor = entry.getValue();
+            exportProcessor.cancel();
+            Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
+        }
+    }
+
+    /* package */ synchronized void handleFinishImportNotification(
+            int jobId, boolean successful) {
+        Log.i(LOG_TAG, String.format("Received vCard import finish notification (id: %d). "
+                + "Result: %b", jobId, (successful ? "success" : "failure")));
+        if (mRunningJobMapForImport.remove(jobId) == null) {
+            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
+        }
+    }
+
+    /* package */ synchronized void handleFinishExportNotification(
+            int jobId, boolean successful) {
+        Log.i(LOG_TAG, String.format("Received vCard export finish notification (id: %d). "
+                + "Result: %b", jobId, (successful ? "success" : "failure")));
+        if (mRunningJobMapForExport.remove(jobId) == null) {
+            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
+        }
+    }
+
+    /**
+     * Cancels all the import/export requests and call {@link ExecutorService#shutdown()}, which
+     * means this Service becomes no longer ready for import/export requests. Mainly used in
+     * onDestroy().
+     */
+    private synchronized void cancelRequestsAndshutdown() {
+        synchronized (this) {
+            if (mRunningJobMapForImport.size() > 0) {
+                Log.i(LOG_TAG,
+                        String.format("Cancel existing all import requests (remains: ",
+                                mRunningJobMapForImport.size()));
+                cancelAllImportRequest();
+            }
+            if (mRunningJobMapForExport.size() > 0) {
+                Log.i(LOG_TAG,
+                        String.format("Cancel existing all import requests (remains: ",
+                                mRunningJobMapForExport.size()));
+                cancelAllExportRequest();
+            }
+            mExecutorService.shutdown();
+        }
+    }
+
+    /**
+     * Removes import caches stored locally.
+     */
     private void clearCache() {
         Log.i(LOG_TAG, "start removing cache files if exist.");
         final String[] fileLists = fileList();
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 62da845..dacc9cb 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -679,12 +679,14 @@
                 entry.typeString = "";
                 for (EditType type : kind.typeList) {
                     if (type.rawValue == entry.type) {
-                        if (type.customColumn == null) {
-                            // Non-custom type. Get its description from the resource
-                            entry.typeString = context.getString(type.labelRes);
-                        } else {
-                            // Custom type. Read it from the database
-                            entry.typeString = values.getAsString(type.customColumn);
+                        if (!type.unspecifiedType) {
+                            if (type.customColumn == null) {
+                                // Non-custom type. Get its description from the resource
+                                entry.typeString = context.getString(type.labelRes);
+                            } else {
+                                // Custom type. Read it from the database
+                                entry.typeString = values.getAsString(type.customColumn);
+                            }
                         }
                         break;
                     }
diff --git a/src/com/android/contacts/widget/TransitionAnimationView.java b/src/com/android/contacts/widget/TransitionAnimationView.java
index 1b86a6f..114e97d 100644
--- a/src/com/android/contacts/widget/TransitionAnimationView.java
+++ b/src/com/android/contacts/widget/TransitionAnimationView.java
@@ -132,6 +132,12 @@
     }
 
     private void start(View view, boolean closing) {
+        if (mEnterAnimation.isRunning()) {
+            mEnterAnimation.end();
+        }
+        if (mExitAnimation.isRunning()) {
+            mExitAnimation.end();
+        }
         if (view.getVisibility() != View.VISIBLE) {
             if (!closing) {
                 mEnterAnimation.setTarget(view);
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 7530ce7..6891c2f 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -61,7 +61,6 @@
         <item>SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED</item>
         <item>SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED</item>
         <item>TODO: JOIN_CONTACT</item>
-        <item>ACTION_GET_MULTIPLE_PHONES</item>
 
         <!-- Edit Contact -->
         <item>EDIT (content uri with only id)</item>
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 3683a80..66079ad 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -60,8 +60,6 @@
 
     private static final String CONTACT_LIST_ACTIVITY_CLASS_NAME =
             "com.android.contacts.activities.ContactBrowserActivity";
-    private static final String MULTIPLE_PHONE_PICKER_ACTIVITY_CLASS_NAME =
-            "com.android.contacts.MultiplePhonePickerActivity";
 
     private static final int LIST_DEFAULT = 0;
     private static final int LIST_ALL_CONTACTS_ACTION = 1;
@@ -104,34 +102,33 @@
     private static final int SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED = 38;
     private static final int SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = 39;
     private static final int JOIN_CONTACT = 40;
-    private static final int ACTION_GET_MULTIPLE_PHONES = 41;
 
-    private static final int EDIT_CONTACT = 42;
-    private static final int EDIT_CONTACT_LOOKUP = 43;
-    private static final int EDIT_CONTACT_LOOKUP_ID = 44;
-    private static final int EDIT_RAW_CONTACT = 45;
-    private static final int EDIT_LEGACY = 46;
-    private static final int EDIT_NEW_CONTACT = 47;
-    private static final int EDIT_NEW_CONTACT_WITH_DATA = 48;
-    private static final int EDIT_NEW_CONTACT_FOR_ACCOUNT = 49;
-    private static final int EDIT_NEW_CONTACT_FOR_ACCOUNT_WITH_DATA = 50;
-    private static final int EDIT_NEW_RAW_CONTACT = 51;
-    private static final int EDIT_NEW_LEGACY = 52;
+    private static final int EDIT_CONTACT = 41;
+    private static final int EDIT_CONTACT_LOOKUP = 42;
+    private static final int EDIT_CONTACT_LOOKUP_ID = 43;
+    private static final int EDIT_RAW_CONTACT = 44;
+    private static final int EDIT_LEGACY = 45;
+    private static final int EDIT_NEW_CONTACT = 46;
+    private static final int EDIT_NEW_CONTACT_WITH_DATA = 47;
+    private static final int EDIT_NEW_CONTACT_FOR_ACCOUNT = 48;
+    private static final int EDIT_NEW_CONTACT_FOR_ACCOUNT_WITH_DATA = 49;
+    private static final int EDIT_NEW_RAW_CONTACT = 50;
+    private static final int EDIT_NEW_LEGACY = 51;
 
-    private static final int VIEW_CONTACT = 53;
-    private static final int VIEW_CONTACT_LOOKUP = 54;
-    private static final int VIEW_CONTACT_LOOKUP_ID = 55;
-    private static final int VIEW_RAW_CONTACT = 56;
-    private static final int VIEW_LEGACY = 57;
+    private static final int VIEW_CONTACT = 52;
+    private static final int VIEW_CONTACT_LOOKUP = 53;
+    private static final int VIEW_CONTACT_LOOKUP_ID = 54;
+    private static final int VIEW_RAW_CONTACT = 55;
+    private static final int VIEW_LEGACY = 56;
 
-    private static final int DIAL = 58;
-    private static final int DIAL_phone = 59;
-    private static final int DIAL_person = 60;
-    private static final int DIAL_voicemail = 61;
-    private static final int CALL_BUTTON = 62;
-    private static final int DIAL_tel = 63;
-    private static final int VIEW_tel = 64;
-    private static final int VIEW_calllog = 65;
+    private static final int DIAL = 57;
+    private static final int DIAL_phone = 58;
+    private static final int DIAL_person = 59;
+    private static final int DIAL_voicemail = 60;
+    private static final int CALL_BUTTON = 61;
+    private static final int DIAL_tel = 62;
+    private static final int VIEW_tel = 63;
+    private static final int VIEW_calllog = 64;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -379,15 +376,6 @@
                 // TODO
                 break;
             }
-            case ACTION_GET_MULTIPLE_PHONES: {
-                Intent intent = new Intent(Intents.ACTION_GET_MULTIPLE_PHONES);
-                intent.setType(Phone.CONTENT_TYPE);
-                intent.putExtra(Intents.EXTRA_PHONE_URIS, new Uri[] {
-                        Uri.parse("tel:555-1212"), Uri.parse("tel:555-2121")
-                });
-                startMultiplePhoneSelectionActivityForResult(intent);
-                break;
-            }
             case EDIT_CONTACT: {
                 final long contactId = findArbitraryContactWithPhoneNumber();
                 final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
@@ -580,13 +568,6 @@
         startActivity(intent);
     }
 
-    private void startMultiplePhoneSelectionActivityForResult(Intent intent) {
-        intent.setComponent(
-                new ComponentName(ANDROID_CONTACTS_PACKAGE,
-                        MULTIPLE_PHONE_PICKER_ACTIVITY_CLASS_NAME));
-        startActivityForResult(intent, 13);
-    }
-
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         Intent intent = new Intent(this, ResultActivity.class);
diff --git a/tests/src/com/android/contacts/vcard/ImportProcessorTest.java b/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
deleted file mode 100644
index 72245ba..0000000
--- a/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-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;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.util.List;
-
-public class ImportProcessorTest extends AndroidTestCase {
-    private static final String LOG_TAG = "ImportProcessorTest";
-    private ImportProcessor mImportProcessor;
-
-    private String mCopiedFileName;
-
-    // XXX: better way to copy stream?
-    private Uri copyToLocal(final String fileName) throws IOException {
-        final Context context = getContext();
-        // We need to use Context of this unit test runner (not of test to be tested),
-        // as only the former knows assets to be copied.
-        final Context testContext = getTestContext();
-        final ContentResolver resolver = testContext.getContentResolver();
-        mCopiedFileName = fileName;
-        ReadableByteChannel inputChannel = null;
-        WritableByteChannel outputChannel = null;
-        Uri destUri;
-        try {
-            inputChannel = Channels.newChannel(testContext.getAssets().open(fileName));
-            destUri = Uri.parse(context.getFileStreamPath(fileName).toURI().toString());
-            outputChannel =
-                    getContext().openFileOutput(fileName,
-                            Context.MODE_WORLD_WRITEABLE).getChannel();
-            final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
-            while (inputChannel.read(buffer) != -1) {
-                buffer.flip();
-                outputChannel.write(buffer);
-                buffer.compact();
-            }
-            buffer.flip();
-            while (buffer.hasRemaining()) {
-                outputChannel.write(buffer);
-            }
-        } finally {
-            if (inputChannel != null) {
-                try {
-                    inputChannel.close();
-                } catch (IOException e) {
-                    Log.w(LOG_TAG, "Failed to close inputChannel.");
-                }
-            }
-            if (outputChannel != null) {
-                try {
-                    outputChannel.close();
-                } catch(IOException e) {
-                    Log.w(LOG_TAG, "Failed to close outputChannel");
-                }
-            }
-        }
-        return destUri;
-    }
-
-    @Override
-    public void setUp() {
-        mImportProcessor = new ImportProcessor(getContext());
-        mImportProcessor.ensureInit();
-        mCopiedFileName = null;
-    }
-
-    @Override
-    public void tearDown() {
-        if (!TextUtils.isEmpty(mCopiedFileName)) {
-            getContext().deleteFile(mCopiedFileName);
-            mCopiedFileName = null;
-        }
-    }
-
-    /**
-     * Confirms {@link ImportProcessor#readOneVCard(android.net.Uri, int, String,
-     * com.android.vcard.VCardInterpreter, int[])} successfully handles correct input.
-     */
-    public void testProcessSimple() throws IOException {
-        final Uri uri = copyToLocal("v21_simple.vcf");
-        final int vcardType = VCardSourceDetector.PARSE_TYPE_UNKNOWN;
-        final String charset = null;
-        final VCardInterpreter interpreter = new EmptyVCardInterpreter();
-        final int[] versions = new int[] {
-                ImportVCardActivity.VCARD_VERSION_V21
-        };
-
-        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() {
-    }
-}
diff --git a/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java b/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java
deleted file mode 100644
index 4765b38..0000000
--- a/tests/src/com/android/contacts/vcard/MockVCardEntryCommitter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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