• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.contacts;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.ProgressDialog;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.DialogInterface.OnCancelListener;
26 import android.content.DialogInterface.OnClickListener;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.PowerManager;
30 import android.syncml.pim.VBuilder;
31 import android.syncml.pim.VBuilderCollection;
32 import android.syncml.pim.VParser;
33 import android.syncml.pim.vcard.VCardDataBuilder;
34 import android.syncml.pim.vcard.VCardEntryCounter;
35 import android.syncml.pim.vcard.VCardException;
36 import android.syncml.pim.vcard.VCardNestedException;
37 import android.syncml.pim.vcard.VCardParser_V21;
38 import android.syncml.pim.vcard.VCardParser_V30;
39 import android.syncml.pim.vcard.VCardSourceDetector;
40 import android.syncml.pim.vcard.VCardVersionException;
41 import android.text.SpannableStringBuilder;
42 import android.text.Spanned;
43 import android.text.style.RelativeSizeSpan;
44 import android.util.Log;
45 
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.IOException;
49 import java.text.DateFormat;
50 import java.text.SimpleDateFormat;
51 import java.util.Arrays;
52 import java.util.Date;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Set;
56 import java.util.Vector;
57 
58 class VCardFile {
59     private String mName;
60     private String mCanonicalPath;
61     private long mLastModified;
62 
VCardFile(String name, String canonicalPath, long lastModified)63     public VCardFile(String name, String canonicalPath, long lastModified) {
64         mName = name;
65         mCanonicalPath = canonicalPath;
66         mLastModified = lastModified;
67     }
68 
getName()69     public String getName() {
70         return mName;
71     }
72 
getCanonicalPath()73     public String getCanonicalPath() {
74         return mCanonicalPath;
75     }
76 
getLastModified()77     public long getLastModified() {
78         return mLastModified;
79     }
80 }
81 
82 /**
83  * Class for importing vCard. Several user interaction will be required while reading
84  * (selecting a file, waiting a moment, etc.)
85  */
86 public class ImportVCardActivity extends Activity {
87     private static final String LOG_TAG = "ImportVCardActivity";
88     private static final boolean DO_PERFORMANCE_PROFILE = false;
89 
90     private ProgressDialog mProgressDialog;
91     private Handler mHandler = new Handler();
92     private boolean mLastNameComesBeforeFirstName;
93 
94     private class CancelListener
95         implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
onClick(DialogInterface dialog, int which)96         public void onClick(DialogInterface dialog, int which) {
97             finish();
98         }
99 
onCancel(DialogInterface dialog)100         public void onCancel(DialogInterface dialog) {
101             finish();
102         }
103     }
104 
105     private CancelListener mCancelListener = new CancelListener();
106 
107     private class ErrorDisplayer implements Runnable {
108         private String mErrorMessage;
109 
ErrorDisplayer(String errorMessage)110         public ErrorDisplayer(String errorMessage) {
111             mErrorMessage = errorMessage;
112         }
113 
run()114         public void run() {
115             String message =
116                 getString(R.string.reading_vcard_failed_message, mErrorMessage);
117             AlertDialog.Builder builder =
118                 new AlertDialog.Builder(ImportVCardActivity.this)
119                     .setTitle(getString(R.string.reading_vcard_failed_title))
120                     .setIcon(android.R.drawable.ic_dialog_alert)
121                     .setMessage(message)
122                     .setOnCancelListener(mCancelListener)
123                     .setPositiveButton(android.R.string.ok, mCancelListener);
124             builder.show();
125         }
126     }
127 
128     private class VCardReadThread extends Thread
129             implements DialogInterface.OnCancelListener {
130         private String mCanonicalPath;
131         private List<VCardFile> mVCardFileList;
132         private ContentResolver mResolver;
133         private VCardParser_V21 mVCardParser;
134         private boolean mCanceled;
135         private PowerManager.WakeLock mWakeLock;
136 
VCardReadThread(String canonicalPath)137         public VCardReadThread(String canonicalPath) {
138             mCanonicalPath = canonicalPath;
139             mVCardFileList = null;
140             init();
141         }
142 
VCardReadThread(List<VCardFile> vcardFileList)143         public VCardReadThread(List<VCardFile> vcardFileList) {
144             mCanonicalPath = null;
145             mVCardFileList = vcardFileList;
146             init();
147         }
148 
init()149         private void init() {
150             Context context = ImportVCardActivity.this;
151             mResolver = context.getContentResolver();
152             PowerManager powerManager = (PowerManager)context.getSystemService(
153                     Context.POWER_SERVICE);
154             mWakeLock = powerManager.newWakeLock(
155                     PowerManager.SCREEN_DIM_WAKE_LOCK |
156                     PowerManager.ON_AFTER_RELEASE, LOG_TAG);
157         }
158 
159         @Override
finalize()160         public void finalize() {
161             if (mWakeLock != null && mWakeLock.isHeld()) {
162                 mWakeLock.release();
163             }
164         }
165 
166         @Override
run()167         public void run() {
168             mWakeLock.acquire();
169             // Some malicious vCard data may make this thread broken
170             // (e.g. OutOfMemoryError).
171             // Even in such cases, some should be done.
172             try {
173                 if (mCanonicalPath != null) {
174                     mProgressDialog.setProgressNumberFormat("");
175                     mProgressDialog.setProgress(0);
176 
177                     // Count the number of VCard entries
178                     mProgressDialog.setIndeterminate(true);
179                     long start;
180                     if (DO_PERFORMANCE_PROFILE) {
181                         start = System.currentTimeMillis();
182                     }
183                     VCardEntryCounter counter = new VCardEntryCounter();
184                     VCardSourceDetector detector = new VCardSourceDetector();
185                     VBuilderCollection builderCollection = new VBuilderCollection(
186                             Arrays.asList(counter, detector));
187                     boolean result;
188                     try {
189                         result = readOneVCard(mCanonicalPath,
190                                 VParser.DEFAULT_CHARSET, builderCollection, null, true);
191                     } catch (VCardNestedException e) {
192                         try {
193                             // Assume that VCardSourceDetector was able to detect the source.
194                             // Try again with the detector.
195                             result = readOneVCard(mCanonicalPath,
196                                     VParser.DEFAULT_CHARSET, counter, detector, false);
197                         } catch (VCardNestedException e2) {
198                             result = false;
199                             Log.e(LOG_TAG, "Must not reach here. " + e2);
200                         }
201                     }
202                     if (DO_PERFORMANCE_PROFILE) {
203                         long time = System.currentTimeMillis() - start;
204                         Log.d(LOG_TAG, "time for counting the number of vCard entries: " +
205                                 time + " ms");
206                     }
207                     if (!result) {
208                         return;
209                     }
210 
211                     mProgressDialog.setProgressNumberFormat(
212                             getString(R.string.reading_vcard_contacts));
213                     mProgressDialog.setIndeterminate(false);
214                     mProgressDialog.setMax(counter.getCount());
215                     String charset = detector.getEstimatedCharset();
216                     doActuallyReadOneVCard(charset, true, detector);
217                 } else {
218                     mProgressDialog.setProgressNumberFormat(
219                             getString(R.string.reading_vcard_files));
220                     mProgressDialog.setMax(mVCardFileList.size());
221                     mProgressDialog.setProgress(0);
222                     for (VCardFile vcardFile : mVCardFileList) {
223                         if (mCanceled) {
224                             return;
225                         }
226                         String canonicalPath = vcardFile.getCanonicalPath();
227 
228                         VCardSourceDetector detector = new VCardSourceDetector();
229                         try {
230                             if (!readOneVCard(canonicalPath, VParser.DEFAULT_CHARSET, detector,
231                                     null, true)) {
232                                 continue;
233                             }
234                         } catch (VCardNestedException e) {
235                             // Assume that VCardSourceDetector was able to detect the source.
236                         }
237                         String charset = detector.getEstimatedCharset();
238                         doActuallyReadOneVCard(charset, false, detector);
239                         mProgressDialog.incrementProgressBy(1);
240                     }
241                 }
242             } finally {
243                 mWakeLock.release();
244                 mProgressDialog.dismiss();
245                 finish();
246             }
247         }
248 
doActuallyReadOneVCard(String charset, boolean doIncrementProgress, VCardSourceDetector detector)249         private void doActuallyReadOneVCard(String charset, boolean doIncrementProgress,
250                 VCardSourceDetector detector) {
251             VCardDataBuilder builder;
252             final Context context = ImportVCardActivity.this;
253             if (charset != null) {
254                 builder = new VCardDataBuilder(mResolver,
255                         mProgressDialog,
256                         context.getString(R.string.reading_vcard_message),
257                         mHandler,
258                         charset,
259                         charset,
260                         false,
261                         mLastNameComesBeforeFirstName);
262             } else {
263                 builder = new VCardDataBuilder(mResolver,
264                         mProgressDialog,
265                         context.getString(R.string.reading_vcard_message),
266                         mHandler,
267                         null,
268                         null,
269                         false,
270                         mLastNameComesBeforeFirstName);
271                 charset = VParser.DEFAULT_CHARSET;
272             }
273             if (doIncrementProgress) {
274                 builder.setOnProgressRunnable(new Runnable() {
275                     public void run() {
276                         mProgressDialog.incrementProgressBy(1);
277                     }
278                 });
279             }
280             try {
281                 readOneVCard(mCanonicalPath, charset, builder, detector, false);
282             } catch (VCardNestedException e) {
283                 Log.e(LOG_TAG, "Must not reach here.");
284             }
285             builder.showDebugInfo();
286         }
287 
readOneVCard(String canonicalPath, String charset, VBuilder builder, VCardSourceDetector detector, boolean throwNestedException)288         private boolean readOneVCard(String canonicalPath, String charset, VBuilder builder,
289                 VCardSourceDetector detector, boolean throwNestedException)
290                 throws VCardNestedException {
291             FileInputStream is;
292             try {
293                 is = new FileInputStream(canonicalPath);
294                 mVCardParser = new VCardParser_V21(detector);
295 
296                 try {
297                     mVCardParser.parse(is, charset, builder, mCanceled);
298                 } catch (VCardVersionException e1) {
299                     try {
300                         is.close();
301                     } catch (IOException e) {
302                     }
303                     is = new FileInputStream(canonicalPath);
304 
305                     try {
306                         mVCardParser = new VCardParser_V30();
307                         mVCardParser.parse(is, charset, builder, mCanceled);
308                     } catch (VCardVersionException e2) {
309                         throw new VCardException("vCard with unspported version.");
310                     }
311                 } finally {
312                     if (is != null) {
313                         try {
314                             is.close();
315                         } catch (IOException e) {
316                         }
317                     }
318                 }
319                 mVCardParser.showDebugInfo();
320             } catch (IOException e) {
321                 Log.e(LOG_TAG, "IOException was emitted: " + e);
322 
323                 mProgressDialog.dismiss();
324 
325                 mHandler.post(new ErrorDisplayer(
326                         getString(R.string.fail_reason_io_error) +
327                         " (" + e.getMessage() + ")"));
328                 return false;
329             } catch (VCardNestedException e) {
330                 if (throwNestedException) {
331                     throw e;
332                 } else {
333                     Log.e(LOG_TAG, "VCardNestedException was emitted: " + e);
334                     mHandler.post(new ErrorDisplayer(
335                             getString(R.string.fail_reason_vcard_parse_error) +
336                             " (" + e.getMessage() + ")"));
337                     return false;
338                 }
339             } catch (VCardException e) {
340                 Log.e(LOG_TAG, "VCardException was emitted: " + e);
341 
342                 mHandler.post(new ErrorDisplayer(
343                         getString(R.string.fail_reason_vcard_parse_error) +
344                         " (" + e.getMessage() + ")"));
345                 return false;
346             }
347             return true;
348         }
349 
onCancel(DialogInterface dialog)350         public void onCancel(DialogInterface dialog) {
351             mCanceled = true;
352             if (mVCardParser != null) {
353                 mVCardParser.cancel();
354             }
355         }
356     }
357 
358     private class ImportTypeSelectedListener implements
359             DialogInterface.OnClickListener {
360         public static final int IMPORT_ALL = 0;
361         public static final int IMPORT_ONE = 1;
362 
363         private List<VCardFile> mVCardFileList;
364         private int mCurrentIndex;
365 
ImportTypeSelectedListener(List<VCardFile> vcardFileList)366         public ImportTypeSelectedListener(List<VCardFile> vcardFileList) {
367             mVCardFileList = vcardFileList;
368         }
369 
onClick(DialogInterface dialog, int which)370         public void onClick(DialogInterface dialog, int which) {
371             if (which == DialogInterface.BUTTON_POSITIVE) {
372                 if (mCurrentIndex == IMPORT_ALL) {
373                     importAllVCardFromSDCard(mVCardFileList);
374                 } else {
375                     showVCardFileSelectDialog(mVCardFileList);
376                 }
377             } else if (which == DialogInterface.BUTTON_NEGATIVE) {
378                 finish();
379             } else {
380                 mCurrentIndex = which;
381             }
382         }
383     }
384 
385     private class VCardSelectedListener implements DialogInterface.OnClickListener {
386         private List<VCardFile> mVCardFileList;
387         private int mCurrentIndex;
388 
VCardSelectedListener(List<VCardFile> vcardFileList)389         public VCardSelectedListener(List<VCardFile> vcardFileList) {
390             mVCardFileList = vcardFileList;
391             mCurrentIndex = 0;
392         }
393 
onClick(DialogInterface dialog, int which)394         public void onClick(DialogInterface dialog, int which) {
395             if (which == DialogInterface.BUTTON_POSITIVE) {
396                 importOneVCardFromSDCard(mVCardFileList.get(mCurrentIndex).getCanonicalPath());
397             } else if (which == DialogInterface.BUTTON_NEGATIVE) {
398                 finish();
399             } else {
400                 // Some file is selected.
401                 mCurrentIndex = which;
402             }
403         }
404     }
405 
406     /**
407      * Thread scanning VCard from SDCard. After scanning, the dialog which lets a user select
408      * a vCard file is shown. After the choice, VCardReadThread starts running.
409      */
410     private class VCardScanThread extends Thread implements OnCancelListener, OnClickListener {
411         private boolean mCanceled;
412         private boolean mGotIOException;
413         private File mRootDirectory;
414 
415         // null when search operation is canceled.
416         private List<VCardFile> mVCardFiles;
417 
418         // To avoid recursive link.
419         private Set<String> mCheckedPaths;
420         private PowerManager.WakeLock mWakeLock;
421 
422         private class CanceledException extends Exception {
423         }
424 
VCardScanThread(File sdcardDirectory)425         public VCardScanThread(File sdcardDirectory) {
426             mCanceled = false;
427             mGotIOException = false;
428             mRootDirectory = sdcardDirectory;
429             mCheckedPaths = new HashSet<String>();
430             mVCardFiles = new Vector<VCardFile>();
431             PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService(
432                     Context.POWER_SERVICE);
433             mWakeLock = powerManager.newWakeLock(
434                     PowerManager.SCREEN_DIM_WAKE_LOCK |
435                     PowerManager.ON_AFTER_RELEASE, LOG_TAG);
436         }
437 
438         @Override
run()439         public void run() {
440             try {
441                 mWakeLock.acquire();
442                 getVCardFileRecursively(mRootDirectory);
443             } catch (CanceledException e) {
444                 mCanceled = true;
445             } catch (IOException e) {
446                 mGotIOException = true;
447             } finally {
448                 mWakeLock.release();
449             }
450 
451             if (mCanceled) {
452                 mVCardFiles = null;
453             }
454 
455             mProgressDialog.dismiss();
456 
457             if (mGotIOException) {
458                 mHandler.post(new Runnable() {
459                     public void run() {
460                         String message = (getString(R.string.scanning_sdcard_failed_message,
461                                 getString(R.string.fail_reason_io_error)));
462 
463                         AlertDialog.Builder builder =
464                             new AlertDialog.Builder(ImportVCardActivity.this)
465                                 .setTitle(R.string.scanning_sdcard_failed_title)
466                                 .setIcon(android.R.drawable.ic_dialog_alert)
467                                 .setMessage(message)
468                                 .setOnCancelListener(mCancelListener)
469                                 .setPositiveButton(android.R.string.ok, mCancelListener);
470                         builder.show();
471                     }
472                 });
473             } else if (mCanceled) {
474                 finish();
475             } else {
476                 mHandler.post(new Runnable() {
477                     public void run() {
478                         int size = mVCardFiles.size();
479                         final Context context = ImportVCardActivity.this;
480                         if (size == 0) {
481                             String message = (getString(R.string.scanning_sdcard_failed_message,
482                                     getString(R.string.fail_reason_no_vcard_file)));
483 
484                             AlertDialog.Builder builder =
485                                 new AlertDialog.Builder(context)
486                                     .setTitle(R.string.scanning_sdcard_failed_title)
487                                     .setMessage(message)
488                                     .setOnCancelListener(mCancelListener)
489                                     .setPositiveButton(android.R.string.ok, mCancelListener);
490                             builder.show();
491                             return;
492                         } else if (context.getResources().getBoolean(
493                                 R.bool.config_import_all_vcard_from_sdcard_automatically)) {
494                             importAllVCardFromSDCard(mVCardFiles);
495                         } else if (size == 1) {
496                             importOneVCardFromSDCard(mVCardFiles.get(0).getCanonicalPath());
497                         } else if (context.getResources().getBoolean(
498                                 R.bool.config_allow_users_select_all_vcard_import)) {
499                             showSelectImportTypeDialog(mVCardFiles);
500                         } else {
501                             showVCardFileSelectDialog(mVCardFiles);
502                         }
503                     }
504                 });
505             }
506         }
507 
getVCardFileRecursively(File directory)508         private void getVCardFileRecursively(File directory)
509                 throws CanceledException, IOException {
510             if (mCanceled) {
511                 throw new CanceledException();
512             }
513 
514             for (File file : directory.listFiles()) {
515                 if (mCanceled) {
516                     throw new CanceledException();
517                 }
518                 String canonicalPath = file.getCanonicalPath();
519                 if (mCheckedPaths.contains(canonicalPath)) {
520                     continue;
521                 }
522 
523                 mCheckedPaths.add(canonicalPath);
524 
525                 if (file.isDirectory()) {
526                     getVCardFileRecursively(file);
527                 } else if (canonicalPath.toLowerCase().endsWith(".vcf") &&
528                         file.canRead()){
529                     String fileName = file.getName();
530                     VCardFile vcardFile = new VCardFile(
531                             fileName, canonicalPath, file.lastModified());
532                     mVCardFiles.add(vcardFile);
533                 }
534             }
535         }
536 
onCancel(DialogInterface dialog)537         public void onCancel(DialogInterface dialog) {
538             mCanceled = true;
539         }
540 
onClick(DialogInterface dialog, int which)541         public void onClick(DialogInterface dialog, int which) {
542             if (which == DialogInterface.BUTTON_NEGATIVE) {
543                 mCanceled = true;
544             }
545         }
546     }
547 
548 
importOneVCardFromSDCard(String canonicalPath)549     private void importOneVCardFromSDCard(String canonicalPath) {
550         VCardReadThread thread = new VCardReadThread(canonicalPath);
551         showReadingVCardDialog(thread);
552         thread.start();
553     }
554 
importAllVCardFromSDCard(List<VCardFile> vcardFileList)555     private void importAllVCardFromSDCard(List<VCardFile> vcardFileList) {
556         VCardReadThread thread = new VCardReadThread(vcardFileList);
557         showReadingVCardDialog(thread);
558         thread.start();
559     }
560 
showSelectImportTypeDialog(List<VCardFile> vcardFileList)561     private void showSelectImportTypeDialog(List<VCardFile> vcardFileList) {
562         DialogInterface.OnClickListener listener =
563             new ImportTypeSelectedListener(vcardFileList);
564         AlertDialog.Builder builder =
565             new AlertDialog.Builder(ImportVCardActivity.this)
566                 .setTitle(R.string.select_vcard_title)
567                 .setPositiveButton(android.R.string.ok, listener)
568                 .setOnCancelListener(mCancelListener)
569                 .setNegativeButton(android.R.string.cancel, mCancelListener);
570 
571         String[] items = new String[2];
572         items[ImportTypeSelectedListener.IMPORT_ALL] =
573             getString(R.string.import_all_vcard_string);
574         items[ImportTypeSelectedListener.IMPORT_ONE] =
575             getString(R.string.import_one_vcard_string);
576         builder.setSingleChoiceItems(items,
577                 ImportTypeSelectedListener.IMPORT_ALL, listener);
578         builder.show();
579     }
580 
showVCardFileSelectDialog(List<VCardFile> vcardFileList)581     private void showVCardFileSelectDialog(List<VCardFile> vcardFileList) {
582         int size = vcardFileList.size();
583         DialogInterface.OnClickListener listener =
584             new VCardSelectedListener(vcardFileList);
585         AlertDialog.Builder builder =
586             new AlertDialog.Builder(this)
587                 .setTitle(R.string.select_vcard_title)
588                 .setPositiveButton(android.R.string.ok, listener)
589                 .setOnCancelListener(mCancelListener)
590                 .setNegativeButton(android.R.string.cancel, mCancelListener);
591 
592         CharSequence[] items = new CharSequence[size];
593         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
594         for (int i = 0; i < size; i++) {
595             VCardFile vcardFile = vcardFileList.get(i);
596             SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
597             stringBuilder.append(vcardFile.getName());
598             stringBuilder.append('\n');
599             int indexToBeSpanned = stringBuilder.length();
600             // Smaller date text looks better, since each file name becomes easier to read.
601             // The value set to RelativeSizeSpan is arbitrary. You can change it to any other
602             // value (but the value bigger than 1.0f would not make nice appearance :)
603             stringBuilder.append(
604                         "(" + dateFormat.format(new Date(vcardFile.getLastModified())) + ")");
605             stringBuilder.setSpan(
606                     new RelativeSizeSpan(0.7f), indexToBeSpanned, stringBuilder.length(),
607                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
608             items[i] = stringBuilder;
609         }
610         builder.setSingleChoiceItems(items, 0, listener);
611         builder.show();
612     }
613 
showReadingVCardDialog(DialogInterface.OnCancelListener listener)614     private void showReadingVCardDialog(DialogInterface.OnCancelListener listener) {
615         String title = getString(R.string.reading_vcard_title);
616         String message = getString(R.string.reading_vcard_message);
617         mProgressDialog = new ProgressDialog(this);
618         mProgressDialog.setTitle(title);
619         mProgressDialog.setMessage(message);
620         mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
621         mProgressDialog.setOnCancelListener(listener);
622         mProgressDialog.show();
623     }
624 
625     @Override
onCreate(Bundle bundle)626     protected void onCreate(Bundle bundle) {
627         super.onCreate(bundle);
628 
629         mLastNameComesBeforeFirstName = getResources().getBoolean(
630                 com.android.internal.R.bool.config_lastname_comes_before_firstname);
631 
632         startImportVCardFromSdCard();
633     }
634 
635     /**
636      * Tries to start importing VCard. If there's no SDCard available,
637      * an error dialog is shown. If there is, start scanning using another thread
638      * and shows a progress dialog. Several interactions will occur.
639      * This method should be called from a thread with a looper (like Activity).
640      */
startImportVCardFromSdCard()641     public void startImportVCardFromSdCard() {
642         File file = new File("/sdcard");
643         if (!file.exists() || !file.isDirectory() || !file.canRead()) {
644             new AlertDialog.Builder(this)
645                     .setTitle(R.string.no_sdcard_title)
646                     .setIcon(android.R.drawable.ic_dialog_alert)
647                     .setMessage(R.string.no_sdcard_message)
648                     .setOnCancelListener(mCancelListener)
649                     .setPositiveButton(android.R.string.ok, mCancelListener)
650                     .show();
651         } else {
652             String title = getString(R.string.searching_vcard_title);
653             String message = getString(R.string.searching_vcard_message);
654 
655             mProgressDialog = ProgressDialog.show(this, title, message, true, false);
656             VCardScanThread thread = new VCardScanThread(file);
657             mProgressDialog.setOnCancelListener(thread);
658             thread.start();
659         }
660     }
661 }
662