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.
8 * Handler of device event.
11 function DeviceHandler() {
13 * Map of device path and mount status of devices.
14 * @type {Object.<string, DeviceHandler.MountStatus>}
17 this.mountStatus_ = {};
19 chrome.fileBrowserPrivate.onDeviceChanged.addListener(
20 this.onDeviceChanged_.bind(this));
21 chrome.fileBrowserPrivate.onMountCompleted.addListener(
22 this.onMountCompleted_.bind(this));
29 * @param {string} prefix Prefix of notification ID.
30 * @param {string} title String ID of title.
31 * @param {string} message String ID of message.
34 DeviceHandler.Notification = function(prefix, title, message) {
36 * Prefix of notification ID.
48 * String ID of message.
51 this.message = message;
57 * @type {DeviceHandler.Notification}
60 DeviceHandler.Notification.DEVICE = new DeviceHandler.Notification(
62 'REMOVABLE_DEVICE_DETECTION_TITLE',
63 'REMOVABLE_DEVICE_SCANNING_MESSAGE');
66 * @type {DeviceHandler.Notification}
69 DeviceHandler.Notification.DEVICE_FAIL = new DeviceHandler.Notification(
71 'REMOVABLE_DEVICE_DETECTION_TITLE',
72 'DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
75 * @type {DeviceHandler.Notification}
78 DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED =
79 new DeviceHandler.Notification(
81 'REMOVABLE_DEVICE_DETECTION_TITLE',
82 'EXTERNAL_STORAGE_DISABLED_MESSAGE');
85 * @type {DeviceHandler.Notification}
88 DeviceHandler.Notification.FORMAT_START = new DeviceHandler.Notification(
90 'FORMATTING_OF_DEVICE_PENDING_TITLE',
91 'FORMATTING_OF_DEVICE_PENDING_MESSAGE');
94 * @type {DeviceHandler.Notification}
97 DeviceHandler.Notification.FORMAT_SUCCESS = new DeviceHandler.Notification(
99 'FORMATTING_OF_DEVICE_FINISHED_TITLE',
100 'FORMATTING_FINISHED_SUCCESS_MESSAGE');
103 * @type {DeviceHandler.Notification}
106 DeviceHandler.Notification.FORMAT_FAIL = new DeviceHandler.Notification(
108 'FORMATTING_OF_DEVICE_FAILED_TITLE',
109 'FORMATTING_FINISHED_FAILURE_MESSAGE');
112 * Shows the notification for the device path.
113 * @param {string} devicePath Device path.
114 * @param {string=} opt_message Message overrides the default message.
116 DeviceHandler.Notification.prototype.show = function(devicePath, opt_message) {
117 chrome.notifications.create(
118 this.makeId_(devicePath),
121 title: str(this.title),
122 message: opt_message || str(this.message),
123 iconUrl: chrome.runtime.getURL('/common/images/icon96.png')
129 * Hides the notification for the device path.
130 * @param {string} devicePath Device path.
132 DeviceHandler.Notification.prototype.hide = function(devicePath) {
133 chrome.notifications.clear(this.makeId_(devicePath), function() {});
137 * Makes a notification ID for the device path.
138 * @param {string} devicePath Device path.
139 * @return {string} Notification ID.
142 DeviceHandler.Notification.prototype.makeId_ = function(devicePath) {
143 return this.prefix + ':' + devicePath;
147 * Handles notifications from C++ sides.
148 * @param {DeviceEvent} event Device event.
151 DeviceHandler.prototype.onDeviceChanged_ = function(event) {
152 switch (event.type) {
154 DeviceHandler.Notification.DEVICE.show(event.devicePath);
155 this.mountStatus_[event.devicePath] = DeviceHandler.MountStatus.NO_RESULT;
158 DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED.show(
161 case 'scan_canceled':
162 DeviceHandler.Notification.DEVICE.hide(event.devicePath);
165 DeviceHandler.Notification.DEVICE.hide(event.devicePath);
166 DeviceHandler.Notification.DEVICE_FAIL.hide(event.devicePath);
167 DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED.hide(
169 delete this.mountStatus_[event.devicePath];
172 DeviceHandler.Notification.FORMAT_START.show(event.devicePath);
174 case 'format_success':
175 DeviceHandler.Notification.FORMAT_START.hide(event.devicePath);
176 DeviceHandler.Notification.FORMAT_SUCCESS.show(event.devicePath);
179 DeviceHandler.Notification.FORMAT_START.hide(event.devicePath);
180 DeviceHandler.Notification.FORMAT_FAIL.show(event.devicePath);
186 * Mount status for the device.
187 * Each multi-partition devices can obtain multiple mount completed events.
188 * This status shows what results are already obtained for the device.
192 DeviceHandler.MountStatus = Object.freeze({
193 // There is no mount results on the device.
194 NO_RESULT: 'noResult',
195 // There is no error on the device.
197 // There is only parent errors, that can be overrided by child results.
198 ONLY_PARENT_ERROR: 'onlyParentError',
199 // There is one child error.
200 CHILD_ERROR: 'childError',
201 // There is multiple child results and at least one is failure.
202 MULTIPART_ERROR: 'multipartError'
206 * Handles mount completed events to show notifications for removable devices.
207 * @param {MountCompletedEvent} event Mount completed event.
210 DeviceHandler.prototype.onMountCompleted_ = function(event) {
211 // If this is remounting, which happens when resuming ChromeOS, the device has
212 // already inserted to the computer. So we suppress the notification.
213 var volume = event.volumeMetadata;
214 if (!volume.deviceType || event.isRemounting)
217 var getFirstStatus = function(event) {
218 if (event.status === 'success')
219 return DeviceHandler.MountStatus.SUCCESS;
220 else if (event.volumeMetadata.isParentDevice)
221 return DeviceHandler.MountStatus.ONLY_PARENT_ERROR;
223 return DeviceHandler.MountStatus.CHILD_ERROR;
226 // Update the current status.
227 switch (this.mountStatus_[volume.devicePath]) {
228 // If there is no related device, do nothing.
231 // If the multipart error message has already shown, do nothing because the
232 // message does not changed by the following mount results.
233 case DeviceHandler.MULTIPART_ERROR:
235 // If this is the first result, hide the scanning notification.
236 case DeviceHandler.MountStatus.NO_RESULT:
237 DeviceHandler.Notification.DEVICE.hide(volume.devicePath);
238 this.mountStatus_[volume.devicePath] = getFirstStatus(event);
240 // If there are only parent errors, and the new result is child's one, hide
241 // the parent error. (parent device contains partition table, which is
243 case DeviceHandler.MountStatus.ONLY_PARENT_ERROR:
244 if (!volume.isParentDevice)
245 DeviceHandler.Notification.DEVICE_FAIL.hide(volume.devicePath);
246 this.mountStatus_[volume.devicePath] = getFirstStatus(event);
248 // We have a multi-partition device for which at least one mount
250 case DeviceHandler.MountStatus.SUCCESS:
251 case DeviceHandler.MountStatus.CHILD_ERROR:
252 if (this.mountStatus_[volume.devicePath] ===
253 DeviceHandler.MountStatus.SUCCESS &&
254 event.status === 'success') {
255 this.mountStatus_[volume.devicePath] =
256 DeviceHandler.MountStatus.SUCCESS;
258 this.mountStatus_[volume.devicePath] =
259 DeviceHandler.MountStatus.MULTIPART_ERROR;
264 // Show the notification for the current errors.
265 // If there is no error, do not show/update the notification.
267 switch (this.mountStatus_[volume.devicePath]) {
268 case DeviceHandler.MountStatus.MULTIPART_ERROR:
269 message = volume.deviceLabel ?
270 strf('MULTIPART_DEVICE_UNSUPPORTED_MESSAGE', volume.deviceLabel) :
271 str('MULTIPART_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
273 case DeviceHandler.MountStatus.CHILD_ERROR:
274 case DeviceHandler.MountStatus.ONLY_PARENT_ERROR:
275 if (event.status === 'error_unsuported_filesystem') {
276 message = volume.deviceLabel ?
277 strf('DEVICE_UNSUPPORTED_MESSAGE', volume.deviceLabel) :
278 str('DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
280 message = volume.deviceLabel ?
281 strf('DEVICE_UNKNOWN_MESSAGE', volume.deviceLabel) :
282 str('DEVICE_UNKNOWN_DEFAULT_MESSAGE');
287 DeviceHandler.Notification.DEVICE_FAIL.hide(volume.devicePath);
288 DeviceHandler.Notification.DEVICE_FAIL.show(volume.devicePath, message);