Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
456 lines
14 KiB
JavaScript
Executable file
456 lines
14 KiB
JavaScript
Executable file
/* global _, debug, ConfigManager, Toolkit, SimManager */
|
|
/* exported addAlarmTimeout, setNextReset, addNetworkUsageAlarm,
|
|
getTopUpTimeout, Common, sendBalanceThresholdNotification
|
|
*/
|
|
'use strict';
|
|
|
|
function addAlarmTimeout(type, delay) {
|
|
var handlerContainer = document.getElementById('message-handler');
|
|
return handlerContainer.contentWindow.addAlarmTimeout(type, delay);
|
|
}
|
|
|
|
function setNextReset(when, callback) {
|
|
var handlerContainer = document.getElementById('message-handler');
|
|
return handlerContainer ?
|
|
handlerContainer.contentWindow.setNextReset(when, callback) :
|
|
window.setNextReset(when, callback);
|
|
}
|
|
|
|
function getTopUpTimeout(callback) {
|
|
var handlerContainer = document.getElementById('message-handler');
|
|
return handlerContainer ?
|
|
handlerContainer.contentWindow.getTopUpTimeout(callback) :
|
|
window.getTopUpTimeout(callback);
|
|
}
|
|
|
|
function addNetworkUsageAlarm(dataInterface, dataLimit, onsuccess, onerror) {
|
|
var handlerContainer = document.getElementById('message-handler');
|
|
if (handlerContainer) {
|
|
handlerContainer.contentWindow
|
|
.addNetworkUsageAlarm(dataInterface, dataLimit, onsuccess, onerror);
|
|
} else {
|
|
window.addNetworkUsageAlarm(dataInterface, dataLimit, onsuccess, onerror);
|
|
}
|
|
}
|
|
|
|
function sendBalanceThresholdNotification(remaining, settings, callback) {
|
|
var handlerContainer = document.getElementById('message-handler');
|
|
if (handlerContainer) {
|
|
handlerContainer.contentWindow
|
|
.sendBalanceThresholdNotification(remaining, settings, callback);
|
|
}
|
|
}
|
|
|
|
function resetTelephony(callback) {
|
|
ConfigManager.setOption({
|
|
lastTelephonyReset: new Date(),
|
|
lastTelephonyActivity: {
|
|
calltime: 0,
|
|
smscount: 0,
|
|
timestamp: new Date()
|
|
}
|
|
}, callback);
|
|
}
|
|
|
|
var Common = {
|
|
|
|
COST_CONTROL_APP: 'app://costcontrol.gaiamobile.org',
|
|
|
|
DATA_USAGE_WARNING: 0.8,
|
|
|
|
allApps: null,
|
|
|
|
allAppsLoaded: false,
|
|
|
|
allNetworkInterfaces: null,
|
|
|
|
allNetworkInterfaceLoaded: false,
|
|
//XXX: Group of apps, whose traffic will be added to the system app.
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1079609
|
|
specialApps: [
|
|
'app://search.gaiamobile.org/manifest.webapp'
|
|
],
|
|
|
|
SYSTEM_MANIFEST: 'app://system.gaiamobile.org/manifest.webapp',
|
|
|
|
BROWSER_APP: {
|
|
manifestURL: 'app://browser.gaiamobile.org/manifest.webapp',
|
|
origin: '',
|
|
manifest: {
|
|
icons: {
|
|
'84': '/shared/resources/branding/browser_84.png',
|
|
'126': '/shared/resources/branding/browser_126.png',
|
|
'142': '/shared/resources/branding/browser_142.png',
|
|
'189': '/shared/resources/branding/browser_189.png',
|
|
'284': '/shared/resources/branding/browser_284.png'
|
|
},
|
|
name: 'browser'
|
|
}
|
|
},
|
|
|
|
startFTE: function(mode) {
|
|
var iframe = document.getElementById('fte_view');
|
|
|
|
window.addEventListener('message', function handler(e) {
|
|
if (e.origin !== Common.COST_CONTROL_APP) {
|
|
return;
|
|
}
|
|
|
|
if (e.data.type === 'fte_ready') {
|
|
window.removeEventListener('message', handler);
|
|
|
|
iframe.classList.remove('non-ready');
|
|
|
|
// PERFORMANCE MARKERS
|
|
// Designates that the app's *core* chrome or navigation interface
|
|
// exists in the DOM and is marked as ready to be displayed.
|
|
window.performance.mark('navigationLoaded');
|
|
|
|
// Designates that the app's *core* chrome or navigation interface
|
|
// has its events bound and is ready for user interaction.
|
|
window.performance.mark('navigationInteractive');
|
|
|
|
// Designates that the app is visually loaded (e.g.: all of the
|
|
// "above-the-fold" content exists in the DOM and is marked as
|
|
// ready to be displayed).
|
|
window.performance.mark('visuallyLoaded');
|
|
|
|
// Designates that the app has its events bound for the minimum
|
|
// set of functionality to allow the user to interact with the
|
|
// "above-the-fold" content.
|
|
window.performance.mark('contentInteractive');
|
|
|
|
// Start up ended when FTE ready
|
|
window.performance.mark('fullyLoaded');
|
|
}
|
|
});
|
|
|
|
iframe.src = '/fte.html' + '#' + mode;
|
|
},
|
|
|
|
closeFTE: function() {
|
|
var iframe = window.parent.document.getElementById('fte_view');
|
|
iframe.classList.add('non-ready');
|
|
iframe.src = '';
|
|
},
|
|
|
|
startApp: function() {
|
|
parent.postMessage({
|
|
type: 'fte_finished',
|
|
data: ''
|
|
}, Common.COST_CONTROL_APP);
|
|
},
|
|
|
|
closeApplication: function() {
|
|
return setTimeout(function _close() {
|
|
debug('Closing.');
|
|
window.close();
|
|
});
|
|
},
|
|
|
|
modalAlert: function(message) {
|
|
alert(message);
|
|
},
|
|
|
|
get localize() {
|
|
return navigator.mozL10n.setAttributes;
|
|
},
|
|
|
|
// Returns whether exists an nsIDOMNetworkStatsInterfaces object
|
|
// that meet the argument function criteria
|
|
getInterface: function getInterface(findFunction) {
|
|
if (!Common.allNetworkInterfaceLoaded) {
|
|
debug('Network interfaces are not ready yet');
|
|
var header = _('data-usage');
|
|
var msg = _('loading-interface-data');
|
|
this.modalAlert(header + '\n' + msg);
|
|
return;
|
|
}
|
|
|
|
if (Common.allNetworkInterfaces) {
|
|
return Common.allNetworkInterfaces.find(findFunction);
|
|
}
|
|
},
|
|
|
|
getDataSIMInterface: function _getDataSIMInterface(iccId) {
|
|
if (!iccId) {
|
|
console.warn('Undefined icc identifier, unable get data interface');
|
|
return;
|
|
}
|
|
if (iccId) {
|
|
var findCurrentInterface = function(networkInterface) {
|
|
if (networkInterface.id === iccId) {
|
|
return networkInterface;
|
|
}
|
|
};
|
|
return this.getInterface(findCurrentInterface);
|
|
}
|
|
return undefined;
|
|
},
|
|
|
|
getWifiInterface: function _getWifiInterface() {
|
|
var findWifiInterface = function(networkInterface) {
|
|
if (networkInterface.type === navigator.mozNetworkStats.WIFI) {
|
|
return networkInterface;
|
|
}
|
|
};
|
|
return this.getInterface(findWifiInterface);
|
|
},
|
|
|
|
loadApps: function() {
|
|
return new Promise(function(resolve, reject) {
|
|
if (Common.allAppsLoaded) {
|
|
resolve(Common.allApps);
|
|
return;
|
|
}
|
|
|
|
var request = window.navigator.mozApps.mgmt.getAll();
|
|
request.onsuccess = function(event) {
|
|
var appList = event.target.result;
|
|
// XXX : The data traffic of the filtered apps will be automatically
|
|
// counted as residual data. This traffic is added to the system app
|
|
// on the drawApps method located on "js/views/datausage.js"
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1084010#c0
|
|
Common.allApps = appList.filter(function(app) {
|
|
return Common.specialApps.indexOf(app.manifestURL) === -1;
|
|
});
|
|
Common.allApps.push(Common.BROWSER_APP);
|
|
Common.allAppsLoaded = true;
|
|
resolve(Common.allApps);
|
|
};
|
|
|
|
request.onerror = function() {
|
|
reject(new Error('Apps could not be loaded'));
|
|
};
|
|
});
|
|
},
|
|
|
|
loadNetworkInterfaces: function(onsuccess, onerror) {
|
|
var networks = navigator.mozNetworkStats.getAvailableNetworks();
|
|
|
|
networks.onsuccess = function() {
|
|
Common.allNetworkInterfaces = networks.result;
|
|
Common.allNetworkInterfaceLoaded = true;
|
|
if (onsuccess) {
|
|
onsuccess();
|
|
}
|
|
};
|
|
|
|
networks.onerror = function() {
|
|
console.error('Error when trying to load network interfaces');
|
|
if (onerror) {
|
|
onerror();
|
|
}
|
|
};
|
|
},
|
|
|
|
getApp: function(manifestURL) {
|
|
return this.allApps.find(function(app) {
|
|
return app.manifestURL === manifestURL;
|
|
});
|
|
},
|
|
|
|
getAppManifest: function(app) {
|
|
return app.manifest || app.updateManifest;
|
|
},
|
|
|
|
getLocalizedAppName: function(app) {
|
|
// If is System App returns label others
|
|
if (app.manifestURL === Common.SYSTEM_MANIFEST) {
|
|
return _('data-usage-other-apps');
|
|
}
|
|
// Browser app does not exist, we have to provide the localized app name
|
|
if (app.manifestURL === Common.BROWSER_APP.manifestURL) {
|
|
return _('data-usage-browser-app');
|
|
}
|
|
var manifest = this.getAppManifest(app);
|
|
var userLang = document.documentElement.lang;
|
|
var locales = manifest.locales;
|
|
var localized = locales && locales[userLang] && locales[userLang].name;
|
|
|
|
return localized || manifest.name;
|
|
},
|
|
|
|
getAppIcon: function(app) {
|
|
var manifest = this.getAppManifest(app);
|
|
var icons = manifest.icons;
|
|
var defaultImage = '../style/images/app/icons/default.png';
|
|
|
|
if (!icons || !Object.keys(icons).length) {
|
|
return defaultImage;
|
|
}
|
|
|
|
// The preferred size is 30 by the default. If we use HDPI device, we may
|
|
// use the image larger than 30 * 1.5 = 45 pixels.
|
|
var preferredIconSize = 30 * (window.devicePixelRatio || 1);
|
|
var preferredSize = Number.MAX_VALUE;
|
|
var max = 0;
|
|
|
|
for (var size in icons) {
|
|
size = parseInt(size, 10);
|
|
if (size > max) {
|
|
max = size;
|
|
}
|
|
|
|
if (size >= preferredIconSize && size < preferredSize) {
|
|
preferredSize = size;
|
|
}
|
|
}
|
|
// If there is an icon matching the preferred size, we return the result,
|
|
// if there isn't, we will return the maximum available size.
|
|
if (preferredSize === Number.MAX_VALUE) {
|
|
preferredSize = max;
|
|
}
|
|
|
|
var url = icons[preferredSize];
|
|
if (url) {
|
|
return !(/^(http|https|data):/.test(url)) ? app.origin + url : url;
|
|
} else {
|
|
return defaultImage;
|
|
}
|
|
},
|
|
|
|
getDataLimit: function _getDataLimit(settings) {
|
|
var multiplier = (settings.dataLimitUnit === 'MB') ?
|
|
1000000 : 1000000000;
|
|
return settings.dataLimitValue * multiplier;
|
|
},
|
|
|
|
resetData: function _resetData(mode, onsuccess, onerror) {
|
|
// Get all availabe Interfaces
|
|
var wifiInterface = Common.getWifiInterface();
|
|
|
|
// Ask reset for all available Interfaces
|
|
var wifiClearRequest, mobileClearRequest;
|
|
|
|
// onerror callback builder
|
|
var getOnErrorFor = function(networkInterface) {
|
|
return function() {
|
|
if (wifiClearRequest) {
|
|
wifiClearRequest.onerror = undefined;
|
|
}
|
|
if (mobileClearRequest) {
|
|
mobileClearRequest.onerror = undefined;
|
|
}
|
|
(typeof onerror === 'function') && onerror(networkInterface);
|
|
};
|
|
};
|
|
if ((mode === 'all' || mode === 'wifi') && wifiInterface) {
|
|
wifiClearRequest = navigator.mozNetworkStats.clearStats(wifiInterface);
|
|
wifiClearRequest.onerror = getOnErrorFor('wi-Fi');
|
|
}
|
|
if (mode === 'all' || mode === 'mobile') {
|
|
SimManager.requestDataSimIcc(function(dataSim) {
|
|
var currentSimcardInterface = Common.getDataSIMInterface(dataSim.iccId);
|
|
if (currentSimcardInterface) {
|
|
mobileClearRequest = navigator.mozNetworkStats
|
|
.clearStats(currentSimcardInterface);
|
|
mobileClearRequest.onerror = getOnErrorFor('simcard');
|
|
mobileClearRequest.onsuccess = function _restoreDataLimitAlarm() {
|
|
ConfigManager.requestSettings(dataSim.iccId,
|
|
function _onSettings(settings) {
|
|
if (settings.dataLimit) {
|
|
// Restore network alarm
|
|
addNetworkUsageAlarm(currentSimcardInterface,
|
|
Common.getDataLimit(settings),
|
|
function _addNetworkUsageAlarmOK() {
|
|
ConfigManager.setOption({ 'dataUsageNotified': false });
|
|
});
|
|
}
|
|
});
|
|
};
|
|
}
|
|
});
|
|
}
|
|
// Set last Reset
|
|
if (mode === 'all') {
|
|
ConfigManager.setOption({ lastCompleteDataReset: new Date() });
|
|
} else {
|
|
// Else clausure prevents running the update event twice
|
|
ConfigManager.setOption({ lastDataReset: new Date() });
|
|
}
|
|
|
|
// call onsuccess
|
|
if (typeof onsuccess === 'function') {
|
|
onsuccess();
|
|
}
|
|
},
|
|
|
|
resetAll: function _resetAll(callback) {
|
|
function logResetDataError(networkInterface) {
|
|
console.log('Error when trying to reset ' + networkInterface +
|
|
' interface');
|
|
}
|
|
|
|
Common.resetData('all', thenResetTelephony, logResetDataError);
|
|
|
|
function thenResetTelephony() {
|
|
resetTelephony(callback);
|
|
}
|
|
},
|
|
|
|
// Next automatic reset date based on user preferences
|
|
updateNextReset: function _updateNextReset(trackingPeriod, value, callback) {
|
|
if (trackingPeriod === 'never') {
|
|
setNextReset(null, callback); // remove any alarm
|
|
return;
|
|
}
|
|
|
|
var nextReset, today = new Date();
|
|
|
|
// Recalculate with month period
|
|
if (trackingPeriod === 'monthly') {
|
|
var month, year;
|
|
var monthday = parseInt(value, 10);
|
|
month = today.getMonth();
|
|
year = today.getFullYear();
|
|
if (today.getDate() >= monthday) {
|
|
month = (month + 1) % 12;
|
|
if (month === 0) {
|
|
year++;
|
|
}
|
|
}
|
|
nextReset = new Date(year, month, monthday);
|
|
if (monthday !== nextReset.getDate()) {
|
|
var LAST_DAY_OF_PREVIOUS_MONTH = 0;
|
|
// If monthday is not equal to nextReset day, it means that the selected
|
|
// reset day does not exist (e.g. 30 Feb). In this case, the reset day
|
|
// must be the last day of the previous month
|
|
nextReset.setDate(LAST_DAY_OF_PREVIOUS_MONTH);
|
|
}
|
|
|
|
// Recalculate with week period
|
|
} else if (trackingPeriod === 'weekly') {
|
|
var oneDay = 24 * 60 * 60 * 1000;
|
|
var weekday = parseInt(value, 10);
|
|
var daysToTarget = weekday - today.getDay();
|
|
if (daysToTarget <= 0) {
|
|
daysToTarget = 7 + daysToTarget;
|
|
}
|
|
nextReset = new Date();
|
|
nextReset.setTime(nextReset.getTime() + oneDay * daysToTarget);
|
|
Toolkit.toMidnight(nextReset);
|
|
}
|
|
|
|
// remove oldAlarm and set the new one
|
|
setNextReset(nextReset, callback);
|
|
},
|
|
|
|
localizeWeekdaySelector: function _localizeWeekdaySelector(selector) {
|
|
var weekStartsOnMonday =
|
|
parseInt(navigator.mozL10n.get('firstDayOfTheWeek'), 10) === 1;
|
|
debug('Week starts on monday?', weekStartsOnMonday);
|
|
var monday = selector.querySelector('.monday');
|
|
var sunday = selector.querySelector('.sunday');
|
|
var list = monday.parentNode;
|
|
if (weekStartsOnMonday) {
|
|
debug('Monday, Tuesday...');
|
|
list.insertBefore(monday, list.childNodes[0]); // monday is the first
|
|
list.appendChild(sunday); // sunday is the last
|
|
} else {
|
|
debug('Sunday, Monday...');
|
|
list.insertBefore(sunday, list.childNodes[0]); // sunday is the first
|
|
list.insertBefore(monday, sunday.nextSibling); // monday is the second
|
|
}
|
|
}
|
|
};
|