/* global DownloadFormatter, NotificationScreen, DownloadUI, DownloadHelper,
MozActivity, DownloadStore */
'use strict';
/**
* This is the constructor that will represent a download notification
* in the system
*
* @param {Object} download object provided by the API.
*/
function DownloadNotification(download) {
this.download = download;
this.fileName = DownloadFormatter.getFileName(download);
this.state = 'started';
this.id = DownloadFormatter.getUUID(download);
// We have to listen for state changes
this.listener = this._update.bind(this);
this.download.addEventListener('statechange', this.listener);
if (download.state === 'started') {
NotificationScreen.addNotification(this._getInfo());
} else {
// For adopted downloads, it is possible for the download to already be
// completed.
this._update();
}
}
DownloadNotification.prototype = {
/**
* This method knows when the toaster should be displayed. Basically
* the toaster shouldn't be displayed if the download state does not change
* or the download was stopped by the user or because of connectivity lost
*
* @return {boolean} True whether the toaster should be displayed.
*/
_wontNotify: function dn_wontNotify() {
var download = this.download;
return this.state === download.state ||
download.state === 'downloading' ||
(download.state === 'stopped' && download.error === null);
},
/**
* It updates the notification when the download state changes.
*/
_update: function dn_update() {
if (this.download.state === 'finalized') {
// If the user delete the file, we will see this state and what we have to
// do, is to remove the notification
this._close();
return;
}
var noNotify = this._wontNotify();
this.state = this.download.state;
if (this.download.state === 'stopped') {
this._onStopped();
}
var info = this._getInfo();
if (noNotify) {
info.noNotify = true;
}
if (this.state === 'downloading') {
info.mozbehavior = {
noscreen: true
};
}
NotificationScreen.addNotification(info);
if (this.state === 'succeeded') {
this._onSucceeded();
}
},
_onStopped: function dn_onStopped() {
if (this.download.error !== null) {
// Error attr will be not null when a download is stopped because
// something failed
this.state = 'failed';
this._onError();
} else if (!window.navigator.onLine) {
// Remain downloading state when the connectivity was lost
this.state = 'downloading';
}
},
_onError: function dn_onError() {
var result = parseInt(this.download.error.message);
switch (result) {
case DownloadUI.ERRORS.NO_MEMORY:
DownloadUI.show(DownloadUI.TYPE.NO_MEMORY,
this.download,
true);
break;
case DownloadUI.ERRORS.NO_SDCARD:
DownloadUI.show(DownloadUI.TYPE.NO_SDCARD,
this.download,
true);
break;
case DownloadUI.ERRORS.UNMOUNTED_SDCARD:
DownloadUI.show(DownloadUI.TYPE.UNMOUNTED_SDCARD,
this.download,
true);
break;
default:
DownloadHelper.getFreeSpace((function gotFreeMemory(bytes) {
if (bytes === 0) {
DownloadUI.show(DownloadUI.TYPE.NO_MEMORY, this.download, true);
}
}).bind(this));
}
},
_onSucceeded: function dn_onSucceeded() {
this._storeDownload(this.download);
},
/**
* This method stores complete downloads to share them with the download list
* in settings app
*
* @param {Object} The download object provided by the API.
*/
_storeDownload: function dn_storeDownload(download) {
var req = DownloadStore.add(download);
req.onsuccess = (function _storeDownloadOnSuccess(request) {
// We don't care about any more state changes to the download.
this.download.removeEventListener('statechange', this.listener);
// Update the download object to the datastore representation.
this.download = req.result;
}).bind(this);
req.onerror = function _storeDownloadOnError(e) {
console.error('Exception storing the download', download.id, '(',
download.url, ').', e.target.error);
};
},
_ICONS_PATH: 'style/notifications/images/',
_ICONS_EXTENSION: '.png',
/**
* It returns the icon depending on current state
*
* @return {String} Icon path.
*/
_getIcon: function dn_getIcon() {
var icon = (this.state === 'downloading' ? 'downloading' : 'download');
return this._ICONS_PATH + icon + this._ICONS_EXTENSION;
},
/**
* This method returns an object to update the notification composed by the
* text, icon and type
*
* @return {object} Object descriptor.
*/
_getInfo: function dn_getInfo() {
var state = this.state;
var _ = navigator.mozL10n.get;
var info = {
id: this.id,
title: _('download_' + state),
icon: this._getIcon(),
type: 'download-notification-' + state
};
if (state === 'downloading') {
info.text = _('download_downloading_text_2', {
name: this.fileName,
percentage: DownloadFormatter.getPercentage(this.download)
});
} else {
info.text = _('download_text_by_default', {
name: this.fileName
});
}
return info;
},
/**
* Closes the notification
*/
_close: function dn_close() {
NotificationScreen.removeNotification(this.id);
this.onClose();
},
/**
* It performs the action when the notification is clicked by the user
* depending on state:
*
* - 'downloading' -> launch the download list
* - 'stopped' -> show confirmation to resume the download
* - 'finalized' -> show confirmation to retry the download
* - 'succeeded' -> open the downloaded file
*
* @param {function} Function that will be invoked when the notification
* is removed from utility tray.
*/
onClick: function dn_onClick(done) {
var cb = (function() {
this._close();
done();
}).bind(this);
var req;
switch (this.download.state) {
case 'downloading':
// Launching settings > download list
/* jshint nonew: false */
new MozActivity({
name: 'configure',
data: {
target: 'device',
section: 'downloads'
}
});
/* jshint nonew: true */
// The notification won't be removed when users open the download list
// activity.onsuccess = activity.onerror = cb;
break;
case 'stopped':
// Prompts the user if he wishes to retry the download
req = DownloadUI.show(null, this.download, true);
// The notification won't be removed when users decline to resume
// req.oncancel = cb;
req.onconfirm = this.download.resume.bind(this.download);
break;
case 'succeeded':
// Attempts to open the file
var download = this.download;
req = DownloadHelper.open(download);
req.onerror = function req_onerror() {
DownloadHelper.handlerError(req.error, download);
};
cb();
break;
}
},
/**
* This method releases memory destroying the notification object
*/
onClose: function dn_onClose() {
this.download.onstatechange = this.download = this.id = this.state = null;
}
};