Source: js/download/download_notification.js

  1. /* global DownloadFormatter, NotificationScreen, DownloadUI, DownloadHelper,
  2. MozActivity, DownloadStore */
  3. 'use strict';
  4. /**
  5. * This is the constructor that will represent a download notification
  6. * in the system
  7. *
  8. * @param {Object} download object provided by the API.
  9. */
  10. function DownloadNotification(download) {
  11. this.download = download;
  12. this.fileName = DownloadFormatter.getFileName(download);
  13. this.state = 'started';
  14. this.id = DownloadFormatter.getUUID(download);
  15. // We have to listen for state changes
  16. this.listener = this._update.bind(this);
  17. this.download.addEventListener('statechange', this.listener);
  18. if (download.state === 'started') {
  19. NotificationScreen.addNotification(this._getInfo());
  20. } else {
  21. // For adopted downloads, it is possible for the download to already be
  22. // completed.
  23. this._update();
  24. }
  25. }
  26. DownloadNotification.prototype = {
  27. /**
  28. * This method knows when the toaster should be displayed. Basically
  29. * the toaster shouldn't be displayed if the download state does not change
  30. * or the download was stopped by the user or because of connectivity lost
  31. *
  32. * @return {boolean} True whether the toaster should be displayed.
  33. */
  34. _wontNotify: function dn_wontNotify() {
  35. var download = this.download;
  36. return this.state === download.state ||
  37. download.state === 'downloading' ||
  38. (download.state === 'stopped' && download.error === null);
  39. },
  40. /**
  41. * It updates the notification when the download state changes.
  42. */
  43. _update: function dn_update() {
  44. if (this.download.state === 'finalized') {
  45. // If the user delete the file, we will see this state and what we have to
  46. // do, is to remove the notification
  47. this._close();
  48. return;
  49. }
  50. var noNotify = this._wontNotify();
  51. this.state = this.download.state;
  52. if (this.download.state === 'stopped') {
  53. this._onStopped();
  54. }
  55. var info = this._getInfo();
  56. if (noNotify) {
  57. info.noNotify = true;
  58. }
  59. if (this.state === 'downloading') {
  60. info.mozbehavior = {
  61. noscreen: true
  62. };
  63. }
  64. NotificationScreen.addNotification(info);
  65. if (this.state === 'succeeded') {
  66. this._onSucceeded();
  67. }
  68. },
  69. _onStopped: function dn_onStopped() {
  70. if (this.download.error !== null) {
  71. // Error attr will be not null when a download is stopped because
  72. // something failed
  73. this.state = 'failed';
  74. this._onError();
  75. } else if (!window.navigator.onLine) {
  76. // Remain downloading state when the connectivity was lost
  77. this.state = 'downloading';
  78. }
  79. },
  80. _onError: function dn_onError() {
  81. var result = parseInt(this.download.error.message);
  82. switch (result) {
  83. case DownloadUI.ERRORS.NO_MEMORY:
  84. DownloadUI.show(DownloadUI.TYPE.NO_MEMORY,
  85. this.download,
  86. true);
  87. break;
  88. case DownloadUI.ERRORS.NO_SDCARD:
  89. DownloadUI.show(DownloadUI.TYPE.NO_SDCARD,
  90. this.download,
  91. true);
  92. break;
  93. case DownloadUI.ERRORS.UNMOUNTED_SDCARD:
  94. DownloadUI.show(DownloadUI.TYPE.UNMOUNTED_SDCARD,
  95. this.download,
  96. true);
  97. break;
  98. default:
  99. DownloadHelper.getFreeSpace((function gotFreeMemory(bytes) {
  100. if (bytes === 0) {
  101. DownloadUI.show(DownloadUI.TYPE.NO_MEMORY, this.download, true);
  102. }
  103. }).bind(this));
  104. }
  105. },
  106. _onSucceeded: function dn_onSucceeded() {
  107. this._storeDownload(this.download);
  108. },
  109. /**
  110. * This method stores complete downloads to share them with the download list
  111. * in settings app
  112. *
  113. * @param {Object} The download object provided by the API.
  114. */
  115. _storeDownload: function dn_storeDownload(download) {
  116. var req = DownloadStore.add(download);
  117. req.onsuccess = (function _storeDownloadOnSuccess(request) {
  118. // We don't care about any more state changes to the download.
  119. this.download.removeEventListener('statechange', this.listener);
  120. // Update the download object to the datastore representation.
  121. this.download = req.result;
  122. }).bind(this);
  123. req.onerror = function _storeDownloadOnError(e) {
  124. console.error('Exception storing the download', download.id, '(',
  125. download.url, ').', e.target.error);
  126. };
  127. },
  128. _ICONS_PATH: 'style/notifications/images/',
  129. _ICONS_EXTENSION: '.png',
  130. /**
  131. * It returns the icon depending on current state
  132. *
  133. * @return {String} Icon path.
  134. */
  135. _getIcon: function dn_getIcon() {
  136. var icon = (this.state === 'downloading' ? 'downloading' : 'download');
  137. return this._ICONS_PATH + icon + this._ICONS_EXTENSION;
  138. },
  139. /**
  140. * This method returns an object to update the notification composed by the
  141. * text, icon and type
  142. *
  143. * @return {object} Object descriptor.
  144. */
  145. _getInfo: function dn_getInfo() {
  146. var state = this.state;
  147. var _ = navigator.mozL10n.get;
  148. var info = {
  149. id: this.id,
  150. title: _('download_' + state),
  151. icon: this._getIcon(),
  152. type: 'download-notification-' + state
  153. };
  154. if (state === 'downloading') {
  155. info.text = _('download_downloading_text_2', {
  156. name: this.fileName,
  157. percentage: DownloadFormatter.getPercentage(this.download)
  158. });
  159. } else {
  160. info.text = _('download_text_by_default', {
  161. name: this.fileName
  162. });
  163. }
  164. return info;
  165. },
  166. /**
  167. * Closes the notification
  168. */
  169. _close: function dn_close() {
  170. NotificationScreen.removeNotification(this.id);
  171. this.onClose();
  172. },
  173. /**
  174. * It performs the action when the notification is clicked by the user
  175. * depending on state:
  176. *
  177. * - 'downloading' -> launch the download list
  178. * - 'stopped' -> show confirmation to resume the download
  179. * - 'finalized' -> show confirmation to retry the download
  180. * - 'succeeded' -> open the downloaded file
  181. *
  182. * @param {function} Function that will be invoked when the notification
  183. * is removed from utility tray.
  184. */
  185. onClick: function dn_onClick(done) {
  186. var cb = (function() {
  187. this._close();
  188. done();
  189. }).bind(this);
  190. var req;
  191. switch (this.download.state) {
  192. case 'downloading':
  193. // Launching settings > download list
  194. /* jshint nonew: false */
  195. new MozActivity({
  196. name: 'configure',
  197. data: {
  198. target: 'device',
  199. section: 'downloads'
  200. }
  201. });
  202. /* jshint nonew: true */
  203. // The notification won't be removed when users open the download list
  204. // activity.onsuccess = activity.onerror = cb;
  205. break;
  206. case 'stopped':
  207. // Prompts the user if he wishes to retry the download
  208. req = DownloadUI.show(null, this.download, true);
  209. // The notification won't be removed when users decline to resume
  210. // req.oncancel = cb;
  211. req.onconfirm = this.download.resume.bind(this.download);
  212. break;
  213. case 'succeeded':
  214. // Attempts to open the file
  215. var download = this.download;
  216. req = DownloadHelper.open(download);
  217. req.onerror = function req_onerror() {
  218. DownloadHelper.handlerError(req.error, download);
  219. };
  220. cb();
  221. break;
  222. }
  223. },
  224. /**
  225. * This method releases memory destroying the notification object
  226. */
  227. onClose: function dn_onClose() {
  228. this.download.onstatechange = this.download = this.id = this.state = null;
  229. }
  230. };