Index: ui/file_manager/file_manager/background/js/media_import_handler.js |
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js |
index 2e8c0143b107fe5a25fb3b67b4985b407686cb6b..0135363a2493de1a1392f2277b1cf3c93141a121 100644 |
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js |
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js |
@@ -13,12 +13,16 @@ var importer = importer || {}; |
importer.ImportRunner = function() {}; |
/** |
+ * @typedef {function():(!Promise<!DirectoryEntry>)} |
+ */ |
+importer.ImportRunner.DestinationFactory; |
+ |
+/** |
* Imports all media identified by scanResult. |
* |
* @param {!importer.ScanResult} scanResult |
- * @param {!importer.MediaImportHandler.DestinationFactory=} opt_destination A |
+ * @param {!importer.ImportRunner.DestinationFactory=} opt_destination A |
* function that returns the directory into which media will be imported. |
- * The function will be executed only when the import task actually runs. |
* |
* @return {!importer.MediaImportHandler.ImportTask} The resulting import task. |
*/ |
@@ -33,8 +37,10 @@ importer.ImportRunner.prototype.importFromScanResult; |
* |
* @param {!ProgressCenter} progressCenter |
* @param {!importer.HistoryLoader} historyLoader |
+ * @param {!importer.DuplicateFinder} duplicateFinder |
*/ |
-importer.MediaImportHandler = function(progressCenter, historyLoader) { |
+importer.MediaImportHandler = |
+ function(progressCenter, historyLoader, duplicateFinder) { |
/** @private {!ProgressCenter} */ |
this.progressCenter_ = progressCenter; |
@@ -44,19 +50,16 @@ importer.MediaImportHandler = function(progressCenter, historyLoader) { |
/** @private {!importer.TaskQueue} */ |
this.queue_ = new importer.TaskQueue(); |
+ /** @private {!importer.DuplicateFinder} */ |
+ this.duplicateFinder_ = duplicateFinder; |
+ |
/** @private {number} */ |
this.nextTaskId_ = 0; |
}; |
-/** |
- * @typedef {function():(!Promise<!DirectoryEntry>)} |
- */ |
-importer.MediaImportHandler.DestinationFactory; |
- |
/** @override */ |
importer.MediaImportHandler.prototype.importFromScanResult = |
function(scanResult, opt_destination) { |
- |
var destination = opt_destination || |
importer.MediaImportHandler.defaultDestination.getImportDestination; |
@@ -64,9 +67,10 @@ importer.MediaImportHandler.prototype.importFromScanResult = |
this.generateTaskId_(), |
this.historyLoader_, |
scanResult, |
- destination); |
+ destination, |
+ this.duplicateFinder_); |
- task.addObserver(this.onTaskProgress_.bind(this)); |
+ task.addObserver(this.onTaskProgress_.bind(this, task)); |
this.queue_.queueTask(task); |
@@ -84,12 +88,12 @@ importer.MediaImportHandler.prototype.generateTaskId_ = function() { |
/** |
* Sends updates to the ProgressCenter when an import is happening. |
* |
- * @param {!importer.TaskQueue.UpdateType} updateType |
* @param {!importer.TaskQueue.Task} task |
+ * @param {string} updateType |
* @private |
*/ |
importer.MediaImportHandler.prototype.onTaskProgress_ = |
- function(updateType, task) { |
+ function(task, updateType) { |
var UpdateType = importer.TaskQueue.UpdateType; |
var item = this.progressCenter_.getItemById(task.taskId); |
@@ -143,22 +147,28 @@ importer.MediaImportHandler.prototype.onTaskProgress_ = |
* @param {string} taskId |
* @param {!importer.HistoryLoader} historyLoader |
* @param {!importer.ScanResult} scanResult |
- * @param {!importer.MediaImportHandler.DestinationFactory} destinationFactory A |
+ * @param {!importer.ImportRunner.DestinationFactory} destinationFactory A |
* function that returns the directory into which media will be imported. |
+ * @param {!importer.DuplicateFinder} duplicateFinder A duplicate-finder linked |
+ * to the import destination, that will be used to deduplicate imports. |
*/ |
importer.MediaImportHandler.ImportTask = function( |
taskId, |
historyLoader, |
scanResult, |
- destinationFactory) { |
+ destinationFactory, |
+ duplicateFinder) { |
importer.TaskQueue.BaseTask.call(this, taskId); |
/** @private {string} */ |
this.taskId_ = taskId; |
- /** @private {!importer.MediaImportHandler.DestinationFactory} */ |
+ /** @private {!importer.ImportRunner.DestinationFactory} */ |
this.destinationFactory_ = destinationFactory; |
+ /** @private {!importer.DuplicateFinder} */ |
+ this.deduplicator_ = duplicateFinder; |
+ |
/** @private {!importer.ScanResult} */ |
this.scanResult_ = scanResult; |
@@ -184,6 +194,24 @@ importer.MediaImportHandler.ImportTask = function( |
this.canceled_ = false; |
}; |
+/** |
+ * Update types that are specific to ImportTask. Clients can add Observers to |
+ * ImportTask to listen for these kinds of updates. |
+ * @enum {string} |
+ */ |
+importer.MediaImportHandler.ImportTask.UpdateType = { |
+ ENTRY_CHANGED: 'ENTRY_CHANGED' |
+}; |
+ |
+/** |
+ * Auxilliary info for ENTRY_CHANGED notifications. |
+ * @typedef {{ |
+ * sourceUrl: string, |
+ * destination: !Entry |
+ * }} |
+ */ |
+importer.MediaImportHandler.ImportTask.EntryChangedInfo; |
+ |
/** @struct */ |
importer.MediaImportHandler.ImportTask.prototype = { |
/** @return {number} Number of imported bytes */ |
@@ -267,19 +295,47 @@ importer.MediaImportHandler.ImportTask.prototype.importTo_ = |
* @param {function()} completionCallback Called after this operation is |
* complete. |
* @param {!FileEntry} entry The entry to import. |
- * @param {number} index The entry's index in the scan results. |
* @private |
*/ |
importer.MediaImportHandler.ImportTask.prototype.importOne_ = |
- function(destination, completionCallback, entry, index) { |
+ function(destination, completionCallback, entry) { |
if (this.canceled_) { |
this.notify(importer.TaskQueue.UpdateType.CANCELED); |
return; |
} |
+ this.deduplicator_.checkDuplicate(entry) |
+ .then( |
+ /** @param {boolean} isDuplicate */ |
+ function(isDuplicate) { |
+ if (isDuplicate) { |
+ // If the given file is a duplicate, don't import it again. Just |
+ // update the progress indicator. |
+ // TODO(kenobi): Update import history to mark the dupe as sync'd. |
+ this.processedBytes_ += entry.size; |
+ this.notify(importer.TaskQueue.UpdateType.PROGRESS); |
+ return Promise.resolve(); |
+ } else { |
+ return this.copy_(entry, destination); |
+ } |
+ }.bind(this)) |
+ .then(completionCallback); |
+}; |
+ |
+/** |
+ * @param {!FileEntry} entry The file to copy. |
+ * @param {!DirectoryEntry} destination The destination directory. |
+ * @return {!Promise<!FileEntry>} Resolves to the destination file when the copy |
+ * is complete. |
+ * @private |
+ */ |
+importer.MediaImportHandler.ImportTask.prototype.copy_ = |
+ function(entry, destination) { |
// A count of the current number of processed bytes for this entry. |
var currentBytes = 0; |
+ var resolver = new importer.Resolver(); |
+ |
/** |
* Updates the task when the copy code reports progress. |
* @param {string} sourceUrl |
@@ -297,28 +353,38 @@ importer.MediaImportHandler.ImportTask.prototype.importOne_ = |
/** |
* Updates the task when the new file has been created. |
* @param {string} sourceUrl |
- * @param {Entry} destEntry |
+ * @param {Entry} destinationEntry |
* @this {importer.MediaImportHandler.ImportTask} |
*/ |
- var onEntryChanged = function(sourceUrl, destEntry) { |
+ var onEntryChanged = function(sourceUrl, destinationEntry) { |
this.processedBytes_ -= currentBytes; |
this.processedBytes_ += entry.size; |
- this.onEntryChanged_(sourceUrl, destEntry); |
+ destinationEntry.size = entry.size; |
+ this.notify( |
+ importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED, |
+ { |
+ sourceUrl: sourceUrl, |
+ destination: destinationEntry |
+ }); |
this.notify(importer.TaskQueue.UpdateType.PROGRESS); |
}; |
- /** @this {importer.MediaImportHandler.ImportTask} */ |
- var onComplete = function() { |
+ /** |
+ * @param {Entry} destinationEntry The new destination entry. |
+ * @this {importer.MediaImportHandler.ImportTask} |
+ */ |
+ var onComplete = function(destinationEntry) { |
this.cancelCallback_ = null; |
this.markAsCopied_(entry, destination); |
this.notify(importer.TaskQueue.UpdateType.PROGRESS); |
- completionCallback(); |
+ resolver.resolve(destinationEntry); |
}; |
/** @this {importer.MediaImportHandler.ImportTask} */ |
var onError = function(error) { |
this.cancelCallback_ = null; |
this.onError_(error); |
+ resolver.reject(error); |
}; |
this.cancelCallback_ = fileOperationUtil.copyTo( |
@@ -329,6 +395,8 @@ importer.MediaImportHandler.ImportTask.prototype.importOne_ = |
onProgress.bind(this), |
onComplete.bind(this), |
onError.bind(this)); |
+ |
+ return resolver.promise; |
}; |
/** |
@@ -374,7 +442,7 @@ importer.MediaImportHandler.ImportTask.prototype.onError_ = function(error) { |
/** |
* Namespace for a default import destination factory. The |
- * defaultDestionation.getImportDestination function creates and returns the |
+ * defaultDestination.getImportDestination function creates and returns the |
* directory /photos/YYYY-MM-DD in the user's Google Drive. YYYY-MM-DD is the |
* current date. |
*/ |