Merge "Show Notification when parsing vCard failed." into honeycomb
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 95dad37..b0dffca 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -745,11 +745,12 @@
          we believe it is rather rare in the actual world. -->
     <string name="fail_reason_vcard_parse_error">Failed to parse vCard for unexpected reason</string>
 
-    <!-- The failed reason shown when the current vCard importer cannot parse the file since the
-         parser is incomplete (actually, there's missing part in the current vCard parser, though
-         our understanding is that the functionality missed by the current vCard parser
-         is rarely needed in the actual vCard...). -->
-    <string name="fail_reason_vcard_not_supported_error">Failed to parse vCard though it seems in valid format, since the current implementation does not support it</string>
+    <!-- The failed reason shown when vCard importer doesn't support the format.
+         This may be shown when the vCard is corrupted [CHAR LIMIT=40] -->
+    <string name="fail_reason_not_supported">The format is not supported.</string>
+
+    <!-- Message used when vCard import has failed. [CHAR LIMIT=40] -->
+    <string name="vcard_import_failed">Failed to import vCard</string>
 
     <!-- The failure message shown when the system could not find any vCard file.
          (with extension ".vcf" in USB storage.)
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 71bd376..8cda708 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -34,6 +34,8 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.ProgressDialog;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -47,6 +49,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
@@ -57,6 +60,7 @@
 import android.text.TextUtils;
 import android.text.style.RelativeSizeSpan;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.io.File;
 import java.io.IOException;
@@ -101,6 +105,11 @@
 
     private static final String SECURE_DIRECTORY_NAME = ".android_secure";
 
+    /**
+     * Notification id used when error happened before sending an import request to VCardServer.
+     */
+    private static final int DEFAULT_NOTIFICATION_ID = 1000;
+
     final static String CACHED_URIS = "cached_uris";
 
     private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener;
@@ -121,6 +130,8 @@
 
     private String mErrorMessage;
 
+    private Handler mHandler = new Handler();
+
     private static class VCardFile {
         private final String mName;
         private final String mCanonicalPath;
@@ -155,6 +166,7 @@
             mResId = R.id.dialog_error_with_message;
             mErrorMessage = errorMessage;
         }
+        @Override
         public void run() {
             showDialog(mResId);
         }
@@ -162,10 +174,11 @@
 
     private class CancelListener
         implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+        @Override
         public void onClick(DialogInterface dialog, int which) {
             finish();
         }
-
+        @Override
         public void onCancel(DialogInterface dialog) {
             finish();
         }
@@ -285,8 +298,18 @@
                         Log.w(LOG_TAG, "destUri is null");
                         break;
                     }
-                    final ImportRequest parameter = constructImportRequest(
-                            localDataUri, sourceUri);
+                    final ImportRequest parameter;
+                    try {
+                        parameter = constructImportRequest(localDataUri, sourceUri);
+                    } catch (VCardException e) {
+                        Log.e(LOG_TAG, "Maybe the file is in wrong format", e);
+                        showFailureNotification(R.string.fail_reason_not_supported);
+                        return;
+                    } catch (IOException e) {
+                        Log.e(LOG_TAG, "Unexpected IOException", e);
+                        showFailureNotification(R.string.fail_reason_io_error);
+                        return;
+                    }
                     if (mCanceled) {
                         Log.i(LOG_TAG, "vCard cache operation is canceled.");
                         return;
@@ -371,7 +394,8 @@
          * information. This variable populates {@link ImportRequest#originalUri}.
          */
         private ImportRequest constructImportRequest(
-                final Uri localDataUri, final Uri originalUri) {
+                final Uri localDataUri, final Uri originalUri)
+                throws IOException, VCardException {
             final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
             VCardEntryCounter counter = null;
             VCardSourceDetector detector = null;
@@ -417,13 +441,8 @@
                 vcardVersion = shouldUseV30 ? VCARD_VERSION_V30 : VCARD_VERSION_V21;
             } catch (VCardNestedException e) {
                 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, "VCardException during constructing ImportRequest", e);
-                return null;
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "IOException during constructing ImportRequest", e);
-                return null;
+                // Go through without throwing the Exception, as we may be able to detect the
+                // version before it
             }
             return new ImportRequest(mAccount,
                     localDataUri, originalUri,
@@ -943,4 +962,21 @@
             showDialog(R.id.dialog_searching_vcard);
         }
     }
+
+    private void showFailureNotification(int reasonId) {
+        final NotificationManager notificationManager =
+                (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+        final Notification notification =
+                VCardService.constructImportFailureNotification(
+                        ImportVCardActivity.this,
+                        getString(reasonId));
+        notificationManager.notify(DEFAULT_NOTIFICATION_ID, notification);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Toast.makeText(ImportVCardActivity.this,
+                        getString(R.string.vcard_import_failed), Toast.LENGTH_LONG).show();
+            }
+        });
+    }
 }
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index efa93d4..13d938c 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -491,6 +491,23 @@
     }
 
     /**
+     * Constructs a Notification telling the vCard import has failed.
+     *
+     * @param context
+     * @param reason The reason why the import has failed. Shown in description field.
+     */
+    /* package */ static Notification constructImportFailureNotification(
+            Context context, String reason) {
+        return new Notification.Builder(context)
+                .setAutoCancel(true)
+                .setSmallIcon(android.R.drawable.stat_notify_error)
+                .setContentTitle(context.getString(R.string.vcard_import_failed))
+                .setContentText(reason)
+                .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
+                .getNotification();
+    }
+
+    /**
      * Returns an appropriate file name for vCard export. Returns null when impossible.
      *
      * @return destination path for a vCard file to be exported. null on error and mErrorReason