Merge "Moving contact saving to the service"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d09904a..4c75705 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -680,13 +680,13 @@
-->
<string name="description_plus_button">plus</string>
- <!-- Dialog title shown when USB storage does not exist [CHAR LIMIT=25] -->
- <string name="no_sdcard_title" product="nosdcard">USB storage unavailable</string>
+ <!-- Dialog title shown when (USB) storage does not exist [CHAR LIMIT=25] -->
+ <string name="no_sdcard_title" product="nosdcard">Storage unavailable</string>
<!-- Dialog title shown when SD Card does not exist -->
<string name="no_sdcard_title" product="default">No SD card</string>
- <!-- Dialog message shown when USB storage does not exist [CHAR LIMIT=30] -->
- <string name="no_sdcard_message" product="nosdcard">No USB storage detected</string>
+ <!-- Dialog message shown when (USB) storage does not exist [CHAR LIMIT=30] -->
+ <string name="no_sdcard_message" product="nosdcard">No storage detected</string>
<!-- Dialog message shown when SDcard does not exist -->
<string name="no_sdcard_message" product="default">No SD card detected</string>
@@ -696,13 +696,13 @@
<!-- Action string for selecting SIM for importing contacts -->
<string name="import_from_sim">Import from SIM card</string>
- <!-- Action string for selecting USB storage for importing contacts [CHAR LIMIT=25] -->
- <string name="import_from_sdcard" product="nosdcard">Import from USB storage</string>
+ <!-- Action string for selecting (USB) storage for importing contacts [CHAR LIMIT=25] -->
+ <string name="import_from_sdcard" product="nosdcard">Import from storage</string>
<!-- Action string for selecting SD Card for importing contacts -->
<string name="import_from_sdcard" product="default">Import from SD card</string>
- <!-- Action that exports all contacts to USB storage [CHAR LIMIT=25] -->
- <string name="export_to_sdcard" product="nosdcard">Export to USB storage</string>
+ <!-- Action that exports all contacts to (USB) storage [CHAR LIMIT=25] -->
+ <string name="export_to_sdcard" product="nosdcard">Export to storage</string>
<!-- Action that exports all contacts to SD Card -->
<string name="export_to_sdcard" product="default">Export to SD card</string>
@@ -724,19 +724,19 @@
than one vCard files available in the system. -->
<string name="import_all_vcard_string">Import all vCard files</string>
- <!-- Dialog message shown when searching VCard data from SD Card [CHAR LIMIT=NONE] -->
- <string name="searching_vcard_message" product="nosdcard">Searching for vCard data in USB storage</string>
+ <!-- Dialog message shown when searching VCard data from (USB) storage [CHAR LIMIT=NONE] -->
+ <string name="searching_vcard_message" product="nosdcard">Searching for vCard data in storage</string>
<!-- Dialog message shown when searching VCard data from SD Card -->
<string name="searching_vcard_message" product="default">Searching for vCard data on SD card</string>
<!-- Dialog title shown when scanning VCard data failed. [CHAR LIMIT=NONE] -->
- <string name="scanning_sdcard_failed_title" product="nosdcard">Scanning USB storage failed</string>
+ <string name="scanning_sdcard_failed_title" product="nosdcard">Scanning storage failed</string>
<!-- Dialog title shown when scanning VCard data failed. -->
<string name="scanning_sdcard_failed_title" product="default">Scanning SD card failed</string>
<!-- Dialog message shown when searching VCard data failed.
An exact reason for the failure should [CHAR LIMIT=NONE] -->
- <string name="scanning_sdcard_failed_message" product="nosdcard">Scanning USB storage failed (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
+ <string name="scanning_sdcard_failed_message" product="nosdcard">Scanning storage failed (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
<!-- Dialog message shown when searching VCard data failed.
An exact reason for the failure should -->
<string name="scanning_sdcard_failed_message" product="default">Scanning SD card failed (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
@@ -762,9 +762,9 @@
<string name="vcard_import_failed">Failed to import vCard</string>
<!-- The failure message shown when the system could not find any vCard file.
- (with extension ".vcf" in USB storage.)
+ (with extension ".vcf" in (USB) storage.)
[CHAR LIMIT=128] -->
- <string name="import_failure_no_vcard_file" product="nosdcard">No vCard file found in the USB storage</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard">No vCard file found in the storage</string>
<!-- The failure message shown when the system could not find any vCard file.
(with extension ".vcf" in SDCard.)
[CHAR LIMIT=128] -->
@@ -864,7 +864,7 @@
<!-- The failed reason shown when vCard exporter could not create a file for the vCard since
there are too many files relevant to vCard. [CHAR LIMIT=NONE] -->
- <string name="fail_reason_too_many_vcard" product="nosdcard">Too many vCard files in the USB storage</string>
+ <string name="fail_reason_too_many_vcard" product="nosdcard">Too many vCard files in the storage</string>
<!-- The failed reason shown when vCard exporter could not create a file for the vCard since
there are too many files relevant to vCard. -->
<string name="fail_reason_too_many_vcard" product="default">Too many vCard files on the SD card</string>
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index c5f293b..3285a05 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -178,6 +178,11 @@
}
Log.i(LOG_TAG, "Successfully finished exporting vCard " + request.destUri);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Ask MediaScanner to scan the file: " + request.destUri.getPath());
+ }
+ mService.updateMediaScanner(request.destUri.getPath());
+
successful = true;
final String filename = uri.getLastPathSegment();
final String title = mService.getString(R.string.exporting_vcard_finished_title,
diff --git a/src/com/android/contacts/vcard/ProcessorBase.java b/src/com/android/contacts/vcard/ProcessorBase.java
index 833090d..073bcbb 100644
--- a/src/com/android/contacts/vcard/ProcessorBase.java
+++ b/src/com/android/contacts/vcard/ProcessorBase.java
@@ -39,6 +39,7 @@
*/
public abstract int getType();
+ @Override
public abstract void run();
/**
@@ -49,8 +50,11 @@
*
* @see Future#cancel(boolean)
*/
+ @Override
public abstract boolean cancel(boolean mayInterruptIfRunning);
+ @Override
public abstract boolean isCancelled();
+ @Override
public abstract boolean isDone();
/**
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index 6422163..a4c0480 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.media.MediaScannerConnection;
+import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -36,8 +38,10 @@
import android.widget.Toast;
import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -101,6 +105,33 @@
}
});
+ private class CustomMediaScannerConnectionClient implements MediaScannerConnectionClient {
+ final MediaScannerConnection mConnection;
+ final String mPath;
+
+ public CustomMediaScannerConnectionClient(String path) {
+ mConnection = new MediaScannerConnection(VCardService.this, this);
+ mPath = path;
+ }
+
+ public void start() {
+ mConnection.connect();
+ }
+
+ @Override
+ public void onMediaScannerConnected() {
+ if (DEBUG) { Log.d(LOG_TAG, "Connected to MediaScanner. Start scanning."); }
+ mConnection.scanFile(mPath, null);
+ }
+
+ @Override
+ public void onScanCompleted(String path, Uri uri) {
+ if (DEBUG) { Log.d(LOG_TAG, "scan completed: " + path); }
+ mConnection.disconnect();
+ removeConnectionClient(this);
+ }
+ }
+
private NotificationManager mNotificationManager;
// Should be single thread, as we don't want to simultaneously handle import and export
@@ -113,6 +144,11 @@
// Key is jobId.
private final Map<Integer, ProcessorBase> mRunningJobMap =
new HashMap<Integer, ProcessorBase>();
+ // Stores ScannerConnectionClient objects until they finish scanning requested files.
+ // Uses List class for simplicity. It's not costly as we won't have multiple objects in
+ // almost all cases.
+ private final List<CustomMediaScannerConnectionClient> mRemainingScannerConnections =
+ new ArrayList<CustomMediaScannerConnectionClient>();
/* ** vCard exporter params ** */
// If true, VCardExporter is able to emits files longer than 8.3 format.
@@ -286,7 +322,7 @@
} else {
Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
}
- stopServiceWhenNoJob();
+ stopServiceIfAppropriate();
}
private synchronized void handleRequestAvailableExportDestination(Message msg) {
@@ -310,10 +346,11 @@
}
/**
- * Checks job list and call {@link #stopSelf()} when there's no job now.
- * A new job cannot be submitted any more after this call.
+ * Checks job list and call {@link #stopSelf()} when there's no job and no scanner connection
+ * is remaining.
+ * A new job (import/export) cannot be submitted any more after this call.
*/
- private synchronized void stopServiceWhenNoJob() {
+ private synchronized void stopServiceIfAppropriate() {
if (mRunningJobMap.size() > 0) {
for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
final int jobId = entry.getKey();
@@ -327,11 +364,41 @@
}
}
+ if (!mRemainingScannerConnections.isEmpty()) {
+ Log.i(LOG_TAG, "MediaScanner update is in progress.");
+ return;
+ }
+
Log.i(LOG_TAG, "No unfinished job. Stop this service.");
mExecutorService.shutdown();
stopSelf();
}
+ /* package */ synchronized void updateMediaScanner(String path) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "MediaScanner is being updated: " + path);
+ }
+
+ if (mExecutorService.isShutdown()) {
+ Log.w(LOG_TAG, "MediaScanner update is requested after executor's being shut down. " +
+ "Ignoring the update request");
+ return;
+ }
+ final CustomMediaScannerConnectionClient client =
+ new CustomMediaScannerConnectionClient(path);
+ mRemainingScannerConnections.add(client);
+ client.start();
+ }
+
+ private synchronized void removeConnectionClient(
+ CustomMediaScannerConnectionClient client) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Removing custom MediaScannerConnectionClient.");
+ }
+ mRemainingScannerConnections.remove(client);
+ stopServiceIfAppropriate();
+ }
+
/* package */ synchronized void handleFinishImportNotification(
int jobId, boolean successful) {
if (DEBUG) {
@@ -341,7 +408,7 @@
if (mRunningJobMap.remove(jobId) == null) {
Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
}
- stopServiceWhenNoJob();
+ stopServiceIfAppropriate();
}
/* package */ synchronized void handleFinishExportNotification(
@@ -362,7 +429,7 @@
mReservedDestination.remove(path);
}
- stopServiceWhenNoJob();
+ stopServiceIfAppropriate();
}
/**