Chromium Code Reviews| Index: ui/file_manager/file_manager/foreground/js/import_controller.js |
| diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js |
| index 67c96358332cb522efcf7e40bf1e281b003ad61a..439fc43ec381633f6fcbf7b2142c184702e19910 100644 |
| --- a/ui/file_manager/file_manager/foreground/js/import_controller.js |
| +++ b/ui/file_manager/file_manager/foreground/js/import_controller.js |
| @@ -55,12 +55,8 @@ importer.ImportController = |
| /** @private {!importer.CommandWidget} */ |
| this.commandWidget_ = commandWidget; |
| - /** |
| - * A cache of scans by volumeId, directory URL. |
| - * Currently only scans of directories are cached. |
| - * @private {!Object.<string, !Object.<string, !importer.ScanResult>>} |
| - */ |
| - this.cachedScans_ = {}; |
| + /** @type {!importer.ScanManager} */ |
| + this.scanManager_ = new importer.ScanManager(scanner); |
| /** |
| * The active import task, if any. |
| @@ -83,6 +79,9 @@ importer.ImportController = |
| this.environment_.addDirectoryChangedListener( |
| this.onDirectoryChanged_.bind(this)); |
| + this.environment_.addSelectionChangedListener( |
| + this.onSelectionChanged_.bind(this)); |
| + |
| this.commandWidget_.addExecuteListener( |
| this.execute.bind(this)); |
| }; |
| @@ -95,7 +94,7 @@ importer.ImportController = |
| */ |
| importer.ImportController.prototype.onScanEvent_ = function(event, result) { |
| if (event === importer.ScanEvent.INVALIDATED) { |
| - this.resetScanCache_(); |
| + this.scanManager_.reset(); |
| } |
| if (event === importer.ScanEvent.FINALIZED || |
| event === importer.ScanEvent.INVALIDATED) { |
| @@ -112,9 +111,10 @@ importer.ImportController.prototype.execute = function() { |
| console.assert(!this.activeImportTask_, |
| 'Cannot execute while an import task is already active.'); |
| metrics.recordEnum('CloudImport.UserAction', 'IMPORT_INITIATED'); |
| - var result = this.getScanForImport_(); |
| + var scan = this.getScan_(); |
| + assert(scan != null); |
| var importTask = this.importRunner_.importFromScanResult( |
| - result, |
| + scan, |
| importer.Destination.GOOGLE_DRIVE); |
| this.activeImportTask_ = importTask; |
| @@ -129,16 +129,10 @@ importer.ImportController.prototype.execute = function() { |
| */ |
| importer.ImportController.prototype.onImportFinished_ = function(task) { |
| this.activeImportTask_ = null; |
| - this.resetScanCache_(); |
| + this.scanManager_.reset(); |
|
hirono
2015/02/02 01:07:01
It's better to reset cache when the import is fini
Steve McKay
2015/02/02 15:52:37
Ahh, yeah. Multiple-windows. Good thinking. We pro
|
| this.pushUpdate_(); |
| }; |
| -/** @private */ |
| -importer.ImportController.prototype.resetScanCache_ = function() { |
| - // TODO(smckay): Actively cancel each scan. |
| - this.cachedScans_ = {}; |
| -}; |
| - |
| /** |
| * Push an update to the command widget. |
| * @private |
| @@ -163,48 +157,38 @@ importer.ImportController.prototype.getCommandUpdate = function() { |
| // If there is no Google Drive mount, Drive may be disabled |
| // or the machine may be running in guest mode. |
| - if (this.environment_.isGoogleDriveMounted()) { |
| - var entries = this.environment_.getSelection(); |
| - |
| - // Enabled if user has a selection and it consists entirely of files |
| - // that: |
| - // 1) are of a recognized media type |
| - // 2) reside on a removable media device |
| - // 3) in the DCIM directory |
| - if (entries.length) { |
| - if (entries.every( |
| - importer.isEligibleEntry.bind(null, this.environment_))) { |
| - return importer.ImportController.createUpdate_( |
| - importer.ResponseId.EXECUTABLE, entries.length); |
| - } |
| - } else if (this.isCurrentDirectoryScannable_()) { |
| - var scan = this.getCurrentDirectoryScan_(); |
| - if (scan.isFinal()) { |
| - if (scan.getFileEntries().length === 0) { |
| - return importer.ImportController.createUpdate_( |
| - importer.ResponseId.NO_MEDIA); |
| - } else { |
| - return this.fitsInAvailableSpace_(scan).then( |
| - /** @param {boolean} fits */ |
| - function(fits) { |
| - return fits ? |
| - importer.ImportController.createUpdate_( |
| - importer.ResponseId.EXECUTABLE, |
| - scan.getFileEntries().length) : |
| - importer.ImportController.createUpdate_( |
| - importer.ResponseId.INSUFFICIENT_SPACE, |
| - scan.getTotalBytes()); |
| - }); |
| - } |
| - } else { |
| - return importer.ImportController.createUpdate_( |
| - importer.ResponseId.SCANNING); |
| - } |
| - } |
| + if (!this.environment_.isGoogleDriveMounted()) { |
| + return importer.ImportController.createUpdate_( |
| + importer.ResponseId.HIDDEN); |
| } |
| - return importer.ImportController.createUpdate_( |
| - importer.ResponseId.HIDDEN); |
| + var scan = this.getScan_(); |
| + if (!scan) { |
| + return importer.ImportController.createUpdate_( |
| + importer.ResponseId.HIDDEN); |
| + } |
| + |
| + if (!scan.isFinal()) { |
| + return importer.ImportController.createUpdate_( |
| + importer.ResponseId.SCANNING); |
| + } |
| + |
| + if (scan.getFileEntries().length === 0) { |
| + return importer.ImportController.createUpdate_( |
| + importer.ResponseId.NO_MEDIA); |
| + } |
| + |
| + return this.fitsInAvailableSpace_(scan).then( |
| + /** @param {boolean} fits */ |
| + function(fits) { |
| + return fits ? |
| + importer.ImportController.createUpdate_( |
| + importer.ResponseId.EXECUTABLE, |
| + scan.getFileEntries().length) : |
| + importer.ImportController.createUpdate_( |
| + importer.ResponseId.INSUFFICIENT_SPACE, |
| + scan.getTotalBytes()); |
| + }); |
| }.bind(this)); |
| }; |
| @@ -304,48 +288,27 @@ importer.ImportController.prototype.fitsInAvailableSpace_ = |
| /** |
| * Get or create scan for the current directory or file selection. |
| * |
| - * @return {!importer.ScanResult} A scan result object that may be |
| - * actively scanning. |
| + * @return {importer.ScanResult} A scan result object that may be |
| + * actively scanning. Null if scan is not possible in current |
| + * context. |
| * @private |
| */ |
| -importer.ImportController.prototype.getScanForImport_ = function() { |
| +importer.ImportController.prototype.getScan_ = function() { |
| var entries = this.environment_.getSelection(); |
| if (entries.length) { |
| if (entries.every( |
| importer.isEligibleEntry.bind(null, this.environment_))) { |
| - return this.scanner_.scan(entries); |
| + return this.scanManager_.getSelectionScan(entries); |
| } |
| - } else { |
| - return this.getCurrentDirectoryScan_(); |
| - } |
| -}; |
| - |
| -/** |
| - * Get or create scan for the current directory. |
| - * |
| - * @return {!importer.ScanResult} A scan result object that may be |
| - * actively scanning. |
| - * @private |
| - */ |
| -importer.ImportController.prototype.getCurrentDirectoryScan_ = function() { |
| - console.assert(this.isCurrentDirectoryScannable_()); |
| - var directory = this.environment_.getCurrentDirectory(); |
| - var volumeId = this.environment_.getVolumeInfo(directory).volumeId; |
| + } else if (this.isCurrentDirectoryScannable_()) { |
| + var directory = this.environment_.getCurrentDirectory(); |
| + var volumeId = this.environment_.getVolumeInfo(directory).volumeId; |
| - // Lazily initialize the cache for volumeId. |
| - if (!this.cachedScans_.hasOwnProperty(volumeId)) { |
| - this.cachedScans_[volumeId] = {}; |
| + return this.scanManager_.getDirectoryScan(volumeId, directory); |
| } |
| - var url = directory.toURL(); |
| - var scan = this.cachedScans_[volumeId][url]; |
| - if (!scan) { |
| - scan = this.scanner_.scan([directory]); |
| - this.cachedScans_[volumeId][url] = scan; |
| - } |
| - assert(!scan.isInvalidated()); |
| - return scan; |
| + return null; |
| }; |
| /** |
| @@ -353,7 +316,7 @@ importer.ImportController.prototype.getCurrentDirectoryScan_ = function() { |
| * @private |
| */ |
| importer.ImportController.prototype.onVolumeUnmounted_ = function(volumeId) { |
| - this.resetScanCache_(); |
| + this.scanManager_.reset(); |
|
hirono
2015/02/02 01:07:01
nit: We can check volume-unmounted event in the ba
Steve McKay
2015/02/02 15:52:37
Yep. Basically the same TODO (moving scan cache an
|
| this.pushUpdate_(); |
| }; |
| @@ -362,6 +325,12 @@ importer.ImportController.prototype.onDirectoryChanged_ = function() { |
| this.pushUpdate_(); |
| }; |
| +/** @private */ |
| +importer.ImportController.prototype.onSelectionChanged_ = function() { |
| + this.scanManager_.clearSelectionScan(); |
| + this.pushUpdate_(); |
| +}; |
| + |
| /** |
| * Interface abstracting away the concrete file manager available |
| * to commands. By hiding file manager we make it easy to test |
| @@ -416,6 +385,13 @@ importer.ControllerEnvironment.prototype.addVolumeUnmountListener; |
| importer.ControllerEnvironment.prototype.addDirectoryChangedListener; |
| /** |
| + * Installs an 'selection-changed' listener. Listener is called when |
| + * user selected files is changed. |
| + * @param {function()} listener |
| + */ |
| +importer.ControllerEnvironment.prototype.addSelectionChangedListener; |
| + |
| +/** |
| * Class providing access to various pieces of information in the |
| * FileManager environment, like the current directory, volumeinfo lookup |
| * By hiding file manager we make it easy to test importer.ImportController. |
| @@ -425,9 +401,13 @@ importer.ControllerEnvironment.prototype.addDirectoryChangedListener; |
| * |
| * @param {!FileManager} fileManager |
| */ |
| -importer.RuntimeControllerEnvironment = function(fileManager) { |
| +importer.RuntimeControllerEnvironment = |
| + function(fileManager, selectionHandler) { |
| /** @private {!FileManager} */ |
| this.fileManager_ = fileManager; |
| + |
| + /** @private {!FileSelectionHandler} */ |
| + this.selectionHandler_ = selectionHandler; |
| }; |
| /** @override */ |
| @@ -512,6 +492,14 @@ importer.RuntimeControllerEnvironment.prototype.addDirectoryChangedListener = |
| listener); |
| }; |
| +/** @override */ |
| +importer.RuntimeControllerEnvironment.prototype.addSelectionChangedListener = |
| + function(listener) { |
| + this.selectionHandler_.addEventListener( |
| + FileSelectionHandler.EventType.CHANGE, |
| + listener); |
| +}; |
| + |
| /** |
| * Class that adapts from the new non-command button to the old |
| * command style interface. |
| @@ -573,3 +561,91 @@ importer.RuntimeCommandWidget.prototype.update = function(update) { |
| this.buttonElement_.style.display = update.visible ? 'block' : 'none'; |
| this.iconElement_.setAttribute('icon', update.coreIcon); |
| }; |
| + |
| + |
| +/** |
| + * A cache for ScanResults. |
| + * |
| + * @constructor |
| + * @struct |
| + * |
| + * @param {!importer.MediaScanner} scanner |
| + */ |
| +importer.ScanManager = function(scanner) { |
| + /** @private {!importer.MediaScanner} */ |
| + this.scanner_ = scanner; |
| + |
| + /** |
| + * The most recent scan based on user selected files (instead of directories). |
| + * @private {importer.ScanResult} |
| + */ |
| + this.lastSelectionScan_ = null; |
| + |
| + /** |
| + * A cache of scans by volumeId, directory URL. |
| + * Currently only scans of directories are cached. |
| + * @private {!Object.<string, !Object.<string, !importer.ScanResult>>} |
| + */ |
| + this.cachedScans_ = {}; |
|
hirono
2015/02/02 01:07:01
Do we need to have multiple scan caches? If we had
Steve McKay
2015/02/02 15:52:37
TODO will address this...and make the need for mul
|
| +}; |
| + |
| +/** |
| + * Forgets all scans. |
| + */ |
| +importer.ScanManager.prototype.reset = function() { |
| + this.clearSelectionScan(); |
| + this.clearDirectoryScans(); |
| +}; |
| + |
| +/** |
| + * Forgets the selection scans. |
| + */ |
| +importer.ScanManager.prototype.clearSelectionScan = function() { |
| + this.lastSelectionScan_ = null; |
| +}; |
| + |
| +/** |
| + * Forgets directory scans. |
| + */ |
| +importer.ScanManager.prototype.clearDirectoryScans = function() { |
| + this.cachedScans_ = {}; |
|
hirono
2015/02/02 01:07:01
Please check whether the scans are invalidated or
Steve McKay
2015/02/02 15:52:37
That would be unnecessary coupling with the callin
|
| +}; |
| + |
| +/** |
| + * Returns a scan for the directory. |
| + * |
| + * @param {!Array.<!FileEntry>} entries |
| + * |
| + * @return {!importer.ScanResult} |
| + */ |
| +importer.ScanManager.prototype.getSelectionScan = |
| + function(entries) { |
| + if (!this.lastSelectionScan_) { |
| + this.lastSelectionScan_ = this.scanner_.scan(entries); |
| + } |
| + return this.lastSelectionScan_; |
| +}; |
| + |
| +/** |
| + * Returns a scan for the directory. |
| + * |
| + * @param {string} volumeId |
| + * @param {!DirectoryEntry} directory |
| + * |
| + * @return {!importer.ScanResult} |
| + */ |
| +importer.ScanManager.prototype.getDirectoryScan = |
| + function(volumeId, directory) { |
| + // Lazily initialize the cache for volumeId. |
| + if (!(volumeId in this.cachedScans_)) { |
| + this.cachedScans_[volumeId] = {}; |
| + } |
| + |
| + var url = directory.toURL(); |
| + var scan = this.cachedScans_[volumeId][url]; |
| + if (!scan) { |
| + scan = this.scanner_.scan([directory]); |
| + this.cachedScans_[volumeId][url] = scan; |
| + } |
| + return scan; |
| +}; |