Make ImportVCardActivity cache vCard file into local data directory.
Bug: 2612502
Change-Id: I169bc481ec42ac97d48c91efbff6ad53df986008
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 3da8741..f84da9c 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -47,7 +47,7 @@
<item type="id" name="dialog_select_import_type"/>
<item type="id" name="dialog_select_one_vcard"/>
<item type="id" name="dialog_select_multiple_vcard"/>
- <item type="id" name="dialog_reading_vcard"/>
+ <item type="id" name="dialog_cache_vcard"/>
<item type="id" name="dialog_io_exception"/>
<item type="id" name="dialog_error_with_message"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e95dc3c..452b72a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -717,6 +717,9 @@
(with extension ".vcf" in SDCard.) -->
<string name="fail_reason_no_vcard_file">No vCard file found on the SD card</string>
+ <!-- Fail reason shown when vCard importer failed to look over meta information stored in vCard file(s). -->
+ <string name="fail_reason_failed_to_collect_vcard_meta_info">Failed to collect meta information of given vCard file(s).</string>
+
<!-- The failed reason shown when the import of some of vCard files failed during multiple vCard
files import. It includes the case where all files were failed to be imported. -->
<string name="fail_reason_failed_to_read_files">One or more files failed to be imported (%s).</string>
@@ -727,6 +730,14 @@
<!-- Dialog title shown when a user is asked to select vCard file -->
<string name="select_vcard_title">Select vCard file</string>
+ <!-- The title shown when vCard importer is caching files to be imported into local temporary
+ data storage. -->
+ <string name="caching_vcard_title">Caching vCard(s) to local temporary storage</string>
+
+ <!-- The message shown when vCard importer is caching files to be imported into local temporary
+ data storage. -->
+ <string name="caching_vcard_message">Importer is caching vCard(s) to local temporary storage. Actual import will start soon.</string>
+
<!-- The message shown while reading vCard(s).
First argument is current index of contacts to be imported.
Second argument is the total number of contacts.
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index 7ad9228..164f539 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -21,6 +21,7 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -39,12 +40,29 @@
import com.android.contacts.model.Sources;
import com.android.contacts.util.AccountSelectionUtil;
+import com.android.vcard.VCardEntryCounter;
+import com.android.vcard.VCardInterpreterCollection;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.VCardSourceDetector;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardNestedException;
+import com.android.vcard.exception.VCardNotSupportedException;
+import com.android.vcard.exception.VCardVersionException;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channel;
+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;
@@ -67,7 +85,11 @@
private static final int SELECT_ACCOUNT = 0;
- /* package */ static final String VCARD_URI_ARRAY = "vcard_uri_array";
+ /* 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 USE_V30_ARRAY = "use_v30";
+ /* package */ static final String ENTRY_COUNT_ARRAY = "entry_count";
// Run on the UI thread. Must not be null except after onDestroy().
private Handler mHandler = new Handler();
@@ -80,10 +102,13 @@
private Uri mUri;
private ProgressDialog mProgressDialogForScanVCard;
+ private ProgressDialog mProgressDialogForCacheVCard;
private List<VCardFile> mAllVCardFileList;
private VCardScanThread mVCardScanThread;
+ private VCardCacheThread mVCardCacheThread;
+
private String mErrorMessage;
private static class VCardFile {
@@ -138,6 +163,288 @@
private CancelListener mCancelListener = new CancelListener();
+ /**
+ * Caches all vCard data into local data directory so that we allow
+ * {@link ImportVCardService} to access all the contents in given Uris, some of
+ * which may not be accessible from other components due to permission problem.
+ * (Activity which gives the Uri may allow only this Activity to access that content,
+ * not the ohter components like {@link ImportVCardService}.
+ *
+ * We also allow the Service to happen to exit during the vCard import procedure.
+ */
+ private class VCardCacheThread extends Thread
+ implements DialogInterface.OnCancelListener {
+ private static final String CACHE_FILE_PREFIX = "import_tmp_";
+ private boolean mCanceled;
+ private PowerManager.WakeLock mWakeLock;
+ private VCardParser mVCardParser;
+ private final Uri[] mSourceUris;
+
+ // Not using Uri[] since we throw this object to Service via Intent.
+ private final String[] mDestUriStrings;
+
+ private final int[] mEstimatedVCardTypes;
+ private final String[] mEstimatedCharsets;
+ private final boolean[] mShouldUseV30;
+ private final int[] mEntryCounts;
+
+ public VCardCacheThread(final Uri[] sourceUris) {
+ mSourceUris = sourceUris;
+ final int length = sourceUris.length;
+
+ mDestUriStrings = new String[length];
+ mEstimatedVCardTypes = new int[length];
+ mEstimatedCharsets = new String[length];
+ mShouldUseV30 = new boolean[length];
+ mEntryCounts = new int[length];
+
+ final Context context = ImportVCardActivity.this;
+ 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() {
+ final Context context = ImportVCardActivity.this;
+ final ContentResolver resolver = context.getContentResolver();
+ String errorMessage = null;
+ mWakeLock.acquire();
+
+ try {
+ clearOldCache();
+
+ // We may be able to read content of each vCard file during copying them
+ // to local storage, but currently vCard code does not allow us to do so.
+ //
+ // Note that Uris given from caller applications may not be opened twice,
+ // so we have to use local data after this copy instead of relying on
+ // the original source Uris.
+ copyVCardToLocal();
+ if (mCanceled) {
+ return;
+ }
+ Log.d("@@@", "Caching done. Count the number of vCard entries.");
+ if (!collectVCardMetaInfo()) {
+ Log.e(LOG_TAG, "Failed to collect vCard meta information");
+ runOnUIThread(new DialogDisplayer(
+ getString(R.string.fail_reason_failed_to_collect_vcard_meta_info)));
+ return;
+ }
+ if (mCanceled) {
+ return;
+ }
+ for (int i = 0; i < mEntryCounts.length; i++) {
+ Log.d("@@@", String.format("length for %s: %d",
+ mDestUriStrings[i], mEntryCounts[i]));
+ }
+ } catch (OutOfMemoryError e) {
+ Log.e(LOG_TAG, "OutOfMemoryError");
+ // We should take care of this case since Android devices may have
+ // smaller memory than we usually expect.
+ System.gc();
+ runOnUIThread(new DialogDisplayer(
+ getString(R.string.fail_reason_io_error) +
+ ": " + e.getLocalizedMessage()));
+ return;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ runOnUIThread(new DialogDisplayer(
+ getString(R.string.fail_reason_io_error) +
+ ": " + e.getLocalizedMessage()));
+ return;
+ } finally {
+
+ mWakeLock.release();
+ mProgressDialogForCacheVCard.dismiss();
+ }
+
+ // TODO(dmiyakawa): do we need runOnUIThread?
+ final Intent intent = new Intent(
+ ImportVCardActivity.this, ImportVCardService.class);
+ intent.putExtra(VCARD_URI_ARRAY, mDestUriStrings);
+ intent.putExtra(SelectAccountActivity.ACCOUNT_NAME, mAccountName);
+ intent.putExtra(SelectAccountActivity.ACCOUNT_TYPE, mAccountType);
+ intent.putExtra(ESTIMATED_VCARD_TYPE_ARRAY, mEstimatedVCardTypes);
+ intent.putExtra(ESTIMATED_CHARSET_ARRAY, mEstimatedCharsets);
+ intent.putExtra(USE_V30_ARRAY, mShouldUseV30);
+ intent.putExtra(ENTRY_COUNT_ARRAY, mEntryCounts);
+ startService(intent);
+ finish();
+ }
+
+
+ private boolean collectVCardMetaInfo() {
+ final ContentResolver resolver =
+ ImportVCardActivity.this.getContentResolver();
+ final int length = mDestUriStrings.length;
+ try {
+ for (int i = 0; i < length; i++) {
+ if (mCanceled) {
+ return false;
+ }
+ final Uri uri = Uri.parse(mDestUriStrings[i]);
+ boolean shouldUseV30 = false;
+ InputStream is;
+ VCardEntryCounter counter;
+ VCardSourceDetector detector;
+ VCardInterpreterCollection interpreter;
+
+ is = resolver.openInputStream(uri);
+ mVCardParser = new VCardParser_V21();
+ try {
+ counter = new VCardEntryCounter();
+ detector = new VCardSourceDetector();
+ interpreter =
+ new VCardInterpreterCollection(
+ Arrays.asList(counter, detector));
+ mVCardParser.parse(is, interpreter);
+ } catch (VCardVersionException e1) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+
+ shouldUseV30 = true;
+ is = resolver.openInputStream(uri);
+ mVCardParser = new VCardParser_V30();
+ try {
+ counter = new VCardEntryCounter();
+ detector = new VCardSourceDetector();
+ interpreter =
+ new VCardInterpreterCollection(
+ Arrays.asList(counter, detector));
+ mVCardParser.parse(is, interpreter);
+ } catch (VCardVersionException e2) {
+ throw new VCardException("vCard with unspported version.");
+ }
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ mEstimatedVCardTypes[i] = detector.getEstimatedType();
+ mEstimatedCharsets[i] = detector.getEstimatedCharset();
+ mShouldUseV30[i] = shouldUseV30;
+ mEntryCounts[i] = counter.getCount();
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
+ return false;
+ } catch (VCardNestedException e) {
+ Log.w(LOG_TAG, "Nested Exception is found (it may be false-positive).");
+ } catch (VCardNotSupportedException e) {
+ return false;
+ } catch (VCardException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private void copyVCardToLocal() throws IOException {
+ final Context context = ImportVCardActivity.this;
+ final ContentResolver resolver = context.getContentResolver();
+ ReadableByteChannel inputChannel = null;
+ WritableByteChannel outputChannel = null;
+ try {
+ int length = mSourceUris.length;
+ for (int i = 0; i < length; i++) {
+ if (mCanceled) {
+ Log.d(LOG_TAG, "Canceled during import");
+ break;
+ }
+ // XXX: better way to copy stream?
+ inputChannel = Channels.newChannel(resolver.openInputStream(mSourceUris[i]));
+ final String filename = CACHE_FILE_PREFIX + i + ".vcf";
+ mDestUriStrings[i] =
+ context.getFileStreamPath(filename).toURI().toString();
+ Log.d("@@@", "temporary file: " + filename + ", dest: " + mDestUriStrings[i]);
+ outputChannel =
+ context.openFileOutput(filename, Context.MODE_PRIVATE).getChannel();
+ {
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
+ while (inputChannel.read(buffer) != -1) {
+ if (mCanceled) {
+ Log.d(LOG_TAG, "Canceled during caching " + mSourceUris[i]);
+ break;
+ }
+ buffer.flip();
+ outputChannel.write(buffer);
+ buffer.compact();
+ }
+ buffer.flip();
+ while (buffer.hasRemaining()) {
+ outputChannel.write(buffer);
+ }
+ }
+
+ // Avoid double close() in the "finally" block bellow.
+ Channel tmp = inputChannel;
+ inputChannel = null;
+ tmp.close();
+ tmp = outputChannel;
+ outputChannel = null;
+ tmp.close();
+ }
+ } finally {
+ if (inputChannel != null) {
+ try {
+ inputChannel.close();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to close inputChannel.");
+ }
+ }
+ if (outputChannel != null) {
+ try {
+ outputChannel.close();
+ } catch(IOException e) {
+ Log.w(LOG_TAG, "Failed to close outputChannel");
+ }
+ }
+ }
+ }
+
+ /**
+ * We (currently) don't have any way to clean up cache files used in the previous
+ * import process,
+ * TODO(dmiyakawa): Can we do it after Service being done?
+ */
+ private void clearOldCache() {
+ final Context context = ImportVCardActivity.this;
+ final String[] fileLists = context.fileList();
+ for (String fileName : fileLists) {
+ if (fileName.startsWith(CACHE_FILE_PREFIX)) {
+ Log.d(LOG_TAG, "Remove temporary file: " + fileName);
+ context.deleteFile(fileName);
+ }
+ }
+ }
+
+ public void cancel() {
+ mCanceled = true;
+ if (mVCardParser != null) {
+ mVCardParser.cancel();
+ }
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ cancel();
+ }
+ }
+
private class ImportTypeSelectedListener implements
DialogInterface.OnClickListener {
public static final int IMPORT_ONE = 0;
@@ -366,16 +673,17 @@
}
private void importVCard(final String[] uriStrings) {
- final Intent intent = new Intent(this, ImportVCardService.class);
- intent.putExtra(VCARD_URI_ARRAY, uriStrings);
- intent.putExtra(SelectAccountActivity.ACCOUNT_NAME, mAccountName);
- intent.putExtra(SelectAccountActivity.ACCOUNT_TYPE, mAccountType);
-
- // TODO: permission is not migrated to ImportVCardService, so some exception is
- // thrown when reading some Uri, permission of which is temporarily guaranteed
- // to ImportVCardActivity, not ImportVCardService.
- startService(intent);
- finish();
+ final int length = uriStrings.length;
+ final Uri[] uris = new Uri[length];
+ for (int i = 0; i < length; i++) {
+ uris[i] = Uri.parse(uriStrings[i]);
+ }
+ runOnUIThread(new Runnable() {
+ public void run() {
+ mVCardCacheThread = new VCardCacheThread(uris);
+ showDialog(R.id.dialog_cache_vcard);
+ }
+ });
}
private Dialog getSelectImportTypeDialog() {
@@ -543,6 +851,19 @@
case R.id.dialog_select_one_vcard: {
return getVCardFileSelectDialog(false);
}
+ case R.id.dialog_cache_vcard: {
+ if (mProgressDialogForCacheVCard == null) {
+ final String title = getString(R.string.caching_vcard_title);
+ final String message = getString(R.string.caching_vcard_message);
+ mProgressDialogForCacheVCard = new ProgressDialog(this);
+ mProgressDialogForCacheVCard.setTitle(title);
+ mProgressDialogForCacheVCard.setMessage(message);
+ mProgressDialogForCacheVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialogForCacheVCard.setOnCancelListener(mVCardCacheThread);
+ mVCardCacheThread.start();
+ }
+ return mProgressDialogForCacheVCard;
+ }
case R.id.dialog_io_exception: {
String message = (getString(R.string.scanning_sdcard_failed_message,
getString(R.string.fail_reason_io_error)));
diff --git a/src/com/android/contacts/ImportVCardService.java b/src/com/android/contacts/ImportVCardService.java
index 1482969..2be6468 100644
--- a/src/com/android/contacts/ImportVCardService.java
+++ b/src/com/android/contacts/ImportVCardService.java
@@ -141,7 +141,11 @@
mCreatedUris.clear();
final Account account;
- final Uri[] uris;
+ final Uri uri;
+ final int estimatedType;
+ final String estimatedCharset;
+ final boolean useV30;
+ final int entryCount;
final int id;
final boolean needReview;
synchronized (mContext) {
@@ -151,97 +155,59 @@
} else {
final PendingInput pendingInput = mPendingInputs.poll();
account = pendingInput.account;
- uris = pendingInput.uris;
+ uri = pendingInput.uri;
+ estimatedType = pendingInput.estimatedType;
+ estimatedCharset = pendingInput.estimatedCharset;
+ useV30 = pendingInput.useV30;
+ entryCount = pendingInput.entryCount;
id = pendingInput.id;
}
}
- runInternal(account, uris, id);
- doFinishNotification(id, uris);
+ runInternal(account, uri, estimatedType, estimatedCharset,
+ useV30, entryCount, id);
+ doFinishNotification(id, uri);
}
Log.i(LOG_TAG, "Successfully imported. Total: " + mTotalCount);
stopSelf();
}
- private void runInternal(Account account, Uri[] uris, int id) {
+ private void runInternal(Account account,
+ Uri uri, int estimatedType, String estimatedCharset,
+ boolean useV30, int entryCount,
+ int id) {
int totalCount = 0;
final ArrayList<VCardSourceDetector> detectorList =
new ArrayList<VCardSourceDetector>();
- // First scan all Uris with a default charset and try to understand an exact
- // charset to be used to each Uri. Note that detector would return null when
- // it does not know an appropriate charset, so stick to use the default
- // at that time.
- // TODO: notification for first scanning?
- for (Uri uri : uris) {
- if (mCanceled) {
- return;
- }
- final VCardEntryCounter counter = new VCardEntryCounter();
- final VCardSourceDetector detector = new VCardSourceDetector();
- final VCardInterpreterCollection interpreterCollection =
- new VCardInterpreterCollection(Arrays.asList(counter, detector));
- if (!readOneVCard(uri, VCardConfig.VCARD_TYPE_UNKNOWN, null,
- interpreterCollection)) {
- mErrorUris.add(uri);
- }
-
- totalCount += counter.getCount();
- detectorList.add(detector);
- }
-
- if (mErrorUris.size() > 0) {
- final StringBuilder builder = new StringBuilder();
- builder.append("Error happened on ");
- for (Uri errorUri : mErrorUris) {
- builder.append("\"");
- builder.append(errorUri.toString());
- builder.append("\"");
- }
- Log.e(LOG_TAG, builder.toString());
- doErrorNotification(id);
- return;
- }
-
- if (uris.length != detectorList.size()) {
- Log.e(LOG_TAG,
- "The number of Uris to be imported is different from that of " +
- "charset to be used.");
- doErrorNotification(id);
+ if (mCanceled) {
return;
}
// First scanning is over. Try to import each vCard, which causes side effects.
- mTotalCount = totalCount;
+ mTotalCount += entryCount;
mCurrentCount = 0;
- for (int i = 0; i < uris.length; i++) {
- if (mCanceled) {
- Log.w(LOG_TAG, "Canceled during importing (with storing data in database)");
- // TODO: implement cancel correctly.
- return;
- }
- final Uri uri = uris[i];
+ if (mCanceled) {
+ Log.w(LOG_TAG, "Canceled during importing (with storing data in database)");
+ // TODO: implement cancel correctly.
+ return;
+ }
- final VCardSourceDetector detector = detectorList.get(i);
- final int vcardType = detector.getEstimatedType();
- final String charset = detector.getEstimatedCharset(); // May be null.
+ final VCardEntryConstructor constructor =
+ new VCardEntryConstructor(estimatedType, account, estimatedCharset);
+ final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
+ constructor.addEntryHandler(committer);
+ constructor.addEntryHandler(new ProgressNotifier(id));
- final VCardEntryConstructor constructor =
- new VCardEntryConstructor(vcardType, account, charset);
- final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
- constructor.addEntryHandler(committer);
- constructor.addEntryHandler(new ProgressNotifier(id));
-
- if (!readOneVCard(uri, vcardType, charset, constructor)) {
- Log.e(LOG_TAG, "Failed to read \"" + uri.toString() + "\" " +
- "while first scan was successful.");
- }
+ if (!readOneVCard(uri, estimatedType, estimatedCharset, constructor)) {
+ Log.e(LOG_TAG, "Failed to read \"" + uri.toString() + "\" " +
+ "while first scan was successful.");
+ }
final List<Uri> createdUris = committer.getCreatedUris();
if (createdUris != null && createdUris.size() > 0) {
mCreatedUris.addAll(createdUris);
} else {
Log.w(LOG_TAG, "Created Uris is null (src = " + uri.toString() + "\"");
}
- }
}
private boolean readOneVCard(Uri uri, int vcardType, String charset,
@@ -330,7 +296,7 @@
mNotificationManager.notify(id, notification);
}
- private void doFinishNotification(int id, Uri[] uris) {
+ private void doFinishNotification(int id, Uri uri) {
final Notification notification = new Notification();
notification.icon = android.R.drawable.stat_sys_download_done;
final String title = mContext.getString(R.string.reading_vcard_finished_title);
@@ -342,10 +308,9 @@
RawContacts.CONTENT_URI, rawContactId));
intent = new Intent(Intent.ACTION_VIEW, contactUri);
- final String text = ((uris.length == 1) ? uris[0].getPath() : "");
final PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, intent, 0);
- notification.setLatestEventInfo(mContext, title, text, pendingIntent);
+ notification.setLatestEventInfo(mContext, title, "", pendingIntent);
mNotificationManager.notify(id, notification);
}
@@ -364,17 +329,29 @@
private static class PendingInput {
public final Account account;
- public final Uri[] uris;
+ public final Uri uri;
+ public final int estimatedType;
+ public final String estimatedCharset;
+ public final boolean useV30;
+ public final int entryCount;
public final int id;
- public PendingInput(Account account, Uri[] uris, int id) {
+ public PendingInput(Account account,
+ Uri uri, int estimatedType, String estimatedCharset,
+ boolean useV30, int entryCount,
+ int id) {
this.account = account;
- this.uris = uris;
+ this.uri = uri;
+ this.estimatedType = estimatedType;
+ this.estimatedCharset = estimatedCharset;
+ this.useV30 = useV30;
+ this.entryCount = entryCount;
this.id = id;
}
}
- // The two classes bellow must be called inside the synchronized block, using this context.
+ // The two classes bellow must be called inside the synchronized block, using this
+ // Activity as a Context.
private boolean mNowRunning;
private final Queue<PendingInput> mPendingInputs = new LinkedList<PendingInput>();
@@ -423,6 +400,16 @@
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
}
+ // TODO: use this.
+ final int[] estimatedTypeArray =
+ intent.getIntArrayExtra(ImportVCardActivity.ESTIMATED_VCARD_TYPE_ARRAY);
+ final String[] estimatedCharsetArray =
+ intent.getStringArrayExtra(ImportVCardActivity.ESTIMATED_CHARSET_ARRAY);
+ final boolean[] useV30Array =
+ intent.getBooleanArrayExtra(ImportVCardActivity.USE_V30_ARRAY);
+ final int[] entryCountArray =
+ intent.getIntArrayExtra(ImportVCardActivity.ENTRY_COUNT_ARRAY);
+
final Account account = tryGetAccount(intent);
final Uri[] uris = tryGetUris(intent);
if (uris == null) {
@@ -433,8 +420,30 @@
return START_NOT_STICKY;
}
+ int length = uris.length;
+ if (estimatedTypeArray.length < length) {
+ Log.w(LOG_TAG, String.format("estimatedTypeArray.length < length (%d, %d)",
+ estimatedTypeArray.length, length));
+ length = estimatedTypeArray.length;
+ }
+ if (useV30Array.length < length) {
+ Log.w(LOG_TAG, String.format("useV30Array.length < length (%d, %d)",
+ useV30Array.length, length));
+ length = useV30Array.length;
+ }
+ if (entryCountArray.length < length) {
+ Log.w(LOG_TAG, String.format("entryCountArray.length < length (%d, %d)",
+ entryCountArray.length, length));
+ length = entryCountArray.length;
+ }
+
synchronized (this) {
- mPendingInputs.add(new PendingInput(account, uris, startId));
+ for (int i = 0; i < length; i++) {
+ mPendingInputs.add(new PendingInput(account,
+ uris[i], estimatedTypeArray[i], estimatedCharsetArray[i],
+ useV30Array[i], entryCountArray[i],
+ startId));
+ }
if (!mNowRunning) {
Toast.makeText(this, getString(R.string.vcard_importer_start_message),
Toast.LENGTH_LONG).show();