Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
961 lines
29 KiB
JavaScript
Executable file
961 lines
29 KiB
JavaScript
Executable file
/* global AppUpdatable, LazyLoader, MozActivity, NotificationScreen, Service,
|
|
SettingsListener, SystemBanner, SystemUpdatable */
|
|
|
|
'use strict';
|
|
|
|
/*
|
|
* The UpdateManager is a central component for apps *and* system updates.
|
|
* The user can start or cancel all downloads at once.
|
|
* This component also makes sure of bothering the user to a minimum by
|
|
* showing active notifications for new updates/errors only once.
|
|
*
|
|
* It maintains 2 queues of Updatable objects.
|
|
* - updatesQueue for available updates
|
|
* - downloadsQueue for active downloads
|
|
*/
|
|
|
|
|
|
(function(exports) {
|
|
var UpdateManager = {
|
|
_mgmt: null,
|
|
_downloading: false,
|
|
_uncompressing: false,
|
|
_downloadedBytes: 0,
|
|
_errorTimeout: null,
|
|
_wifiLock: null,
|
|
_systemUpdateDisplayed: false,
|
|
_startedDownloadUsingDataConnection: false,
|
|
_settings: null,
|
|
_hasDialog: false,
|
|
UPDATE_NOTIF_ID: 'update-notification',
|
|
NOTIFICATION_BUFFERING_TIMEOUT: 30 * 1000,
|
|
TOASTER_TIMEOUT: 1200,
|
|
UPDATE_2G_SETT: 'update.2g.enabled',
|
|
UPDATE_2G: false,
|
|
ROAMING_SETTING_KEY: 'ril.data.roaming_enabled',
|
|
DATA_TYPES_NO_ALLOWED: ['edge', 'gprs', '1xrtt', 'is95a', 'is95b'],
|
|
WIFI_PRIORITIZED: true,
|
|
WIFI_PRIORITIZED_KEY: 'app.update.wifi-prioritized',
|
|
connection2G: false,
|
|
|
|
container: null,
|
|
message: null,
|
|
toaster: null,
|
|
toasterMessage: null,
|
|
laterButton: null,
|
|
notnowButton: null,
|
|
downloadButton: null,
|
|
downloadViaDataConnectionButton: null,
|
|
downloadDialog: null,
|
|
downloadViaDataConnectionTitle: null,
|
|
downloadViaDataConnectionDialog: null,
|
|
downloadViaDataConnectionMessage: null,
|
|
downloadDialogTitle: null,
|
|
downloadDialogList: null,
|
|
lastUpdatesAvailable: 0,
|
|
_notificationTimeout: null,
|
|
|
|
updatableApps: [],
|
|
systemUpdatable: null,
|
|
updatesQueue: [],
|
|
downloadsQueue: [],
|
|
|
|
start: function() {
|
|
if (!this._mgmt) {
|
|
this._mgmt = navigator.mozApps.mgmt;
|
|
}
|
|
|
|
this._mgmt.getAll().onsuccess = (function gotAll(evt) {
|
|
var apps = evt.target.result;
|
|
apps.forEach(function appIterator(app) {
|
|
/* jshint nonew: false */
|
|
new AppUpdatable(app);
|
|
});
|
|
}).bind(this);
|
|
|
|
this._settings = navigator.mozSettings;
|
|
|
|
this.systemUpdatable = new SystemUpdatable();
|
|
|
|
this.container = document.getElementById('update-manager-container');
|
|
this.message = this.container.querySelector('.title-container');
|
|
|
|
this.toaster = document.getElementById('update-manager-toaster');
|
|
this.toasterMessage = this.toaster.querySelector('.message');
|
|
|
|
this.laterButton = document.getElementById('updates-later-button');
|
|
this.notnowButton =
|
|
document.getElementById('updates-viaDataConnection-notnow-button');
|
|
this.downloadButton = document.getElementById('updates-download-button');
|
|
this.downloadViaDataConnectionButton =
|
|
document.getElementById('updates-viaDataConnection-download-button');
|
|
this.downloadDialog = document.getElementById('updates-download-dialog');
|
|
this.downloadDialogTitle = this.downloadDialog.querySelector('h1');
|
|
this.downloadDialogList = this.downloadDialog.querySelector('ul');
|
|
this.downloadViaDataConnectionDialog =
|
|
document.getElementById('updates-viaDataConnection-dialog');
|
|
this.downloadViaDataConnectionMessage =
|
|
this.downloadViaDataConnectionDialog.querySelector('p');
|
|
this.downloadViaDataConnectionTitle =
|
|
this.downloadViaDataConnectionDialog.querySelector('h1');
|
|
|
|
this.container.onclick = this.containerClicked.bind(this);
|
|
this.laterButton.onclick = this.cancelPrompt.bind(this);
|
|
this.toaster.onclick = this.toasterClicked.bind(this);
|
|
this.downloadButton.onclick = this.requestDownloads.bind(this);
|
|
this.downloadDialogList.onchange = this.updateDownloadButton.bind(this);
|
|
this.notnowButton.onclick =
|
|
this.cancelDataConnectionUpdatesPrompt.bind(this);
|
|
this.downloadViaDataConnectionButton.onclick =
|
|
this.requestDownloads.bind(this);
|
|
|
|
window.addEventListener('mozChromeEvent', this);
|
|
window.addEventListener('applicationinstall', this);
|
|
window.addEventListener('applicationuninstall', this);
|
|
window.addEventListener('online', this);
|
|
window.addEventListener('offline', this);
|
|
window.addEventListener('lockscreen-appopened', this);
|
|
window.addEventListener('home', this);
|
|
window.addEventListener('holdhome', this);
|
|
|
|
SettingsListener.observe('gaia.system.checkForUpdates', false,
|
|
this.checkForUpdates.bind(this));
|
|
|
|
// We maintain the the edge and nowifi data attributes to show
|
|
// a warning on the download dialog
|
|
window.addEventListener('wifi-statuschange', this);
|
|
this.updateWifiStatus();
|
|
this.updateOnlineStatus();
|
|
},
|
|
|
|
requestDownloads: function um_requestDownloads(evt) {
|
|
evt.preventDefault();
|
|
if (evt.target == this.downloadViaDataConnectionButton) {
|
|
this._startedDownloadUsingDataConnection = true;
|
|
this.startDownloads();
|
|
} else {
|
|
this.promptOrDownload();
|
|
}
|
|
},
|
|
|
|
startDownloads: function um_startDownloads() {
|
|
this.downloadViaDataConnectionDialog.classList.remove('visible');
|
|
this._closeDownloadDialog();
|
|
Service.request('UtilityTray:show');
|
|
|
|
var checkValues = {};
|
|
var dialog = this.downloadDialogList;
|
|
var checkboxes = dialog.querySelectorAll('gaia-checkbox');
|
|
for (var i = 0; i < checkboxes.length; i++) {
|
|
var checkbox = checkboxes[i];
|
|
checkValues[checkbox.dataset.position] = checkbox.checked;
|
|
}
|
|
|
|
this.updatesQueue.forEach(function(updatable, index) {
|
|
// The user opted out of the download
|
|
if (updatable.app && !checkValues[index]) {
|
|
return;
|
|
}
|
|
|
|
updatable.download();
|
|
});
|
|
|
|
this._downloadedBytes = 0;
|
|
this.render();
|
|
},
|
|
|
|
cancelAllDownloads: function um_cancelAllDownloads() {
|
|
Service.request('hideCustomDialog');
|
|
this._hasDialog = false;
|
|
|
|
// We're emptying the array while iterating
|
|
while (this.downloadsQueue.length) {
|
|
var updatable = this.downloadsQueue[0];
|
|
updatable.cancelDownload();
|
|
this.removeFromDownloadsQueue(updatable);
|
|
}
|
|
},
|
|
|
|
requestErrorBanner: function um_requestErrorBanner() {
|
|
if (this._errorTimeout) {/* jshint nonew: false */
|
|
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
this._errorTimeout = setTimeout(function waitForMore() {
|
|
LazyLoader.load(['js/system_banner.js']).then(function() {
|
|
var systemBanner = new SystemBanner();
|
|
systemBanner.show('downloadError');
|
|
self._errorTimeout = null;
|
|
})['catch'](function(err) {
|
|
console.error(err);
|
|
});
|
|
}, this.NOTIFICATION_BUFFERING_TIMEOUT);
|
|
},
|
|
|
|
promptOrDownload: function um_promptOrDownload() {
|
|
var self = this;
|
|
|
|
if (self.downloadDialog.dataset.online == 'false') {
|
|
self.showPromptNoConnection();
|
|
return;
|
|
}
|
|
|
|
if (self._wifiAvailable()) {
|
|
self._startedDownloadUsingDataConnection = false;
|
|
self.startDownloads();
|
|
return;
|
|
}
|
|
|
|
var wifiPrioritized = self.getWifiPrioritized();
|
|
var update2GEnabled = self.getUpdate2GEnabled();
|
|
|
|
// We can download the update only if the current connection
|
|
// is not forbidden for download
|
|
var conns = window.navigator.mozMobileConnections;
|
|
if (!conns) {
|
|
// B2G desktop/Mulet
|
|
if (navigator.onLine) {
|
|
self.startDownloads();
|
|
return;
|
|
}
|
|
console.error('mozMobileConnections is not available we can ' +
|
|
'not update the phone.');
|
|
self.showPromptNoConnection();
|
|
return;
|
|
}
|
|
|
|
var dataType;
|
|
// In DualSim only one of them will have data active
|
|
for (var i = 0; i < conns.length && !dataType; i++) {
|
|
dataType = conns[i].data.type;
|
|
}
|
|
if (!dataType) {
|
|
console.error('There are not wifi connection nor data ' +
|
|
'connection. We can not download update');
|
|
self.showForbiddenDownload();
|
|
return;
|
|
}
|
|
|
|
if (self.DATA_TYPES_NO_ALLOWED.indexOf(dataType) >= 0) {
|
|
self.connection2G = true;
|
|
} else {
|
|
self.connection2G = false;
|
|
}
|
|
|
|
// If it's not connected to a wifi we need to verify what kind of
|
|
// connection it has
|
|
Promise.all([wifiPrioritized, update2GEnabled]).then(function(values) {
|
|
var prioritized = values[0];
|
|
var update2G = values[1];
|
|
// If update 2G is available we don't need to know what kind
|
|
// of connection the phone has
|
|
if (update2G) {
|
|
if (prioritized) {
|
|
self.showPromptWifiPrioritized();
|
|
} else {
|
|
self.showPrompt3GAdditionalCostIfNeeded();
|
|
}
|
|
return;
|
|
}
|
|
|
|
//2G connection
|
|
if (self.connection2G && self._systemUpdateDisplayed) {
|
|
self.showForbiddenDownload();
|
|
return;
|
|
}
|
|
|
|
//3G connection
|
|
if (prioritized) {
|
|
self.showPromptWifiPrioritized();
|
|
} else {
|
|
self.showPrompt3GAdditionalCostIfNeeded();
|
|
}
|
|
});
|
|
},
|
|
|
|
containerClicked: function um_containerClicker() {
|
|
if (this._downloading) {
|
|
if (this._uncompressing) {
|
|
// If notification was clicked during uncompression, do nothing.
|
|
return;
|
|
}
|
|
var cancel = {
|
|
title: 'no',
|
|
callback: this.cancelPrompt.bind(this)
|
|
};
|
|
|
|
var confirm = {
|
|
title: 'yes',
|
|
callback: this.cancelAllDownloads.bind(this)
|
|
};
|
|
|
|
Service.request('showCustomDialog',
|
|
'cancelAllDownloads',
|
|
'wantToCancelAll',
|
|
cancel,
|
|
confirm
|
|
);
|
|
this._hasDialog = true;
|
|
} else {
|
|
this.showDownloadPrompt();
|
|
}
|
|
|
|
Service.request('UtilityTray:hide');
|
|
},
|
|
|
|
toasterClicked: function um_toasterClicked() {
|
|
if (this._downloading) {
|
|
return;
|
|
}
|
|
|
|
this.showDownloadPrompt();
|
|
Service.request('UtilityTray:hide');
|
|
},
|
|
|
|
showForbiddenDownload: function um_showForbiddenDownload() {
|
|
//Close any dialog if there is any open
|
|
Service.request('hideCustomDialog');
|
|
var ok = {
|
|
title: 'ok',
|
|
callback: this.cancelPrompt.bind(this)
|
|
};
|
|
|
|
window.dispatchEvent(new CustomEvent('updatepromptshown'));
|
|
|
|
Service.request('showCustomDialog',
|
|
'systemUpdate', 'downloadUpdatesVia2GForbidden3', ok, null);
|
|
this._hasDialog = true;
|
|
},
|
|
|
|
showPromptNoConnection: function um_showPromptNoConnection() {
|
|
//Close any dialog if there is any open
|
|
Service.request('hideCustomDialog');
|
|
var ok = {
|
|
title: 'ok',
|
|
callback: this.cancelPrompt.bind(this)
|
|
};
|
|
|
|
window.dispatchEvent(new CustomEvent('updatepromptshown'));
|
|
|
|
Service.request('showCustomDialog',
|
|
'systemUpdate', 'downloadOfflineWarning2', ok, null);
|
|
this._hasDialog = true;
|
|
},
|
|
|
|
showDownloadPrompt: function um_showDownloadPrompt() {
|
|
var _localize = navigator.mozL10n.setAttributes;
|
|
|
|
this._systemUpdateDisplayed = false;
|
|
_localize(this.downloadDialogTitle, 'numberOfUpdates', {
|
|
n: this.updatesQueue.length
|
|
});
|
|
|
|
// System update should always be on top
|
|
this.updatesQueue.sort(function sortUpdates(updatable, otherUpdatable) {
|
|
if (!updatable.app) {
|
|
return -1;
|
|
}
|
|
if (!otherUpdatable.app) {
|
|
return 1;
|
|
}
|
|
|
|
if (updatable.name < otherUpdatable.name) {
|
|
return -1;
|
|
}
|
|
if (updatable.name > otherUpdatable.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
|
|
this.downloadDialogList.innerHTML = '';
|
|
this.updatesQueue.forEach(function updatableIterator(updatable, index) {
|
|
var listItem = document.createElement('li');
|
|
var nameDetails;
|
|
|
|
// The user can choose not to update an app
|
|
if (updatable instanceof SystemUpdatable) {
|
|
var checkContainer = document.createElement('label');
|
|
_localize(checkContainer, 'required');
|
|
checkContainer.classList.add('required');
|
|
this._systemUpdateDisplayed = true;
|
|
listItem.appendChild(checkContainer);
|
|
nameDetails = listItem;
|
|
} else {
|
|
var checkbox = document.createElement('gaia-checkbox');
|
|
checkbox.dataset.position = index;
|
|
checkbox.checked = true;
|
|
|
|
var label = document.createElement('label');
|
|
nameDetails = label;
|
|
|
|
checkbox.appendChild(label);
|
|
listItem.appendChild(checkbox);
|
|
}
|
|
|
|
var name = document.createElement('span');
|
|
name.classList.add('name');
|
|
if (updatable.nameL10nId) {
|
|
_localize(name, updatable.nameL10nId, updatable.nameL10nArgs);
|
|
} else {
|
|
name.textContent = updatable.name;
|
|
}
|
|
nameDetails.appendChild(name);
|
|
|
|
if (updatable.size) {
|
|
var sizeItem = document.createElement('span');
|
|
sizeItem.textContent = this._humanizeSize(updatable.size);
|
|
nameDetails.appendChild(sizeItem);
|
|
} else {
|
|
nameDetails.classList.add('nosize');
|
|
}
|
|
|
|
if (updatable.buildID) {
|
|
var buildId = document.createElement('span');
|
|
buildId.classList.add('font-light');
|
|
_localize(buildId, 'build-id', { buildid: updatable.buildID });
|
|
listItem.appendChild(buildId);
|
|
}
|
|
|
|
if (updatable.detailsURL && (updatable.detailsURL !== 'about:blank')) {
|
|
var detailsUrl = document.createElement('a');
|
|
_localize(detailsUrl, 'view-release-notes');
|
|
listItem.appendChild(detailsUrl);
|
|
listItem.onclick = (function(event) {
|
|
event.preventDefault();
|
|
|
|
var activity = new MozActivity({
|
|
name: 'view',
|
|
data: {
|
|
type: 'url',
|
|
url: updatable.detailsURL
|
|
}
|
|
});
|
|
|
|
activity.onsuccess = this.cancelPrompt.bind(this);
|
|
}).bind(this);
|
|
}
|
|
|
|
this.downloadDialogList.appendChild(listItem);
|
|
}, this);
|
|
|
|
window.dispatchEvent(new CustomEvent('updatepromptshown'));
|
|
this._hasDialog = true;
|
|
this.downloadDialog.classList.add('visible');
|
|
this.updateDownloadButton();
|
|
},
|
|
|
|
updateDownloadButton: function() {
|
|
if (this._systemUpdateDisplayed) {
|
|
this.downloadButton.disabled = false;
|
|
return;
|
|
}
|
|
|
|
var disabled = true;
|
|
|
|
var dialog = this.downloadDialogList;
|
|
var checkboxes = dialog.querySelectorAll('gaia-checkbox');
|
|
for (var i = 0; i < checkboxes.length; i++) {
|
|
if (checkboxes[i].checked) {
|
|
disabled = false;
|
|
break;
|
|
}
|
|
}
|
|
this.downloadButton.disabled = disabled;
|
|
},
|
|
|
|
cancelPrompt: function um_cancelPrompt() {
|
|
Service.request('hideCustomDialog');
|
|
this._hasDialog = false;
|
|
this._closeDownloadDialog();
|
|
},
|
|
|
|
cancelDataConnectionUpdatesPrompt: function um_cancelDCUpdatesPrompt() {
|
|
Service.request('hideCustomDialog');
|
|
this._hasDialog = false;
|
|
this.downloadViaDataConnectionDialog.classList.remove('visible');
|
|
this._closeDownloadDialog();
|
|
},
|
|
|
|
getWifiPrioritized: function um_getWifiPrioritized() {
|
|
var wifiPrioritized = this.WIFI_PRIORITIZED;
|
|
var settings = window.navigator.mozSettings;
|
|
var self = this;
|
|
var getRequest = settings.createLock().get(this.WIFI_PRIORITIZED_KEY);
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
getRequest.onerror = function() {
|
|
resolve(wifiPrioritized);
|
|
};
|
|
getRequest.onsuccess = function() {
|
|
var prioritized = getRequest.result[self.WIFI_PRIORITIZED_KEY];
|
|
if (typeof prioritized !== 'boolean') {
|
|
prioritized = wifiPrioritized;
|
|
}
|
|
resolve(prioritized);
|
|
};
|
|
});
|
|
},
|
|
|
|
getUpdate2GEnabled: function um_getUpdate2GEnabled() {
|
|
var update2G = this.UPDATE_2G;
|
|
var settings = window.navigator.mozSettings;
|
|
var self = this;
|
|
var getRequest = settings.createLock().get(this.UPDATE_2G_SETT);
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
getRequest.onerror = function() {
|
|
resolve(update2G);
|
|
};
|
|
getRequest.onsuccess = function() {
|
|
var setting = getRequest.result[self.UPDATE_2G_SETT];
|
|
if (typeof setting !== 'boolean') {
|
|
setting = update2G;
|
|
}
|
|
resolve(setting);
|
|
};
|
|
});
|
|
},
|
|
|
|
showPrompt3GAdditionalCostIfNeeded:
|
|
function um_showPrompt3GAdditionalCostIfNeeded() {
|
|
this._openDownloadViaDataDialog();
|
|
Service.request('hideCustomDialog');
|
|
this._hasDialog = false;
|
|
},
|
|
|
|
showPromptWifiPrioritized:
|
|
function um_showPromptWifiPrioritized(downloadCallback) {
|
|
if (!downloadCallback) {
|
|
downloadCallback = this.showPrompt3GAdditionalCostIfNeeded;
|
|
}
|
|
var notNow = {
|
|
title: 'notNow',
|
|
callback: this.cancelPrompt.bind(this)
|
|
};
|
|
|
|
var download = {
|
|
title: 'download',
|
|
recommend: true,
|
|
callback: downloadCallback.bind(this)
|
|
};
|
|
|
|
var messageL10n = this.connection2G ? 'downloadWifiPrioritizedUsing2G' :
|
|
'downloadWifiPrioritized3';
|
|
|
|
this._closeDownloadDialog();
|
|
|
|
window.dispatchEvent(new CustomEvent('updatepromptshown'));
|
|
|
|
Service.request('UtilityTray:hide');
|
|
Service.request('showCustomDialog',
|
|
'systemUpdate',
|
|
messageL10n,
|
|
notNow,
|
|
download
|
|
);
|
|
this._hasDialog = true;
|
|
},
|
|
|
|
downloadProgressed: function um_downloadProgress(bytes) {
|
|
if (bytes > 0) {
|
|
this._downloadedBytes += bytes;
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
downloaded: function um_downloaded(udatable) {
|
|
if (this._startedDownloadUsingDataConnection) {
|
|
this._startedDownloadUsingDataConnection = false;
|
|
}
|
|
},
|
|
|
|
startedUncompressing: function um_startedUncompressing() {
|
|
this._uncompressing = true;
|
|
this.render();
|
|
},
|
|
|
|
render: function um_render() {
|
|
var _localize = navigator.mozL10n.setAttributes;
|
|
var l10nId = 'updateAvailableInfo';
|
|
|
|
if ((this.updatesQueue.length === 1) &&
|
|
(this.updatesQueue[0] instanceof SystemUpdatable)) {
|
|
l10nId = 'systemUpdateAvailableInfo';
|
|
}
|
|
|
|
_localize(this.toasterMessage, l10nId, {
|
|
n: this.updatesQueue.length - this.lastUpdatesAvailable
|
|
});
|
|
|
|
if (this._downloading) {
|
|
if (this._uncompressing && this.downloadsQueue.length === 1) {
|
|
_localize(this.message, 'uncompressingMessage');
|
|
} else {
|
|
_localize(this.message, 'downloadingUpdateMessage', {
|
|
progress: this._humanizeSize(this._downloadedBytes)
|
|
});
|
|
}
|
|
} else {
|
|
_localize(this.message, l10nId, { n: this.updatesQueue.length });
|
|
}
|
|
|
|
var css = this.container.classList;
|
|
this._downloading ? css.add('downloading') : css.remove('downloading');
|
|
},
|
|
|
|
addToUpdatableApps: function um_addtoUpdatableapps(updatableApp) {
|
|
this.updatableApps.push(updatableApp);
|
|
},
|
|
|
|
removeFromAll: function um_removeFromAll(updatableApp) {
|
|
var removeIndex = this.updatableApps.indexOf(updatableApp);
|
|
if (removeIndex === -1) {
|
|
return;
|
|
}
|
|
|
|
var removedApp = this.updatableApps[removeIndex];
|
|
this.removeFromUpdatesQueue(removedApp);
|
|
|
|
removedApp.uninit();
|
|
this.updatableApps.splice(removeIndex, 1);
|
|
},
|
|
|
|
addToUpdatesQueue: function um_addToUpdatesQueue(updatable) {
|
|
if (this._downloading) {
|
|
return;
|
|
}
|
|
|
|
if (updatable.app &&
|
|
updatable.app.installState !== 'installed') {
|
|
return;
|
|
}
|
|
|
|
if (updatable.app &&
|
|
this.updatableApps.indexOf(updatable) === -1) {
|
|
return;
|
|
}
|
|
|
|
var alreadyThere = this.updatesQueue.some(function lookup(u) {
|
|
return (u.app === updatable.app);
|
|
});
|
|
if (alreadyThere) {
|
|
return;
|
|
}
|
|
|
|
this.updatesQueue.push(updatable);
|
|
|
|
if (this._notificationTimeout === null) {
|
|
this._notificationTimeout = setTimeout(
|
|
this.displayNotificationAndToaster.bind(this),
|
|
this.NOTIFICATION_BUFFERING_TIMEOUT);
|
|
}
|
|
this.render();
|
|
},
|
|
|
|
displayNotificationAndToaster: function um_displayNotificationAndToaster() {
|
|
this._notificationTimeout = null;
|
|
if (this.updatesQueue.length && !this._downloading) {
|
|
this.lastUpdatesAvailable = this.updatesQueue.length;
|
|
this.displayNotificationIfHidden();
|
|
this.toaster.classList.add('displayed');
|
|
var self = this;
|
|
setTimeout(function waitToHide() {
|
|
self.toaster.classList.remove('displayed');
|
|
}, this.TOASTER_TIMEOUT);
|
|
}
|
|
},
|
|
|
|
removeFromUpdatesQueue: function um_removeFromUpdatesQueue(updatable) {
|
|
var removeIndex = this.updatesQueue.indexOf(updatable);
|
|
if (removeIndex === -1) {
|
|
return;
|
|
}
|
|
|
|
this.updatesQueue.splice(removeIndex, 1);
|
|
this.lastUpdatesAvailable = this.updatesQueue.length;
|
|
|
|
if (this.updatesQueue.length === 0) {
|
|
this.hideNotificationIfDisplayed();
|
|
}
|
|
|
|
this.render();
|
|
},
|
|
|
|
addToDownloadsQueue: function um_addToDownloadsQueue(updatable) {
|
|
if (updatable.app &&
|
|
this.updatableApps.indexOf(updatable) === -1) {
|
|
return;
|
|
}
|
|
|
|
var alreadyThere = this.downloadsQueue.some(function lookup(u) {
|
|
return (u.app === updatable.app);
|
|
});
|
|
if (alreadyThere) {
|
|
return;
|
|
}
|
|
|
|
this.downloadsQueue.push(updatable);
|
|
|
|
if (this.downloadsQueue.length === 1) {
|
|
this._downloading = true;
|
|
Service.request('incDownloads');
|
|
this._wifiLock = navigator.requestWakeLock('wifi');
|
|
|
|
this.displayNotificationIfHidden();
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
removeFromDownloadsQueue: function um_removeFromDownloadsQueue(updatable) {
|
|
var removeIndex = this.downloadsQueue.indexOf(updatable);
|
|
if (removeIndex === -1) {
|
|
return;
|
|
}
|
|
|
|
this.downloadsQueue.splice(removeIndex, 1);
|
|
|
|
if (this.downloadsQueue.length === 0) {
|
|
this._downloading = false;
|
|
this._uncompressing = false;
|
|
Service.request('decDownloads');
|
|
this._downloadedBytes = 0;
|
|
this.checkStatuses();
|
|
|
|
if (this._wifiLock) {
|
|
try {
|
|
this._wifiLock.unlock();
|
|
} catch (e) {
|
|
// this can happen if the lock is already unlocked
|
|
console.error('error during unlock', e);
|
|
}
|
|
|
|
this._wifiLock = null;
|
|
}
|
|
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
hideNotificationIfDisplayed: function() {
|
|
if (this.container.classList.contains('displayed')) {
|
|
this.container.classList.remove('displayed');
|
|
NotificationScreen.removeUnreadNotification(this.UPDATE_NOTIF_ID);
|
|
}
|
|
},
|
|
|
|
displayNotificationIfHidden: function() {
|
|
if (!this.container.classList.contains('displayed')) {
|
|
this.container.classList.add('displayed');
|
|
NotificationScreen.addUnreadNotification(this.UPDATE_NOTIF_ID);
|
|
}
|
|
},
|
|
|
|
checkStatuses: function um_checkStatuses() {
|
|
this.updatableApps.forEach(function(updatableApp) {
|
|
var app = updatableApp.app;
|
|
if (app.downloadAvailable) {
|
|
this.addToUpdatesQueue(updatableApp);
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
oninstall: function um_oninstall(evt) {
|
|
var app = evt.application;
|
|
/* jshint nonew: false */
|
|
new AppUpdatable(app);
|
|
},
|
|
|
|
onuninstall: function um_onuninstall(evt) {
|
|
this.updatableApps.some(function appIterator(updatableApp, index) {
|
|
// The application object we get from the event
|
|
// has only origin and manifestURL properties
|
|
if (updatableApp.app.manifestURL === evt.application.manifestURL) {
|
|
this.removeFromAll(updatableApp);
|
|
return true;
|
|
}
|
|
return false;
|
|
}, this);
|
|
},
|
|
|
|
handleEvent: function um_handleEvent(evt) {
|
|
if (!evt.type) {
|
|
return;
|
|
}
|
|
|
|
switch (evt.type) {
|
|
case 'applicationinstall':
|
|
this.oninstall(evt.detail);
|
|
break;
|
|
case 'applicationuninstall':
|
|
this.onuninstall(evt.detail);
|
|
break;
|
|
case 'offline':
|
|
this.updateOnlineStatus();
|
|
break;
|
|
case 'online':
|
|
this.updateOnlineStatus();
|
|
break;
|
|
case 'wifi-statuschange':
|
|
this.updateWifiStatus();
|
|
break;
|
|
case 'lockscreen-appopened':
|
|
if (this.systemUpdatable.showingApplyPrompt) {
|
|
this.systemUpdatable.declineInstallWait();
|
|
}
|
|
this.downloadViaDataConnectionDialog.classList.remove('visible');
|
|
this._closeDownloadDialog();
|
|
// only request while update manager shows a dialog
|
|
if (this._hasDialog) {
|
|
Service.request('hideCustomDialog');
|
|
}
|
|
break;
|
|
case 'home':
|
|
case 'holdhome':
|
|
if (this._hasDialog) {
|
|
this.cancelPrompt();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (evt.type !== 'mozChromeEvent') {
|
|
return;
|
|
}
|
|
|
|
var detail = evt.detail;
|
|
|
|
if (detail.type && detail.type === 'update-available') {
|
|
this.systemUpdatable.size = detail.size;
|
|
this.systemUpdatable.buildID = detail.buildID;
|
|
this.systemUpdatable.detailsURL = detail.detailsURL;
|
|
this.systemUpdatable.nameL10nArgs = { version: detail.displayVersion };
|
|
this.systemUpdatable.rememberKnownUpdate();
|
|
this.addToUpdatesQueue(this.systemUpdatable);
|
|
}
|
|
},
|
|
|
|
updateOnlineStatus: function su_updateOnlineStatus() {
|
|
var online = (navigator && 'onLine' in navigator) ?
|
|
navigator.onLine : true;
|
|
this.downloadDialog.dataset.online = online;
|
|
},
|
|
|
|
_wifiAvailable: function su_wifiAvailable() {
|
|
var wifiManager = window.navigator.mozWifiManager;
|
|
if (!wifiManager) {
|
|
return;
|
|
}
|
|
return wifiManager.connection.status == 'connected';
|
|
},
|
|
|
|
updateWifiStatus: function su_updateWifiStatus() {
|
|
this.downloadDialog.dataset.nowifi = !this._wifiAvailable();
|
|
},
|
|
|
|
checkForUpdates: function su_checkForUpdates(shouldCheck) {
|
|
if (!shouldCheck) {
|
|
return;
|
|
}
|
|
|
|
this._dispatchEvent('force-update-check');
|
|
|
|
if (!this._settings) {
|
|
return;
|
|
}
|
|
|
|
var lock = this._settings.createLock();
|
|
lock.set({
|
|
'gaia.system.checkForUpdates': false
|
|
});
|
|
},
|
|
|
|
_openDownloadViaDataDialog: function um_downloadViaDataDialog() {
|
|
var _ = navigator.mozL10n.setAttributes;
|
|
var connections = window.navigator.mozMobileConnections;
|
|
var dataType;
|
|
var sim;
|
|
|
|
if (!connections) {
|
|
this.showForbiddenDownload();
|
|
return;
|
|
}
|
|
// In DualSim only one of them will have data active
|
|
for (var i = 0; i < connections.length && !dataType; i++) {
|
|
dataType = connections[i].data.type;
|
|
sim = connections[i];
|
|
}
|
|
|
|
if (!dataType) {
|
|
//No connection available
|
|
self.showForbiddenDownload();
|
|
return;
|
|
}
|
|
var dataRoamingSettingPromise = this._getDataRoamingSetting();
|
|
dataRoamingSettingPromise.then(function(roaming) {
|
|
if (roaming && sim.data.roaming) {
|
|
_(this.downloadViaDataConnectionTitle,
|
|
'downloadUpdatesViaDataRoamingConnection');
|
|
_(this.downloadViaDataConnectionMessage,
|
|
'downloadUpdatesViaDataRoamingConnectionMessage');
|
|
window.dispatchEvent(new CustomEvent('updatepromptshown'));
|
|
this.downloadViaDataConnectionDialog.classList.add('visible');
|
|
return;
|
|
}
|
|
this._startedDownloadUsingDataConnection = true;
|
|
this.startDownloads();
|
|
}.bind(this));
|
|
},
|
|
|
|
_getDataRoamingSetting: function um_getDataRoamingSetting() {
|
|
var lock = this._settings.createLock();
|
|
var reqDataRoaming = lock.get(this.ROAMING_SETTING_KEY);
|
|
var dataRoamingSettingPromise;
|
|
var self = this;
|
|
|
|
dataRoamingSettingPromise = new Promise(function(resolve, reject) {
|
|
reqDataRoaming.onsuccess = function() {
|
|
resolve(reqDataRoaming.result[self.ROAMING_SETTING_KEY]);
|
|
};
|
|
|
|
reqDataRoaming.onerror = function() {
|
|
resolve(false);
|
|
};
|
|
});
|
|
|
|
return dataRoamingSettingPromise;
|
|
},
|
|
|
|
_dispatchEvent: function um_dispatchEvent(type, result) {
|
|
var event = document.createEvent('CustomEvent');
|
|
var data = { type: type };
|
|
if (result) {
|
|
data.result = result;
|
|
}
|
|
|
|
event.initCustomEvent('mozContentEvent', true, true, data);
|
|
window.dispatchEvent(event);
|
|
},
|
|
|
|
// This is going to be part of l10n.js
|
|
_humanizeSize: function um_humanizeSize(bytes) {
|
|
var _ = navigator.mozL10n.get;
|
|
var units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
|
|
|
if (!bytes) {
|
|
return '0.00 ' + _(units[0]);
|
|
}
|
|
|
|
var e = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2) + ' ' +
|
|
_(units[e]);
|
|
},
|
|
|
|
_closeDownloadDialog: function um_closeDownloadDialog() {
|
|
window.dispatchEvent(new CustomEvent('updateprompthidden'));
|
|
this.downloadDialog.classList.remove('visible');
|
|
}
|
|
};
|
|
exports.UpdateManager = UpdateManager;
|
|
}(window));
|