diff options
Diffstat (limited to 'src/com/android/contacts/common/vcard')
4 files changed, 48 insertions, 413 deletions
diff --git a/src/com/android/contacts/common/vcard/ExportVCardActivity.java b/src/com/android/contacts/common/vcard/ExportVCardActivity.java index 9ee02fe2..1a1b5e82 100644 --- a/src/com/android/contacts/common/vcard/ExportVCardActivity.java +++ b/src/com/android/contacts/common/vcard/ExportVCardActivity.java @@ -26,22 +26,15 @@ import android.content.ServiceConnection; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; import android.provider.OpenableColumns; import android.text.BidiFormatter; import android.text.TextDirectionHeuristics; -import android.text.TextUtils; import android.util.Log; import com.android.contacts.common.R; import com.android.contacts.common.activity.RequestImportVCardPermissionsActivity; -import java.io.File; - /** * Shows a dialog confirming the export and asks actual vCard export to {@link VCardService} * @@ -54,7 +47,6 @@ public class ExportVCardActivity extends Activity implements ServiceConnection, private static final String LOG_TAG = "VCardExport"; private static final boolean DEBUG = VCardService.DEBUG; private static final int REQUEST_CREATE_DOCUMENT = 100; - private static final String VCARD_MIME_TYPE = "text/vcard"; /** * True when this Activity is connected to {@link VCardService}. @@ -134,7 +126,7 @@ public class ExportVCardActivity extends Activity implements ServiceConnection, // Have the user choose where vcards will be exported to final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(VCARD_MIME_TYPE); + intent.setType(VCardService.VCARD_MIME_TYPE); intent.putExtra(Intent.EXTRA_TITLE, mBidiFormatter.unicodeWrap( getString(R.string.exporting_vcard_filename), TextDirectionHeuristics.LTR)); startActivityForResult(intent, REQUEST_CREATE_DOCUMENT); diff --git a/src/com/android/contacts/common/vcard/ImportVCardActivity.java b/src/com/android/contacts/common/vcard/ImportVCardActivity.java index 658e65af..79d51ad3 100644 --- a/src/com/android/contacts/common/vcard/ImportVCardActivity.java +++ b/src/com/android/contacts/common/vcard/ImportVCardActivity.java @@ -22,26 +22,21 @@ import android.app.Dialog; import android.app.Notification; import android.app.NotificationManager; import android.app.ProgressDialog; +import android.content.ClipData; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.ServiceConnection; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; import android.provider.OpenableColumns; -import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.RelativeSizeSpan; import android.util.Log; import android.widget.Toast; @@ -49,7 +44,6 @@ import com.android.contacts.common.R; import com.android.contacts.common.activity.RequestImportVCardPermissionsActivity; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.account.AccountWithDataSet; -import com.android.contacts.common.util.AccountSelectionUtil; import com.android.vcard.VCardEntryCounter; import com.android.vcard.VCardParser; import com.android.vcard.VCardParser_V21; @@ -67,15 +61,9 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; -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.Set; -import java.util.Vector; /** * The class letting users to import vCard. This includes the UI part for letting them select @@ -91,33 +79,21 @@ public class ImportVCardActivity extends Activity { private static final int SELECT_ACCOUNT = 0; - /* package */ static final String VCARD_URI_ARRAY = "vcard_uri"; - /* package */ static final String ESTIMATED_VCARD_TYPE_ARRAY = "estimated_vcard_type"; - /* package */ static final String ESTIMATED_CHARSET_ARRAY = "estimated_charset"; - /* package */ static final String VCARD_VERSION_ARRAY = "vcard_version"; - /* package */ static final String ENTRY_COUNT_ARRAY = "entry_count"; - /* package */ final static int VCARD_VERSION_AUTO_DETECT = 0; /* package */ final static int VCARD_VERSION_V21 = 1; /* package */ final static int VCARD_VERSION_V30 = 2; - private static final String SECURE_DIRECTORY_NAME = ".android_secure"; + private static final int REQUEST_OPEN_DOCUMENT = 100; /** * Notification id used when error happened before sending an import request to VCardServer. */ private static final int FAILURE_NOTIFICATION_ID = 1; - final static String CACHED_URIS = "cached_uris"; - private AccountWithDataSet mAccount; - private ProgressDialog mProgressDialogForScanVCard; private ProgressDialog mProgressDialogForCachingVCard; - private List<VCardFile> mAllVCardFileList; - private VCardScanThread mVCardScanThread; - private VCardCacheThread mVCardCacheThread; private ImportRequestConnection mConnection; /* package */ VCardImportExportListener mListener; @@ -126,30 +102,6 @@ public class ImportVCardActivity extends Activity { private Handler mHandler = new Handler(); - private static class VCardFile { - private final String mName; - private final String mCanonicalPath; - private final 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; - } - } - // Runs on the UI thread. private class DialogDisplayer implements Runnable { private final int mResId; @@ -516,243 +468,10 @@ public class ImportVCardActivity extends Activity { } } - 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: - importVCardFromSDCard(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>(); - final 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)); - } - } - importVCardFromSDCard(selectedVCardFileList); - } else { - importVCardFromSDCard(mAllVCardFileList.get(mCurrentIndex)); - } - } 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 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() { - 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_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(). - final File[] files = directory.listFiles(); - if (files == null) { - final String currentDirectoryPath = directory.getCanonicalPath(); - final String secureDirectoryPath = - mRootDirectory.getCanonicalPath().concat(SECURE_DIRECTORY_NAME); - if (!TextUtils.equals(currentDirectoryPath, secureDirectoryPath)) { - Log.w(LOG_TAG, "listFiles() returned null (directory: " + directory + ")"); - } - 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 onCancel(DialogInterface dialog) { - mCanceled = true; - } - - public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_NEGATIVE) { - mCanceled = true; - } - } - } - - private void startVCardSelectAndImport() { - int size = mAllVCardFileList.size(); - if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard_automatically) || - size == 1) { - importVCardFromSDCard(mAllVCardFileList); - } 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 importVCardFromSDCard(final List<VCardFile> selectedVCardFileList) { - final int size = selectedVCardFileList.size(); - String[] uriStrings = new String[size]; - int i = 0; - for (VCardFile vcardFile : selectedVCardFileList) { - uriStrings[i] = "file://" + vcardFile.getCanonicalPath(); - i++; - } - importVCard(uriStrings); - } - - private void importVCardFromSDCard(final VCardFile vcardFile) { - importVCard(new Uri[] {Uri.parse("file://" + vcardFile.getCanonicalPath())}); - } - private void importVCard(final Uri uri) { importVCard(new Uri[] {uri}); } - private void importVCard(final String[] uriStrings) { - final int length = uriStrings.length; - final Uri[] uris = new Uri[length]; - for (int i = 0; i < length; i++) { - uris[i] = Uri.parse(uriStrings[i]); - } - importVCard(uris); - } - private void importVCard(final Uri[] uris) { runOnUiThread(new Runnable() { @Override @@ -766,61 +485,6 @@ public class ImportVCardActivity extends Activity { }); } - private Dialog getSelectImportTypeDialog() { - final DialogInterface.OnClickListener listener = new ImportTypeSelectedListener(); - final 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); - - final 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 getVCardFileSelectDialog(boolean multipleSelect) { - final int size = mAllVCardFileList.size(); - final VCardSelectedListener listener = new VCardSelectedListener(multipleSelect); - final 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 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) { super.onCreate(bundle); @@ -875,60 +539,64 @@ public class ImportVCardActivity extends Activity { } finish(); } + } else if (requestCode == REQUEST_OPEN_DOCUMENT) { + if (resultCode == Activity.RESULT_OK) { + final ClipData clipData = intent.getClipData(); + if (clipData != null) { + final ArrayList<Uri> uris = new ArrayList<>(); + for (int i = 0; i < clipData.getItemCount(); i++) { + ClipData.Item item = clipData.getItemAt(i); + final Uri uri = item.getUri(); + if (uri != null) { + uris.add(uri); + } + } + if (uris.isEmpty()) { + Log.w(LOG_TAG, "No vCard was selected for import"); + finish(); + } else { + Log.i(LOG_TAG, "Multiple vCards selected for import: " + uris); + importVCard(uris.toArray(new Uri[0])); + } + } else { + final Uri uri = intent.getData(); + if (uri != null) { + Log.i(LOG_TAG, "vCard selected for import: " + uri); + importVCard(uri); + } else { + Log.w(LOG_TAG, "No vCard was selected for import"); + finish(); + } + } + } else { + if (resultCode != Activity.RESULT_CANCELED) { + Log.w(LOG_TAG, "Result code was not OK nor CANCELED" + resultCode); + } + finish(); + } } } private void startImport() { - Intent intent = getIntent(); // Handle inbound files - Uri uri = intent.getData(); + Uri uri = getIntent().getData(); if (uri != null) { Log.i(LOG_TAG, "Starting vCard import using Uri " + uri); importVCard(uri); } else { Log.i(LOG_TAG, "Start vCard without Uri. The user will select vCard manually."); - doScanExternalStorageAndImportVCard(); + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType(VCardService.VCARD_MIME_TYPE); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); + startActivityForResult(intent, REQUEST_OPEN_DOCUMENT); } } @Override protected Dialog onCreateDialog(int resId, Bundle bundle) { switch (resId) { - case R.id.dialog_searching_vcard: { - if (mProgressDialogForScanVCard == null) { - String message = getString(R.string.searching_vcard_message); - mProgressDialogForScanVCard = - ProgressDialog.show(this, "", message, true, false); - mProgressDialogForScanVCard.setOnCancelListener(mVCardScanThread); - mVCardScanThread.start(); - } - return mProgressDialogForScanVCard; - } - case R.id.dialog_sdcard_not_found: { - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setMessage(R.string.no_sdcard_message) - .setOnCancelListener(mCancelListener) - .setPositiveButton(android.R.string.ok, mCancelListener); - return builder.create(); - } - case R.id.dialog_vcard_not_found: { - final String message = getString(R.string.import_failure_no_vcard_file); - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .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_cache_vcard: { if (mProgressDialogForCachingVCard == null) { final String title = getString(R.string.caching_vcard_title); @@ -942,16 +610,6 @@ public class ImportVCardActivity extends Activity { } return mProgressDialogForCachingVCard; } - 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) - .setIconAttribute(android.R.attr.alertDialogIcon) - .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)) { @@ -991,22 +649,6 @@ public class ImportVCardActivity extends Activity { } } - /** - * 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() { - // 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); - } - } - /* package */ void showFailureNotification(int reasonId) { final NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/src/com/android/contacts/common/vcard/SelectAccountActivity.java b/src/com/android/contacts/common/vcard/SelectAccountActivity.java index d05810db..66bfbca1 100644 --- a/src/com/android/contacts/common/vcard/SelectAccountActivity.java +++ b/src/com/android/contacts/common/vcard/SelectAccountActivity.java @@ -56,7 +56,7 @@ public class SelectAccountActivity extends Activity { // - 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 int resId = R.string.import_from_sdcard; + final int resId = R.string.import_from_vcf_file; final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this); final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true); if (accountList.size() == 0) { @@ -99,7 +99,7 @@ public class SelectAccountActivity extends Activity { @Override protected Dialog onCreateDialog(int resId, Bundle bundle) { switch (resId) { - case R.string.import_from_sdcard: { + case R.string.import_from_vcf_file: { if (mAccountSelectionListener == null) { throw new NullPointerException( "mAccountSelectionListener must not be null."); diff --git a/src/com/android/contacts/common/vcard/VCardService.java b/src/com/android/contacts/common/vcard/VCardService.java index b78c06f8..ddd3c1fe 100644 --- a/src/com/android/contacts/common/vcard/VCardService.java +++ b/src/com/android/contacts/common/vcard/VCardService.java @@ -66,6 +66,7 @@ public class VCardService extends Service { /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_"; + /* package */ static final String VCARD_MIME_TYPE = "text/vcard"; private class CustomMediaScannerConnectionClient implements MediaScannerConnectionClient { final MediaScannerConnection mConnection; |