diff --git a/apps/settings/index.html b/apps/settings/index.html index 1b369b1d444f..7a6269e27f9b 100644 --- a/apps/settings/index.html +++ b/apps/settings/index.html @@ -71,7 +71,6 @@ - diff --git a/apps/settings/js/airplane_mode.js b/apps/settings/js/airplane_mode.js index 28935b6e6dea..38bec712677f 100644 --- a/apps/settings/js/airplane_mode.js +++ b/apps/settings/js/airplane_mode.js @@ -10,37 +10,250 @@ var AirplaneMode = { - element: null, + /** + * Counter for operstions before radiosafe event + * Updated when we toggle radio mode + * @private + */ + _ops: 0, - init: function apm_init() { - var self = this; - var settings = window.navigator.mozSettings; + /** + * Whether or not we are firing the event for airplane mode + * @private + */ + _doNotify: false, - if (!settings) { + element: document.getElementById('airplaneMode-input'), + + /** + * Enable the radio state + */ + enableRadioSwitch: function() { + this.element.disabled = false; + }, + + /** + * Notifies apps that components are in a stable state + * This waits until all components are enabled after changing airplane mode + * @param {String} the setting name. + */ + notify: function(name) { + if (!this._doNotify) return; + + this._ops--; + if (this._ops === 0) { + this._doNotify = false; + this.enableRadioSwitch(); } + }, + + _initRadioSwitch: function() { + var mobileConnections = window.navigator.mozMobileConnections; + var self = this; + var isError = false; + var setCount = 0; + + var setRadioAfterReqsCalled = function(enabled) { + if (setCount !== mobileConnections.length) { + return; + } else { + if (isError) { + setAirplaneModeEnabled(enabled); + } else { + SettingsListener.getSettingsLock().set( + {'ril.radio.disabled': !enabled} + ); + } + } + }; + + var doSetRadioEnabled = function doSetRadioEnabled(i, enabled) { + var conn = mobileConnections[i]; + var req = conn.setRadioEnabled(enabled); + setCount++; + + req.onsuccess = function() { + setRadioAfterReqsCalled(enabled); + }; + req.onerror = function() { + isError = true; + setRadioAfterReqsCalled(enabled); + }; + }; - this.element = document.getElementById('airplaneMode-input'); + var setRadioEnabled = function setRadioEnabled(i, enabled) { + var conn = mobileConnections[i]; + if (conn.radioState !== 'enabling' && + conn.radioState !== 'disabling' && + conn.radioState !== null) { + doSetRadioEnabled(i, enabled); + } else { + conn.addEventListener('radiostatechange', + function radioStateChangeHandler() { + if (conn.radioState == 'enabling' || + conn.radioState == 'disabling' || + conn.radioState == null) { + return; + } + conn.removeEventListener('radiostatechange', + radioStateChangeHandler); + doSetRadioEnabled(i, enabled); + }); + } + }; + + var setAirplaneModeEnabled = function setAirplaneModeEnabled(enabled) { + isError = false; + setCount = 0; + // set airplane mode `true` + // means setRadioEnabled `false` + enabled = !enabled; + if (mobileConnections.length == 1) { + setRadioEnabled(0, enabled); + } else { + setRadioEnabled(0, enabled); + setRadioEnabled(1, enabled); + } + }; + + SettingsListener.observe('ril.radio.disabled', false, function(value) { + self.element.disabled = false; + self.element.checked = value; + }); - // handle change on radio this.element.addEventListener('change', function(e) { this.disabled = true; - AirplaneModeHelper.setEnabled(this.checked); + setAirplaneModeEnabled(this.checked); }); + }, - // initial status - var status = AirplaneModeHelper.getStatus(); - this.element.checked = (status === 'enabled') ? true : false; - this.element.disabled = false; + init: function apm_init() { + var mobileConnection = getMobileConnection(); + var wifiManager = WifiHelper.getWifiManager(); + var nfcManager = getNfc(); + + var settings = Settings.mozSettings; + if (!settings) + return; + + var self = this; + this._initRadioSwitch(); + + var mobileDataEnabled = false; + settings.addObserver('ril.data.enabled', function(e) { + mobileDataEnabled = e.settingValue; + self.notify('ril.data.enabled'); + }); + + var bluetoothEnabled = false; + var wifiEnabled = false; + var geolocationEnabled = false; + var nfcEnabled = false; + settings.addObserver('geolocation.enabled', function(e) { + geolocationEnabled = e.settingValue; + self.notify('geolocation.enabled'); + }); + + // when wifi is really enabled, notify if needed + window.addEventListener('wifi-enabled', function() { + wifiEnabled = true; + self.notify('wifi.enabled'); + }); + + // when wifi is really disabled, notify if needed + window.addEventListener('wifi-disabled', function() { + wifiEnabled = false; + self.notify('wifi.enabled'); + }); + + if (window.gBluetooth) { + // when bluetooth is really enabled, notify if needed + window.addEventListener('bluetooth-adapter-added', function() { + bluetoothEnabled = true; + self.notify('bluetooth.enabled'); + }); + + // when bluetooth is really disabled, notify if needed + window.addEventListener('bluetooth-disabled', function() { + bluetoothEnabled = false; + self.notify('bluetooth.enabled'); + }); + } + settings.addObserver('nfc.enabled', function(e) { + nfcEnabled = e.settingValue; + self.notify('nfc.enabled'); + }); + + + var restoreMobileData = false; + var restoreBluetooth = false; + var restoreWifi = false; + var restoreGeolocation = false; + var restoreNfc = false; + + settings.addObserver('ril.radio.disabled', function(e) { + // Reset notification params + self._ops = 0; + self._doNotify = true; + + if (e.settingValue) { + if (mobileConnection) { + restoreMobileData = mobileDataEnabled; + if (mobileDataEnabled) + self._ops++; + } + + // Bluetooth. + if (window.gBluetooth) { + restoreBluetooth = bluetoothEnabled; + if (bluetoothEnabled) + self._ops++; + } + + // Wifi. + if (wifiManager) { + restoreWifi = wifiEnabled; + if (wifiEnabled) + self._ops++; + } + + // Geolocation + restoreGeolocation = geolocationEnabled; + if (geolocationEnabled) + self._ops++; + + // NFC + restoreNfc = nfcEnabled; + if (nfcManager) { + self._ops++; + } - // handle transition - AirplaneModeHelper.addEventListener('statechange', function(status) { - if (status === 'enabled' || status === 'disabled') { - self.element.checked = (status === 'enabled') ? true : false; - self.element.disabled = false; } else { - self.element.disabled = true; + // Don't count mobile data if it's already on + if (mobileConnection && !mobileDataEnabled && restoreMobileData) + self._ops++; + + // Don't count Bluetooth if it's already on + if (window.gBluetooth && !gBluetooth.enabled && restoreBluetooth) + self._ops++; + + // Don't count Wifi if it's already on + if (wifiManager && !wifiManager.enabled && restoreWifi) + self._ops++; + + // Don't count Geolocation if it's already on + if (!geolocationEnabled && restoreGeolocation) + self._ops++; + + // Don't count NFC if it's already on + if (nfcManager && !nfcEnabled && restoreNfc) + self._ops++; } + + // If we have zero operations to perform, enable the radio switch + if (self._ops === 0) + self.enableRadioSwitch(); }); } }; diff --git a/apps/settings/js/settings.js b/apps/settings/js/settings.js index 3bede739f16a..78916c9995aa 100644 --- a/apps/settings/js/settings.js +++ b/apps/settings/js/settings.js @@ -803,7 +803,6 @@ window.addEventListener('load', function loadSettings() { LazyLoader.load(['shared/js/wifi_helper.js'], displayDefaultPanel); LazyLoader.load([ - 'shared/js/airplane_mode_helper.js', 'js/airplane_mode.js', 'js/battery.js', 'shared/js/async_storage.js', diff --git a/apps/settings/test/unit/airplane_mode_helper_test.js b/apps/settings/test/unit/airplane_mode_helper_test.js deleted file mode 100644 index 2e5a2388fe52..000000000000 --- a/apps/settings/test/unit/airplane_mode_helper_test.js +++ /dev/null @@ -1,184 +0,0 @@ -/* global MockNavigatorSettings, mocha, AirplaneModeHelper - */ -'use strict'; - -requireApp( - 'settings/shared/test/unit/mocks/mock_navigator_moz_settings.js'); - -mocha.globals(['AirplaneModeHelper']); - -suite('AirplaneModeHelper > ', function() { - var realMozSettings; - var kEventName = 'statechange'; - var kCommunicationKey = 'airplaneMode.enabled'; - var kStatusKey = 'airplaneMode.status'; - - suiteSetup(function() { - realMozSettings = window.navigator.mozSettings; - window.navigator.mozSettings = MockNavigatorSettings; - }); - - suiteTeardown(function() { - window.navigator.mozSettings = realMozSettings; - }); - - suite('init > ', function() { - setup(function(done) { - this.sinon.spy(navigator.mozSettings, 'addObserver'); - require('/shared/js/airplane_mode_helper.js', done); - }); - - test('we added observers on ' + kStatusKey, function() { - assert.isFunction(navigator.mozSettings.mObservers[kStatusKey][0]); - }); - }); - - suite('getStatus > ', function() { - var fakeStatus; - setup(function() { - fakeStatus = 'disabling'; - AirplaneModeHelper._cachedStatus = fakeStatus; - }); - test('getCurrentStatus returns right status', function() { - assert.equal(AirplaneModeHelper.getStatus(), fakeStatus); - }); - }); - - suite('addEventListener > ', function() { - var fakeEventName = 'thisIsFakeEventName'; - var fakeCallback = function() {}; - - setup(function() { - AirplaneModeHelper._callbacks = []; - }); - - suite('add event with wrong keyword', function() { - setup(function() { - AirplaneModeHelper.addEventListener(fakeEventName, fakeCallback); - }); - test('can\'t add callbacks', function() { - assert.equal(AirplaneModeHelper._callbacks.length, 0); - }); - }); - - suite('add event with right keyword', function() { - setup(function() { - AirplaneModeHelper.addEventListener(kEventName, fakeCallback); - }); - test('can add callbacks', function() { - assert.equal(AirplaneModeHelper._callbacks.length, 1); - }); - }); - }); - - suite('removeEventListener > ', function() { - var fakeEventName = 'thisIsFakeEventName'; - var fakeCallback = function() {}; - - setup(function() { - AirplaneModeHelper._callbacks = [fakeCallback]; - }); - - suite('remove event with wrong keyword', function() { - setup(function() { - AirplaneModeHelper.removeEventListener(fakeEventName, fakeCallback); - }); - test('can\'t remove callbacks', function() { - assert.equal(AirplaneModeHelper._callbacks.length, 1); - }); - }); - - suite('remove event with right keyword', function() { - setup(function() { - AirplaneModeHelper.removeEventListener(kEventName, fakeCallback); - }); - test('can remove callbacks', function() { - assert.equal(AirplaneModeHelper._callbacks.length, 0); - }); - }); - }); - - suite('setEnabled > ', function() { - suite('setEnabled(true) when enabling', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'enabling'; - AirplaneModeHelper.setEnabled(true); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(false) when enabling', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'enabling'; - AirplaneModeHelper.setEnabled(false); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(true) when disabling', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'disabling'; - AirplaneModeHelper.setEnabled(true); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(false) when disabling', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'disabling'; - AirplaneModeHelper.setEnabled(false); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(true) when enabled', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'enabled'; - AirplaneModeHelper.setEnabled(true); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(false) when disabled', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'disabled'; - AirplaneModeHelper.setEnabled(false); - }); - test('will do nothing', function() { - assert.isUndefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(true) when disabled', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'disabled'; - AirplaneModeHelper.setEnabled(true); - }); - test('will change airplane mode status', function() { - assert.isDefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - suite('setEnabled(false) when enabled', function() { - setup(function() { - AirplaneModeHelper._cachedStatus = 'enabled'; - AirplaneModeHelper.setEnabled(false); - }); - test('will change airplane mode status', function() { - assert.isDefined( - window.navigator.mozSettings.mSettings[kCommunicationKey]); - }); - }); - - }); -}); diff --git a/apps/settings/test/unit/messaging_test.js b/apps/settings/test/unit/messaging_test.js index 1d1fea5ee7a9..837cd3fc60f0 100644 --- a/apps/settings/test/unit/messaging_test.js +++ b/apps/settings/test/unit/messaging_test.js @@ -5,11 +5,10 @@ requireApp('settings/shared/test/unit/mocks/mock_navigator_moz_settings.js'); requireApp('settings/test/unit/mock_l10n.js'); -requireApp('settings/shared/test/unit/mocks/mock_settings_listener.js'); +requireApp('settings/shared/js/settings_listener.js'); requireApp('settings/js/messaging.js'); var mocksForMessaging = new MocksHelper([ - 'SettingsListener', 'IccHelper' ]).init(); diff --git a/apps/system/js/airplane_mode.js b/apps/system/js/airplane_mode.js index 4a3eec1929bf..a52236e13b86 100644 --- a/apps/system/js/airplane_mode.js +++ b/apps/system/js/airplane_mode.js @@ -1,13 +1,93 @@ -/* global SettingsListener, AirplaneMode */ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; -(function(exports) { +var AirplaneMode = { + _enabled: null, + set enabled(value) { + if (value !== this._enabled) { + var mobileConnections = window.navigator.mozMobileConnections; + var self = this; + var isError = false; + var setCount = 0; + + var setRadioAfterReqsCalled = function(enabled) { + if (setCount !== mobileConnections.length) { + return; + } else { + if (isError) { + self._enabled = enabled; + setAirplaneModeEnabled(self._enabled); + } else { + self._enabled = !enabled; + SettingsListener.getSettingsLock().set( + {'ril.radio.disabled': self._enabled} + ); + } + } + }; + + var doSetRadioEnabled = function doSetRadioEnabled(i, enabled) { + var conn = mobileConnections[i]; + var req = conn.setRadioEnabled(enabled); + setCount++; + + req.onsuccess = function() { + setRadioAfterReqsCalled(enabled); + }; + req.onerror = function() { + isError = true; + setRadioAfterReqsCalled(enabled); + }; + }; + + var setRadioEnabled = function setRadioEnabled(i, enabled) { + var conn = mobileConnections[i]; + if (conn.radioState !== 'enabling' && + conn.radioState !== 'disabling' && + conn.radioState !== null) { + doSetRadioEnabled(i, enabled); + } else { + conn.addEventListener('radiostatechange', + function radioStateChangeHandler() { + if (conn.radioState == 'enabling' || + conn.radioState == 'disabling' || + conn.radioState == null) { + return; + } + conn.removeEventListener('radiostatechange', + radioStateChangeHandler); + doSetRadioEnabled(i, enabled); + }); + } + }; + + var setAirplaneModeEnabled = function setAirplaneModeEnabled(enabled) { + // set airplane mode `true` + // means setRadioEnabled `false` + enabled = !enabled; + if (mobileConnections.length == 1) { + setRadioEnabled(0, enabled); + } else { + setRadioEnabled(0, enabled); + setRadioEnabled(1, enabled); + } + }; - var AirplaneModeServiceHelper = { - _settings: { + setAirplaneModeEnabled(value); + } + }, + + get enabled() { + return this._enabled; + }, + + init: function apm_init() { + if (!window.navigator.mozSettings) + return; + + var settings = { // mozSetting state for Data connection, Bluetooth, Wifi, GPS 'ril.data.enabled': false, 'bluetooth.enabled': false, @@ -21,16 +101,24 @@ 'wifi.suspended': false, 'geolocation.suspended': false, 'nfc.suspended': false - }, + }; + + // observe the corresponding mozSettings + for (var key in settings) { + (function(settingID) { + SettingsListener.observe(settingID, false, function(value) { + settings[settingID] = value; + }); + })(key); + } + // turn off the mozSetting corresponding to `key' // and remember its initial state by storing it in another setting - _suspend: function(key) { - var enabled = this._settings[key + '.enabled']; - var suspended = this._settings[key + '.suspended']; - - if (suspended) { + function suspend(key) { + var enabled = settings[key + '.enabled']; + var suspended = settings[key + '.suspended']; + if (suspended) return; - } // remember the state before switching it to false var sset = {}; @@ -43,11 +131,12 @@ eset[key + '.enabled'] = false; SettingsListener.getSettingsLock().set(eset); } - }, + } + // turn on the mozSetting corresponding to `key' // if it has been suspended by the airplane mode - _restore: function(key) { - var suspended = this._settings[key + '.suspended']; + function restore(key) { + var suspended = settings[key + '.suspended']; // clear the 'suspended' state var sset = {}; @@ -60,381 +149,104 @@ rset[key + '.enabled'] = true; SettingsListener.getSettingsLock().set(rset); } - }, - isEnabled: function(key) { - return this._settings[key + '.enabled']; - }, - isSuspended: function(key) { - return this._settings[key + '.suspended']; - }, - init: function() { - var self = this; + } - // observe the corresponding mozSettings - for (var key in this._settings) { - (function(settingID) { - SettingsListener.observe(settingID, false, function(value) { - self._settings[settingID] = value; - }); - })(key); - } - }, - updateStatus: function(value) { - var mozSettings = window.navigator.mozSettings; - var bluetooth = window.navigator.mozBluetooth; - var wifiManager = window.navigator.mozWifiManager; - var mobileData = window.navigator.mozMobileConnections[0] && - window.navigator.mozMobileConnections[0].data; - var fmRadio = window.navigator.mozFMRadio; + var mozSettings = window.navigator.mozSettings; + var bluetooth = window.navigator.mozBluetooth; + var wifiManager = window.navigator.mozWifiManager; + var mobileData = window.navigator.mozMobileConnections[0] && + window.navigator.mozMobileConnections[0].data; + var fmRadio = window.navigator.mozFMRadio; + + // Note that we don't restore Wifi tethering when leaving airplane mode + // because Wifi tethering can't be switched on before data connection is + // established. + var self = this; + function updateStatus(value) { if (value) { + // Entering airplane mode. + self.enabled = true; + // Turn off mobile data: // we toggle the mozSettings value here just for the sake of UI, // platform RIL disconnects mobile data when // 'ril.radio.disabled' is true. if (mobileData) { - this._suspend('ril.data'); + suspend('ril.data'); } // Turn off Bluetooth. if (bluetooth) { - this._suspend('bluetooth'); + suspend('bluetooth'); } // Turn off Wifi and Wifi tethering. if (wifiManager) { - this._suspend('wifi'); + suspend('wifi'); SettingsListener.getSettingsLock().set({ 'tethering.wifi.enabled': false }); } // Turn off Geolocation. - this._suspend('geolocation'); + suspend('geolocation'); // Turn off NFC - this._suspend('nfc'); + suspend('nfc'); // Turn off FM Radio. if (fmRadio && fmRadio.enabled) { fmRadio.disable(); } } else { - // Note that we don't restore Wifi tethering when leaving airplane mode - // because Wifi tethering can't be switched on before data connection is - // established. + // Leaving airplane mode. + self.enabled = false; // Don't attempt to turn on mobile data if it's already on - if (mobileData && !this._settings['ril.data.enabled']) { - this._restore('ril.data'); + if (mobileData && !settings['ril.data.enabled']) { + restore('ril.data'); } // Don't attempt to turn on Bluetooth if it's already on if (bluetooth && !bluetooth.enabled) { - this._restore('bluetooth'); + restore('bluetooth'); } // Don't attempt to turn on Wifi if it's already on if (wifiManager && !wifiManager.enabled) { - this._restore('wifi'); + restore('wifi'); } // Don't attempt to turn on Geolocation if it's already on - if (!this._settings['geolocation.enabled']) { - this._restore('geolocation'); + if (!settings['geolocation.enabled']) { + restore('geolocation'); } - if (!this._settings['nfc.enabled']) { - this._restore('nfc'); + if (!settings['nfc.enabled']) { + restore('nfc'); } } } - }; - - // main - var AirplaneMode = { - /* - * We will cache the helper as our internal value - */ - _serviceHelper: AirplaneModeServiceHelper, - - /* - * This is an internal key to store current state of AirplaneMode - */ - _enabled: null, - - /* - * This is an event mapping table that will help us wait for - * specific event from its manager to make sure we are now - * in airplane mode or not. - */ - _checkedActionsMap: { - wifi: { - enabled: 'wifi-enabled', - disabled: 'wifi-disabled' - }, - bluetooth: { - enabled: 'bluetooth-adapter-added', - disabled: 'bluetooth-disabled' - } - }, - - /* - * When turning on / off airplane mode, we will start watching - * needed events to make sure we are in airplane mode or not. - * - * @param {boolean} value - * @param {Object} checkedActions - */ - watchEvents: function(value, checkedActions) { - var self = this; - for (var serviceName in this._checkedActionsMap) { - - // if we are waiting for specific service - if (serviceName in checkedActions) { - var action = value ? 'disabled' : 'enabled'; - var eventName = this._checkedActionsMap[serviceName][action]; - - // then we will start watch events coming from its manager - window.addEventListener(eventName, - (function(eventName, serviceName) { - return function toUpdateAirplaneMode() { - window.removeEventListener(eventName, toUpdateAirplaneMode); - checkedActions[serviceName] = true; - self._updateAirplaneModeStatus(checkedActions); - }; - }(eventName, serviceName))); - } - } - }, - - /* - * This is a ES5 feature that can help the others easily get/set - * AirplaneMode. - * - * @param {boolean} value - */ - set enabled(value) { - if (value !== this._enabled) { - var self = this; - var setCount = 0; - var isError = false; - var checkedActions = this._getCheckedActions(value); - var mobileConnections = window.navigator.mozMobileConnections; - - // start watching events - this.watchEvents(value, checkedActions); - - var setRadioAfterReqsCalled = function(enabled) { - if (setCount !== mobileConnections.length) { - return; - } else { - if (isError) { - self._enabled = enabled; - setAirplaneModeEnabled(self._enabled); - } else { - self._enabled = !enabled; - checkedActions['conn'] = true; - self._updateAirplaneModeStatus(checkedActions); - } - } - }; - var doSetRadioEnabled = function doSetRadioEnabled(i, enabled) { - var conn = mobileConnections[i]; - var req = conn.setRadioEnabled(enabled); - setCount++; - - req.onsuccess = function() { - setRadioAfterReqsCalled(enabled); - }; - req.onerror = function() { - isError = true; - setRadioAfterReqsCalled(enabled); - }; - }; - - var setRadioEnabled = function setRadioEnabled(i, enabled) { - var conn = mobileConnections[i]; - if (conn.radioState !== 'enabling' && - conn.radioState !== 'disabling' && - conn.radioState !== null) { - doSetRadioEnabled(i, enabled); - } else { - conn.addEventListener('radiostatechange', - function radioStateChangeHandler() { - if (conn.radioState == 'enabling' || - conn.radioState == 'disabling' || - conn.radioState == null) { - return; - } - conn.removeEventListener('radiostatechange', - radioStateChangeHandler); - doSetRadioEnabled(i, enabled); - }); - } - }; - - var setAirplaneModeEnabled = function setAirplaneModeEnabled(enabled) { - // set airplane mode `true` - // means setRadioEnabled `false` - enabled = !enabled; - for (var i = 0; i < mobileConnections.length; i++) { - setRadioEnabled(i, enabled); - } - }; - - setAirplaneModeEnabled(value); - this._serviceHelper.updateStatus(value); - } - }, - - /* - * This is a ES5 feature that can help the others easily get AirplaneMode - * states. - * - * @return {boolean} - */ - get enabled() { - return this._enabled; - }, - - /* - * In order to make sure all needed managers work successfully. We have to - * use this method to update airplaneMode related keys to tell - * AirplaneModeHelper our current states and is finised or not. - */ - _updateAirplaneModeStatus: function(checkActions) { - var self = this; - var areAllActionsDone; - - areAllActionsDone = this._areCheckedActionsAllDone(checkActions); - - if (areAllActionsDone) { - var req = SettingsListener.getSettingsLock().set({ - 'airplaneMode.enabled': self._enabled, - 'airplaneMode.status': self._enabled ? 'enabled' : 'disabled', - // NOTE - // this is for backward compatibility, - // because we will update this value only when airplane mode - // is on / off, it will not affect apps using this value - 'ril.radio.disabled': self._enabled - }); - } else { - // keep updating the status to reflect current status - SettingsListener.getSettingsLock().set({ - 'airplaneMode.status': self._enabled ? 'enabling' : 'disabling' - }); - } - }, - - /* - * By default, these three API takes longer time and with success / error - * callback. we just have to wait for these three items. - * - * @param {boolean} value - * @return {Object} checkedActions - */ - _getCheckedActions: function(value) { - // we have to re-init all need-to-check managers - var checkedActions = {}; - - if (value === true) { - // check connection - if (window.navigator.mozMobileConnections) { - checkedActions.conn = false; - } - - // check bluetooth - if (this._serviceHelper.isEnabled('bluetooth')) { - checkedActions.bluetooth = false; - } - - // check wifi - if (this._serviceHelper.isEnabled('wifi')) { - checkedActions.wifi = false; - } - } - else { - // check connection - if (window.navigator.mozMobileConnections) { - checkedActions.conn = false; - } - - // check bluetooth - if (this._serviceHelper.isSuspended('bluetooth')) { - checkedActions.bluetooth = false; - } - - // check wifi - if (this._serviceHelper.isSuspended('wifi')) { - checkedActions.wifi = false; - } - } - - return checkedActions; - }, - - /* - * We have to use this method to check whether all actions - * are done or not. - * - * @return {boolean} - */ - _areCheckedActionsAllDone: function(checkedActions) { - for (var key in checkedActions) { - if (checkedActions[key] === false) { - return false; - } - } - return true; - }, - - /* - * Entry point - */ - init: function apm_init() { - var self = this; - - if (!window.navigator.mozSettings) { - return; - } - - var mozSettings = window.navigator.mozSettings; - var mozMobileConnections = window.navigator.mozMobileConnections; - - this._serviceHelper.init(); - - // Initialize radio state - var request = - SettingsListener.getSettingsLock().get('airplaneMode.enabled'); - - request.onsuccess = function() { - var enabled = request.result['airplaneMode.enabled']; - self.enabled = enabled; - }; - - // monitor airplaneMode communication key change - mozSettings.addObserver('airplaneMode.enabled', function(e) { - self.enabled = e.settingValue; - }); - - /* - * If we are in airplane mode and the user just dial out an - * emergency call, we have to exit airplane mode. - */ - mozMobileConnections[0].addEventListener('radiostatechange', - function() { - if (mozMobileConnections[0].radioState === 'enabled' && - self._enabled === true) { - self.enabled = false; - } - }); - } - }; + // Initialize radio state + var request = SettingsListener.getSettingsLock().get('ril.radio.disabled'); + request.onsuccess = function() { + var enabled = !!request.result['ril.radio.disabled']; + // See bug 933659 + // Gecko stops using the settings key 'ril.radio.disabled' to turn + // off RIL radio. We need to remove the code that checks existence of the + // new API after bug 856553 lands. + self.enabled = enabled; + }; + + // Observe settings changes + mozSettings.addObserver('ril.radio.disabled', function(e) { + updateStatus(e.settingValue); + }); + } +}; - exports.AirplaneMode = AirplaneMode; -})(window); if (document && (document.readyState === 'complete' || document.readyState === 'interactive')) { diff --git a/apps/system/test/unit/airplane_mode_test.js b/apps/system/test/unit/airplane_mode_test.js deleted file mode 100644 index c09072f5c218..000000000000 --- a/apps/system/test/unit/airplane_mode_test.js +++ /dev/null @@ -1,384 +0,0 @@ -/* global MockNavigatorSettings, AirplaneMode, MocksHelper, - MockNavigatorMozMobileConnections, SettingsListener, - MockLock, MockWifiManager, MockBluetooth */ -'use strict'; - -requireApp( - 'system/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js'); -requireApp('system/shared/test/unit/mocks/mock_settings_listener.js'); -requireApp('system/shared/test/unit/mocks/mock_navigator_moz_settings.js'); -requireApp('system/test/unit/mock_bluetooth.js'); -requireApp('system/test/unit/mock_wifi_manager.js'); -requireApp('system/js/airplane_mode.js'); - -var mocksForAirplaneMode = new MocksHelper([ - 'Bluetooth', - 'WifiManager', - 'SettingsListener', - 'NavigatorMozMobileConnections' -]).init(); - -suite('system/airplane_mode.js', function() { - var realSettings; - var realMobileConnections; - - mocksForAirplaneMode.attachTestHelpers(); - - suiteSetup(function() { - realSettings = window.navigator.mozSettings; - window.navigator.mozSettings = MockNavigatorSettings; - - realMobileConnections = window.navigator.mozMobileConnections; - window.navigator.mozMobileConnections = MockNavigatorMozMobileConnections; - }); - - suiteTeardown(function() { - window.navigator.mozSettings = realSettings; - - MockNavigatorMozMobileConnections.mTeardown(); - window.navigator.mozMobileConnections = realMobileConnections; - }); - - setup(function() { - // we will do a lot of manipulations on SettingsListener, - // let's clean it all for every test - MockLock.clear(); - }); - - suite('init > ', function() { - suiteSetup(function() { - AirplaneMode.init(); - }); - test('AirplaneModeServiceHelper is registered', function() { - assert.isObject(AirplaneMode._serviceHelper); - }); - test('AirplaneModeServiceHelper starts observing key changes', - function() { - var checkedServiceKeys = [ - 'bluetooth.enabled', - 'wifi.enabled', - 'geolocation.enabled', - 'nfc.enabled' - ]; - checkedServiceKeys.forEach(function(key) { - assert.ok(key in SettingsListener.mCallbacks); - }); - }); - }); - - suite('set enabled to true', function() { - suite('but _enabled is true already, do nothing', function() { - setup(function() { - this.sinon.spy(AirplaneMode._serviceHelper, 'updateStatus'); - AirplaneMode._enabled = true; - AirplaneMode.enabled = true; - }); - test('nothing happend', function() { - assert.isFalse(AirplaneMode._serviceHelper.updateStatus.called); - }); - }); - - suite('_enabled is false, keep running', function() { - setup(function() { - this.sinon.stub(AirplaneMode._serviceHelper, 'updateStatus'); - AirplaneMode._enabled = false; - - // we have to make eventListeners to the newest state - MockNavigatorMozMobileConnections.mAddMobileConnection(); - }); - - teardown(function() { - // we have to make eventListeners to the newest state - MockNavigatorMozMobileConnections.mTeardown(); - }); - - suite('conn0 is enabling, conn1 is enabling', function() { - setup(function() { - setConnection(0, 'enabling'); - setConnection(1, 'enabling'); - AirplaneMode.enabled = true; - }); - test('we will cache doSetRadioEnabled for later use', function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mEventListeners.radiostatechange[0]); - assert.isFunction( - conns[1].mEventListeners.radiostatechange[0]); - }); - }); - - suite('conn0 is enabling, conn1 is enabled', function() { - setup(function() { - setConnection(0, 'enabling'); - setConnection(1, 'enabled'); - AirplaneMode.enabled = true; - }); - test('no further steps because setCount is not 2', function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mEventListeners.radiostatechange[0]); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - - // but because setCount is not 2, we will not - // execute further steps - conns[1].mCachedRadioEnabledReq.onsuccess(); - - var checkedActions = AirplaneMode._getCheckedActions(true); - assert.isFalse(checkedActions.conn); - }); - }); - - suite('conn0 is enabled, conn1 is enabled', function() { - // we will wait for these two services - suiteSetup(function() { - setSettingOnServiceHelper('wifi.enabled', true); - setSettingOnServiceHelper('bluetooth.enabled', true); - }); - suiteTeardown(function() { - setSettingOnServiceHelper('wifi.enabled', false); - setSettingOnServiceHelper('bluetooth.enabled', false); - }); - setup(function() { - setConnection(0, 'enabled'); - setConnection(1, 'enabled'); - AirplaneMode.enabled = true; - }); - test('no further steps because we are waiting other events', - function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mCachedRadioEnabledReq.onsuccess); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - // assume radio is done - conns[0].mCachedRadioEnabledReq.onsuccess(); - conns[1].mCachedRadioEnabledReq.onsuccess(); - - // but because we are still waiting for the other window event, - // we we will not execute further steps - var checkedActions = AirplaneMode._getCheckedActions(true); - assert.isFalse( - AirplaneMode._areCheckedActionsAllDone(checkedActions)); - - var lock = getLastSettingsLock(); - assert.equal(lock['airplaneMode.status'], 'enabling'); - }); - }); - - suite('conn0 is enabled, conn1 is enabled', function() { - setup(function() { - setConnection(0, 'enabled'); - setConnection(1, 'enabled'); - AirplaneMode.enabled = true; - }); - test('all other services are also done, we are in airplaneMode', - function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mCachedRadioEnabledReq.onsuccess); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - // setRadioEnabled operation is done - conns[0].mCachedRadioEnabledReq.onsuccess(); - conns[1].mCachedRadioEnabledReq.onsuccess(); - - emitEvent('wifi-disabled'); - emitEvent('bluetooth-disabled'); - - var lock = getLastSettingsLock(); - assert.equal(lock['airplaneMode.status'], 'enabled'); - }); - }); - }); - }); - - suite('set enabled to false', function() { - suite('but _enabled is false already, do nothing', function() { - setup(function() { - this.sinon.spy(AirplaneMode._serviceHelper, 'updateStatus'); - AirplaneMode._enabled = false; - AirplaneMode.enabled = false; - }); - test('nothing happend', function() { - assert.isFalse(AirplaneMode._serviceHelper.updateStatus.called); - }); - }); - - suite('_enabled is true, keep running', function() { - setup(function() { - this.sinon.stub(AirplaneMode._serviceHelper, 'updateStatus'); - AirplaneMode._enabled = true; - - // we have to make eventListeners to the newest state - MockNavigatorMozMobileConnections.mAddMobileConnection(); - }); - - teardown(function() { - // we have to make eventListeners to the newest state - MockNavigatorMozMobileConnections.mTeardown(); - }); - - suite('conn0 is disabling, conn1 is disabling', function() { - setup(function() { - setConnection(0, 'disabling'); - setConnection(1, 'disabling'); - AirplaneMode.enabled = false; - }); - test('we will cache doSetRadioEnabled for later use', function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mEventListeners.radiostatechange[0]); - assert.isFunction( - conns[1].mEventListeners.radiostatechange[0]); - }); - }); - - suite('conn0 is disabling, conn1 is disabled', function() { - setup(function() { - setConnection(0, 'disabling'); - setConnection(1, 'disabled'); - - // AirplaneMode._checkedActions.conn = false; - AirplaneMode.enabled = false; - }); - test('no further steps because setCount is not 2', function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mEventListeners.radiostatechange[0]); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - // but because setCount is not 2, we will not - // execute further steps - conns[1].mCachedRadioEnabledReq.onsuccess(); - // assert.isFalse(AirplaneMode._checkedActions.conn); - }); - }); - - suite('conn0 is disabled, conn1 is disabled', function() { - suiteSetup(function() { - setSettingOnServiceHelper('wifi.suspended', true); - setSettingOnServiceHelper('bluetooth.suspended', true); - }); - suiteTeardown(function() { - setSettingOnServiceHelper('wifi.suspended', false); - setSettingOnServiceHelper('bluetooth.suspended', false); - }); - setup(function() { - setConnection(0, 'disabled'); - setConnection(1, 'disabled'); - AirplaneMode.enabled = false; - }); - test('no further steps because we are waiting other events', - function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mCachedRadioEnabledReq.onsuccess); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - // setRadioEnabled operation is done - conns[0].mCachedRadioEnabledReq.onsuccess(); - conns[1].mCachedRadioEnabledReq.onsuccess(); - - // but because we are still waiting for the other window event, - // we we will not execute further steps - var checkedActions = AirplaneMode._getCheckedActions(false); - assert.isFalse( - AirplaneMode._areCheckedActionsAllDone(checkedActions)); - - var lock = getLastSettingsLock(); - assert.equal(lock['airplaneMode.status'], 'disabling'); - }); - }); - - suite('conn0 is disabled, conn1 is disabled', function() { - setup(function() { - setConnection(0, 'disabled'); - setConnection(1, 'disabled'); - AirplaneMode.enabled = false; - }); - test('all other services are also done, we are leaving airplaneMode', - function() { - var conns = window.navigator.mozMobileConnections; - assert.isFunction( - conns[0].mCachedRadioEnabledReq.onsuccess); - assert.isFunction( - conns[1].mCachedRadioEnabledReq.onsuccess); - - // setRadioEnabled operation is done - conns[0].mCachedRadioEnabledReq.onsuccess(); - conns[1].mCachedRadioEnabledReq.onsuccess(); - - emitEvent('wifi-enabled'); - emitEvent('bluetooth-adapter-added'); - - var lock = getLastSettingsLock(); - assert.equal(lock['airplaneMode.status'], 'disabled'); - }); - }); - }); - }); - - suite('AirplaneMode is enabled now', function() { - suiteSetup(function() { - // we need registered radiostatechange event - AirplaneMode.init(); - }); - suite('but users want to dial out an emergency call', function() { - setup(function() { - AirplaneMode._enabled = true; - setConnection(0, 'enabled'); - setConnection(1, 'enabled'); - }); - test('we will leave airplane mode', function() { - var conns = window.navigator.mozMobileConnections; - - // got a radiostatechange from gecko - conns[0].mEventListeners.radiostatechange[0](); - - // setRadioEnabled operation is done - conns[0].mCachedRadioEnabledReq.onsuccess(); - conns[1].mCachedRadioEnabledReq.onsuccess(); - - emitEvent('wifi-enabled'); - emitEvent('bluetooth-adapter-added'); - - var lock = getLastSettingsLock(); - assert.equal(lock['airplaneMode.status'], 'disabled'); - }); - }); - }); - - - // test helpers - - function setSettingOnServiceHelper(key, value) { - AirplaneMode._serviceHelper._settings[key] = value; - } - - function setConnection(connIndex, status) { - MockNavigatorMozMobileConnections[connIndex].radioState = status; - } - - function setServiceEnable(serviceName, enabled) { - Object.defineProperty(window.navigator, serviceName, { - configurable: true, - enabled: enabled - }); - } - - function emitEvent(eventName) { - var evt = document.createEvent('CustomEvent'); - evt.initCustomEvent(eventName, true, false, null); - window.dispatchEvent(evt); - } - - function getLastSettingsLock() { - return MockLock.locks[MockLock.locks.length - 1]; - } -}); diff --git a/build/settings.js b/build/settings.js index 04d3ca9439d0..d387aa2df6a9 100644 --- a/build/settings.js +++ b/build/settings.js @@ -234,8 +234,6 @@ function execute(options) { 'ril.mms.retrieval_mode': 'automatic-home', 'ril.mms.authtype': 'notDefined', 'dom.mms.operatorSizeLimitation': 307200, - 'airplaneMode.enabled': false, - 'airplaneMode.status': 'disabled', 'ril.radio.preferredNetworkType': '', 'ril.radio.disabled': false, 'ril.supl.apn': '', diff --git a/shared/js/airplane_mode_helper.js b/shared/js/airplane_mode_helper.js deleted file mode 100644 index 520a9901550c..000000000000 --- a/shared/js/airplane_mode_helper.js +++ /dev/null @@ -1,85 +0,0 @@ -/* exported AirplaneModeHelper */ -'use strict'; - -/* - * AirplaneModeHelper is a helper that makes apps enable / disable - * airplane mode easily. It will expose two methods for you : - * 1. AirplaneModeHelper.setEnabled(); - * 2. AirplaneModeHelper.addEventListener('statechange', callback) - * 3. AirplaneModeHelper.removeEventListener('statechange', callback) - * 3. AirplaneModeHelper.getStatus(callback); - */ -(function(exports) { - - // constants - const kEventName = 'statechange'; - const kCommunicationKey = 'airplaneMode.enabled'; - const kStatusKey = 'airplaneMode.status'; - - // main - var AirplaneModeHelper = { - _mozSettings: window.navigator.mozSettings, - _callbacks: [], - _cachedStatus: '', - getStatus: function() { - return this._cachedStatus; - }, - addEventListener: function(eventName, callback) { - if (eventName === kEventName) { - this._callbacks.push(callback); - } - }, - removeEventListener: function(eventName, callback) { - if (eventName === kEventName) { - var index = this._callbacks.indexOf(callback); - if (index >= 0) { - this._callbacks.splice(index, 1); - } - } - }, - setEnabled: function(enabled) { - var status = this.getStatus(); - - if (status === 'enabling' || status === 'disabling') { - // do nothing when transition - } - else { - if (enabled && status === 'enabled' || - !enabled && status === 'disabled') { - return; - } - - var setObj = {}; - setObj[kCommunicationKey] = enabled; - this._mozSettings.createLock().set(setObj); - } - }, - init: function() { - var self = this; - - // init _cachedStatus - var lock = window.navigator.mozSettings.createLock(); - var req = lock.get(kStatusKey); - req.onsuccess = function() { - self._cachedStatus = req.result[kStatusKey]; - self._callbacks.forEach(function(callback) { - callback(self._cachedStatus); - }); - }; - - this._mozSettings.addObserver(kStatusKey, function(evt) { - var currentStatus = evt.settingValue; - self._cachedStatus = currentStatus; - self._callbacks.forEach(function(callback) { - callback(currentStatus); - }); - }); - } - }; - - exports.AirplaneModeHelper = AirplaneModeHelper; - - // by default, we will add observer immediately for you right after - // you include this helper - AirplaneModeHelper.init(); -})(this); diff --git a/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js b/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js index c96507617307..e7447b1d94e7 100644 --- a/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js +++ b/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js @@ -4,17 +4,12 @@ function MockMobileconnection() { var props = ['voice', 'data', 'iccId', 'radioState', 'iccInfo']; var eventListeners = null; - var radioEnabledReq = null; function mnmmc_init() { props.forEach(function(prop) { _mock[prop] = null; }); - eventListeners = { - 'iccinfochange': [], - 'radiostatechange': [] - }; - radioEnabledReq = {}; + eventListeners = { 'iccinfochange': [] }; } function mnmmc_addEventListener(type, callback) { @@ -48,19 +43,11 @@ } } - function mnmmc_setRadioEnabled() { - return radioEnabledReq; - } - var _mock = { addEventListener: mnmmc_addEventListener, removeEventListener: mnmmc_removeEventListener, triggerEventListeners: mnmmc_triggerEventListeners, - setRadioEnabled: mnmmc_setRadioEnabled, mTeardown: mnmmc_init, - get mCachedRadioEnabledReq() { - return radioEnabledReq; - }, get mEventListeners() { return eventListeners; }