/* globals SIMSlotManager, Notification, NotificationHelper, MozActivity, Promise, LazyLoader, BaseModule */ 'use strict'; (function() { var EU_ROAMING_FILE_PATH = '/resources/eu-roaming.json'; /** * EuRoamingManager observes the mobile codes of the current connected * operator and display a notification when necessary. * * @class EuRoamingManager */ function EuRoamingManager(core) { this._connections = Array.slice(core.mobileConnections || []); this._simMobileCodes = []; this._curNetworkMobileCodes = []; this._homeOperatorList = null; this._foreignOperatorList = null; } BaseModule.create(EuRoamingManager, { name: 'EuRoamingManager', TAG_PREFIX: 'euRoamingNotificaton', EU_ROAMING_ENABLED_KEY: 'eu-roaming.enabled', EU_ROAMING_NOTIFICATION_STATE_KEY: 'eu-roaming.notification.state', NOTIFICATION_STATES: { UNAVAILABLE: 'unavailable', DISPLAYED: 'displayed', OPENED: 'opened' }, /** * Starts the module. * * @memberof EuRoamingManager.prototype */ _start: function() { // Clear existing notifications. this._clearNotifications(); this._init(); }, /** * Load the EU roaming file and register related listeners when necessary. * * @memberof EuRoamingManager.prototype * @returns {Promise} */ _init: function() { var that = this; return this._loadJSON(EU_ROAMING_FILE_PATH) .then(function(result) { if (!result) { return; } that._homeOperatorList = result.home; that._foreignOperatorList = result.foreign; if (that._homeOperatorList && that._foreignOperatorList && Object.keys(that._homeOperatorList).length > 0 && Object.keys(that._foreignOperatorList).length > 0) { that._initValues(); that._addSimChangeListener(); that._addDataChangeListener(); // Check the current state ar first. that._connections.forEach( that._tryShowEuRoamingNotification.bind(that)); } }); }, /** * Initialzes values. * * @memberof EuRoamingManager.prototype */ _initValues: function() { // Initialize the default values. SIMSlotManager.getSlots().forEach(function(simSlot, index) { this._curNetworkMobileCodes.push(null); var iccInfo = simSlot && simSlot.simCard && simSlot.simCard.iccInfo; if (iccInfo) { this._simMobileCodes.push({ mcc: iccInfo.mcc, mnc: iccInfo.mnc }); } else { this._simMobileCodes.push(null); } }, this); }, /** * Check if the mobile codes is a valid EU romaing home operator. * * @memberof EuRoamingManager.prototype * @param {String} mcc * @param {String} mnc * @returns {Boolean} */ _isEURoamingHomeOperator: function(mcc, mnc) { if (!this._homeOperatorList || !mcc || !mnc) { return false; } return !!this._homeOperatorList[mcc][mnc]; }, /** * Check if the mobile codes is a valid EU romaing foreign operator. * * @memberof EuRoamingManager.prototype * @param {String} mcc * @param {String} mnc * @returns {Boolean} */ _isEURoamingForeignOperator: function(mcc, mnc) { if (!this._foreignOperatorList || !mcc || !mnc) { return false; } return !!this._foreignOperatorList[mcc][mnc]; }, /** * Add listeners on events related to the change of the mobile codes stored * on the icc card. * * @memberof EuRoamingManager.prototype */ _addSimChangeListener: function() { ['updated', 'iccinfochange', 'cardstatechange'].forEach( function(eventName) { window.addEventListener('simslot-' + eventName, this._onSimChanged.bind(this)); }, this); }, /** * Add listeners on events related to the change of the mobile codes of the * currently connected data network. * * @memberof EuRoamingManager.prototype */ _addDataChangeListener: function() { this._connections.forEach(function(connection, index) { connection.addEventListener('datachange', function() { this._tryShowEuRoamingNotification(connection, index); }.bind(this)); }, this); }, /** * The function stores the mobile codes of the currently used sim card and * try to display the notification. * * @memberof EuRoamingManager.prototype */ _onSimChanged: function(event) { var simSlot = event.detail; var iccInfo = simSlot && simSlot.simCard && simSlot.simCard.iccInfo; if (!iccInfo) { return; } this._simMobileCodes[simSlot.index] = { mcc: iccInfo.mcc, mnc: iccInfo.mnc }; this._tryShowEuRoamingNotification(simSlot.conn, simSlot.index); }, /** * The function displays the EU roaming notification when: * - Roaming on the newly connected data network. * - The mobile codes of the operators of the sim card are listed in the * "home" section of the EU roaming file. * - The mobile codes of the operators of the connected data network are * listed in the "foreign" section of the EU roaming file. * * @memberof EuRoamingManager.prototype * @param {MobileConnection} connection * @param {Number} index * @returns {Promise} */ _tryShowEuRoamingNotification: function(connection, index) { var data = connection.data; var network = data && data.network; if (!network) { return; } var simMobileCode = this._simMobileCodes[index]; var simMcc = simMobileCode && simMobileCode.mcc; var simMnc = simMobileCode && simMobileCode.mnc; var curNetworkMobileCode = this._curNetworkMobileCodes[index]; var curNetworkMcc = curNetworkMobileCode && curNetworkMobileCode.mcc; var curNetworkMnc = curNetworkMobileCode && curNetworkMobileCode.mnc; if (network.mcc !== curNetworkMcc || network.mnc !== curNetworkMnc) { // a new operator detected. this._curNetworkMobileCodes[index] = { mcc: network.mcc, mnc: network.mnc }; if (data.roaming && // check if the operator of the sim is in the EU regulation list. this._isEURoamingHomeOperator(simMcc, simMnc) && // check if the new operator is a supported foreign operator or not. this._isEURoamingForeignOperator(network.mcc, network.mnc)) { return this._getState(this.EU_ROAMING_NOTIFICATION_STATE_KEY + index) .then(function(notificationState) { this._setState(this.EU_ROAMING_ENABLED_KEY + index, true); // display the notification if it has never been opened before. if (notificationState !== this.NOTIFICATION_STATES.OPENED) { this._showNotification(index); return this._setState( this.EU_ROAMING_NOTIFICATION_STATE_KEY + index, this.NOTIFICATION_STATES.DISPLAYED); } }.bind(this)).catch(function(err) { console.error(err); }); } else { return Promise.all([ this._setState(this.EU_ROAMING_ENABLED_KEY + index, false), this._setState(this.EU_ROAMING_NOTIFICATION_STATE_KEY + index, this.NOTIFICATION_STATES.UNAVAILABLE) ]).catch(function(err) { console.error(err); }); } } }, /** * Clear all notifications of EU roaming. * * @memberof EuRoamingManager.prototype * @returns {Promise} */ _clearNotifications: function() { var that = this; return Notification.get().then(function(notifications) { notifications.forEach(function(notification) { var tag = notification && notification.tag; if (!tag || !tag.startsWith(that.TAG_PREFIX)) { return; } notification.close(); }); }); }, /** * Shows the EU roaming notification of a sim card. * * @memberof EuRoamingManager.prototype * @param {Number} serviceId */ _showNotification: function(serviceId) { var iconUrl = window.location.origin + '/style/eu_roaming_manager/' + 'eu_roaming.png'; var options = { bodyL10n: 'euRoamingNotificationMsg', icon: iconUrl, tag: this.TAG_PREFIX + serviceId, mozbehavior: { showOnlyOnce: true } }; return NotificationHelper.send('euRoamingNotificationTitle', options).then( notification => { notification.onclick = function() { this._triggerSettingsActivity(serviceId); }.bind(this); notification.onclose = function() { this._setState(this.EU_ROAMING_NOTIFICATION_STATE_KEY + serviceId, this.NOTIFICATION_STATES.OPENED); }; return notification; }); }, /** * Invokes an activity for setting the EU roaming apn. * * @memberof EuRoamingManager.prototype * @param {Number} serviceId */ _triggerSettingsActivity: function(serviceId) { var activity = new MozActivity({ name: 'configure', data: { target: 'device', section: 'apn-list', options: { role: 'activity', type: 'default', serviceId: serviceId } } }); activity.onsuccess = function() {}; }, _getState: function(key) { return new Promise(function(resolve, reject) { var req = navigator.mozSettings.createLock().get(key); req.onsuccess = function() { resolve(req.result[key]); }; req.onerror = function() { reject('get state ' + key + ' error'); }; }); }, _setState: function(key, state) { return new Promise(function(resolve, reject) { var obj = {}; obj[key] = state; var req = navigator.mozSettings.createLock().set(obj); req.onsuccess = resolve; req.onerror = function() { reject('set state ' + key + ' error'); }; }); }, _loadJSON: function(path) { return LazyLoader.getJSON(path).then(function(json) { return Promise.resolve(json); }, function(error) { return Promise.resolve(null); }); } }); }());