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