OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 // Namespace |
| 6 var importer = importer || {}; |
| 7 |
| 8 /** |
| 9 * @typedef {{ |
| 10 * label_id: string, |
| 11 * visible: boolean, |
| 12 * executable: boolean |
| 13 * }} |
| 14 */ |
| 15 importer.UpdateResponse; |
| 16 |
| 17 /** @enum {importer.UpdateResponse} */ |
| 18 importer.UpdateResponses = { |
| 19 HIDDEN: { |
| 20 label_id: 'CLOUD_IMPORT_BUTTON_LABEL', |
| 21 visible: false, |
| 22 executable: false |
| 23 }, |
| 24 SCANNING: { |
| 25 label_id: 'CLOUD_IMPORT_SCANNING_BUTTON_LABEL', |
| 26 visible: true, |
| 27 executable: false |
| 28 }, |
| 29 EXECUTABLE: { |
| 30 label_id: 'CLOUD_IMPORT_BUTTON_LABEL', |
| 31 visible: true, |
| 32 executable: true |
| 33 } |
| 34 }; |
| 35 |
| 36 /** |
| 37 * Class that orchestrates background activity and UI changes on |
| 38 * behalf of Cloud Import. |
| 39 * |
| 40 * @constructor |
| 41 * @struct |
| 42 * |
| 43 * @param {!importer.ControllerEnvironment} environment The class providing |
| 44 * access to runtime environmental information, like the current directory, |
| 45 * volume lookup and so-on. |
| 46 * @param {!importer.MediaScanner} scanner |
| 47 * @param {!importer.ImportRunner} importRunner |
| 48 * @param {function()} commandUpdateHandler |
| 49 */ |
| 50 importer.ImportController = |
| 51 function(environment, scanner, importRunner, commandUpdateHandler) { |
| 52 |
| 53 /** @private {!importer.ControllerEnvironment} */ |
| 54 this.environment_ = environment; |
| 55 |
| 56 /** @private {!importer.ImportRunner} */ |
| 57 this.importRunner_ = importRunner; |
| 58 |
| 59 /** @private {!importer.MediaScanner} */ |
| 60 this.scanner_ = scanner; |
| 61 |
| 62 /** @private {function()} */ |
| 63 this.updateCommands_ = commandUpdateHandler; |
| 64 |
| 65 /** @private {!importer.ScanObserver} */ |
| 66 this.scanObserverBound_ = this.onScanEvent_.bind(this); |
| 67 |
| 68 this.scanner_.addObserver(this.scanObserverBound_); |
| 69 |
| 70 /** @private {!Object.<string, !importer.ScanResult>} */ |
| 71 this.directoryScans_ = {}; |
| 72 }; |
| 73 |
| 74 /** |
| 75 * @param {!importer.ScanEvent} event Command event. |
| 76 * @param {importer.ScanResult} result |
| 77 */ |
| 78 importer.ImportController.prototype.onScanEvent_ = function(event, result) { |
| 79 // TODO(smckay): only do this if this is a directory scan. |
| 80 if (event === importer.ScanEvent.FINALIZED) { |
| 81 this.updateCommands_(); |
| 82 } |
| 83 }; |
| 84 |
| 85 /** |
| 86 * Executes import against the current directory. Should only |
| 87 * be called when the current directory has been validated |
| 88 * by calling "update" on this class. |
| 89 */ |
| 90 importer.ImportController.prototype.execute = function() { |
| 91 metrics.recordEnum('CloudImport.UserAction', 'IMPORT_INITIATED'); |
| 92 var result = this.getScanForImport_(); |
| 93 this.importRunner_.importFromScanResult(result); |
| 94 }; |
| 95 |
| 96 /** |
| 97 * @return {!importer.UpdateResponse} response |
| 98 */ |
| 99 importer.ImportController.prototype.update = function() { |
| 100 |
| 101 // If there is no Google Drive mount, Drive may be disabled |
| 102 // or the machine may be running in guest mode. |
| 103 if (!this.environment_.isGoogleDriveMounted()) { |
| 104 return importer.UpdateResponses.HIDDEN; |
| 105 } |
| 106 |
| 107 var entries = this.environment_.getSelection(); |
| 108 |
| 109 // Enabled if user has a selection and it consists entirely of files |
| 110 // that: |
| 111 // 1) are of a recognized media type |
| 112 // 2) reside on a removable media device |
| 113 // 3) in the DCIM dir |
| 114 if (entries.length) { |
| 115 if (entries.every( |
| 116 importer.isEligibleEntry.bind(null, this.environment_))) { |
| 117 // TODO(smckay): Include entry count in label. |
| 118 return importer.UpdateResponses.EXECUTABLE; |
| 119 } |
| 120 } else if (this.isCurrentDirectoryScannable_()) { |
| 121 var scan = this.getCurrentDirectoryScan_(); |
| 122 return scan.isFinal() ? |
| 123 importer.UpdateResponses.EXECUTABLE : |
| 124 importer.UpdateResponses.SCANNING; |
| 125 } |
| 126 |
| 127 return importer.UpdateResponses.HIDDEN; |
| 128 }; |
| 129 |
| 130 /** |
| 131 * @return {boolean} true if the current directory is scan eligible. |
| 132 * @private |
| 133 */ |
| 134 importer.ImportController.prototype.isCurrentDirectoryScannable_ = |
| 135 function() { |
| 136 var directory = this.environment_.getCurrentDirectory(); |
| 137 return !!directory && |
| 138 importer.isMediaDirectory(directory, this.environment_); |
| 139 }; |
| 140 |
| 141 /** |
| 142 * Get or create scan for the current directory or file selection. |
| 143 * |
| 144 * @return {!importer.ScanResult} A scan result object that may be |
| 145 * actively scanning. |
| 146 * @private |
| 147 */ |
| 148 importer.ImportController.prototype.getScanForImport_ = function() { |
| 149 var entries = this.environment_.getSelection(); |
| 150 |
| 151 if (entries.length) { |
| 152 if (entries.every( |
| 153 importer.isEligibleEntry.bind(null, this.environment_))) { |
| 154 return this.scanner_.scan(entries); |
| 155 } |
| 156 } else { |
| 157 return this.getCurrentDirectoryScan_(); |
| 158 } |
| 159 }; |
| 160 |
| 161 /** |
| 162 * Get or create scan for the current directory. |
| 163 * |
| 164 * @return {!importer.ScanResult} A scan result object that may be |
| 165 * actively scanning. |
| 166 * @private |
| 167 */ |
| 168 importer.ImportController.prototype.getCurrentDirectoryScan_ = function() { |
| 169 console.assert(this.isCurrentDirectoryScannable_()); |
| 170 var directory = this.environment_.getCurrentDirectory(); |
| 171 var url = directory.toURL(); |
| 172 var scan = this.directoryScans_[url]; |
| 173 if (!scan) { |
| 174 scan = this.scanner_.scan([directory]); |
| 175 // TODO(smckay): evict scans when a device is unmounted or changed. |
| 176 this.directoryScans_[url] = scan; |
| 177 } |
| 178 return scan; |
| 179 }; |
| 180 |
| 181 /** |
| 182 * Interface abstracting away the concrete file manager available |
| 183 * to commands. By hiding file manager we make it easy to test |
| 184 * ImportController. |
| 185 * |
| 186 * @interface |
| 187 * @extends {VolumeManagerCommon.VolumeInfoProvider} |
| 188 */ |
| 189 importer.ControllerEnvironment = function() {}; |
| 190 |
| 191 /** |
| 192 * Returns the current file selection, if any. May be empty. |
| 193 * @return {!Array.<!Entry>} |
| 194 */ |
| 195 importer.ControllerEnvironment.prototype.getSelection; |
| 196 |
| 197 /** |
| 198 * Returns the directory entry for the current directory. |
| 199 * @return {!DirectoryEntry} |
| 200 */ |
| 201 importer.ControllerEnvironment.prototype.getCurrentDirectory; |
| 202 |
| 203 /** |
| 204 * Returns true if the Drive mount is present. |
| 205 * @return {boolean} |
| 206 */ |
| 207 importer.ControllerEnvironment.prototype.isGoogleDriveMounted; |
| 208 |
| 209 |
| 210 /** |
| 211 * Class providing access to various pieces of information in the |
| 212 * FileManager environment, like the current directory, volumeinfo lookup |
| 213 * By hiding file manager we make it easy to test importer.ImportController. |
| 214 * |
| 215 * @constructor |
| 216 * @implements {importer.ControllerEnvironment} |
| 217 * |
| 218 * @param {!FileManager} fileManager |
| 219 */ |
| 220 importer.RuntimeControllerEnvironment = function(fileManager) { |
| 221 /** @private {!FileManager} */ |
| 222 this.fileManager_ = fileManager; |
| 223 }; |
| 224 |
| 225 /** @override */ |
| 226 importer.RuntimeControllerEnvironment.prototype.getSelection = |
| 227 function() { |
| 228 return this.fileManager_.getSelection().entries; |
| 229 }; |
| 230 |
| 231 /** @override */ |
| 232 importer.RuntimeControllerEnvironment.prototype.getCurrentDirectory = |
| 233 function() { |
| 234 return /** @type {!DirectoryEntry} */ ( |
| 235 this.fileManager_.getCurrentDirectoryEntry()); |
| 236 }; |
| 237 |
| 238 /** @override */ |
| 239 importer.RuntimeControllerEnvironment.prototype.getVolumeInfo = |
| 240 function(entry) { |
| 241 return this.fileManager_.volumeManager.getVolumeInfo(entry); |
| 242 }; |
| 243 |
| 244 /** @override */ |
| 245 importer.RuntimeControllerEnvironment.prototype.isGoogleDriveMounted = |
| 246 function() { |
| 247 var drive = this.fileManager_.volumeManager.getCurrentProfileVolumeInfo( |
| 248 VolumeManagerCommon.VolumeType.DRIVE); |
| 249 return !!drive; |
| 250 }; |
OLD | NEW |