Add Cancel capability toward vCard import procedure.
Bug: 2801638
Change-Id: Ia563fa4aed48ad01a6fbb29b350a35c46b7085a3
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 57f2c35..992bf5d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -508,6 +508,9 @@
</intent-filter>
</activity>
+ <activity android:name=".vcard.CancelImportActivity"
+ android:theme="@style/BackgroundOnly" />
+
<activity android:name=".vcard.SelectAccountActivity"
android:theme="@style/BackgroundOnly" />
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 77673e3..b1ed87f 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -20,7 +20,7 @@
<item type="id" name="dialog_sync_add"/>
<item type="id" name="dialog_import_export"/>
- <!-- For ImportVCardActivity -->
+ <!-- For vcard.ImportVCardActivity -->
<item type="id" name="dialog_searching_vcard"/>
<item type="id" name="dialog_sdcard_not_found"/>
<item type="id" name="dialog_vcard_not_found"/>
@@ -31,6 +31,9 @@
<item type="id" name="dialog_io_exception"/>
<item type="id" name="dialog_error_with_message"/>
+ <!-- For vcard.CancelImportActivity -->
+ <item type="id" name="dialog_cancel_import_confirmation"/>
+
<!-- For ContactDeletionInteraction -->
<item type="id" name="dialog_delete_contact_confirmation"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 43e6489..ecfe5c8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -768,9 +768,12 @@
<!-- The title shown when reading vCard is canceled (probably by a user) -->
<string name="reading_vcard_canceled_title">Reading vCard data was canceled</string>
- <!-- The title shown when reading vCard is canceled (probably by a user) -->
+ <!-- The title shown when reading vCard finished -->
<string name="importing_vcard_finished_title">Finished importing vCard</string>
+ <!-- The title shown when importing vCard is canceled (probably by a user) -->
+ <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>
@@ -847,6 +850,14 @@
<!-- Message in progress bar while exporting contact list to a file "(current number) of (total number) contacts" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
<string name="exporting_contact_list_progress"><xliff:g id="current_number">%s</xliff:g> of <xliff:g id="total_number">%s</xliff:g> contacts</string>
+ <!-- Title shown in a Dialog confirming a user's cancel request toward existing vCard import. -->
+ <string name="cancel_import_confirmation_title">Canceling import vCard</string>
+
+ <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard import.
+ The argument is the Uri for the vCard import the user wants to cancel.
+ -->
+ <string name="cancel_import_confirmation_message">Are you sure to cancel importing vCard?</string>
+
<!-- The string used to describe Contacts as a searchable item within system search settings. -->
<string name="search_settings_description">Names of your contacts</string>
diff --git a/src/com/android/contacts/vcard/CancelImportActivity.java b/src/com/android/contacts/vcard/CancelImportActivity.java
new file mode 100644
index 0000000..22de0da
--- /dev/null
+++ b/src/com/android/contacts/vcard/CancelImportActivity.java
@@ -0,0 +1,129 @@
+/*
+ * 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.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.contacts.R;
+
+/**
+ * The Activity for canceling ongoing vCard import.
+ *
+ * Currently we ignore tha case where there are more than one import requests
+ * with a same Uri in the queue.
+ */
+public class CancelImportActivity extends Activity {
+ private final String LOG_TAG = "CancelImportActivity";
+
+ /* package */ final String EXTRA_TARGET_URI = "extra_target_uri";
+
+ private class CustomConnection implements ServiceConnection {
+ private Messenger mMessenger;
+ public void doBindService() {
+ bindService(new Intent(CancelImportActivity.this,
+ VCardService.class), this, Context.BIND_AUTO_CREATE);
+ }
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mMessenger = new Messenger(service);
+
+ try {
+ mMessenger.send(Message.obtain(null,
+ VCardService.MSG_CANCEL_IMPORT_REQUEST,
+ null));
+ finish();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
+ CancelImportActivity.this.showDialog(R.string.fail_reason_unknown);
+ }
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mMessenger = null;
+ }
+ }
+
+ private class RequestCancelImportListener implements DialogInterface.OnClickListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mConnection.doBindService();
+ }
+ }
+
+ private class CancelListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }
+
+ private final CancelListener mCancelListener = new CancelListener();
+ private final CustomConnection mConnection = new CustomConnection();
+ // private String mTargetUri;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ showDialog(R.id.dialog_cancel_import_confirmation);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int resId, Bundle bundle) {
+ switch (resId) {
+
+ case R.id.dialog_cancel_import_confirmation: {
+ return getConfirmationDialog();
+ }
+ case R.string.fail_reason_unknown:
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.reading_vcard_failed_title))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(getString(resId))
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ }
+ return super.onCreateDialog(resId, bundle);
+ }
+
+ private Dialog getConfirmationDialog() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.cancel_import_confirmation_title)
+ .setMessage(R.string.cancel_import_confirmation_message)
+ .setPositiveButton(android.R.string.ok, new RequestCancelImportListener())
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
+ return builder.create();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/CancelImportRequest.java b/src/com/android/contacts/vcard/CancelImportRequest.java
new file mode 100644
index 0000000..dd10187
--- /dev/null
+++ b/src/com/android/contacts/vcard/CancelImportRequest.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Class representing one request for canceling vCard import (given as a Uri).
+ */
+public class CancelImportRequest {
+ public final Uri uri;
+ public CancelImportRequest(Uri uri) {
+ this.uri = uri;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 427d62d..e19aaf1 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -20,11 +20,17 @@
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;
@@ -54,10 +60,39 @@
* recreate multiple instances, as this holds total count of vCard entries to be imported.
*/
public class ImportProcessor {
- private static final String LOG_TAG = "ImportRequestProcessor";
+ private static final String LOG_TAG = ImportProcessor.class.getSimpleName();
+
+ private class CustomConnection implements ServiceConnection {
+ private Messenger mMessenger;
+ public void doBindService() {
+ mContext.bindService(new Intent(mContext, VCardService.class),
+ this, Context.BIND_AUTO_CREATE);
+ }
+
+ public void sendFinisheNotification() {
+ 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 final CustomConnection mConnection = new CustomConnection();
+
private ContentResolver mResolver;
private NotificationManager mNotificationManager;
@@ -107,6 +142,8 @@
public ImportProcessor(final Context context) {
mContext = context;
+
+ mConnection.doBindService();
}
/**
@@ -180,6 +217,8 @@
// imported in this procedure. Instead, we show them entries only when
// there's just one created uri.
doFinishNotification(mCreatedUris.size() > 0 ? mCreatedUris.get(0) : null);
+ mConnection.sendFinisheNotification();
+ mContext.unbindService(mConnection);
} finally {
// TODO: verify this works fine.
mReadyForRequest = false; // Just in case.
@@ -257,10 +296,16 @@
private void doFinishNotification(final Uri createdUri) {
final Notification notification = new Notification();
- notification.icon = android.R.drawable.stat_sys_download_done;
+ final String title;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
- final String title = mContext.getString(R.string.importing_vcard_finished_title);
+ if (isCanceled()) {
+ notification.icon = android.R.drawable.stat_notify_error;
+ title = mContext.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);
+ }
final Intent intent;
if (createdUri != null) {
diff --git a/src/com/android/contacts/vcard/ImportProgressNotifier.java b/src/com/android/contacts/vcard/ImportProgressNotifier.java
index 286bb43..99a6f55 100644
--- a/src/com/android/contacts/vcard/ImportProgressNotifier.java
+++ b/src/com/android/contacts/vcard/ImportProgressNotifier.java
@@ -23,7 +23,6 @@
import android.widget.RemoteViews;
import com.android.contacts.R;
-import com.android.contacts.activities.ContactBrowserActivity;
import com.android.vcard.VCardEntry;
import com.android.vcard.VCardEntryHandler;
@@ -97,7 +96,7 @@
final PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0,
- new Intent(context, ContactBrowserActivity.class),
+ new Intent(context, CancelImportActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
notification.contentIntent = pendingIntent;
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 6374af5..1140ff8 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -183,6 +183,7 @@
}
}
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mPendingRequests) {
mMessenger = new Messenger(service);
@@ -209,6 +210,7 @@
}
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mPendingRequests) {
if (!mPendingRequests.isEmpty()) {
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index 58e1333..02daf78 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -30,10 +30,12 @@
* The class responsible for importing vCard from one ore multiple Uris.
*/
public class VCardService extends Service {
- private final static String LOG_TAG = "ImportVCardService";
+ private final static String LOG_TAG = VCardService.class.getSimpleName();
/* 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;
@@ -45,15 +47,26 @@
private static final int IMPORT_NOTIFICATION_THRESHOLD = 10;
public class ImportRequestHandler extends Handler {
- private final ImportProcessor mImportProcessor =
- new ImportProcessor(VCardService.this);
- private final ExportProcessor mExportProcessor =
- new ExportProcessor(VCardService.this);
+ private ImportProcessor mImportProcessor;
+ private ExportProcessor mExportProcessor = new ExportProcessor(VCardService.this);
+
+ public ImportRequestHandler() {
+ super();
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_IMPORT_REQUEST: {
final ImportRequest parameter = (ImportRequest)msg.obj;
+
+ if (mImportProcessor == null) {
+ 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);
if (parameter.entryCount > IMPORT_NOTIFICATION_THRESHOLD) {
Toast.makeText(VCardService.this,
@@ -70,6 +83,14 @@
Toast.LENGTH_LONG).show();
break;
}
+ case MSG_CANCEL_IMPORT_REQUEST: {
+ mImportProcessor.cancel();
+ break;
+ }
+ case MSG_NOTIFY_IMPORT_FINISHED: {
+ Log.d(LOG_TAG, "MSG_NOTIFY_IMPORT_FINISHED");
+ break;
+ }
default: {
Log.e(LOG_TAG, "Unknown request type: " + msg.what);
super.hasMessages(msg.what);
@@ -78,7 +99,8 @@
}
}
- private Messenger mMessenger = new Messenger(new ImportRequestHandler());
+ private ImportRequestHandler mHandler = new ImportRequestHandler();
+ private Messenger mMessenger = new Messenger(mHandler);
@Override
public int onStartCommand(Intent intent, int flags, int id) {