You are on page 1of 18

/* * * * * * * * * Copyright (C) 2009 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 import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import android.accounts.Account; android.app.Activity; android.app.AlertDialog; android.app.Dialog; android.app.ProgressDialog; android.content.ContentResolver; android.content.ContentUris; android.content.Context; android.content.DialogInterface; android.content.Intent; android.content.DialogInterface.OnCancelListener; android.content.DialogInterface.OnClickListener; android.net.Uri; android.os.Bundle; android.os.Environment; android.os.Handler; android.os.PowerManager; android.pim.vcard.VCardConfig; android.pim.vcard.VCardEntryCommitter; android.pim.vcard.VCardEntryConstructor; android.pim.vcard.VCardEntryCounter; android.pim.vcard.VCardInterpreter; android.pim.vcard.VCardInterpreterCollection; android.pim.vcard.VCardParser_V21; android.pim.vcard.VCardParser_V30; android.pim.vcard.VCardSourceDetector; android.pim.vcard.exception.VCardException; android.pim.vcard.exception.VCardNestedException; android.pim.vcard.exception.VCardNotSupportedException; android.pim.vcard.exception.VCardVersionException; android.provider.ContactsContract.RawContacts; android.text.SpannableStringBuilder; android.text.Spanned; android.text.TextUtils; android.text.style.RelativeSizeSpan; android.util.Log;

import com.android.contacts.model.Sources; import com.android.contacts.util.AccountSelectionUtil; import java.io.File; import java.io.IOException;

import import import import import import import import import import import

java.io.InputStream; java.text.DateFormat; java.text.SimpleDateFormat; java.util.ArrayList; java.util.Arrays; java.util.Date; java.util.HashSet; java.util.List; java.util.Locale; java.util.Set; java.util.Vector;

class More ...VCardFile { private String mName; private String mCanonicalPath; private long mLastModified; public More ...VCardFile(String name, String canonicalPath, long lastModifi ed) { mName = name; mCanonicalPath = canonicalPath; mLastModified = lastModified; } public String More ...getName() { return mName; } public String More ...getCanonicalPath() { return mCanonicalPath; } public long More ...getLastModified() { return mLastModified; } } public class More ...ImportVCardActivity extends Activity { private static final String LOG_TAG = "ImportVCardActivity"; private static final boolean DO_PERFORMANCE_PROFILE = false; // Run on the UI thread. Must not be null except after onDestroy(). private Handler mHandler = new Handler(); private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListen er; private Account mAccount; private ProgressDialog mProgressDialogForScanVCard; private private private private List<VCardFile> mAllVCardFileList; VCardScanThread mVCardScanThread; VCardReadThread mVCardReadThread; ProgressDialog mProgressDialogForReadVCard;

private String mErrorMessage; private boolean mNeedReview = false;

// Runs on the UI thread. private class More ...DialogDisplayer implements Runnable { private final int mResId; public More ...DialogDisplayer(int resId) { mResId = resId; } public More ...DialogDisplayer(String errorMessage) { mResId = R.id.dialog_error_with_message; mErrorMessage = errorMessage; } public void More ...run() { showDialog(mResId); } } private class More ...CancelListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelList ener { public void More ...onClick(DialogInterface dialog, int which) { finish(); } public void More ...onCancel(DialogInterface dialog) { finish(); } } private CancelListener mCancelListener = new CancelListener(); private class More ...VCardReadThread extends Thread implements DialogInterface.OnCancelListener { private ContentResolver mResolver; private VCardParser_V21 mVCardParser; private boolean mCanceled; private PowerManager.WakeLock mWakeLock; private Uri mUri; private File mTempFile; private List<VCardFile> mSelectedVCardFileList; private List<String> mErrorFileNameList; public More ...VCardReadThread(Uri uri) { mUri = uri; init(); } public More ...VCardReadThread(final List<VCardFile> selectedVCardFileLi st) { mSelectedVCardFileList = selectedVCardFileList; mErrorFileNameList = new ArrayList<String>(); init(); } private void More ...init() { Context context = ImportVCardActivity.this; mResolver = context.getContentResolver(); PowerManager powerManager = (PowerManager)context.getSystemService( Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK |

PowerManager.ON_AFTER_RELEASE, LOG_TAG); } @Override public void More ...finalize() { if (mWakeLock != null && mWakeLock.isHeld()) { mWakeLock.release(); } } @Override public void More ...run() { boolean shouldCallFinish = true; mWakeLock.acquire(); Uri createdUri = null; mTempFile = null; // Some malicious vCard data may make this thread broken // (e.g. OutOfMemoryError). // Even in such cases, some should be done. try { if (mUri != null) { // Read one vCard expressed by mUri final Uri targetUri = mUri; mProgressDialogForReadVCard.setProgressNumberFormat(""); mProgressDialogForReadVCard.setProgress(0); // Count the number of VCard entries mProgressDialogForReadVCard.setIndeterminate(true); long start; if (DO_PERFORMANCE_PROFILE) { start = System.currentTimeMillis(); } VCardEntryCounter counter = new VCardEntryCounter(); VCardSourceDetector detector = new VCardSourceDetector(); VCardInterpreterCollection builderCollection = new VCardInte rpreterCollection( Arrays.asList(counter, detector)); boolean result; try { result = readOneVCardFile(targetUri, VCardConfig.DEFAULT_CHARSET, builderCollection, null, true, null); } catch (VCardNestedException e) { try { // Assume that VCardSourceDetector was able to detec t the source. // Try again with the detector. result = readOneVCardFile(targetUri, VCardConfig.DEFAULT_CHARSET, counter, detect or, false, null); } catch (VCardNestedException e2) { result = false; Log.e(LOG_TAG, "Must not reach here. " + e2); } } if (DO_PERFORMANCE_PROFILE) { long time = System.currentTimeMillis() - start; Log.d(LOG_TAG, "time for counting the number of vCard en tries: " + time + " ms"); }

if (!result) { shouldCallFinish = false; return; } mProgressDialogForReadVCard.setProgressNumberFormat( getString(R.string.reading_vcard_contacts)); mProgressDialogForReadVCard.setIndeterminate(false); mProgressDialogForReadVCard.setMax(counter.getCount()); String charset = detector.getEstimatedCharset(); createdUri = doActuallyReadOneVCard(targetUri, null, charset , true, detector, mErrorFileNameList); } else { // Read multiple files. mProgressDialogForReadVCard.setProgressNumberFormat( getString(R.string.reading_vcard_files)); mProgressDialogForReadVCard.setMax(mSelectedVCardFileList.si ze()); mProgressDialogForReadVCard.setProgress(0); for (VCardFile vcardFile : mSelectedVCardFileList) { if (mCanceled) { return; } // TODO: detect scheme! final Uri targetUri = Uri.parse("file://" + vcardFile.getCanonicalPath()); VCardSourceDetector detector = new VCardSourceDetector() ; try { if (!readOneVCardFile(targetUri, VCardConfig.DEFAULT _CHARSET, detector, null, true, mErrorFileNameList)) { continue; } } catch (VCardNestedException e) { // Assume that VCardSourceDetector was able to detec t the source. } String charset = detector.getEstimatedCharset(); doActuallyReadOneVCard(targetUri, mAccount, charset, false, detector, mErrorFileNameList); mProgressDialogForReadVCard.incrementProgressBy(1); } } } finally { mWakeLock.release(); mProgressDialogForReadVCard.dismiss(); if (mTempFile != null) { if (!mTempFile.delete()) { Log.w(LOG_TAG, "Failed to delete a cache file."); } mTempFile = null; } // finish() is called via mCancelListener, which is used in Dial ogDisplayer. if (shouldCallFinish && !isFinishing()) { if (mErrorFileNameList == null || mErrorFileNameList.isEmpty ()) {

finish(); if (mNeedReview) { mNeedReview = false; Log.v("importVCardActivity", "Prepare to review the imported contact"); if (createdUri != null) { // get contact_id of this raw_contact final long rawContactId = ContentUris.parseId(cr eatedUri); Uri contactUri = RawContacts.getContactLookupUri ( getContentResolver(), ContentUris.withAp pendedId( RawContacts.CONTENT_URI, rawCont actId)); Intent viewIntent = new Intent(Intent.ACTION_VIE W, contactUri); startActivity(viewIntent); } } } else { StringBuilder builder = new StringBuilder(); boolean first = true; for (String fileName : mErrorFileNameList) { if (first) { first = false; } else { builder.append(", "); } builder.append(fileName); } runOnUIThread(new DialogDisplayer( getString(R.string.fail_reason_failed_to_read_fi les, builder.toString()))); } } } } private Uri More ...doActuallyReadOneVCard(Uri uri, Account account, String charset, boolean showEntryParseProgress, VCardSourceDetector detector, List<String> errorFileNameList) { final Context context = ImportVCardActivity.this; VCardEntryConstructor builder; final String currentLanguage = Locale.getDefault().getLanguage(); int vcardType = VCardConfig.getVCardTypeFromString( context.getString(R.string.config_import_vcard_type)); if (charset != null) { builder = new VCardEntryConstructor(charset, charset, false, vca rdType, mAccount); } else { charset = VCardConfig.DEFAULT_CHARSET; builder = new VCardEntryConstructor(null, null, false, vcardType , mAccount); } VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);

builder.addEntryHandler(committer); if (showEntryParseProgress) { builder.addEntryHandler(new ProgressShower(mProgressDialogForRea dVCard, context.getString(R.string.reading_vcard_message), ImportVCardActivity.this, mHandler)); } try { if (!readOneVCardFile(uri, charset, builder, detector, false, nu ll)) { return null; } } catch (VCardNestedException e) { Log.e(LOG_TAG, "Never reach here."); } final ArrayList<Uri> createdUris = committer.getCreatedUris(); return (createdUris == null || createdUris.size() != 1) ? null : cre atedUris.get(0); } private boolean More ...readOneVCardFile(Uri uri, String charset, VCardInterpreter builder, VCardSourceDetector detector, boolean throwNestedException, List<String> errorFileNameList) throws VCardNestedException { InputStream is; try { is = mResolver.openInputStream(uri); mVCardParser = new VCardParser_V21(detector); try { mVCardParser.parse(is, charset, builder, mCanceled); } catch (VCardVersionException e1) { try { is.close(); } catch (IOException e) { } if (builder instanceof VCardEntryConstructor) { // Let the object clean up internal temporal objects, ((VCardEntryConstructor)builder).clear(); } is = mResolver.openInputStream(uri); try { mVCardParser = new VCardParser_V30(); mVCardParser.parse(is, charset, builder, mCanceled); } catch (VCardVersionException e2) { throw new VCardException("vCard with unspported version. "); } } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } catch (IOException e) {

Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage()); mProgressDialogForReadVCard.dismiss(); if (errorFileNameList != null) { errorFileNameList.add(uri.toString()); } else { runOnUIThread(new DialogDisplayer( getString(R.string.fail_reason_io_error) + ": " + e.getLocalizedMessage())); } return false; } catch (VCardNotSupportedException e) { if ((e instanceof VCardNestedException) && throwNestedException) { throw (VCardNestedException)e; } if (errorFileNameList != null) { errorFileNameList.add(uri.toString()); } else { runOnUIThread(new DialogDisplayer( getString(R.string.fail_reason_vcard_not_supported_e rror) + " (" + e.getMessage() + ")")); } return false; } catch (VCardException e) { if (errorFileNameList != null) { errorFileNameList.add(uri.toString()); } else { runOnUIThread(new DialogDisplayer( getString(R.string.fail_reason_vcard_parse_error) + " (" + e.getMessage() + ")")); } return false; } return true; } public void More ...cancel() { mCanceled = true; if (mVCardParser != null) { mVCardParser.cancel(); } } public void More ...onCancel(DialogInterface dialog) { cancel(); } } private class More ...ImportTypeSelectedListener implements DialogInterface.OnClickListener { public static final int IMPORT_ONE = 0; public static final int IMPORT_MULTIPLE = 1; public static final int IMPORT_ALL = 2; public static final int IMPORT_TYPE_SIZE = 3; private int mCurrentIndex;

public void More ...onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { switch (mCurrentIndex) { case IMPORT_ALL: importMultipleVCardFromSDCard(mAllVCardFileList); break; case IMPORT_MULTIPLE: showDialog(R.id.dialog_select_multiple_vcard); break; default: showDialog(R.id.dialog_select_one_vcard); break; } } else if (which == DialogInterface.BUTTON_NEGATIVE) { finish(); } else { mCurrentIndex = which; } } } private class More ...VCardSelectedListener implements DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickL istener { private int mCurrentIndex; private Set<Integer> mSelectedIndexSet; public More ...VCardSelectedListener(boolean multipleSelect) { mCurrentIndex = 0; if (multipleSelect) { mSelectedIndexSet = new HashSet<Integer>(); } } public void More ...onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { if (mSelectedIndexSet != null) { List<VCardFile> selectedVCardFileList = new ArrayList<VCardF ile>(); int size = mAllVCardFileList.size(); // We'd like to sort the files by its index, so we do not us e Set iterator. for (int i = 0; i < size; i++) { if (mSelectedIndexSet.contains(i)) { selectedVCardFileList.add(mAllVCardFileList.get(i)); } } importMultipleVCardFromSDCard(selectedVCardFileList); } else { String canonicalPath = mAllVCardFileList.get(mCurrentIndex). getCanonicalPath(); final Uri uri = Uri.parse("file://" + canonicalPath); importOneVCardFromSDCard(uri); } } else if (which == DialogInterface.BUTTON_NEGATIVE) { finish(); } else { // Some file is selected. mCurrentIndex = which; if (mSelectedIndexSet != null) {

if (mSelectedIndexSet.contains(which)) { mSelectedIndexSet.remove(which); } else { mSelectedIndexSet.add(which); } } } } public void More ...onClick(DialogInterface dialog, int which, boolean i sChecked) { if (mSelectedIndexSet == null || (mSelectedIndexSet.contains(which) == isChecked)) { Log.e(LOG_TAG, String.format("Inconsist state in index %d (%s)", which, mAllVCardFileList.get(which).getCanonicalPath())); } else { onClick(dialog, which); } } } private class More ...VCardScanThread extends Thread implements OnCancelListener , OnClickListener { private boolean mCanceled; private boolean mGotIOException; private File mRootDirectory; // To avoid recursive link. private Set<String> mCheckedPaths; private PowerManager.WakeLock mWakeLock; private class More ...CanceledException extends Exception { } public More ...VCardScanThread(File sdcardDirectory) { mCanceled = false; mGotIOException = false; mRootDirectory = sdcardDirectory; mCheckedPaths = new HashSet<String>(); PowerManager powerManager = (PowerManager)ImportVCardActivity.this.g etSystemService( Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, LOG_TAG); } @Override public void More ...run() { mAllVCardFileList = new Vector<VCardFile>(); try { mWakeLock.acquire(); getVCardFileRecursively(mRootDirectory); } catch (CanceledException e) { mCanceled = true; } catch (IOException e) { mGotIOException = true; } finally { mWakeLock.release();

} if (mCanceled) { mAllVCardFileList = null; } mProgressDialogForScanVCard.dismiss(); mProgressDialogForScanVCard = null; if (mGotIOException) { runOnUIThread(new DialogDisplayer(R.id.dialog_io_exception)); } else if (mCanceled) { finish(); } else { int size = mAllVCardFileList.size(); final Context context = ImportVCardActivity.this; if (size == 0) { runOnUIThread(new DialogDisplayer(R.id.dialog_vcard_not_foun d)); } else { startVCardSelectAndImport(); } } } private void More ...getVCardFileRecursively(File directory) throws CanceledException, IOException { if (mCanceled) { throw new CanceledException(); } // e.g. secured directory may return null toward listFiles(). final File[] files = directory.listFiles(); if (files == null) { Log.w(LOG_TAG, "listFiles() returned null (directory: " + direct ory + ")"); return; } for (File file : directory.listFiles()) { if (mCanceled) { throw new CanceledException(); } String canonicalPath = file.getCanonicalPath(); if (mCheckedPaths.contains(canonicalPath)) { continue; } mCheckedPaths.add(canonicalPath); if (file.isDirectory()) { getVCardFileRecursively(file); } else if (canonicalPath.toLowerCase().endsWith(".vcf") && file.canRead()){ String fileName = file.getName(); VCardFile vcardFile = new VCardFile( fileName, canonicalPath, file.lastModified()); mAllVCardFileList.add(vcardFile); } } }

public void More ...onCancel(DialogInterface dialog) { mCanceled = true; } public void More ...onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_NEGATIVE) { mCanceled = true; } } } private void More ...startVCardSelectAndImport() { int size = mAllVCardFileList.size(); if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard _automatically)) { importMultipleVCardFromSDCard(mAllVCardFileList); } else if (size == 1) { String canonicalPath = mAllVCardFileList.get(0).getCanonicalPath(); Uri uri = Uri.parse("file://" + canonicalPath); importOneVCardFromSDCard(uri); } else if (getResources().getBoolean(R.bool.config_allow_users_select_al l_vcard_import)) { runOnUIThread(new DialogDisplayer(R.id.dialog_select_import_type)); } else { runOnUIThread(new DialogDisplayer(R.id.dialog_select_one_vcard)); } } private void More ...importMultipleVCardFromSDCard(final List<VCardFile> sel ectedVCardFileList) { runOnUIThread(new Runnable() { public void More ...run() { mVCardReadThread = new VCardReadThread(selectedVCardFileList); showDialog(R.id.dialog_reading_vcard); } }); } private void More ...importOneVCardFromSDCard(final Uri uri) { runOnUIThread(new Runnable() { public void More ...run() { mVCardReadThread = new VCardReadThread(uri); showDialog(R.id.dialog_reading_vcard); } }); } private Dialog More ...getSelectImportTypeDialog() { DialogInterface.OnClickListener listener = new ImportTypeSelectedListener(); AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.select_vcard_title) .setPositiveButton(android.R.string.ok, listener) .setOnCancelListener(mCancelListener) .setNegativeButton(android.R.string.cancel, mCancelListener); String[] items = new String[ImportTypeSelectedListener.IMPORT_TYPE_SIZE] ; items[ImportTypeSelectedListener.IMPORT_ONE] =

getString(R.string.import_one_vcard_string); items[ImportTypeSelectedListener.IMPORT_MULTIPLE] = getString(R.string.import_multiple_vcard_string); items[ImportTypeSelectedListener.IMPORT_ALL] = getString(R.string.import_all_vcard_string); builder.setSingleChoiceItems(items, ImportTypeSelectedListener.IMPORT_ON E, listener); return builder.create(); } private Dialog More ...getVCardFileSelectDialog(boolean multipleSelect) { int size = mAllVCardFileList.size(); VCardSelectedListener listener = new VCardSelectedListener(multipleSelec t); AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.select_vcard_title) .setPositiveButton(android.R.string.ok, listener) .setOnCancelListener(mCancelListener) .setNegativeButton(android.R.string.cancel, mCancelListener); CharSequence[] items = new CharSequence[size]; DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < size; i++) { VCardFile vcardFile = mAllVCardFileList.get(i); SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); stringBuilder.append(vcardFile.getName()); stringBuilder.append('\n'); int indexToBeSpanned = stringBuilder.length(); // Smaller date text looks better, since each file name becomes easi er to read. // The value set to RelativeSizeSpan is arbitrary. You can change it to any other // value (but the value bigger than 1.0f would not make nice appeara nce :) stringBuilder.append( "(" + dateFormat.format(new Date(vcardFile.getLastModifi ed())) + ")"); stringBuilder.setSpan( new RelativeSizeSpan(0.7f), indexToBeSpanned, stringBuilder. length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); items[i] = stringBuilder; } if (multipleSelect) { builder.setMultiChoiceItems(items, (boolean[])null, listener); } else { builder.setSingleChoiceItems(items, 0, listener); } return builder.create(); } @Override protected void More ...onCreate(Bundle bundle) { super.onCreate(bundle); final Intent intent = getIntent(); if (intent != null) { final String accountName = intent.getStringExtra("account_name"); final String accountType = intent.getStringExtra("account_type");

if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountTyp e)) { mAccount = new Account(accountName, accountType); } } else { Log.e(LOG_TAG, "intent does not exist"); } // The caller often does not know account information at all, so we show the UI instead. if (mAccount == null) { // There's three possibilities: // - more than one accounts -> ask the user // - just one account -> use the account without asking the user // - no account -> use phone-local storage without asking the user final Sources sources = Sources.getInstance(this); final List<Account> accountList = sources.getAccounts(true); final int size = accountList.size(); if (size > 1) { final int resId = R.string.import_from_sdcard; mAccountSelectionListener = new AccountSelectionUtil.AccountSelectedListener( this, accountList, resId) { @Override public void More ...onClick(DialogInterface dialog, int whic h) { dialog.dismiss(); mAccount = mAccountList.get(which); // Instead of using Intent mechanism, call the relevant private method, // to avoid throwing an Intent to itself again. startImport(); } }; showDialog(resId); return; } else { mAccount = size > 0 ? accountList.get(0) : null; } } startImport(); } private void More ...startImport() { Intent intent = getIntent(); final String action = intent.getAction(); final Uri uri = intent.getData(); Log.v(LOG_TAG, "action = " + action + " ; path = " + uri); if (Intent.ACTION_VIEW.equals(action)) { // Import the file directly and then go to EDIT screen mNeedReview = true; } if (uri != null) { importOneVCardFromSDCard(uri); } else { doScanExternalStorageAndImportVCard(); } }

@Override protected Dialog More ...onCreateDialog(int resId) { switch (resId) { case R.string.import_from_sdcard: { if (mAccountSelectionListener == null) { throw new NullPointerException( "mAccountSelectionListener must not be null."); } return AccountSelectionUtil.getSelectAccountDialog(this, resId, mAccountSelectionListener, new CancelListener()); } case R.id.dialog_searching_vcard: { if (mProgressDialogForScanVCard == null) { String title = getString(R.string.searching_vcard_title); String message = getString(R.string.searching_vcard_message) ; mProgressDialogForScanVCard = ProgressDialog.show(this, title, message, true, false); mProgressDialogForScanVCard.setOnCancelListener(mVCardScanTh read); mVCardScanThread.start(); } return mProgressDialogForScanVCard; } case R.id.dialog_sdcard_not_found: { AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.no_sdcard_title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(R.string.no_sdcard_message) .setOnCancelListener(mCancelListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } case R.id.dialog_vcard_not_found: { String message = (getString(R.string.scanning_sdcard_failed_mess age, getString(R.string.fail_reason_no_vcard_file))); AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.scanning_sdcard_failed_title) .setMessage(message) .setOnCancelListener(mCancelListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } case R.id.dialog_select_import_type: { return getSelectImportTypeDialog(); } case R.id.dialog_select_multiple_vcard: { return getVCardFileSelectDialog(true); } case R.id.dialog_select_one_vcard: { return getVCardFileSelectDialog(false); } case R.id.dialog_reading_vcard: { if (mProgressDialogForReadVCard == null) { String title = getString(R.string.reading_vcard_title); String message = getString(R.string.reading_vcard_message); mProgressDialogForReadVCard = new ProgressDialog(this);

mProgressDialogForReadVCard.setTitle(title); mProgressDialogForReadVCard.setMessage(message); mProgressDialogForReadVCard.setProgressStyle(ProgressDialog. STYLE_HORIZONTAL); mProgressDialogForReadVCard.setOnCancelListener(mVCardReadTh read); mVCardReadThread.start(); } return mProgressDialogForReadVCard; } case R.id.dialog_io_exception: { String message = (getString(R.string.scanning_sdcard_failed_mess age, getString(R.string.fail_reason_io_error))); AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.scanning_sdcard_failed_title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(message) .setOnCancelListener(mCancelListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } case R.id.dialog_error_with_message: { String message = mErrorMessage; if (TextUtils.isEmpty(message)) { Log.e(LOG_TAG, "Error message is null while it must not."); message = getString(R.string.fail_reason_unknown); } AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(getString(R.string.reading_vcard_failed_title)) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(message) .setOnCancelListener(mCancelListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } } return super.onCreateDialog(resId); } @Override protected void More ...onPause() { super.onPause(); if (mVCardReadThread != null) { // The Activity is no longer visible. Stop the thread. mVCardReadThread.cancel(); mVCardReadThread = null; } // ImportVCardActivity should not be persistent. In other words, if ther e's some // event calling onPause(), this Activity should finish its work and giv e the main // screen back to the caller Activity. if (!isFinishing()) { finish(); } }

@Override protected void More ...onDestroy() { // The code assumes the handler runs on the UI thread. If not, // clearing the message queue is not enough, one would have to // make sure that the handler does not run any callback when // this activity isFinishing(). // Need to make sure any worker thread is done before we flush and // nullify the message handler. if (mVCardReadThread != null) { Log.w(LOG_TAG, "VCardReadThread exists while this Activity is now be ing killed!"); mVCardReadThread.cancel(); int attempts = 0; while (mVCardReadThread.isAlive() && attempts < 10) { try { Thread.currentThread().sleep(20); } catch (InterruptedException ie) { // Keep on going until max attempts is reached. } attempts++; } if (mVCardReadThread.isAlive()) { // Find out why the thread did not exit in a timely // fashion. Last resort: increase the sleep duration // and/or the number of attempts. Log.e(LOG_TAG, "VCardReadThread is still alive after max attempt s."); } mVCardReadThread = null; } // Callbacks messages have what == 0. if (mHandler.hasMessages(0)) { mHandler.removeMessages(0); } mHandler = null; // Prevents memory leaks by breaking any circular depe ndency. super.onDestroy(); } private void More ...runOnUIThread(Runnable runnable) { if (mHandler == null) { Log.w(LOG_TAG, "Handler object is null. No dialog is shown."); } else { mHandler.post(runnable); } } @Override public void More ...finalize() { // TODO: This should not be needed. Throw exception instead. if (mVCardReadThread != null) { // Not sure this procedure is really needed, but just in case... Log.e(LOG_TAG, "VCardReadThread exists while this Activity is now be ing killed!"); mVCardReadThread.cancel(); mVCardReadThread = null; }

} private void More ...doScanExternalStorageAndImportVCard() { // TODO: should use getExternalStorageState(). final File file = Environment.getExternalStorageDirectory(); if (!file.exists() || !file.isDirectory() || !file.canRead()) { showDialog(R.id.dialog_sdcard_not_found); } else { mVCardScanThread = new VCardScanThread(file); showDialog(R.id.dialog_searching_vcard); } } }

You might also like