/* * 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 com.android.contacts.model.Sources; import com.android.contacts.util.AccountSelectionUtil; import com.android.contacts.util.VCardEntryApplyBatchHandler; import android.accounts.Account; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnKeyListener; import android.content.Intent; import android.database.sqlite.SQLiteDiskIOException; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.pim.vcard.VCardConfig; import android.pim.vcard.VCardEntryCommitter; import android.pim.vcard.VCardEntryConstructor; import android.pim.vcard.VCardEntryCounter; import android.pim.vcard.VCardInterpreter; import android.pim.vcard.VCardInterpreterCollection; import android.pim.vcard.VCardParser; import android.pim.vcard.VCardParser_V21; import android.pim.vcard.VCardParser_V30; import android.pim.vcard.VCardSourceDetector; import android.pim.vcard.exception.VCardException; import android.pim.vcard.exception.VCardNestedException; import android.pim.vcard.exception.VCardNotSupportedException; import android.pim.vcard.exception.VCardVersionException; import android.provider.ContactsContract.RawContacts; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.RelativeSizeSpan; import android.util.Log; import android.view.KeyEvent; import android.widget.Toast; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.Vector; class VCardFile { private String mName; private String mCanonicalPath; private long mLastModified; public VCardFile(String name, String canonicalPath, long lastModified) { mName = name; mCanonicalPath = canonicalPath; mLastModified = lastModified; } public String getName() { return mName; } public String getCanonicalPath() { return mCanonicalPath; } public long getLastModified() { return mLastModified; } } /** * Class for importing vCard. Several user interaction will be required while reading * (selecting a file, waiting a moment, etc.) * * Note that this Activity assumes that the instance is a "one-shot Activity", which will be * finished (with the method {@link Activity#finish()}) after the import and never reuse * any Dialog in the instance. So this code is careless about the management around managed * dialogs stuffs (like how onCreateDialog() is used). */ public class ImportVCardActivity extends Activity { private static final String LOG_TAG = "ImportVCardActivity"; private static final boolean DO_PERFORMANCE_PROFILE = false; private final static int VCARD_VERSION_V21 = 1; private final static int VCARD_VERSION_V30 = 2; private final static int VCARD_VERSION_V40 = 3; // Run on the UI thread. Must not be null except after onDestroy(). private Handler mHandler = new Handler(); private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener; private Account mAccount; private ProgressDialog mProgressDialogForScanVCard; private List<VCardFile> mAllVCardFileList; private VCardScanThread mVCardScanThread; private VCardReadThread mVCardReadThread; private ProgressDialog mProgressDialogForReadVCard; private String mErrorMessage; private boolean mNeedReview = false; //add by liguxiang 11-25-11 for NEWMS00140264 begin private static boolean mImporting = false; private static int mLastCallState = 0; //add by liguxiang 11-25-11 for NEWMS00140264 end // Runs on the UI thread. private class DialogDisplayer implements Runnable { private final int mResId; public DialogDisplayer(int resId) { mResId = resId; } public DialogDisplayer(String errorMessage) { mResId = R.id.dialog_error_with_message; mErrorMessage = errorMessage; } public void run() { // Show the Dialog only when the parent Activity is still alive. if (!ImportVCardActivity.this.isFinishing()) { showDialog(mResId); } } } private class CancelListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { public void onClick(DialogInterface dialog, int which) { if (null != dialog) { dialog.dismiss(); dialog = null; } mErrorMessage = null; finish(); } public void onCancel(DialogInterface dialog) { if (null != dialog) { dialog.dismiss(); dialog = null; } mErrorMessage = null; finish(); } } private CancelListener mCancelListener = new CancelListener(); private class VCardReadThread extends Thread implements DialogInterface.OnCancelListener { private ContentResolver mResolver; private VCardParser mVCardParser; private boolean mCanceled; private PowerManager.WakeLock mWakeLock; private Uri mUri; private File mTempFile; private List<VCardFile> mSelectedVCardFileList; private List<String> mErrorFileNameList; public VCardReadThread(Uri uri) { mUri = uri; init(); } public VCardReadThread(final List<VCardFile> selectedVCardFileList) { mSelectedVCardFileList = selectedVCardFileList; mErrorFileNameList = new ArrayList<String>(); init(); } private void 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 finalize() { if (mWakeLock != null && mWakeLock.isHeld()) { mWakeLock.release(); } } @Override public void run() { Log.v(LOG_TAG, "VCardReadThread: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. if (mSelectedVCardFileList != null && mUri == null && mSelectedVCardFileList.size() == 1) { VCardFile vcardFile = mSelectedVCardFileList.get(0); mUri = Uri.parse("file://" + vcardFile.getCanonicalPath()); mSelectedVCardFileList = null; } try { if (mUri != null) { // Read one vCard expressed by mUri Log.v(LOG_TAG, "VCardReadThread:mUri != null"); 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(); } final VCardEntryCounter counter = new VCardEntryCounter(); final VCardSourceDetector detector = new VCardSourceDetector(); final VCardInterpreterCollection builderCollection = new VCardInterpreterCollection(Arrays.asList(counter, detector)); boolean result; try { // We don't know which type should be useld to parse the Uri. // It is possble to misinterpret the vCard, but we expect the parser // lets VCardSourceDetector detect the type before the misinterpretation. result = readOneVCardFile(targetUri, VCardConfig.VCARD_TYPE_UNKNOWN, builderCollection, true, null); } catch (VCardNestedException e) { try { final int estimatedVCardType = detector.getEstimatedType(); // Assume that VCardSourceDetector was able to detect the source. // Try again with the detector. result = readOneVCardFile(targetUri, estimatedVCardType, counter, 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 entries: " + time + " ms"); } if (!result) { shouldCallFinish = false; return; } mProgressDialogForReadVCard.setProgressNumberFormat( getString(R.string.reading_vcard_contacts)); mProgressDialogForReadVCard.setIndeterminate(false); mProgressDialogForReadVCard.setMax(counter.getCount()); createdUri = doActuallyReadOneVCard(targetUri, mAccount, true, counter.getCount(), detector, mErrorFileNameList); Log.v(LOG_TAG, "VCardReadThread:createdUri-->" + createdUri); } else { // Read multiple files. mProgressDialogForReadVCard.setProgressNumberFormat( getString(R.string.reading_vcard_files)); mProgressDialogForReadVCard.setMax(mSelectedVCardFileList.size()); mProgressDialogForReadVCard.setProgress(0); int successFileCount = 0; int totalFileCount = mSelectedVCardFileList.size(); for (VCardFile vcardFile : mSelectedVCardFileList) { // TODO: detect scheme! final Uri targetUri = Uri.parse("file://" + vcardFile.getCanonicalPath()); VCardEntryCounter counter = new VCardEntryCounter(); VCardSourceDetector detector = new VCardSourceDetector(); VCardInterpreterCollection builderCollection = new VCardInterpreterCollection(Arrays.asList(counter, detector)); try { if (!readOneVCardFile(targetUri, VCardConfig.VCARD_TYPE_UNKNOWN, builderCollection, true, mErrorFileNameList)) { continue; } } catch (VCardNestedException e) { // Assume that VCardSourceDetector was able to detect the source. } createdUri = doActuallyReadOneVCard(targetUri, mAccount, false, counter.getCount(), detector, mErrorFileNameList); if (null != createdUri) { successFileCount++; } if (mCanceled) { String message = getString(R.string.import_aborted); showToastMessage(message); break; } mProgressDialogForReadVCard.incrementProgressBy(1); } StringBuffer message = new StringBuffer(); message.append(successFileCount) .append(" ") .append(getString(R.string.import_vcard_files_flags)) .append(getString(R.string.label_success)) .append(", ") .append((totalFileCount - successFileCount)) .append(" ") .append(getString(R.string.import_vcard_files_flags)) .append(getString(R.string.label_fail)); showToastMessage(message.toString()); } } 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 DialogDisplayer. if (shouldCallFinish && !isFinishing()) { if (mErrorFileNameList == null || mErrorFileNameList.isEmpty()) { if(!TextUtils.isEmpty(mErrorMessage)) { return; } finish(); if (mNeedReview) { mNeedReview = false; Log.v(LOG_TAG, "Prepare to review the imported contact"); if (createdUri != null) { // get contact_id of this raw_contact final long rawContactId = ContentUris.parseId(createdUri); Uri contactUri = RawContacts.getContactLookupUri( getContentResolver(), ContentUris.withAppendedId( RawContacts.CONTENT_URI, rawContactId)); Intent viewIntent = new Intent(Intent.ACTION_VIEW, 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_files, builder.toString()))); } } } } private Uri doActuallyReadOneVCard(Uri uri, Account account, boolean showEntryParseProgress,int entryCount, VCardSourceDetector detector, List<String> errorFileNameList) { Log.v(LOG_TAG, "doActuallyReadOneVCard"); final Context context = ImportVCardActivity.this; VCardEntryConstructor builder; final String currentLanguage = Locale.getDefault().getLanguage(); int vcardType = detector.getEstimatedType(); if (vcardType == VCardConfig.VCARD_TYPE_UNKNOWN) { vcardType = VCardConfig.getVCardTypeFromString( context.getString(R.string.config_import_vcard_type)); } final String estimatedCharset = detector.getEstimatedCharset(); builder = new VCardEntryConstructor(vcardType, mAccount, estimatedCharset); final VCardEntryApplyBatchHandler committer = new VCardEntryApplyBatchHandler(mResolver, entryCount); builder.addEntryHandler(committer); builder.addEntryHandler(new ProgressShower(mProgressDialogForReadVCard, context.getString(R.string.reading_vcard_message) + "\n" + uri.getLastPathSegment(), ImportVCardActivity.this, mHandler, showEntryParseProgress, entryCount)); try { if (!readOneVCardFile(uri, vcardType, builder, false, null)) { return null; } } catch (VCardNestedException e) { Log.e(LOG_TAG, "Never reach here."); } ArrayList<Uri> createdUris = committer.getCreatedUris(); final int totalCount = entryCount; final int successCount = createdUris.size(); if (mSelectedVCardFileList == null) { StringBuffer message = new StringBuffer(); message.append(" ") .append(successCount) .append(" ") .append(getString(R.string.label_success)) .append(", ") .append((totalCount - successCount)) .append(" ") .append(getString(R.string.label_fail)); showToastMessage(message.toString()); } final Uri successUri; if (successCount > 0) { successUri = createdUris.get(0); } else { successUri = null; } createdUris = null; return successUri; } /** * Charset should be handled by {@link VCardEntryConstructor}. */ private boolean readOneVCardFile(Uri uri, int vcardType, VCardInterpreter interpreter, boolean throwNestedException, List<String> errorFileNameList) throws VCardNestedException { InputStream is; try { is = mResolver.openInputStream(uri); mVCardParser = new VCardParser_V21(vcardType); if (mCanceled) { mVCardParser.cancel(); } try { mVCardParser.parse(is, interpreter); } catch (VCardVersionException e1) { try { is.close(); } catch (IOException e) { } if (interpreter instanceof VCardEntryConstructor) { // Let the object clean up internal temporal objects, ((VCardEntryConstructor)interpreter).clear(); } else if (interpreter instanceof VCardInterpreterCollection) { for (VCardInterpreter elem : ((VCardInterpreterCollection) interpreter).getCollection()) { if (elem instanceof VCardEntryConstructor) { ((VCardEntryConstructor)elem).clear(); } } } is = mResolver.openInputStream(uri); try { mVCardParser = new VCardParser_V30(vcardType); if (mCanceled) { mVCardParser.cancel(); } mVCardParser.parse(is, interpreter); } 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_error) + " (" + 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; } catch (SQLiteDiskIOException e) { Log.d(LOG_TAG, "SQLiteDiskIOException message: " + e.toString()); if (errorFileNameList != null) { errorFileNameList.add(uri.toString()); } else { runOnUIThread(new DialogDisplayer( getString(R.string.fail_reason_io_error) + " (" + e.getMessage() + ")")); } return false; } return true; } public void cancel() { mCanceled = true; if (mVCardParser != null) { mVCardParser.cancel(); } finish(); } public void onCancel(DialogInterface dialog) { cancel(); } } private class 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 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 VCardSelectedListener implements DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener { private int mCurrentIndex; private Set<Integer> mSelectedIndexSet; public VCardSelectedListener(boolean multipleSelect) { mCurrentIndex = 0; if (multipleSelect) { mSelectedIndexSet = new HashSet<Integer>(); } } public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { if (mSelectedIndexSet != null) { List<VCardFile> selectedVCardFileList = new ArrayList<VCardFile>(); int size = mAllVCardFileList.size(); // We'd like to sort the files by its index, so we do not use 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) { releaseAllVCardFileList(); finish(); } else { // Some file is selected. mCurrentIndex = which; if (mSelectedIndexSet != null) { if (mSelectedIndexSet.contains(which)) { mSelectedIndexSet.remove(which); } else { mSelectedIndexSet.add(which); } } } } public void onClick(DialogInterface dialog, int which, boolean isChecked) { 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); } } } /** * Thread scanning VCard from SDCard. After scanning, the dialog which lets a user select * a vCard file is shown. After the choice, VCardReadThread starts running. */ private class 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 CanceledException extends Exception { } public VCardScanThread(File sdcardDirectory) { mCanceled = false; mGotIOException = false; mRootDirectory = sdcardDirectory; mCheckedPaths = new HashSet<String>(); PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService( Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, LOG_TAG); } @Override public void run() { releaseAllVCardFileList(); mAllVCardFileList = new Vector<VCardFile>(); try { mWakeLock.acquire(); Log.v(LOG_TAG, "mRootDirectory:"+mRootDirectory.getPath()); getVCardFileRecursively(mRootDirectory);//recursively get the vcard file in mAllVCardFileList mCheckedPaths.clear(); mCheckedPaths = null; } catch (CanceledException e) { mCanceled = true; } catch (IOException e) { mGotIOException = true; } finally { mWakeLock.release(); } if (mCanceled) { mRootDirectory = null; 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_found)); } else { startVCardSelectAndImport(); } } } private void getVCardFileRecursively(File directory) throws CanceledException, IOException { if (mCanceled) { throw new CanceledException(); } // e.g. secured directory may return null toward listFiles(). File[] files = directory.listFiles(); if (files == null) { Log.w(LOG_TAG, "listFiles() returned null (directory: " + directory + ")"); return; } final int len = files.length; for (int i = 0; i < len; i++) { if (mCanceled) { throw new CanceledException(); } File file = files[i]; String canonicalPath = file.getCanonicalPath();//the absolute path of this file 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()); if (mAllVCardFileList != null) { mAllVCardFileList.add(vcardFile); } } file = null; files[i] = null; } files = null; } public void onCancel(DialogInterface dialog) { releaseAllVCardFileList(); mCanceled = true; finish(); } public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_NEGATIVE) { releaseAllVCardFileList(); mCanceled = true; } } } private void startVCardSelectAndImport() { Log.v(LOG_TAG, "startVCardSelectAndImport"); int size = mAllVCardFileList.size(); if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard_automatically)) { importMultipleVCardFromSDCard(mAllVCardFileList); } else if (size == 1) {//只有一条记录 runOnUIThread(new DialogDisplayer(R.id.dialog_import_one_vcard)); } else if (getResources().getBoolean(R.bool.config_allow_users_select_all_vcard_import)) { runOnUIThread(new DialogDisplayer(R.id.dialog_select_import_type)); } else { runOnUIThread(new DialogDisplayer(R.id.dialog_select_one_vcard)); } } private void importMultipleVCardFromSDCard(List<VCardFile> selectedVCardFileList) { final List<VCardFile> allVCardFileList = selectedVCardFileList; selectedVCardFileList = null; runOnUIThread(new Runnable() { public void run() { mVCardReadThread = new VCardReadThread(allVCardFileList); showDialog(R.id.dialog_reading_vcard); } }); } private void importOneVCardFromSDCard(final Uri uri) { Log.v(LOG_TAG, "importOneVCardFromSDCard"); runOnUIThread(new Runnable() { public void run() { mVCardReadThread = new VCardReadThread(uri); showDialog(R.id.dialog_reading_vcard); } }); } private Dialog 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) .setOnKeyListener(mOnkeyListener) .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_ONE, listener); return builder.create(); } private Dialog importVcardWarn() { return new AlertDialog.Builder(this).setTitle(R.string.import_contacts) .setMessage(R.string.whether_import_from_SD) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { String canonicalPath = mAllVCardFileList.get(0).getCanonicalPath(); Uri uri = Uri.parse("file://" + canonicalPath); Log.v(LOG_TAG, "startVCardSelectAndImport:uri-->" + uri.toString()); importOneVCardFromSDCard(uri); } }).setOnKeyListener(mOnkeyListener).setOnCancelListener(mCancelListener) .setNegativeButton(R.string.cancel, mCancelListener).create(); } private Dialog getVCardFileSelectDialog(boolean multipleSelect) { int size = mAllVCardFileList.size(); VCardSelectedListener listener = new VCardSelectedListener(multipleSelect); AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.select_vcard_title) .setPositiveButton(android.R.string.ok, listener) .setOnCancelListener(mCancelListener) .setOnKeyListener(mOnkeyListener) .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 easier 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 appearance :) stringBuilder.append( "(" + dateFormat.format(new Date(vcardFile.getLastModified())) + ")"); 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 onCreate(Bundle bundle) { if (null != bundle) { super.onCreate(null); finish(); return; } 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(accountType)) { 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 onClick(DialogInterface dialog, int which) { 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 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 onCreateDialog(int resId) { Log.v(LOG_TAG, "onCreateDialog"); mImporting = true; //add by liguxiang 11-25-11 for NEWMS00140264 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(mVCardScanThread); mProgressDialogForScanVCard.setOnKeyListener(mOnkeyListener); 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) .setOnKeyListener(mOnkeyListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } case R.id.dialog_vcard_not_found: { String message = (getString(R.string.scanning_sdcard_failed_message, 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) .setOnKeyListener(mOnkeyListener) .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_import_one_vcard:{ return importVcardWarn(); } 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(mVCardReadThread); mProgressDialogForReadVCard.setOnKeyListener(mOnkeyListener); mVCardReadThread.start(); } return mProgressDialogForReadVCard; } case R.id.dialog_io_exception: { String message = (getString(R.string.scanning_sdcard_failed_message, 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) .setOnKeyListener(mOnkeyListener) .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) .setOnKeyListener(mOnkeyListener) .setPositiveButton(android.R.string.ok, mCancelListener); return builder.create(); } } return super.onCreateDialog(resId); } private OnKeyListener mOnkeyListener = new OnKeyListener(){ @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (KeyEvent.KEYCODE_BACK == keyCode || KeyEvent.KEYCODE_HOME == keyCode) { dialog.cancel(); } if(KeyEvent.KEYCODE_VOLUME_DOWN == keyCode || KeyEvent.KEYCODE_VOLUME_UP == keyCode){ return false; } return true; } }; @Override protected void onPause() { super.onPause(); Log.i(LOG_TAG, "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 there's some // // event calling onPause(), this Activity should finish its work and give the main // // screen back to the caller Activity. // if (!isFinishing()) { // finish(); // } // //mImporting = false; } @Override protected void 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 being 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 attempts."); } mVCardReadThread = null; if (!isFinishing()) { finish(); } } // Callbacks messages have what == 0. if (mHandler.hasMessages(0)) { mHandler.removeMessages(0); } mImporting = false; //modify by dory.zheng for import aborted display to call phone in import complete mHandler = null; // Prevents memory leaks by breaking any circular dependency. super.onDestroy(); } /** * Tries to run a given Runnable object when the UI thread can. Ignore it otherwise */ private void 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 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 being killed!"); mVCardReadThread.cancel(); mVCardReadThread = null; } } /** * Scans vCard in external storage (typically SDCard) and tries to import it. * - When there's no SDCard available, an error dialog is shown. * - When multiple vCard files are available, asks a user to select one. */ private void doScanExternalStorageAndImportVCard() { Log.v(LOG_TAG, "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); Log.v(LOG_TAG, "doScanExternalStorageAndImportVCard"); showDialog(R.id.dialog_searching_vcard); } } private void showToastMessage(final String msg) { Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { public void run() { Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show(); } }); } @Override protected void onStop() { super.onStop(); Log.v(LOG_TAG, "onStop"); // if (mVCardReadThread != null) { // // The Activity is no longer visible. Stop the thread. // mVCardReadThread.cancel(); // mVCardReadThread = null; // } // if (!isFinishing()) { // finish(); // } } /** * release var mAllVCardFileList resource */ private void releaseAllVCardFileList() { if (null != mAllVCardFileList) { mAllVCardFileList.clear(); mAllVCardFileList = null; } } }