2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.contacts;
19 import android.accounts.Account;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.app.ProgressDialog;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.DialogInterface.OnCancelListener;
29 import android.content.DialogInterface.OnClickListener;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.PowerManager;
33 import android.pim.vcard.EntryCommitter;
34 import android.pim.vcard.VCardBuilder;
35 import android.pim.vcard.VCardBuilderCollection;
36 import android.pim.vcard.VCardConfig;
37 import android.pim.vcard.VCardDataBuilder;
38 import android.pim.vcard.VCardEntryCounter;
39 import android.pim.vcard.VCardParser_V21;
40 import android.pim.vcard.VCardParser_V30;
41 import android.pim.vcard.VCardSourceDetector;
42 import android.pim.vcard.exception.VCardException;
43 import android.pim.vcard.exception.VCardNestedException;
44 import android.pim.vcard.exception.VCardNotSupportedException;
45 import android.pim.vcard.exception.VCardVersionException;
46 import android.text.SpannableStringBuilder;
47 import android.text.Spanned;
48 import android.text.TextUtils;
49 import android.text.style.RelativeSizeSpan;
50 import android.util.Log;
53 import java.io.FileInputStream;
54 import java.io.IOException;
55 import java.text.DateFormat;
56 import java.text.SimpleDateFormat;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Date;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Locale;
64 import java.util.Vector;
68 private String mCanonicalPath;
69 private long mLastModified;
71 public VCardFile(String name, String canonicalPath, long lastModified) {
73 mCanonicalPath = canonicalPath;
74 mLastModified = lastModified;
77 public String getName() {
81 public String getCanonicalPath() {
82 return mCanonicalPath;
85 public long getLastModified() {
91 * Class for importing vCard. Several user interaction will be required while reading
92 * (selecting a file, waiting a moment, etc.)
94 public class ImportVCardActivity extends Activity {
95 private static final String LOG_TAG = "ImportVCardActivity";
96 private static final boolean DO_PERFORMANCE_PROFILE = false;
98 private Handler mHandler = new Handler();
99 private Account mAccount;
101 private ProgressDialog mProgressDialogForScanVCard;
103 private List<VCardFile> mAllVCardFileList;
104 private VCardScanThread mVCardScanThread;
105 private VCardReadThread mVCardReadThread;
106 private ProgressDialog mProgressDialogForReadVCard;
108 private String mErrorMessage;
110 private class DialogDisplayer implements Runnable {
111 private final int mResId;
112 public DialogDisplayer(int resId) {
115 public DialogDisplayer(String errorMessage) {
116 mResId = R.id.dialog_error_with_message;
117 mErrorMessage = errorMessage;
120 // Show the Dialog only when the parent Activity is still alive.
121 if (!ImportVCardActivity.this.isFinishing()) {
127 private class CancelListener
128 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
129 public void onClick(DialogInterface dialog, int which) {
133 public void onCancel(DialogInterface dialog) {
138 private CancelListener mCancelListener = new CancelListener();
140 private class VCardReadThread extends Thread
141 implements DialogInterface.OnCancelListener {
142 private ContentResolver mResolver;
143 private VCardParser_V21 mVCardParser;
144 private boolean mCanceled;
145 private PowerManager.WakeLock mWakeLock;
146 private String mCanonicalPath;
148 private List<VCardFile> mSelectedVCardFileList;
149 private List<String> mErrorFileNameList;
151 public VCardReadThread(String canonicalPath) {
152 mCanonicalPath = canonicalPath;
156 public VCardReadThread(final List<VCardFile> selectedVCardFileList) {
157 mCanonicalPath = null;
158 mSelectedVCardFileList = selectedVCardFileList;
159 mErrorFileNameList = new ArrayList<String>();
163 private void init() {
164 Context context = ImportVCardActivity.this;
165 mResolver = context.getContentResolver();
166 PowerManager powerManager = (PowerManager)context.getSystemService(
167 Context.POWER_SERVICE);
168 mWakeLock = powerManager.newWakeLock(
169 PowerManager.SCREEN_DIM_WAKE_LOCK |
170 PowerManager.ON_AFTER_RELEASE, LOG_TAG);
174 public void finalize() {
175 if (mWakeLock != null && mWakeLock.isHeld()) {
182 boolean shouldCallFinish = true;
184 // Some malicious vCard data may make this thread broken
185 // (e.g. OutOfMemoryError).
186 // Even in such cases, some should be done.
188 if (mCanonicalPath != null) { // Read one file
189 mProgressDialogForReadVCard.setProgressNumberFormat("");
190 mProgressDialogForReadVCard.setProgress(0);
192 // Count the number of VCard entries
193 mProgressDialogForReadVCard.setIndeterminate(true);
195 if (DO_PERFORMANCE_PROFILE) {
196 start = System.currentTimeMillis();
198 VCardEntryCounter counter = new VCardEntryCounter();
199 VCardSourceDetector detector = new VCardSourceDetector();
200 VCardBuilderCollection builderCollection = new VCardBuilderCollection(
201 Arrays.asList(counter, detector));
205 result = readOneVCardFile(mCanonicalPath,
206 VCardConfig.DEFAULT_CHARSET, builderCollection, null, true, null);
207 } catch (VCardNestedException e) {
209 // Assume that VCardSourceDetector was able to detect the source.
210 // Try again with the detector.
211 result = readOneVCardFile(mCanonicalPath,
212 VCardConfig.DEFAULT_CHARSET, counter, detector, false, null);
213 } catch (VCardNestedException e2) {
215 Log.e(LOG_TAG, "Must not reach here. " + e2);
218 if (DO_PERFORMANCE_PROFILE) {
219 long time = System.currentTimeMillis() - start;
220 Log.d(LOG_TAG, "time for counting the number of vCard entries: " +
224 shouldCallFinish = false;
228 mProgressDialogForReadVCard.setProgressNumberFormat(
229 getString(R.string.reading_vcard_contacts));
230 mProgressDialogForReadVCard.setIndeterminate(false);
231 mProgressDialogForReadVCard.setMax(counter.getCount());
232 String charset = detector.getEstimatedCharset();
233 doActuallyReadOneVCard(mCanonicalPath, null, charset, true, detector,
235 } else { // Read multiple files.
236 mProgressDialogForReadVCard.setProgressNumberFormat(
237 getString(R.string.reading_vcard_files));
238 mProgressDialogForReadVCard.setMax(mSelectedVCardFileList.size());
239 mProgressDialogForReadVCard.setProgress(0);
241 for (VCardFile vcardFile : mSelectedVCardFileList) {
245 String canonicalPath = vcardFile.getCanonicalPath();
247 VCardSourceDetector detector = new VCardSourceDetector();
249 if (!readOneVCardFile(canonicalPath, VCardConfig.DEFAULT_CHARSET,
250 detector, null, true, mErrorFileNameList)) {
253 } catch (VCardNestedException e) {
254 // Assume that VCardSourceDetector was able to detect the source.
256 String charset = detector.getEstimatedCharset();
257 doActuallyReadOneVCard(canonicalPath, mAccount,
258 charset, false, detector, mErrorFileNameList);
259 mProgressDialogForReadVCard.incrementProgressBy(1);
264 mProgressDialogForReadVCard.dismiss();
265 // finish() is called via mCancelListener, which is used in DialogDisplayer.
266 if (shouldCallFinish && !isFinishing()) {
267 if (mErrorFileNameList == null || mErrorFileNameList.isEmpty()) {
270 StringBuilder builder = new StringBuilder();
271 boolean first = true;
272 for (String fileName : mErrorFileNameList) {
276 builder.append(", ");
278 builder.append(fileName);
281 mHandler.post(new DialogDisplayer(
282 getString(R.string.fail_reason_failed_to_read_files,
283 builder.toString())));
289 private boolean doActuallyReadOneVCard(String canonicalPath, Account account,
290 String charset, boolean showEntryParseProgress,
291 VCardSourceDetector detector, List<String> errorFileNameList) {
292 final Context context = ImportVCardActivity.this;
293 VCardDataBuilder builder;
294 final String currentLanguage = Locale.getDefault().getLanguage();
295 int vcardType = VCardConfig.getVCardTypeFromString(
296 context.getString(R.string.config_import_vcard_type));
297 if (charset != null) {
298 builder = new VCardDataBuilder(charset, charset, false, vcardType, mAccount);
300 charset = VCardConfig.DEFAULT_CHARSET;
301 builder = new VCardDataBuilder(null, null, false, vcardType, mAccount);
303 builder.addEntryHandler(new EntryCommitter(mResolver));
304 if (showEntryParseProgress) {
305 builder.addEntryHandler(new ProgressShower(mProgressDialogForReadVCard,
306 context.getString(R.string.reading_vcard_message),
307 ImportVCardActivity.this,
312 if (!readOneVCardFile(canonicalPath, charset, builder, detector, false, null)) {
315 } catch (VCardNestedException e) {
316 Log.e(LOG_TAG, "Never reach here.");
321 private boolean readOneVCardFile(String canonicalPath, String charset,
322 VCardBuilder builder, VCardSourceDetector detector,
323 boolean throwNestedException, List<String> errorFileNameList)
324 throws VCardNestedException {
327 is = new FileInputStream(canonicalPath);
328 mVCardParser = new VCardParser_V21(detector);
331 mVCardParser.parse(is, charset, builder, mCanceled);
332 } catch (VCardVersionException e1) {
335 } catch (IOException e) {
337 is = new FileInputStream(canonicalPath);
340 mVCardParser = new VCardParser_V30();
341 mVCardParser.parse(is, charset, builder, mCanceled);
342 } catch (VCardVersionException e2) {
343 throw new VCardException("vCard with unspported version.");
349 } catch (IOException e) {
353 } catch (IOException e) {
354 Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
356 mProgressDialogForReadVCard.dismiss();
358 if (errorFileNameList != null) {
359 errorFileNameList.add(canonicalPath);
361 mHandler.post(new DialogDisplayer(
362 getString(R.string.fail_reason_io_error) +
363 ": " + e.getLocalizedMessage()));
366 } catch (VCardNotSupportedException e) {
367 if ((e instanceof VCardNestedException) && throwNestedException) {
368 throw (VCardNestedException)e;
370 if (errorFileNameList != null) {
371 errorFileNameList.add(canonicalPath);
373 mHandler.post(new DialogDisplayer(
374 getString(R.string.fail_reason_vcard_not_supported_error) +
375 " (" + e.getMessage() + ")"));
378 } catch (VCardException e) {
379 if (errorFileNameList != null) {
380 errorFileNameList.add(canonicalPath);
382 mHandler.post(new DialogDisplayer(
383 getString(R.string.fail_reason_vcard_parse_error) +
384 " (" + e.getMessage() + ")"));
391 public void cancel() {
393 if (mVCardParser != null) {
394 mVCardParser.cancel();
398 public void onCancel(DialogInterface dialog) {
403 private class ImportTypeSelectedListener implements
404 DialogInterface.OnClickListener {
405 public static final int IMPORT_ONE = 0;
406 public static final int IMPORT_MULTIPLE = 1;
407 public static final int IMPORT_ALL = 2;
408 public static final int IMPORT_TYPE_SIZE = 3;
410 private int mCurrentIndex;
412 public void onClick(DialogInterface dialog, int which) {
413 if (which == DialogInterface.BUTTON_POSITIVE) {
414 switch (mCurrentIndex) {
416 importMultipleVCardFromSDCard(mAllVCardFileList);
418 case IMPORT_MULTIPLE:
419 showDialog(R.id.dialog_select_multiple_vcard);
422 showDialog(R.id.dialog_select_one_vcard);
425 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
428 mCurrentIndex = which;
433 private class VCardSelectedListener implements
434 DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener {
435 private int mCurrentIndex;
436 private Set<Integer> mSelectedIndexSet;
438 public VCardSelectedListener(boolean multipleSelect) {
440 if (multipleSelect) {
441 mSelectedIndexSet = new HashSet<Integer>();
445 public void onClick(DialogInterface dialog, int which) {
446 if (which == DialogInterface.BUTTON_POSITIVE) {
447 if (mSelectedIndexSet != null) {
448 List<VCardFile> selectedVCardFileList = new ArrayList<VCardFile>();
449 int size = mAllVCardFileList.size();
450 // We'd like to sort the files by its index, so we do not use Set iterator.
451 for (int i = 0; i < size; i++) {
452 if (mSelectedIndexSet.contains(i)) {
453 selectedVCardFileList.add(mAllVCardFileList.get(i));
456 importMultipleVCardFromSDCard(selectedVCardFileList);
458 importOneVCardFromSDCard(mAllVCardFileList.get(mCurrentIndex).getCanonicalPath());
460 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
463 // Some file is selected.
464 mCurrentIndex = which;
465 if (mSelectedIndexSet != null) {
466 if (mSelectedIndexSet.contains(which)) {
467 mSelectedIndexSet.remove(which);
469 mSelectedIndexSet.add(which);
475 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
476 if (mSelectedIndexSet == null || (mSelectedIndexSet.contains(which) == isChecked)) {
477 Log.e(LOG_TAG, String.format("Inconsist state in index %d (%s)", which,
478 mAllVCardFileList.get(which).getCanonicalPath()));
480 onClick(dialog, which);
486 * Thread scanning VCard from SDCard. After scanning, the dialog which lets a user select
487 * a vCard file is shown. After the choice, VCardReadThread starts running.
489 private class VCardScanThread extends Thread implements OnCancelListener, OnClickListener {
490 private boolean mCanceled;
491 private boolean mGotIOException;
492 private File mRootDirectory;
494 // To avoid recursive link.
495 private Set<String> mCheckedPaths;
496 private PowerManager.WakeLock mWakeLock;
498 private class CanceledException extends Exception {
501 public VCardScanThread(File sdcardDirectory) {
503 mGotIOException = false;
504 mRootDirectory = sdcardDirectory;
505 mCheckedPaths = new HashSet<String>();
506 PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService(
507 Context.POWER_SERVICE);
508 mWakeLock = powerManager.newWakeLock(
509 PowerManager.SCREEN_DIM_WAKE_LOCK |
510 PowerManager.ON_AFTER_RELEASE, LOG_TAG);
515 mAllVCardFileList = new Vector<VCardFile>();
518 getVCardFileRecursively(mRootDirectory);
519 } catch (CanceledException e) {
521 } catch (IOException e) {
522 mGotIOException = true;
528 mAllVCardFileList = null;
531 mProgressDialogForScanVCard.dismiss();
532 mProgressDialogForScanVCard = null;
534 if (mGotIOException) {
535 mHandler.post(new DialogDisplayer(R.id.dialog_io_exception));
536 } else if (mCanceled) {
539 int size = mAllVCardFileList.size();
540 final Context context = ImportVCardActivity.this;
542 mHandler.post(new DialogDisplayer(R.id.dialog_vcard_not_found));
544 startVCardSelectAndImport();
549 private void getVCardFileRecursively(File directory)
550 throws CanceledException, IOException {
552 throw new CanceledException();
555 for (File file : directory.listFiles()) {
557 throw new CanceledException();
559 String canonicalPath = file.getCanonicalPath();
560 if (mCheckedPaths.contains(canonicalPath)) {
564 mCheckedPaths.add(canonicalPath);
566 if (file.isDirectory()) {
567 getVCardFileRecursively(file);
568 } else if (canonicalPath.toLowerCase().endsWith(".vcf") &&
570 String fileName = file.getName();
571 VCardFile vcardFile = new VCardFile(
572 fileName, canonicalPath, file.lastModified());
573 mAllVCardFileList.add(vcardFile);
578 public void onCancel(DialogInterface dialog) {
582 public void onClick(DialogInterface dialog, int which) {
583 if (which == DialogInterface.BUTTON_NEGATIVE) {
589 private void startVCardSelectAndImport() {
590 int size = mAllVCardFileList.size();
591 if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard_automatically)) {
592 importMultipleVCardFromSDCard(mAllVCardFileList);
593 } else if (size == 1) {
594 importOneVCardFromSDCard(mAllVCardFileList.get(0).getCanonicalPath());
595 } else if (getResources().getBoolean(R.bool.config_allow_users_select_all_vcard_import)) {
596 mHandler.post(new DialogDisplayer(R.id.dialog_select_import_type));
598 mHandler.post(new DialogDisplayer(R.id.dialog_select_one_vcard));
602 private void importMultipleVCardFromSDCard(final List<VCardFile> selectedVCardFileList) {
603 mHandler.post(new Runnable() {
605 mVCardReadThread = new VCardReadThread(selectedVCardFileList);
606 showDialog(R.id.dialog_reading_vcard);
611 private void importOneVCardFromSDCard(final String canonicalPath) {
612 mHandler.post(new Runnable() {
614 mVCardReadThread = new VCardReadThread(canonicalPath);
615 showDialog(R.id.dialog_reading_vcard);
620 private Dialog getSelectImportTypeDialog() {
621 DialogInterface.OnClickListener listener =
622 new ImportTypeSelectedListener();
623 AlertDialog.Builder builder = new AlertDialog.Builder(this)
624 .setTitle(R.string.select_vcard_title)
625 .setPositiveButton(android.R.string.ok, listener)
626 .setOnCancelListener(mCancelListener)
627 .setNegativeButton(android.R.string.cancel, mCancelListener);
629 String[] items = new String[ImportTypeSelectedListener.IMPORT_TYPE_SIZE];
630 items[ImportTypeSelectedListener.IMPORT_ONE] =
631 getString(R.string.import_one_vcard_string);
632 items[ImportTypeSelectedListener.IMPORT_MULTIPLE] =
633 getString(R.string.import_multiple_vcard_string);
634 items[ImportTypeSelectedListener.IMPORT_ALL] =
635 getString(R.string.import_all_vcard_string);
636 builder.setSingleChoiceItems(items, ImportTypeSelectedListener.IMPORT_ONE, listener);
637 return builder.create();
640 private Dialog getVCardFileSelectDialog(boolean multipleSelect) {
641 int size = mAllVCardFileList.size();
642 VCardSelectedListener listener = new VCardSelectedListener(multipleSelect);
643 AlertDialog.Builder builder =
644 new AlertDialog.Builder(this)
645 .setTitle(R.string.select_vcard_title)
646 .setPositiveButton(android.R.string.ok, listener)
647 .setOnCancelListener(mCancelListener)
648 .setNegativeButton(android.R.string.cancel, mCancelListener);
650 CharSequence[] items = new CharSequence[size];
651 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
652 for (int i = 0; i < size; i++) {
653 VCardFile vcardFile = mAllVCardFileList.get(i);
654 SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
655 stringBuilder.append(vcardFile.getName());
656 stringBuilder.append('\n');
657 int indexToBeSpanned = stringBuilder.length();
658 // Smaller date text looks better, since each file name becomes easier to read.
659 // The value set to RelativeSizeSpan is arbitrary. You can change it to any other
660 // value (but the value bigger than 1.0f would not make nice appearance :)
661 stringBuilder.append(
662 "(" + dateFormat.format(new Date(vcardFile.getLastModified())) + ")");
663 stringBuilder.setSpan(
664 new RelativeSizeSpan(0.7f), indexToBeSpanned, stringBuilder.length(),
665 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
666 items[i] = stringBuilder;
668 if (multipleSelect) {
669 builder.setMultiChoiceItems(items, (boolean[])null, listener);
671 builder.setSingleChoiceItems(items, 0, listener);
673 return builder.create();
676 private Dialog getReadingVCardDialog() {
677 if (mProgressDialogForReadVCard == null) {
678 String title = getString(R.string.reading_vcard_title);
679 String message = getString(R.string.reading_vcard_message);
680 mProgressDialogForReadVCard = new ProgressDialog(this);
681 mProgressDialogForReadVCard.setTitle(title);
682 mProgressDialogForReadVCard.setMessage(message);
683 mProgressDialogForReadVCard.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
684 mProgressDialogForReadVCard.setOnCancelListener(mVCardReadThread);
685 mVCardReadThread.start();
687 return mProgressDialogForReadVCard;
691 protected void onCreate(Bundle bundle) {
692 super.onCreate(bundle);
694 Intent intent = getIntent();
695 if (intent != null) {
696 final String accountName = intent.getStringExtra("account_name");
697 final String accountType = intent.getStringExtra("account_type");
698 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
699 mAccount = new Account(accountName, accountType);
702 Log.e(LOG_TAG, "intent does not exist");
705 startImportVCardFromSdCard();
709 protected Dialog onCreateDialog(int resId) {
711 case R.id.dialog_searching_vcard: {
712 if (mProgressDialogForScanVCard == null) {
713 String title = getString(R.string.searching_vcard_title);
714 String message = getString(R.string.searching_vcard_message);
715 mProgressDialogForScanVCard =
716 ProgressDialog.show(this, title, message, true, false);
717 mProgressDialogForScanVCard.setOnCancelListener(mVCardScanThread);
718 mVCardScanThread.start();
720 return mProgressDialogForScanVCard;
722 case R.id.dialog_sdcard_not_found: {
723 AlertDialog.Builder builder = new AlertDialog.Builder(this)
724 .setTitle(R.string.no_sdcard_title)
725 .setIcon(android.R.drawable.ic_dialog_alert)
726 .setMessage(R.string.no_sdcard_message)
727 .setOnCancelListener(mCancelListener)
728 .setPositiveButton(android.R.string.ok, mCancelListener);
729 return builder.create();
731 case R.id.dialog_vcard_not_found: {
732 String message = (getString(R.string.scanning_sdcard_failed_message,
733 getString(R.string.fail_reason_no_vcard_file)));
734 AlertDialog.Builder builder = new AlertDialog.Builder(this)
735 .setTitle(R.string.scanning_sdcard_failed_title)
737 .setOnCancelListener(mCancelListener)
738 .setPositiveButton(android.R.string.ok, mCancelListener);
739 return builder.create();
741 case R.id.dialog_select_import_type: {
742 return getSelectImportTypeDialog();
744 case R.id.dialog_select_multiple_vcard: {
745 return getVCardFileSelectDialog(true);
747 case R.id.dialog_select_one_vcard: {
748 return getVCardFileSelectDialog(false);
750 case R.id.dialog_reading_vcard: {
751 return getReadingVCardDialog();
753 case R.id.dialog_io_exception: {
754 String message = (getString(R.string.scanning_sdcard_failed_message,
755 getString(R.string.fail_reason_io_error)));
756 AlertDialog.Builder builder = new AlertDialog.Builder(this)
757 .setTitle(R.string.scanning_sdcard_failed_title)
758 .setIcon(android.R.drawable.ic_dialog_alert)
760 .setOnCancelListener(mCancelListener)
761 .setPositiveButton(android.R.string.ok, mCancelListener);
762 return builder.create();
764 case R.id.dialog_error_with_message: {
765 String message = mErrorMessage;
766 if (TextUtils.isEmpty(message)) {
767 Log.e(LOG_TAG, "Error message is null while it must not.");
768 message = getString(R.string.fail_reason_unknown);
770 AlertDialog.Builder builder = new AlertDialog.Builder(this)
771 .setTitle(getString(R.string.reading_vcard_failed_title))
772 .setIcon(android.R.drawable.ic_dialog_alert)
774 .setOnCancelListener(mCancelListener)
775 .setPositiveButton(android.R.string.ok, mCancelListener);
776 return builder.create();
780 return super.onCreateDialog(resId);
784 protected void onStop() {
786 if (mVCardReadThread != null) {
787 // The Activity is no longer visible. Stop the thread.
788 mVCardReadThread.cancel();
789 mVCardReadThread = null;
792 // ImportVCardActivity should not be persistent. In other words, if there's some
793 // event calling onStop(), this Activity should finish its work and give the main
794 // screen back to the caller Activity.
795 if (!isFinishing()) {
801 public void finalize() {
802 if (mVCardReadThread != null) {
803 // Not sure this procedure is really needed, but just in case...
804 Log.w(LOG_TAG, "VCardReadThread exists while this Activity is now being killed!");
805 mVCardReadThread.cancel();
806 mVCardReadThread = null;
813 * Tries to start importing VCard. If there's no SDCard available,
814 * an error dialog is shown. If there is, start scanning using another thread
815 * and shows a progress dialog. Several interactions will occur.
816 * This method should be called from a thread with a looper (like Activity).
818 public void startImportVCardFromSdCard() {
819 File file = new File("/sdcard");
820 if (!file.exists() || !file.isDirectory() || !file.canRead()) {
821 showDialog(R.id.dialog_sdcard_not_found);
823 File sdcardDirectory = new File("/sdcard");
824 mVCardScanThread = new VCardScanThread(sdcardDirectory);
825 showDialog(R.id.dialog_searching_vcard);