Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
80
gaia/apps/settings/js/panels/about/factory_reset.js
Executable file
80
gaia/apps/settings/js/panels/about/factory_reset.js
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Handle factory reset functionality
|
||||
*
|
||||
* @module about/FactoryReset
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @alias module:about/FactoryReset
|
||||
* @class FactoryReset
|
||||
* @returns {FactoryReset}
|
||||
*/
|
||||
var FactoryReset = function() {
|
||||
this._elements = null;
|
||||
};
|
||||
|
||||
FactoryReset.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*
|
||||
* @access public
|
||||
* @memberOf FactoryReset.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function fr_init(elements) {
|
||||
this._elements = elements;
|
||||
if (navigator.mozPower) {
|
||||
this._elements.resetButton.disabled = false;
|
||||
this._elements.resetButton.addEventListener('click',
|
||||
this._resetClick.bind(this));
|
||||
} else {
|
||||
// disable button if mozPower is undefined or can't be used
|
||||
this._elements.resetButton.disabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* click handler.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf FactoryReset.prototype
|
||||
*/
|
||||
_resetClick: function fr__resetClick() {
|
||||
this._elements.resetDialog.hidden = false;
|
||||
this._elements.resetCancel.onclick = function() {
|
||||
this._elements.resetDialog.hidden = true;
|
||||
}.bind(this);
|
||||
this._elements.resetConfirm.onclick = function() {
|
||||
this._factoryReset();
|
||||
this._elements.resetDialog.hidden = true;
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* call mozPower API to reset device
|
||||
*
|
||||
* @access private
|
||||
* @memberOf FactoryReset.prototype
|
||||
*/
|
||||
_factoryReset: function fr__factoryReset() {
|
||||
var power = navigator.mozPower;
|
||||
if (!power) {
|
||||
console.error('Cannot get mozPower');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!power.factoryReset) {
|
||||
console.error('Cannot invoke mozPower.factoryReset()');
|
||||
return;
|
||||
}
|
||||
|
||||
power.factoryReset();
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_factoryReset() {
|
||||
return new FactoryReset();
|
||||
};
|
||||
});
|
||||
121
gaia/apps/settings/js/panels/about/hardware_info.js
Executable file
121
gaia/apps/settings/js/panels/about/hardware_info.js
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Handle HardwareInfo related functionality
|
||||
*
|
||||
* @module about/HardwareInfo
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @alias module:about/HardwareInfo
|
||||
* @class HardwareInfo
|
||||
* @returns {HardwareInfo}
|
||||
*/
|
||||
var HardwareInfo = function() {
|
||||
this._elements = null;
|
||||
};
|
||||
|
||||
HardwareInfo.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HardwareInfo.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function hi_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._loadHardwareInfo();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load hardware related informations.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HardwareInfo.prototype
|
||||
*/
|
||||
_loadHardwareInfo: function hi__loadHardwareInfo() {
|
||||
var _conns = navigator.mozMobileConnections;
|
||||
if (!_conns) {
|
||||
this._elements.deviceInfoPhoneNum.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var _isMultiSim = _conns.length > 1;
|
||||
// Only show the list item when there are valid iccinfos.
|
||||
var _hideListItem = true;
|
||||
|
||||
// update msisdns
|
||||
while (this._elements.deviceInfoMsisdns.hasChildNodes()) {
|
||||
this._elements.deviceInfoMsisdns.removeChild(
|
||||
this._elements.deviceInfoMsisdns.lastChild);
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(_conns, function(conn, index) {
|
||||
var iccId = conn.iccId;
|
||||
if (!iccId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var iccObj = navigator.mozIccManager.getIccById(iccId);
|
||||
if (!iccObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
var iccInfo = iccObj.iccInfo;
|
||||
if (!iccInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
_hideListItem = false;
|
||||
var span = this._renderPhoneNumberElement(iccInfo, index, _isMultiSim);
|
||||
this._elements.deviceInfoMsisdns.appendChild(span);
|
||||
}.bind(this));
|
||||
|
||||
this._elements.deviceInfoPhoneNum.hidden = _hideListItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* render phone number element based on SIM card info.
|
||||
*
|
||||
* If the icc card is gsm card, the phone number is in msisdn.
|
||||
* Otherwise, the phone number is in mdn.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HardwareInfo.prototype
|
||||
* @param {Object} iccInfo iccInfo data
|
||||
* @param {Number} index index number
|
||||
* @param {Boolean} isMultiSim has multiple SIM
|
||||
* @return {HTMLElement} span element with number info
|
||||
*/
|
||||
_renderPhoneNumberElement: function hi__renderPhoneNumberElement(
|
||||
iccInfo, index, isMultiSim) {
|
||||
var span = document.createElement('span');
|
||||
var msisdn = iccInfo.msisdn || iccInfo.mdn;
|
||||
if (msisdn) {
|
||||
if (isMultiSim) {
|
||||
document.l10n.setAttributes(span,
|
||||
'deviceInfo-MSISDN-with-index', {
|
||||
index: index + 1,
|
||||
msisdn: msisdn
|
||||
});
|
||||
} else {
|
||||
span.textContent = msisdn;
|
||||
}
|
||||
} else {
|
||||
if (isMultiSim) {
|
||||
document.l10n.setAttributes(span,
|
||||
'unknown-phoneNumber-sim', { index: index + 1 });
|
||||
} else {
|
||||
span.setAttribute('data-l10n-id', 'unknown-phoneNumber');
|
||||
}
|
||||
}
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_hardwareInfo() {
|
||||
return new HardwareInfo();
|
||||
};
|
||||
});
|
||||
41
gaia/apps/settings/js/panels/about/panel.js
Executable file
41
gaia/apps/settings/js/panels/about/panel.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Used to show Device/Information panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var HardwareInfo = require('panels/about/hardware_info');
|
||||
var UpdateCheck = require('panels/about/update_check');
|
||||
var FactoryReset = require('panels/about/factory_reset');
|
||||
|
||||
return function ctor_support_panel() {
|
||||
var hardwareInfo = HardwareInfo();
|
||||
var factoryReset = FactoryReset();
|
||||
var updateCheck = UpdateCheck();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
hardwareInfo.init({
|
||||
deviceInfoPhoneNum: panel.querySelector('.deviceinfo-phone-num'),
|
||||
deviceInfoMsisdns: panel.querySelector('.deviceInfo-msisdns')
|
||||
});
|
||||
|
||||
updateCheck.init({
|
||||
checkUpdateNow: panel.querySelector('.check-update-now'),
|
||||
lastUpdateDate: panel.querySelector('.last-update-date'),
|
||||
updateStatus: panel.querySelector('.update-status'),
|
||||
systemStatus: panel.querySelector('.system-update-status'),
|
||||
addonUpdateNotify: panel.querySelector('.addon-update-notify')
|
||||
});
|
||||
|
||||
factoryReset.init({
|
||||
resetButton: panel.querySelector('.reset-phone'),
|
||||
resetDialog: panel.querySelector('.reset-phone-dialog'),
|
||||
resetConfirm: panel.querySelector('.confirm-reset-phone'),
|
||||
resetCancel: panel.querySelector('.cancel-reset-phone')
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
246
gaia/apps/settings/js/panels/about/update_check.js
Executable file
246
gaia/apps/settings/js/panels/about/update_check.js
Executable file
|
|
@ -0,0 +1,246 @@
|
|||
/**
|
||||
* Handle Update check related functionality
|
||||
*
|
||||
* @module about/UpdateCheck
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @alias module:about/UpdateCheck
|
||||
* @class UpdateCheck
|
||||
* @returns {UpdateCheck}
|
||||
*/
|
||||
var UpdateCheck = function() {
|
||||
this._elements = null;
|
||||
this._settings = window.navigator.mozSettings;
|
||||
this._checkStatus = {
|
||||
'gecko.updateStatus': {},
|
||||
'apps.updateStatus': {}
|
||||
};
|
||||
};
|
||||
|
||||
UpdateCheck.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf UpdateCheck.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function uc_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._loadLastUpdated();
|
||||
|
||||
this._displayNotifyCheckbox();
|
||||
|
||||
this._elements.checkUpdateNow.addEventListener('click',
|
||||
this._checkForUpdates.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows and hides the "notify me" checkbox depending on whether auto
|
||||
* updates are enabled.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_displayNotifyCheckbox: function uc__displayNotifyCheckBox() {
|
||||
var key = 'addons.auto_update';
|
||||
var request = this._settings.createLock().get(key);
|
||||
|
||||
request.onsuccess = () => {
|
||||
this._elements.addonUpdateNotify
|
||||
.classList.toggle('hidden', !request.result[key]);
|
||||
|
||||
this._settings.addObserver(key, e => {
|
||||
this._elements.addonUpdateNotify
|
||||
.classList.toggle('hidden', !e.settingValue);
|
||||
});
|
||||
};
|
||||
|
||||
request.onerror = err => {
|
||||
console.error('Failed to fetch ', key, err);
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Show last update date.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_loadLastUpdated: function uc__loadLastUpdated() {
|
||||
var key = 'deviceinfo.last_updated';
|
||||
var request = this._settings.createLock().get(key);
|
||||
|
||||
request.onsuccess = function() {
|
||||
var lastUpdated = request.result[key];
|
||||
if (!lastUpdated) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._elements.lastUpdateDate.textContent =
|
||||
new Date(lastUpdated).toLocaleString(navigator.languages, {
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* update result based on return states
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_statusCompleteUpdater: function uc__statusCompleteUpdater() {
|
||||
var hasAllCheckComplete =
|
||||
Object.keys(this._checkStatus).some((setting) =>
|
||||
this._checkStatus[setting].value === 'check-complete'
|
||||
);
|
||||
|
||||
var hasAllResponses =
|
||||
Object.keys(this._checkStatus).every((setting) =>
|
||||
!!this._checkStatus[setting].value
|
||||
);
|
||||
|
||||
if (hasAllCheckComplete) {
|
||||
this._startClearUpdateStatus();
|
||||
}
|
||||
|
||||
// On no-updates we should also remove the checking class.
|
||||
var hasNoUpdatesResult =
|
||||
Object.keys(this._checkStatus).some((setting) =>
|
||||
this._checkStatus[setting].value === 'no-updates'
|
||||
);
|
||||
|
||||
if (hasAllResponses || hasNoUpdatesResult) {
|
||||
this._elements.updateStatus.classList.remove('checking');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* handler for update status.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
* @param {String} setting gecko or app setting
|
||||
* @param {Object} event event contains SettingValue
|
||||
*/
|
||||
_onUpdateStatus: function uc__onUpdateStatus(setting, event) {
|
||||
var value = event.settingValue;
|
||||
this._checkStatus[setting].value = value;
|
||||
|
||||
/**
|
||||
* possible return values:
|
||||
*
|
||||
* - for system updates:
|
||||
* - no-updates
|
||||
* - already-latest-version
|
||||
* - check-complete
|
||||
* - retry-when-online
|
||||
* - check-error-$nsresult
|
||||
* - check-error-http-$code
|
||||
*
|
||||
* - for apps updates:
|
||||
* - check-complete
|
||||
*
|
||||
* use
|
||||
* http://mxr.mozilla.org/mozilla-central/ident?i=setUpdateStatus&tree=mozilla-central&filter=&strict=1
|
||||
* to check if this is still current
|
||||
*/
|
||||
|
||||
var l10nValues = [
|
||||
'no-updates', 'already-latest-version', 'retry-when-online'];
|
||||
|
||||
if (value !== 'check-complete') {
|
||||
var id = l10nValues.indexOf(value) !== -1 ? value : 'check-error';
|
||||
this._elements.systemStatus.setAttribute('data-l10n-id', id);
|
||||
if (id === 'check-error') {
|
||||
console.error('Error checking for system update:', value);
|
||||
}
|
||||
}
|
||||
|
||||
this._statusCompleteUpdater();
|
||||
|
||||
this._settings.removeObserver(setting, this._checkStatus[setting].cb);
|
||||
this._checkStatus[setting].cb = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Timer to keep track of displayed update status
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_clearUpdateStatusTimer: null,
|
||||
|
||||
/**
|
||||
* Start timer to hide the update status, ensuring it is displayed
|
||||
* for a certain period of time.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_startClearUpdateStatus: function uc__clearUpdateStatus() {
|
||||
if (this._clearUpdateStatusTimer) {
|
||||
clearTimeout(this._clearUpdateStatusTimer);
|
||||
}
|
||||
this._clearUpdateStatusTimer =
|
||||
window.setTimeout(this._doClearUpdateStatus.bind(this), 5000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Actually hide the update status
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_doClearUpdateStatus: function uc__clearUpdateStatus() {
|
||||
this._elements.updateStatus.classList.remove('visible');
|
||||
this._elements.systemStatus.textContent = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if there's any update.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf UpdateCheck.prototype
|
||||
*/
|
||||
_checkForUpdates: function uc__checkForUpdates() {
|
||||
if (!navigator.onLine) {
|
||||
this._elements.checkUpdateNow.setAttribute('disabled', 'disabled');
|
||||
document.l10n.formatValue('no-network-when-update').then(msg => {
|
||||
alert(msg);
|
||||
this._elements.checkUpdateNow.removeAttribute('disabled');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._elements.updateStatus.classList.add('checking', 'visible');
|
||||
|
||||
/* remove whatever was there before */
|
||||
this._elements.systemStatus.textContent = '';
|
||||
|
||||
for (var setting in this._checkStatus) {
|
||||
this._checkStatus[setting].cb =
|
||||
this._onUpdateStatus.bind(this, setting);
|
||||
this._settings.addObserver(setting, this._checkStatus[setting].cb);
|
||||
}
|
||||
|
||||
this._settings.createLock().set({
|
||||
'gaia.system.checkForUpdates': true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_updateCheck() {
|
||||
return new UpdateCheck();
|
||||
};
|
||||
});
|
||||
92
gaia/apps/settings/js/panels/about_more_info/commit_info.js
Executable file
92
gaia/apps/settings/js/panels/about_more_info/commit_info.js
Executable file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Show software informations
|
||||
*
|
||||
* @module abou_more_info/commitInfo
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @alias module:abou_more_info/commitInfo
|
||||
* @class commitInfo
|
||||
* @returns {commitInfo}
|
||||
*/
|
||||
var commitInfo = function() {
|
||||
this._elements = {};
|
||||
};
|
||||
|
||||
commitInfo.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf commitInfo.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function mi_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._loadGaiaCommit();
|
||||
},
|
||||
|
||||
/**
|
||||
* convert date to UTC format.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf commitInfo.prototype
|
||||
*/
|
||||
_dateToUTC: function mi__dateToUTC(d) {
|
||||
var arr = [];
|
||||
[
|
||||
d.getUTCFullYear(), (d.getUTCMonth() + 1), d.getUTCDate(),
|
||||
d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds()
|
||||
].forEach(function(n) {
|
||||
arr.push((n >= 10) ? n : '0' + n);
|
||||
});
|
||||
return arr.splice(0, 3).join('-') + ' ' + arr.join(':');
|
||||
},
|
||||
|
||||
/**
|
||||
* show Gaia commit number.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf commitInfo.prototype
|
||||
*/
|
||||
_loadGaiaCommit: function mi__loadGaiaCommit() {
|
||||
const GAIA_COMMIT = 'resources/gaia_commit.txt';
|
||||
|
||||
if (this._elements.dispHash.textContent) {
|
||||
return; // `gaia-commit.txt' has already been loaded
|
||||
}
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = (function(e) {
|
||||
if (req.readyState === 4) {
|
||||
if (req.status === 0 || req.status === 200) {
|
||||
var data = req.responseText.split('\n');
|
||||
|
||||
/**
|
||||
* XXX it would be great to pop a link to the github page
|
||||
* showing the commit, but there doesn't seem to be any way to
|
||||
* tell the browser to do it.
|
||||
*/
|
||||
|
||||
var d = new Date(parseInt(data[1] + '000', 10));
|
||||
this._elements.dispDate.textContent = this._dateToUTC(d);
|
||||
this._elements.dispHash.textContent = data[0].substr(0, 8);
|
||||
} else {
|
||||
console.error('Failed to fetch gaia commit: ', req.statusText);
|
||||
}
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
req.open('GET', GAIA_COMMIT, true); // async
|
||||
req.responseType = 'text';
|
||||
req.send();
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_commitInfo() {
|
||||
return new commitInfo();
|
||||
};
|
||||
});
|
||||
188
gaia/apps/settings/js/panels/about_more_info/device_info.js
Executable file
188
gaia/apps/settings/js/panels/about_more_info/device_info.js
Executable file
|
|
@ -0,0 +1,188 @@
|
|||
/**
|
||||
* Show misc informations
|
||||
*
|
||||
* @module abou_more_info/DeviceInfo
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/** MMI control code used for retrieving a devices's IMEI code. */
|
||||
const GET_IMEI_COMMAND = '*#06#';
|
||||
|
||||
/**
|
||||
* @alias module:abou_more_info/DeviceInfo
|
||||
* @class DeviceInfo
|
||||
* @returns {DeviceInfo}
|
||||
*/
|
||||
var DeviceInfo = function() {
|
||||
this._elements = {};
|
||||
};
|
||||
|
||||
DeviceInfo.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DeviceInfo.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function mi_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._loadImei();
|
||||
this._loadIccId();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the IMEI code corresponding with the specified SIM card slot.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DeviceInfo.prototype
|
||||
* @param {Integer} simSlotIndex The slot whose IMEI code
|
||||
* we want to retrieve.
|
||||
* @return {Promise} A promise that resolves to the IMEI code or rejects
|
||||
* if an error occurred.
|
||||
*/
|
||||
_getImeiCode: function mi__getImeiCode(simSlotIndex) {
|
||||
var dialPromise = navigator.mozTelephony.dial(GET_IMEI_COMMAND,
|
||||
simSlotIndex);
|
||||
|
||||
return dialPromise.then(function about_dialImeiPromise(call) {
|
||||
return call.result.then(function(result) {
|
||||
if (result && result.success &&
|
||||
(result.serviceCode === 'scImei')) {
|
||||
return result.statusMessage;
|
||||
} else {
|
||||
var errorMsg = 'Could not retrieve the IMEI code for SIM ' +
|
||||
simSlotIndex;
|
||||
console.log(errorMsg);
|
||||
return Promise.reject(
|
||||
new Error(errorMsg)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate the IMEI information entry with the provided list of IMEI codes.
|
||||
* If the code is not given or if it's empty then the entry will be marked
|
||||
* as unavailable.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DeviceInfo.prototype
|
||||
* @param {Array} imeis An array of IMEI codes.
|
||||
*/
|
||||
_createImeiField: function mi__createImeiField(imeis) {
|
||||
while (this._elements.deviceInfoImeis.hasChildNodes()) {
|
||||
this._elements.deviceInfoImeis.removeChild(
|
||||
this._elements.deviceInfoImeis.lastChild);
|
||||
}
|
||||
|
||||
if (!imeis || imeis.length === 0) {
|
||||
var span = document.createElement('span');
|
||||
|
||||
span.setAttribute('data-l10n-id', 'unavailable');
|
||||
this._elements.deviceInfoImeis.appendChild(span);
|
||||
} else {
|
||||
imeis.forEach(function(imei, index) {
|
||||
var span = document.createElement('span');
|
||||
|
||||
if (imeis.length > 1) {
|
||||
document.l10n.setAttributes(span,
|
||||
'deviceInfo-IMEI-with-index', {
|
||||
index: index + 1,
|
||||
imei: imei
|
||||
});
|
||||
} else {
|
||||
span.textContent = imei;
|
||||
}
|
||||
|
||||
span.dataset.slot = index;
|
||||
this._elements.deviceInfoImeis.appendChild(span);
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads all the device's IMEI code in the corresponding entry.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DeviceInfo.prototype
|
||||
* @return {Promise} A promise that is resolved when the container has been
|
||||
* fully populated.
|
||||
*/
|
||||
_loadImei: function mi__loadImei() {
|
||||
var conns = navigator.mozMobileConnections;
|
||||
|
||||
if (!navigator.mozTelephony || !conns) {
|
||||
this._elements.listImeis.hidden = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Retrieve all IMEI codes.
|
||||
var promises = [];
|
||||
for (var i = 0; i < conns.length; i++) {
|
||||
promises.push(this._getImeiCode(i));
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return Promise.all(promises).then(function(imeis) {
|
||||
self._createImeiField(imeis);
|
||||
}, function() {
|
||||
self._createImeiField(null);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* show icc id.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DeviceInfo.prototype
|
||||
*/
|
||||
_loadIccId: function mi__loadIccId() {
|
||||
var conns = navigator.mozMobileConnections;
|
||||
|
||||
if (!navigator.mozTelephony || !conns) {
|
||||
this._elements.listIccIds.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var multiSim = conns.length > 1;
|
||||
|
||||
// update iccids
|
||||
while (this._elements.deviceInfoIccIds.hasChildNodes()) {
|
||||
this._elements.deviceInfoIccIds.removeChild(
|
||||
this._elements.deviceInfoIccIds.lastChild);
|
||||
}
|
||||
Array.prototype.forEach.call(conns, function(conn, index) {
|
||||
var span = document.createElement('span');
|
||||
if (conn.iccId) {
|
||||
if (multiSim) {
|
||||
document.l10n.setAttributes(span,
|
||||
'deviceInfo-ICCID-with-index', {
|
||||
index: index + 1,
|
||||
iccid: conn.iccId
|
||||
});
|
||||
} else {
|
||||
span.textContent = conn.iccId;
|
||||
}
|
||||
} else {
|
||||
if (multiSim) {
|
||||
document.l10n.setAttributes(span,
|
||||
'deviceInfo-ICCID-unavailable-sim', {
|
||||
index: index + 1
|
||||
});
|
||||
} else {
|
||||
span.setAttribute('data-l10n-id', 'unavailable');
|
||||
}
|
||||
}
|
||||
this._elements.deviceInfoIccIds.appendChild(span);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_deviceInfo() {
|
||||
return new DeviceInfo();
|
||||
};
|
||||
});
|
||||
80
gaia/apps/settings/js/panels/about_more_info/hardware_info.js
Executable file
80
gaia/apps/settings/js/panels/about_more_info/hardware_info.js
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Show hardware informations
|
||||
*
|
||||
* @module about_more_info/hardwareInfo
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var BtContext = require('modules/bluetooth/bluetooth_context');
|
||||
|
||||
/**
|
||||
* @alias module:about_more_info/HardwareInfo
|
||||
* @class HardwareInfo
|
||||
* @returns {HardwareInfo}
|
||||
*/
|
||||
var HardwareInfo = function() {
|
||||
this._elements = {};
|
||||
};
|
||||
|
||||
HardwareInfo.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HardwareInfo.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function mi_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._loadMacAddress();
|
||||
this._loadBluetoothAddress();
|
||||
},
|
||||
|
||||
/**
|
||||
* observe and show MacAddress.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HardwareInfo.prototype
|
||||
*/
|
||||
_loadMacAddress: function mi__loadMacAddress() {
|
||||
SettingsListener.observe('deviceinfo.mac', '', (macAddress) =>
|
||||
this._elements.deviceInfoMac.textContent = macAddress);
|
||||
},
|
||||
|
||||
/**
|
||||
* refreshing the address field only.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HardwareInfo.prototype
|
||||
* @param {String} address Bluetooth address
|
||||
*/
|
||||
_refreshBluetoothAddress: function mi__refreshBluetoothAddress(address) {
|
||||
// update btAddr
|
||||
if (address == null || address === '') {
|
||||
this._elements.btAddr.setAttribute('data-l10n-id',
|
||||
'bluetooth-address-unavailable');
|
||||
} else {
|
||||
this._elements.btAddr.removeAttribute('data-l10n-id');
|
||||
this._elements.btAddr.textContent = address;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* load Bluetooth address.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HardwareInfo.prototype
|
||||
*/
|
||||
_loadBluetoothAddress: function about_loadBluetoothAddress() {
|
||||
BtContext.observe('address', this._refreshBluetoothAddress.bind(this));
|
||||
this._refreshBluetoothAddress(BtContext.address);
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_hardwareInfo() {
|
||||
return new HardwareInfo();
|
||||
};
|
||||
});
|
||||
38
gaia/apps/settings/js/panels/about_more_info/panel.js
Executable file
38
gaia/apps/settings/js/panels/about_more_info/panel.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Used to show Device/Information/More Information panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var CommitInfo = require('panels/about_more_info/commit_info');
|
||||
var HardwareInfo = require('panels/about_more_info/hardware_info');
|
||||
var DeviceInfo = require('panels/about_more_info/device_info');
|
||||
|
||||
return function ctor_support_panel() {
|
||||
var commitInfo = CommitInfo();
|
||||
var hardwareInfo = HardwareInfo();
|
||||
var deviceInfo = DeviceInfo();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
deviceInfo.init({
|
||||
listImeis: panel.querySelector('.list-imeis'),
|
||||
listIccIds: panel.querySelector('.list-iccids'),
|
||||
deviceInfoImeis: panel.querySelector('.deviceInfo-imeis'),
|
||||
deviceInfoIccIds: panel.querySelector('.deviceInfo-iccids')
|
||||
});
|
||||
|
||||
commitInfo.init({
|
||||
dispDate: panel.querySelector('.gaia-commit-date'),
|
||||
dispHash: panel.querySelector('.gaia-commit-hash')
|
||||
});
|
||||
|
||||
hardwareInfo.init({
|
||||
deviceInfoMac: panel.querySelector('[data-name="deviceinfo.mac"]'),
|
||||
btAddr: panel.querySelector('[data-name="deviceinfo.bt_address"]')
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
41
gaia/apps/settings/js/panels/accessibility/panel.js
Executable file
41
gaia/apps/settings/js/panels/accessibility/panel.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* The accessibility panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
|
||||
return function ctor_accessibilityPanel() {
|
||||
var _screenReaderDesc;
|
||||
var _colorFilterDesc;
|
||||
var _audioDesc;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function accessibilityPanel_onInit(rootElement) {
|
||||
_screenReaderDesc = rootElement.querySelector('#screenReader-desc');
|
||||
_colorFilterDesc = rootElement.querySelector('#colorFilter-desc');
|
||||
_audioDesc = rootElement.querySelector('#audio-desc');
|
||||
|
||||
SettingsListener.observe('accessibility.screenreader', false,
|
||||
function(enabled) {
|
||||
_screenReaderDesc.setAttribute('data-l10n-id',
|
||||
enabled ? 'enabled' : 'disabled');
|
||||
});
|
||||
|
||||
SettingsListener.observe('accessibility.colors.enable', false,
|
||||
function(enabled) {
|
||||
_colorFilterDesc.setAttribute('data-l10n-id',
|
||||
enabled ? 'enabled' : 'disabled');
|
||||
});
|
||||
|
||||
SettingsListener.observe('accessibility.monoaudio.enable', false,
|
||||
function(enabled) {
|
||||
_audioDesc.setAttribute('data-l10n-id',
|
||||
enabled ? 'mono-enabled' : 'mono-disabled');
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
41
gaia/apps/settings/js/panels/accessibility_screenreader/confirm_dialog.js
Executable file
41
gaia/apps/settings/js/panels/accessibility_screenreader/confirm_dialog.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* The screen reader confirmation dialog
|
||||
*/
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
|
||||
var _elements;
|
||||
var _enable;
|
||||
|
||||
var ConfirmDialog = {
|
||||
init: function ConfirmDialog_init(elements, onConfirmDialog) {
|
||||
_elements = elements;
|
||||
|
||||
_elements.container.onclick = function() {
|
||||
_elements.container.hidden = true;
|
||||
};
|
||||
|
||||
_elements.confirmButton.onclick = function(e) {
|
||||
SettingsListener.getSettingsLock().set(
|
||||
{ 'accessibility.screenreader' : _enable });
|
||||
};
|
||||
},
|
||||
|
||||
show: function ConfirmDialog_show(enable) {
|
||||
_enable = enable;
|
||||
var startStop = enable ? 'start' : 'stop';
|
||||
_elements.heading.setAttribute(
|
||||
'data-l10n-id', 'screenReader-confirm-title-' + startStop);
|
||||
_elements.text.setAttribute(
|
||||
'data-l10n-id', 'screenReader-confirm-description-' + startStop);
|
||||
_elements.confirmButton.setAttribute(
|
||||
'data-l10n-id', 'screenReader-confirm-button-' + startStop);
|
||||
_elements.container.hidden = false;
|
||||
}
|
||||
};
|
||||
|
||||
return ConfirmDialog;
|
||||
});
|
||||
44
gaia/apps/settings/js/panels/accessibility_screenreader/panel.js
Executable file
44
gaia/apps/settings/js/panels/accessibility_screenreader/panel.js
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* The accessibility screen reader panel
|
||||
*/
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var ConfirmDialog = require(
|
||||
'panels/accessibility_screenreader/confirm_dialog');
|
||||
|
||||
var kEnabledKey = 'accessibility.screenreader';
|
||||
|
||||
return function ctor_accessibility_screenreader_panel() {
|
||||
var _screenreaderSwitch;
|
||||
return SettingsPanel({
|
||||
onInit: function asr_onInit(rootElement) {
|
||||
_screenreaderSwitch =
|
||||
rootElement.querySelector('.screenreader-enable');
|
||||
var dialogContainer = rootElement.querySelector(
|
||||
'#screenreader-confirm-dialog');
|
||||
|
||||
ConfirmDialog.init({
|
||||
container: dialogContainer,
|
||||
confirmButton: dialogContainer.querySelector('button.danger'),
|
||||
heading: dialogContainer.querySelector('h1'),
|
||||
text: dialogContainer.querySelector('p')});
|
||||
|
||||
_screenreaderSwitch.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
SettingsCache.getSettings(function(results) {
|
||||
ConfirmDialog.show(!results[kEnabledKey]);
|
||||
});
|
||||
});
|
||||
|
||||
SettingsListener.observe(kEnabledKey, false, function(value) {
|
||||
_screenreaderSwitch.checked = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
31
gaia/apps/settings/js/panels/achievements/achievements_list.js
Executable file
31
gaia/apps/settings/js/panels/achievements/achievements_list.js
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* UI for Achievements panel's functionality.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ListView = require('modules/mvvm/list_view');
|
||||
var ObservableArray = require('modules/mvvm/observable_array');
|
||||
var AchievementsTemplate = require(
|
||||
'panels/achievements/achievements_template');
|
||||
|
||||
function AchievementsList(root) {
|
||||
this._achievements = ObservableArray([]);
|
||||
this._listView = ListView(root, this._achievements, AchievementsTemplate);
|
||||
}
|
||||
|
||||
AchievementsList.prototype = {
|
||||
get achievements() {
|
||||
return this._achievements.array;
|
||||
},
|
||||
|
||||
set achievements(achievements) {
|
||||
achievements = achievements || [];
|
||||
this._achievements.reset(achievements);
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_achievements_list(panel) {
|
||||
return new AchievementsList(panel);
|
||||
};
|
||||
});
|
||||
54
gaia/apps/settings/js/panels/achievements/achievements_template.js
Executable file
54
gaia/apps/settings/js/panels/achievements/achievements_template.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* The template function for generating an UI element for an Addon item.
|
||||
*
|
||||
* @module achievements/achievements_template
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
return function achievementsTemplate(achievement, recycled) {
|
||||
var container, span, small, icon, details;
|
||||
var name = achievement.criteria.split('achievements/')[1];
|
||||
|
||||
if (recycled) {
|
||||
container = recycled;
|
||||
details = container.querySelector('span');
|
||||
span = details.querySelector('span');
|
||||
small = details.querySelector('small');
|
||||
icon = container.querySelector('img');
|
||||
} else {
|
||||
container = document.createElement('li');
|
||||
details = document.createElement('span');
|
||||
span = document.createElement('span');
|
||||
small = document.createElement('small');
|
||||
icon = document.createElement('img');
|
||||
|
||||
container.classList.add('achievements-item');
|
||||
details.classList.add('details');
|
||||
details.setAttribute('role', 'presentation');
|
||||
small.classList.add('description');
|
||||
icon.classList.add('icon');
|
||||
icon.setAttribute('aria-hidden', true);
|
||||
|
||||
container.appendChild(icon);
|
||||
details.appendChild(span);
|
||||
details.appendChild(small);
|
||||
container.appendChild(details);
|
||||
}
|
||||
|
||||
icon.src = `../style/images/achievements/${name}.png`;
|
||||
|
||||
span.setAttribute('data-l10n-id', `achievements-${name}-name`);
|
||||
small.setAttribute('data-l10n-id', `achievements-${name}-description`);
|
||||
|
||||
if (achievement.evidence) {
|
||||
container.classList.add('rewarded');
|
||||
container.removeAttribute('aria-disabled');
|
||||
} else {
|
||||
container.classList.remove('rewarded');
|
||||
container.setAttribute('aria-disabled', true);
|
||||
}
|
||||
|
||||
return container;
|
||||
};
|
||||
});
|
||||
28
gaia/apps/settings/js/panels/achievements/panel.js
Executable file
28
gaia/apps/settings/js/panels/achievements/panel.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var AchievementsList = require('panels/achievements/achievements_list');
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
|
||||
return function ctor_achievements_panel() {
|
||||
var achievementsList;
|
||||
var update = achievements => achievementsList.achievements = achievements;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var listElement = panel.querySelector('.achievements-list');
|
||||
achievementsList = AchievementsList(listElement);
|
||||
},
|
||||
|
||||
onBeforeShow: function() {
|
||||
SettingsListener.observe(
|
||||
'achievements', achievementsList.achievements, update);
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
SettingsListener.unobserve('achievements', update);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
143
gaia/apps/settings/js/panels/addon_details/addon_details.js
Executable file
143
gaia/apps/settings/js/panels/addon_details/addon_details.js
Executable file
|
|
@ -0,0 +1,143 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
var AddonManager = require('modules/addon_manager');
|
||||
|
||||
const PREFERRED_ICON_SIZE = 64 * (window.devicePixelRatio || 1);
|
||||
|
||||
/**
|
||||
* This module renders the information of an addon to the UI elements.
|
||||
*
|
||||
* @class AddonDetails
|
||||
* @requires module:shared/manifest_helper
|
||||
* @requires module:modules/addon_manager
|
||||
* @returns {AddonDetails}
|
||||
*/
|
||||
function AddonDetails(elements) {
|
||||
this._boundUpdateEnabledState = this._updateEnabledState.bind(this);
|
||||
this._elements = elements;
|
||||
this._curApp = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the enabled state of an addon.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf AddonDetails.prototype
|
||||
* @param {Boolean} newState
|
||||
*/
|
||||
AddonDetails.prototype._updateEnabledState = function _update(newState) {
|
||||
this._elements.enabledState
|
||||
.setAttribute('data-l10n-id', newState ? 'enabled' : 'disabled');
|
||||
};
|
||||
|
||||
/**
|
||||
* Render details of an app to the UI elements.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf AddonDetails.prototype
|
||||
* @param {Object} app Add-on to render
|
||||
*/
|
||||
AddonDetails.prototype.render = function render(app) {
|
||||
if (this._curApp !== null) {
|
||||
this._curApp.unobserve('enabled', this._boundUpdateEnabledState);
|
||||
}
|
||||
this._curApp = app;
|
||||
if (this._curApp) {
|
||||
this._curApp.observe('enabled', this._boundUpdateEnabledState);
|
||||
this._updateEnabledState(this._curApp.enabled);
|
||||
}
|
||||
|
||||
var l10n = document.l10n;
|
||||
var manifest =
|
||||
new ManifestHelper(app.instance.manifest || app.instance.updateManifest);
|
||||
|
||||
this.updateNames(manifest);
|
||||
|
||||
// Put an icon next to the description
|
||||
navigator.mozApps.mgmt.getIcon(app, PREFERRED_ICON_SIZE).then((blob) => {
|
||||
this._elements.icon.src = URL.createObjectURL(blob);
|
||||
}).catch(() => {
|
||||
this._elements.icon.src = '../style/images/default.png';
|
||||
});
|
||||
|
||||
// Display the add-on version
|
||||
if (manifest.version) {
|
||||
this._elements.version.textContent = 'v' + manifest.version;
|
||||
}
|
||||
|
||||
// Display the add-on description if there is one
|
||||
if (manifest.description) {
|
||||
// If we have a description, the ManifestHelper class returned the
|
||||
// best localized version from the manifest file.
|
||||
this._elements.description.removeAttribute('data-l10n-id');
|
||||
this._elements.description.textContent = manifest.description;
|
||||
} else {
|
||||
// If there is no description, then display "no description" or similar.
|
||||
this._elements.description
|
||||
.setAttribute('data-l10n-id', 'addon-no-description');
|
||||
}
|
||||
|
||||
// Display the add-on developer name if there is one
|
||||
var developerName = manifest.developer && manifest.developer.name;
|
||||
l10n.setAttributes(this._elements.developer, 'addon-developer-name', {
|
||||
developerName: developerName || ''
|
||||
});
|
||||
|
||||
// Old add-on format had 'customizations' field, it is now moved to
|
||||
// 'content_scripts' field
|
||||
var oldAddonFormat = !!manifest.customizations;
|
||||
if (oldAddonFormat) {
|
||||
l10n.setAttributes(this._elements.obsoleteStatusInfo,
|
||||
AddonManager.canDelete(app) ? 'addon-obsolete-can-delete' :
|
||||
'addon-obsolete');
|
||||
}
|
||||
this._elements.obsoleteStatus.hidden = !oldAddonFormat;
|
||||
|
||||
// Get the list of targeted apps
|
||||
this._elements.targetsList.textContent = ''; // Clear any old content here
|
||||
AddonManager.getAddonTargets(app).then((targets) => {
|
||||
var names = [];
|
||||
targets.forEach(function(target) {
|
||||
var manifest =
|
||||
new ManifestHelper(target.manifest || target.updateManifest);
|
||||
names.push(manifest.displayName);
|
||||
});
|
||||
|
||||
names.sort();
|
||||
|
||||
if (names.length < 1) {
|
||||
this._elements.targetsList
|
||||
.setAttribute('data-l10n-id', 'addon-no-targets');
|
||||
} else {
|
||||
this._elements.targetsList.textContent = names.join(', ');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Render app names where appropriate.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf AddonDetails.prototype
|
||||
* @param {JSON} manifest
|
||||
*/
|
||||
AddonDetails.prototype.updateNames = function _updateNames(manifest) {
|
||||
// Display the name of the add-on in the panel header
|
||||
var appnameArgs = { appName: manifest.name };
|
||||
var l10n = document.l10n;
|
||||
|
||||
l10n.setAttributes(
|
||||
this._elements.header, 'addon-details-header1', appnameArgs);
|
||||
l10n.setAttributes(
|
||||
this._elements.name, 'addon-details-header1', appnameArgs);
|
||||
// Put text description for an icon
|
||||
l10n.setAttributes(
|
||||
this._elements.icon, 'accessibility-app-icon', appnameArgs);
|
||||
};
|
||||
|
||||
return function ctor_addon_details(panel) {
|
||||
return new AddonDetails(panel);
|
||||
};
|
||||
});
|
||||
130
gaia/apps/settings/js/panels/addon_details/panel.js
Executable file
130
gaia/apps/settings/js/panels/addon_details/panel.js
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var AddonManager = require('modules/addon_manager');
|
||||
var AddonDetails = require('panels/addon_details/addon_details');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var Toaster = require('shared/toaster');
|
||||
|
||||
return function ctor_addon_details_panel() {
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
this._elements = {
|
||||
body: panel.querySelector('.addon-details-body'),
|
||||
header: panel.querySelector('.addon-details-header'),
|
||||
gaiaHeader: panel.querySelector('gaia-header'),
|
||||
enabledState: panel.querySelector('.addon-enabled details'),
|
||||
name: panel.querySelector('.addon-details-name'),
|
||||
icon: panel.querySelector('.addon-details-icon'),
|
||||
version: panel.querySelector('.addon-details-version'),
|
||||
description: panel.querySelector('.addon-description-text'),
|
||||
developer: panel.querySelector('.addon-developer'),
|
||||
targetsList: panel.querySelector('.addon-targets'),
|
||||
toggle: panel.querySelector('.addon-enabled'),
|
||||
deleteButton: panel.querySelector('.addon-delete'),
|
||||
obsoleteStatus: panel.querySelector('.addon-obsolete-status'),
|
||||
obsoleteStatusInfo: panel.querySelector('.addon-obsolete-info')
|
||||
};
|
||||
this._details = AddonDetails(this._elements);
|
||||
this._boundOnAppEnabledChange = this._onAppEnabledChange.bind(this);
|
||||
|
||||
// Hook up the enable/disable toggle button
|
||||
this._elements.toggle.onchange = this._onToggleChange.bind(this);
|
||||
// Hook up the delete button
|
||||
this._elements.deleteButton.onclick = this._onDelete.bind(this);
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
return Promise.resolve((() => {
|
||||
if (options.addon) {
|
||||
return options.addon;
|
||||
} else if (options.manifestURL) {
|
||||
return AddonManager.findAddonByManifestURL(options.manifestURL);
|
||||
}
|
||||
})()).then((addon) => {
|
||||
this._curAddon = addon;
|
||||
|
||||
// set initial state
|
||||
if (this._curAddon) {
|
||||
this._details.render(this._curAddon);
|
||||
this._elements.toggle.checked =
|
||||
AddonManager.isEnabled(this._curAddon);
|
||||
this._curAddon.observe('enabled', this._boundOnAppEnabledChange);
|
||||
this._elements.deleteButton.disabled =
|
||||
!AddonManager.canDelete(this._curAddon);
|
||||
this._elements.body.hidden = false;
|
||||
} else {
|
||||
console.error('No valid add-on');
|
||||
this._elements.body.hidden = true;
|
||||
}
|
||||
|
||||
this._elements.gaiaHeader.hidden = false;
|
||||
});
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
// Scroll back to the top
|
||||
this._elements.body.scrollTop = 0;
|
||||
|
||||
if (this._curAddon) {
|
||||
this._curAddon.unobserve('enabled', this._boundOnAppEnabledChange);
|
||||
this._curAddon = null;
|
||||
}
|
||||
},
|
||||
|
||||
_showStepsRequired(type) {
|
||||
// If an addon injected a script then we have to restart any
|
||||
// affected apps. And if those apps are system apps, then we
|
||||
// need to reboot the device.
|
||||
if (type === 'reboot') {
|
||||
// XXX We could do a confirm dialog here asking the user if
|
||||
// they want to go ahead and reboot the device now.
|
||||
Toaster.showToast({
|
||||
messageL10nId: 'addon-reboot-required',
|
||||
latency: 3000,
|
||||
useTransition: true
|
||||
});
|
||||
} else if (type === 'restart') {
|
||||
Toaster.showToast({
|
||||
messageL10nId: 'addon-restart-apps-to-disable',
|
||||
latency: 3000,
|
||||
useTransition: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onAppEnabledChange: function(enabled) {
|
||||
this._elements.toggle.checked = enabled;
|
||||
if (!enabled) {
|
||||
AddonManager.getAddonDisableType(this._curAddon).then(
|
||||
this._showStepsRequired);
|
||||
}
|
||||
},
|
||||
|
||||
_onToggleChange: function() {
|
||||
if (this._elements.toggle.checked) {
|
||||
AddonManager.enableAddon(this._curAddon);
|
||||
} else {
|
||||
AddonManager.disableAddon(this._curAddon);
|
||||
}
|
||||
},
|
||||
|
||||
_onDelete: function() {
|
||||
this._elements.deleteButton.disabled = true; // don't delete it twice!
|
||||
var disableType;
|
||||
AddonManager.getAddonDisableType(this._curAddon).then(type => {
|
||||
disableType = type;
|
||||
return AddonManager.deleteAddon(this._curAddon);
|
||||
}).then(() => {
|
||||
this._showStepsRequired(disableType);
|
||||
SettingsService.navigate('addons'); /* go back*/
|
||||
}).catch(reason => {
|
||||
console.error('Addon deletion failed:', reason);
|
||||
// If the user cancelled deletion, we need to reenable the button
|
||||
this._elements.deleteButton.disabled = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
75
gaia/apps/settings/js/panels/addons/addons_list.js
Executable file
75
gaia/apps/settings/js/panels/addons/addons_list.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* UI for Addons panel's functionality.
|
||||
*
|
||||
* @module AddonsList
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var ListView = require('modules/mvvm/list_view');
|
||||
var AddonItemTemplateFactory = require('panels/addons/addons_template');
|
||||
var ObservableArray = require('modules/mvvm/observable_array');
|
||||
|
||||
function AddonsList(root, manager) {
|
||||
this._enabled = false;
|
||||
this._listRoot = root;
|
||||
this._addonManager = manager;
|
||||
this._addons = ObservableArray(this._sortByName(
|
||||
this._addonManager.addons.array));
|
||||
|
||||
['insert', 'remove'].forEach(e =>
|
||||
this._addonManager.addons.addEventListener(e, this._update.bind(this)));
|
||||
|
||||
var addonTemplate = AddonItemTemplateFactory(function onClick(addon) {
|
||||
SettingsService.navigate('addon-details', { addon: addon });
|
||||
});
|
||||
|
||||
this._listView = ListView(this._listRoot, this._addons, addonTemplate);
|
||||
}
|
||||
|
||||
AddonsList.prototype = {
|
||||
set enabled(value) {
|
||||
if (value !== this._enabled) {
|
||||
this._listView.enabled = this._enabled = value;
|
||||
}
|
||||
},
|
||||
|
||||
_sortByName: function(addons) {
|
||||
return addons.sort((a, b) => {
|
||||
return a._app.manifest.name.localeCompare(b._app.manifest.name);
|
||||
});
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
return this._filter ?
|
||||
Promise.all(this._addonManager.addons.array.map(addon =>
|
||||
this._addonManager.addonAffectsApp(addon, this._filter).then(
|
||||
affects => affects ? addon : undefined))).then(
|
||||
addons => this._addons.reset(this._sortByName(
|
||||
addons.filter(addon => addon)))) :
|
||||
Promise.resolve(this._addons.reset(this._sortByName(
|
||||
this._addonManager.addons.array)));
|
||||
},
|
||||
|
||||
setFilter: function(manifestURL) {
|
||||
if (!manifestURL) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this._filter = manifestURL;
|
||||
return this._update();
|
||||
},
|
||||
|
||||
unsetFilter: function() {
|
||||
if (!this._filter) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
delete this._filter;
|
||||
return this._update();
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_addons_list(root, manager) {
|
||||
return new AddonsList(root, manager);
|
||||
};
|
||||
});
|
||||
64
gaia/apps/settings/js/panels/addons/addons_template.js
Executable file
64
gaia/apps/settings/js/panels/addons/addons_template.js
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* The template function for generating an UI element for an Addon item.
|
||||
*
|
||||
* @module addons/addons_template
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
|
||||
const PREFERRED_ICON_SIZE = 30 * (window.devicePixelRatio || 1);
|
||||
|
||||
function addonsTemplate(onClick, addon, recycled) {
|
||||
var manifest = new ManifestHelper(addon.instance.manifest ||
|
||||
addon.instance.updateManifest);
|
||||
var container = null;
|
||||
var link, span, small, icon;
|
||||
if (recycled) {
|
||||
container = recycled;
|
||||
link = container.querySelector('a');
|
||||
span = container.querySelector('span');
|
||||
small = container.querySelector('small');
|
||||
icon = container.querySelector('img');
|
||||
} else {
|
||||
container = document.createElement('li');
|
||||
link = document.createElement('a');
|
||||
span = document.createElement('span');
|
||||
small = document.createElement('small');
|
||||
icon = document.createElement('img');
|
||||
|
||||
link.href = '#';
|
||||
link.classList.add('menu-item');
|
||||
small.classList.add('menu-item-desc');
|
||||
|
||||
link.appendChild(icon);
|
||||
link.appendChild(span);
|
||||
link.appendChild(small);
|
||||
container.appendChild(link);
|
||||
}
|
||||
|
||||
navigator.mozApps.mgmt
|
||||
.getIcon(addon.instance, PREFERRED_ICON_SIZE).then((blob) => {
|
||||
icon.src = URL.createObjectURL(blob);
|
||||
}).catch(() => {
|
||||
icon.src = '../style/images/default.png';
|
||||
});
|
||||
|
||||
span.textContent = manifest.displayName;
|
||||
small.setAttribute('data-l10n-id', addon.enabled ? 'enabled' : 'disabled');
|
||||
addon.observe('enabled', function(enabled) {
|
||||
small.setAttribute('data-l10n-id', enabled ? 'enabled' : 'disabled');
|
||||
});
|
||||
|
||||
link.onclick = () => {
|
||||
onClick(addon);
|
||||
};
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
return function ctor_addonsTemplate(onClick) {
|
||||
return addonsTemplate.bind(null, onClick);
|
||||
};
|
||||
});
|
||||
44
gaia/apps/settings/js/panels/addons/panel.js
Executable file
44
gaia/apps/settings/js/panels/addons/panel.js
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var AddonManager = require('modules/addon_manager');
|
||||
var AddonsList = require('panels/addons/addons_list');
|
||||
|
||||
return function ctor_addons_panel() {
|
||||
var addonsList;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var listElement = panel.querySelector('.addon-list');
|
||||
var moreLink = panel.querySelector('.get-more-addons');
|
||||
|
||||
addonsList = AddonsList(listElement, AddonManager);
|
||||
moreLink.addEventListener('click', function() {
|
||||
moreLink.blur();
|
||||
// The addons list panel will update itself if new addon is installed.
|
||||
var activity = new window.MozActivity({
|
||||
name: 'marketplace-category',
|
||||
data: { slug: 'addons' }
|
||||
});
|
||||
// For workaround jshint.
|
||||
activity.onsuccess = function() {};
|
||||
});
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
if (options && options.manifestURL) {
|
||||
return addonsList.setFilter(options.manifestURL).then(() =>
|
||||
addonsList.enabled = true);
|
||||
} else {
|
||||
addonsList.enabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeHide: function() {
|
||||
return addonsList.unsetFilter().then(() =>
|
||||
addonsList.enabled = false);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
53
gaia/apps/settings/js/panels/apn_editor/apn_editor.js
Executable file
53
gaia/apps/settings/js/panels/apn_editor/apn_editor.js
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* The apn editor module
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ApnEditorConst = require('panels/apn_editor/apn_editor_const');
|
||||
var ApnEditorSession = require('panels/apn_editor/apn_editor_session');
|
||||
|
||||
var APN_PROPERTIES = ApnEditorConst.APN_PROPERTIES;
|
||||
var APN_PROPERTY_DEFAULTS = ApnEditorConst.APN_PROPERTY_DEFAULTS;
|
||||
var VALUE_CONVERTERS = ApnEditorConst.VALUE_CONVERTERS;
|
||||
|
||||
function ApnEditor(rootElement) {
|
||||
this._inputElements = {};
|
||||
APN_PROPERTIES.forEach(function(name) {
|
||||
this._inputElements[name] = rootElement.querySelector('.' + name);
|
||||
}, this);
|
||||
}
|
||||
|
||||
ApnEditor.prototype = {
|
||||
_convertValue: function ae_convertValue(value, converter) {
|
||||
if (converter) {
|
||||
return converter(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
_fillInputElements: function ae_fillInputElements(inputElements, apn) {
|
||||
APN_PROPERTIES.forEach(function(name) {
|
||||
var inputElement = inputElements[name];
|
||||
if (inputElement) {
|
||||
var value = (apn && apn[name.toLowerCase()]) ||
|
||||
APN_PROPERTY_DEFAULTS[name];
|
||||
inputElement.value =
|
||||
this._convertValue(value, VALUE_CONVERTERS.TO_STRING[name]);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
createApn: function ae_createApn(serviceId, apnItem) {
|
||||
this._fillInputElements(this._inputElements, apnItem.apn);
|
||||
return ApnEditorSession(serviceId, 'new', this._inputElements, apnItem);
|
||||
},
|
||||
editApn: function ae_editApn(serviceId, apnItem) {
|
||||
this._fillInputElements(this._inputElements, apnItem.apn);
|
||||
return ApnEditorSession(serviceId, 'edit', this._inputElements, apnItem);
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_apn_editor(rootElement) {
|
||||
return new ApnEditor(rootElement);
|
||||
};
|
||||
});
|
||||
47
gaia/apps/settings/js/panels/apn_editor/apn_editor_const.js
Executable file
47
gaia/apps/settings/js/panels/apn_editor/apn_editor_const.js
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* The apn editor const
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var APN_PROPERTY_DEFAULTS = {
|
||||
'apn': '',
|
||||
'user': '',
|
||||
'password': '',
|
||||
'proxy': '',
|
||||
'port': '',
|
||||
'mmsc': '',
|
||||
'mmsproxy': '',
|
||||
'mmsport': '',
|
||||
'authtype': 'notDefined',
|
||||
'types': ['default'],
|
||||
'protocol': 'notDefined',
|
||||
'roaming_protocol': 'notDefined',
|
||||
'mtu' : ''
|
||||
};
|
||||
|
||||
var APN_PROPERTIES = Object.keys(APN_PROPERTY_DEFAULTS);
|
||||
|
||||
var VALUE_CONVERTERS = {
|
||||
'TO_STRING': {
|
||||
'types': function(types) {
|
||||
if (types && Array.isArray(types) && types.length) {
|
||||
return types.join(', ');
|
||||
} else {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
},
|
||||
'TO_DATA': {
|
||||
'types': function(string) {
|
||||
return string.split(',').map((str) => str.trim());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
get APN_PROPERTIES() { return APN_PROPERTIES; },
|
||||
get APN_PROPERTY_DEFAULTS() { return APN_PROPERTY_DEFAULTS; },
|
||||
get VALUE_CONVERTERS() { return VALUE_CONVERTERS; }
|
||||
};
|
||||
});
|
||||
99
gaia/apps/settings/js/panels/apn_editor/apn_editor_session.js
Executable file
99
gaia/apps/settings/js/panels/apn_editor/apn_editor_session.js
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* The apn editor session module
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ApnSettingsManager = require('modules/apn/apn_settings_manager');
|
||||
var ApnEditorConst = require('panels/apn_editor/apn_editor_const');
|
||||
var ApnUtils = require('modules/apn/apn_utils');
|
||||
|
||||
var APN_PROPERTIES = ApnEditorConst.APN_PROPERTIES;
|
||||
var VALUE_CONVERTERS = ApnEditorConst.VALUE_CONVERTERS;
|
||||
|
||||
function ApnEditorSession(serviceId, mode, inputElements, apnItem) {
|
||||
this._serviceId = serviceId;
|
||||
this._mode = mode;
|
||||
this._inputElements = inputElements;
|
||||
this._apnItem = apnItem;
|
||||
}
|
||||
|
||||
ApnEditorSession.prototype = {
|
||||
_convertValue: function ae_convertValue(value, converter) {
|
||||
if (converter) {
|
||||
return converter(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
_exportApnSetting: function aes_exportApnSetting(inputElements) {
|
||||
var newApnSetting = {};
|
||||
APN_PROPERTIES.forEach(function(name) {
|
||||
var inputElement = inputElements[name];
|
||||
if (inputElement && !inputElement.hidden && inputElement.value) {
|
||||
newApnSetting[name.toLowerCase()] = this._convertValue(
|
||||
inputElement.value, VALUE_CONVERTERS.TO_DATA[name]);
|
||||
}
|
||||
}, this);
|
||||
return newApnSetting;
|
||||
},
|
||||
_commitNew: function aes_commitNew() {
|
||||
var promises = [];
|
||||
var newApnSetting = this._exportApnSetting(this._inputElements);
|
||||
newApnSetting.types.slice().forEach(function(type) {
|
||||
// Clone new settings to make sure the promises do not all
|
||||
// point to the very last one
|
||||
var settingClone = ApnUtils.clone(newApnSetting);
|
||||
settingClone.types = [type];
|
||||
promises.push(
|
||||
ApnSettingsManager.addApn(this._serviceId, settingClone));
|
||||
}, this);
|
||||
return Promise.all(promises);
|
||||
},
|
||||
_commitEdit: function aes_commitEdit() {
|
||||
var promises = [];
|
||||
var newApnSetting = this._exportApnSetting(this._inputElements);
|
||||
if (newApnSetting.types.length === 1) {
|
||||
promises.push(ApnSettingsManager.updateApn(this._serviceId,
|
||||
this._apnItem.id, newApnSetting));
|
||||
} else {
|
||||
newApnSetting.types.forEach(function(type) {
|
||||
var settingClone = ApnUtils.clone(newApnSetting);
|
||||
settingClone.types = [type];
|
||||
if (type === this._apnItem.apn.types[0]) {
|
||||
promises.push(ApnSettingsManager.updateApn(this._serviceId,
|
||||
this._apnItem.id, settingClone));
|
||||
} else {
|
||||
promises.push(ApnSettingsManager.addApn(this._serviceId,
|
||||
settingClone));
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
},
|
||||
commit: function aes_commit() {
|
||||
switch (this._mode) {
|
||||
case 'new':
|
||||
return this._commitNew();
|
||||
case 'edit':
|
||||
return this._commitEdit();
|
||||
default:
|
||||
console.error('invalid mode');
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
cancel: function aes_cancel() {
|
||||
APN_PROPERTIES.forEach(function(name) {
|
||||
this._inputElements[name].value = '';
|
||||
}, this);
|
||||
this._apnItem = null;
|
||||
},
|
||||
get mode() {
|
||||
return this._mode;
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_apnESession(serviceId, mode, inputElements, apnItem) {
|
||||
return new ApnEditorSession(serviceId, mode, inputElements, apnItem);
|
||||
};
|
||||
});
|
||||
155
gaia/apps/settings/js/panels/apn_editor/panel.js
Executable file
155
gaia/apps/settings/js/panels/apn_editor/panel.js
Executable file
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
* The apn editor panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var ApnSettingsManager = require('modules/apn/apn_settings_manager');
|
||||
var ApnEditor = require('panels/apn_editor/apn_editor');
|
||||
|
||||
var _back = function() {
|
||||
SettingsService.navigate('apn-list');
|
||||
};
|
||||
|
||||
var _getDialogService = function() {
|
||||
return new Promise(function(resolve) {
|
||||
require(['modules/dialog_service'], resolve);
|
||||
});
|
||||
};
|
||||
|
||||
var _showChangeApnWarning = function() {
|
||||
return _getDialogService().then((DialogService) => {
|
||||
return DialogService.confirm('change-apn-warning-message1', {
|
||||
title: 'apnSettings',
|
||||
submitButton: {
|
||||
id: 'yes',
|
||||
style: 'recommend'
|
||||
},
|
||||
cancelButton: 'cancel'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var _showDeleteApnWarning = function() {
|
||||
return _getDialogService().then((DialogService) => {
|
||||
return DialogService.confirm('delete-apn-confirm', {
|
||||
title: 'confirmation',
|
||||
submitButton: {
|
||||
id: 'delete-apn',
|
||||
style: 'danger'
|
||||
},
|
||||
cancelButton: 'cancel'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return function ctor_apnEditorPanel() {
|
||||
var _rootElement;
|
||||
var _elements;
|
||||
|
||||
var _apnEditor;
|
||||
var _editorSession;
|
||||
|
||||
var _apnItem;
|
||||
var _apnType;
|
||||
var _serviceId;
|
||||
|
||||
var _leftApp = false;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function ae_onInit(rootElement) {
|
||||
_rootElement = rootElement;
|
||||
_apnEditor = new ApnEditor(rootElement);
|
||||
_elements = {
|
||||
panel: rootElement.querySelector('.panel'),
|
||||
okBtn: rootElement.querySelector('button.ok'),
|
||||
deteleItem: rootElement.querySelector('.apnSettings-btns li.delete'),
|
||||
deleteBtn:
|
||||
rootElement.querySelector('.apnSettings-btns li.delete button')
|
||||
};
|
||||
|
||||
_elements.okBtn.onclick = function() {
|
||||
if (!_editorSession) {
|
||||
_back();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_editorSession.mode === 'new') {
|
||||
_editorSession.commit().then(_back);
|
||||
_editorSession = null;
|
||||
} else if (_editorSession.mode === 'edit') {
|
||||
// Display the warning only when Data roaming is turned on and it is
|
||||
// the current APN in use that’s being edited.
|
||||
Promise.all([
|
||||
ApnSettingsManager.getActiveApnId(_serviceId, _apnType),
|
||||
new Promise((resolve) => {
|
||||
SettingsCache.getSettings(function(results) {
|
||||
resolve(results['ril.data.roaming_enabled']);
|
||||
});
|
||||
})
|
||||
]).then(function(results) {
|
||||
var activeApnId = results[0];
|
||||
var dataRoamingEnabled = results[1];
|
||||
if (activeApnId === _apnItem.id && dataRoamingEnabled) {
|
||||
return _showChangeApnWarning();
|
||||
}
|
||||
}).then(function(result) {
|
||||
if (!result || (result && result.type === 'submit')) {
|
||||
_editorSession.commit().then(_back);
|
||||
_editorSession = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_elements.deleteBtn.onclick = function() {
|
||||
_showDeleteApnWarning().then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
ApnSettingsManager.removeApn(_serviceId, _apnItem.id).then(_back);
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
onBeforeShow: function ae_onBeforeShow(rootElement, options) {
|
||||
// If this flag has been set, which means that users have been left
|
||||
// the app before so we should keep the original state instead of
|
||||
// refreshing it.
|
||||
if (_leftApp) {
|
||||
_leftApp = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var mode = options.mode || 'new';
|
||||
_apnItem = options.item || {};
|
||||
_apnType = options.type || 'default';
|
||||
_serviceId = options.serviceId;
|
||||
|
||||
switch (mode) {
|
||||
case 'new':
|
||||
_elements.deteleItem.hidden = true;
|
||||
var defaultApnItem = {
|
||||
apn: {
|
||||
types: [_apnType]
|
||||
}
|
||||
};
|
||||
_editorSession = _apnEditor.createApn(_serviceId, defaultApnItem);
|
||||
break;
|
||||
case 'edit':
|
||||
_elements.deteleItem.hidden = false;
|
||||
_editorSession = _apnEditor.editApn(_serviceId, _apnItem);
|
||||
break;
|
||||
}
|
||||
_elements.panel.scrollTop = 0;
|
||||
},
|
||||
onHide: function ae_onHide() {
|
||||
_leftApp = document.hidden;
|
||||
if (!_leftApp && _editorSession) {
|
||||
_editorSession.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
67
gaia/apps/settings/js/panels/apn_list/apn_template_factory.js
Executable file
67
gaia/apps/settings/js/panels/apn_list/apn_template_factory.js
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* The template function for generating an UI element for an APN item.
|
||||
*
|
||||
* @module apn_list/apn_template_factory
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
function apnTemplate(apnType, onItemclick, onRadioClick, item) {
|
||||
var rawApn = item.apn;
|
||||
|
||||
// create an <gaia-radio> element
|
||||
var radio = document.createElement('gaia-radio');
|
||||
radio.className = 'split';
|
||||
radio.checked = item.active;
|
||||
radio.name = apnType;
|
||||
|
||||
// include the radio button element in a list item
|
||||
var radioLabel = document.createElement('label');
|
||||
|
||||
radioLabel.classList.add('name');
|
||||
if (!rawApn.carrier || rawApn.carrier === '_custom_') {
|
||||
radioLabel.textContent = rawApn.apn;
|
||||
} else {
|
||||
radioLabel.textContent = rawApn.carrier;
|
||||
}
|
||||
radio.appendChild(radioLabel);
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.classList.add('apn-item');
|
||||
li.appendChild(radio);
|
||||
|
||||
// Keep track if we clicked the label or not.
|
||||
// This is needed because you can't stop the event from propagating to the
|
||||
// shadow DOM from the label currently.
|
||||
var labelClicked = false;
|
||||
|
||||
// Register the handler for click event.
|
||||
// Must listen to click, which matches the event in <gaia-radio>.
|
||||
// Use event capturing because we are overriding the default functionality.
|
||||
if (typeof onRadioClick === 'function') {
|
||||
radio.addEventListener('click', event => {
|
||||
if (!labelClicked && !radio.checked) {
|
||||
onRadioClick(item, radio);
|
||||
}
|
||||
labelClicked = false;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Register the handler for a mouseup event.
|
||||
// Use mouseup so that it wouldn't be stopped by stopPropagation.
|
||||
if (typeof onItemclick === 'function') {
|
||||
radioLabel.addEventListener('mouseup', () => {
|
||||
labelClicked = true;
|
||||
onItemclick(item);
|
||||
});
|
||||
}
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
return function ctor_apnTemplate(apnType, onItemclick, onRadioClick) {
|
||||
return apnTemplate.bind(null, apnType, onItemclick, onRadioClick);
|
||||
};
|
||||
});
|
||||
151
gaia/apps/settings/js/panels/apn_list/panel.js
Executable file
151
gaia/apps/settings/js/panels/apn_list/panel.js
Executable file
|
|
@ -0,0 +1,151 @@
|
|||
/**
|
||||
* The apn list panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var ListView = require('modules/mvvm/list_view');
|
||||
var ApnSettingsManager = require('modules/apn/apn_settings_manager');
|
||||
var ApnTemplateFactory = require('panels/apn_list/apn_template_factory');
|
||||
|
||||
var HEADER_L10N_MAP = {
|
||||
'default': 'dataSettings-header',
|
||||
'mms': 'messageSettings-header',
|
||||
'ims': 'imsSettings-header2',
|
||||
'supl': 'suplSettings-header',
|
||||
'dun': 'dunSettings-header',
|
||||
'fota': 'fotaSettings-header'
|
||||
};
|
||||
|
||||
var _getDialogService = function() {
|
||||
return new Promise(function(resolve) {
|
||||
require(['modules/dialog_service'], resolve);
|
||||
});
|
||||
};
|
||||
|
||||
var _showChangeApnWarning = function() {
|
||||
return _getDialogService().then((DialogService) => {
|
||||
return DialogService.confirm('change-apn-warning-message1', {
|
||||
title: 'apnSettings',
|
||||
submitButton: {
|
||||
id: 'yes',
|
||||
style: 'recommend'
|
||||
},
|
||||
cancelButton: 'cancel'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return function ctor_apn_settings_panel() {
|
||||
var _rootElement;
|
||||
var _mainHeader;
|
||||
var _header;
|
||||
var _apnListViewRoot;
|
||||
var _apnListView;
|
||||
var _addApnBtn;
|
||||
|
||||
var _role;
|
||||
|
||||
var _apnType = 'default';
|
||||
var _serviceId = 0;
|
||||
|
||||
var _onApnItemClick = function(serviceId, apnType, apnItem) {
|
||||
SettingsService.navigate('apn-editor',
|
||||
{
|
||||
mode: 'edit',
|
||||
serviceId: serviceId,
|
||||
type: apnType,
|
||||
item: apnItem
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var _onRadioClick = function(serviceId, apnType, apnItem, radio) {
|
||||
var setActive = function() {
|
||||
apnItem.active = true;
|
||||
radio.checked = true;
|
||||
ApnSettingsManager.setActiveApnId(serviceId, apnType, apnItem.id);
|
||||
};
|
||||
|
||||
SettingsCache.getSettings(function(results) {
|
||||
if (results['ril.data.roaming_enabled'] === true) {
|
||||
// Only display the warning when roaming is enabled.
|
||||
_showChangeApnWarning().then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
setActive();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// XXX: We need to make this to the next tick to the UI gets updated
|
||||
// because we've prevented the default behavior in the handler.
|
||||
setTimeout(function() {
|
||||
setActive();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _onAddApnBtnClick = function(serviceId, apnType) {
|
||||
SettingsService.navigate('apn-editor',
|
||||
{
|
||||
mode: 'new',
|
||||
serviceId: serviceId,
|
||||
type: apnType
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var _onBackBtnClick = function() {
|
||||
if (_role === 'activity') {
|
||||
_role = null;
|
||||
Settings.finishActivityRequest();
|
||||
} else {
|
||||
SettingsService.navigate('apn-settings');
|
||||
}
|
||||
};
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function bp_onInit(rootElement) {
|
||||
_rootElement = rootElement;
|
||||
_mainHeader = rootElement.querySelector('gaia-header');
|
||||
_header = _mainHeader.querySelector('h1');
|
||||
_apnListViewRoot = rootElement.querySelector('.apn-list');
|
||||
_addApnBtn = rootElement.querySelector('button.add-apn');
|
||||
|
||||
_mainHeader.addEventListener('action', _onBackBtnClick);
|
||||
},
|
||||
onBeforeShow: function bp_onBeforeShow(rootElement, options) {
|
||||
_role = options.role || _role;
|
||||
|
||||
// When back from apn editor, there is no type and serviceId specified
|
||||
// so that we use the original type and service id.
|
||||
_apnType = options.type || _apnType;
|
||||
_serviceId = (options.serviceId === undefined) ?
|
||||
_serviceId : options.serviceId;
|
||||
|
||||
_header.setAttribute('data-l10n-id', HEADER_L10N_MAP[_apnType]);
|
||||
|
||||
var apnTemplate =
|
||||
ApnTemplateFactory(_apnType,
|
||||
_onApnItemClick.bind(null, _serviceId, _apnType),
|
||||
_onRadioClick.bind(null, _serviceId, _apnType));
|
||||
|
||||
_addApnBtn.onclick = _onAddApnBtnClick.bind(null, _serviceId, _apnType);
|
||||
|
||||
ApnSettingsManager.queryApns(_serviceId, _apnType)
|
||||
.then(function(apnItems) {
|
||||
_apnListView = ListView(_apnListViewRoot, apnItems, apnTemplate);
|
||||
});
|
||||
},
|
||||
onHide: function bp_onBeforeHide() {
|
||||
if (_apnListView) {
|
||||
_apnListView.destroy();
|
||||
_apnListView = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
71
gaia/apps/settings/js/panels/apn_settings/panel.js
Executable file
71
gaia/apps/settings/js/panels/apn_settings/panel.js
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
/* global DsdsSettings */
|
||||
/**
|
||||
* The apn settings panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var ApnSettingsManager = require('modules/apn/apn_settings_manager');
|
||||
var Toaster = require('shared/toaster');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
|
||||
return function ctor_apn_settings_panel() {
|
||||
var _serviceId = 0;
|
||||
|
||||
var _rootElement;
|
||||
|
||||
var _browseApnItems = function(apnType) {
|
||||
SettingsService.navigate(
|
||||
'apn-list',
|
||||
{
|
||||
type: apnType,
|
||||
serviceId: _serviceId
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var _resetApn = function() {
|
||||
DialogService.confirm('reset-apn-warning-message', {
|
||||
title: 'reset-apn-warning-title',
|
||||
submitButton: { id: 'reset', style: 'danger' },
|
||||
cancelButton: 'cancel'
|
||||
}).then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
ApnSettingsManager.restore(_serviceId).then(function() {
|
||||
var toast = {
|
||||
messageL10nId: 'apnSettings-reset',
|
||||
latency: 3000,
|
||||
useTransition: true
|
||||
};
|
||||
Toaster.showToast(toast);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function asp_onInit(rootElement) {
|
||||
_rootElement = rootElement;
|
||||
var apnBtns = rootElement.querySelectorAll('a[data-apn-type]');
|
||||
var resetBtn = rootElement.querySelector('button.reset-apn');
|
||||
var header = rootElement.querySelector('gaia-header');
|
||||
|
||||
if (DsdsSettings.getNumberOfIccSlots() > 1) {
|
||||
header.dataset.href = '#carrier-detail';
|
||||
} else {
|
||||
header.dataset.href = '#carrier';
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(apnBtns, function(btn) {
|
||||
btn.onclick = _browseApnItems.bind(null, btn.dataset.apnType);
|
||||
});
|
||||
resetBtn.onclick = _resetApn;
|
||||
},
|
||||
onBeforeShow: function asp_onBeforeShow() {
|
||||
_serviceId = DsdsSettings.getIccCardIndexForCellAndDataSettings();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
218
gaia/apps/settings/js/panels/app_permissions_detail/app_permissions_detail.js
Executable file
218
gaia/apps/settings/js/panels/app_permissions_detail/app_permissions_detail.js
Executable file
|
|
@ -0,0 +1,218 @@
|
|||
/**
|
||||
* Handle app_permissions_detail panel's functionality.
|
||||
*/
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var mozApps = require('modules/navigator/mozApps');
|
||||
var mozPerms = require('modules/navigator/mozPermissionSettings');
|
||||
|
||||
var PermissionsDetail = function pd() {
|
||||
this._elements = null;
|
||||
this._permissionsTable = null;
|
||||
this._app = null;
|
||||
};
|
||||
|
||||
PermissionsDetail.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*/
|
||||
init: function pd_init(elements, permissionsTable) {
|
||||
this._elements = elements;
|
||||
this._permissionsTable = permissionsTable;
|
||||
|
||||
// only go back to previous when application is uninstalled
|
||||
window.addEventListener('applicationuninstall', this.back);
|
||||
},
|
||||
|
||||
/**
|
||||
* Back to app_permissions_list panel.
|
||||
*/
|
||||
back: function pd_back() {
|
||||
SettingsService.navigate('appPermissions');
|
||||
},
|
||||
|
||||
/**
|
||||
* Show app detail page.
|
||||
*/
|
||||
showAppDetails: function pd_show_app_details(app, verbose) {
|
||||
this._isValidPerm = verbose ? this._isValidVerbosePerm
|
||||
: this._isExplicitPerm;
|
||||
this._app = app;
|
||||
var table = this._permissionsTable;
|
||||
var elements = this._elements;
|
||||
var manifest = new ManifestHelper(app.manifest ?
|
||||
app.manifest : app.updateManifest);
|
||||
var developer = manifest.developer;
|
||||
elements.detailTitle.textContent = manifest.displayName;
|
||||
elements.uninstallButton.disabled = !app.removable;
|
||||
if (!developer || !('name' in developer)) {
|
||||
elements.developerInfos.hidden = true;
|
||||
elements.developerHeader.hidden = true;
|
||||
} else {
|
||||
elements.developerName.textContent = developer.name;
|
||||
elements.developerInfos.hidden = false;
|
||||
elements.developerHeader.hidden = false;
|
||||
if (!developer.url) {
|
||||
delete elements.developerLink.href;
|
||||
elements.developerUrl.hidden = true;
|
||||
} else {
|
||||
elements.developerUrl.hidden = false;
|
||||
elements.developerLink.href = developer.url;
|
||||
elements.developerUrl.textContent = developer.url;
|
||||
}
|
||||
}
|
||||
if (!mozPerms) {
|
||||
elements.list.hidden = true;
|
||||
return;
|
||||
} else {
|
||||
elements.list.hidden = false;
|
||||
elements.list.innerHTML = '';
|
||||
}
|
||||
|
||||
table.plainPermissions.forEach(function(perm) {
|
||||
var value = mozPerms.get(perm, app.manifestURL,
|
||||
app.origin, false);
|
||||
if (this._isValidPerm(app, perm, value)) {
|
||||
this._insertPermissionSelect(perm, value);
|
||||
}
|
||||
}, this);
|
||||
|
||||
table.composedPermissions.forEach(function appIterator(perm) {
|
||||
var value = null;
|
||||
var display = table.accessModes.some(function modeIterator(mode) {
|
||||
var composedPerm = perm + '-' + mode;
|
||||
value = mozPerms.get(composedPerm, app.manifestURL, app.origin,
|
||||
false);
|
||||
if (this._isValidPerm(app, composedPerm, value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
if (display) {
|
||||
this._insertPermissionSelect(perm, value);
|
||||
}
|
||||
}, this);
|
||||
|
||||
elements.header.hidden = !elements.list.children.length;
|
||||
},
|
||||
|
||||
_isExplicitPerm: function pd_shouldDisplayPerm(app, perm, value) {
|
||||
var isExplicit = mozPerms.isExplicit(perm, app.manifestURL,
|
||||
app.origin, false);
|
||||
return (isExplicit && value !== 'unknown');
|
||||
},
|
||||
|
||||
_isValidVerbosePerm: function pd_displayPermVerbose(app, perm, value) {
|
||||
if (app.manifest.type !== 'certified') {
|
||||
return (value !== 'unknown');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detect event from user's selection of the permission.
|
||||
*/
|
||||
selectValueChanged: function pd_select_value_changed(evt) {
|
||||
var select = evt.target;
|
||||
select.setAttribute('value', select.value);
|
||||
this._changePermission(select.dataset.perm, select.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change permission of the app.
|
||||
*/
|
||||
_changePermission: function pd__change_permission(perm, value) {
|
||||
if (!mozPerms) {
|
||||
return;
|
||||
}
|
||||
|
||||
var table = this._permissionsTable;
|
||||
// We edit the composed permission for all the access modes
|
||||
if (table.composedPermissions.indexOf(perm) !== -1) {
|
||||
table.accessModes.forEach(function modeIterator(mode) {
|
||||
var composedPerm = perm + '-' + mode;
|
||||
|
||||
try {
|
||||
mozPerms.set(composedPerm, value, this._app.manifestURL,
|
||||
this._app.origin, false);
|
||||
} catch (e) {
|
||||
console.warn('Failed to set the ' + composedPerm + 'permission.');
|
||||
}
|
||||
}, this);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mozPerms.set(perm, value, this._app.manifestURL,
|
||||
this._app.origin, false);
|
||||
} catch (e) {
|
||||
console.warn('Failed to set the ' + perm + 'permission.');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show available selection option of permission in app detail dialog.
|
||||
*/
|
||||
_insertPermissionSelect:
|
||||
function pd__insert_permission_select(perm, value) {
|
||||
var item = document.createElement('li');
|
||||
var content = document.createElement('p');
|
||||
var contentL10nId = 'perm-' + perm.replace(':', '-');
|
||||
content.setAttribute('data-l10n-id', contentL10nId);
|
||||
|
||||
var fakeSelect = document.createElement('span');
|
||||
fakeSelect.classList.add('button', 'icon', 'icon-dialog');
|
||||
|
||||
var select = document.createElement('select');
|
||||
select.dataset.perm = perm;
|
||||
|
||||
var askOpt = document.createElement('option');
|
||||
askOpt.value = 'prompt';
|
||||
askOpt.setAttribute('data-l10n-id', 'ask');
|
||||
select.add(askOpt);
|
||||
|
||||
var allowOpt = document.createElement('option');
|
||||
allowOpt.value = 'allow';
|
||||
allowOpt.setAttribute('data-l10n-id', 'allow2');
|
||||
select.add(allowOpt);
|
||||
|
||||
var denyOpt = document.createElement('option');
|
||||
denyOpt.value = 'deny';
|
||||
denyOpt.setAttribute('data-l10n-id', 'block');
|
||||
select.add(denyOpt);
|
||||
|
||||
var opt = select.querySelector('[value="' + value + '"]');
|
||||
opt.setAttribute('selected', true);
|
||||
|
||||
select.value = select.options[select.selectedIndex].textContent;
|
||||
select.setAttribute('value', value);
|
||||
select.onchange = this.selectValueChanged.bind(this);
|
||||
|
||||
item.onclick = function focusSelect() {
|
||||
select.focus();
|
||||
};
|
||||
|
||||
fakeSelect.appendChild(select);
|
||||
item.appendChild(content);
|
||||
item.appendChild(fakeSelect);
|
||||
this._elements.list.appendChild(item);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall the choosed app.
|
||||
*/
|
||||
uninstall: function pd_uninstall() {
|
||||
mozApps.mgmt.uninstall(this._app).onsuccess = () => {
|
||||
this.back();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_app_permissions_detail() {
|
||||
return new PermissionsDetail();
|
||||
};
|
||||
});
|
||||
58
gaia/apps/settings/js/panels/app_permissions_detail/panel.js
Executable file
58
gaia/apps/settings/js/panels/app_permissions_detail/panel.js
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var PermissionDetail =
|
||||
require('panels/app_permissions_detail/app_permissions_detail');
|
||||
|
||||
return function ctor_app_permissions_detail_panel() {
|
||||
var elements = {};
|
||||
var permissionDetailModule = PermissionDetail();
|
||||
var uninstall =
|
||||
permissionDetailModule.uninstall.bind(permissionDetailModule);
|
||||
var back = permissionDetailModule.back.bind(permissionDetailModule);
|
||||
|
||||
function bindEvents(doms) {
|
||||
doms.uninstallButton.addEventListener('click', uninstall);
|
||||
doms.panelHeader.addEventListener('action', back);
|
||||
}
|
||||
|
||||
function unbindEvents(doms) {
|
||||
doms.uninstallButton.removeEventListener('click', uninstall);
|
||||
doms.panelHeader.removeEventListener('action', back);
|
||||
}
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel, options) {
|
||||
this._verbose = null;
|
||||
elements = {
|
||||
panelHeader: panel.querySelector('gaia-header'),
|
||||
uninstallButton: panel.querySelector('.uninstall-app > button'),
|
||||
list: panel.querySelector('.permissionsListHeader + ul'),
|
||||
header: panel.querySelector('.permissionsListHeader'),
|
||||
developerLink: panel.querySelector('.developer-infos > a'),
|
||||
developerName: panel.querySelector('.developer-infos > a > span'),
|
||||
developerUrl: panel.querySelector('.developer-infos > a > small'),
|
||||
developerInfos: panel.querySelector('.developer-infos'),
|
||||
developerHeader: panel.querySelector('.developer-header'),
|
||||
detailTitle: panel.querySelector('.detail-title')
|
||||
};
|
||||
SettingsListener.observe('debug.verbose_app_permissions', false,
|
||||
function(enabled) {
|
||||
this._verbose = enabled;
|
||||
}.bind(this));
|
||||
permissionDetailModule.init(elements, options.permissionsTable);
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
permissionDetailModule.showAppDetails(options.app, this._verbose);
|
||||
bindEvents(elements);
|
||||
},
|
||||
|
||||
onBeforeHide: function() {
|
||||
unbindEvents(elements);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
363
gaia/apps/settings/js/panels/app_permissions_list/app_permissions_list.js
Executable file
363
gaia/apps/settings/js/panels/app_permissions_list/app_permissions_list.js
Executable file
|
|
@ -0,0 +1,363 @@
|
|||
/**
|
||||
* Handle app_permissions_list panel's functionality.
|
||||
*
|
||||
* @module PermissionsList
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
var AppsCache = require('modules/apps_cache');
|
||||
var mozApps = require('modules/navigator/mozApps');
|
||||
var mozPerms = require('modules/navigator/mozPermissionSettings');
|
||||
|
||||
var PermissionsList = function pl() {
|
||||
this._listRoot = null;
|
||||
this._permissionsTable = null;
|
||||
this._permissionTableHaveProcessed = false;
|
||||
this._apps = null;
|
||||
this._enabled = false;
|
||||
};
|
||||
|
||||
PermissionsList.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {HTMLElement} listRoot
|
||||
* @access public
|
||||
*/
|
||||
init: function pl_init(listRoot) {
|
||||
this._listRoot = listRoot;
|
||||
|
||||
this._boundOnAppChoose = this._onAppChoose.bind(this);
|
||||
this._boundOnApplicationInstall = this._onApplicationInstall.bind(this);
|
||||
this._boundOnApplicationUninstall =
|
||||
this._onApplicationUninstall.bind(this);
|
||||
},
|
||||
|
||||
set enabled(value) {
|
||||
if (value !== this._enabled) {
|
||||
this._enabled = value;
|
||||
if (this._enabled) {
|
||||
this._bindEvents();
|
||||
} else {
|
||||
this._unbindEvents();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_bindEvents: function pl__bindEvents() {
|
||||
this._listRoot.addEventListener('click', this._boundOnAppChoose);
|
||||
AppsCache.addEventListener('oninstall', this._boundOnApplicationInstall);
|
||||
AppsCache.addEventListener('onuninstall',
|
||||
this._boundOnApplicationUninstall);
|
||||
},
|
||||
|
||||
_unbindEvents: function pl__unbindEvents() {
|
||||
this._listRoot.removeEventListener('click', this._boundOnAppChoose);
|
||||
AppsCache.removeEventListener('oninstall',
|
||||
this._boundOnApplicationInstall);
|
||||
AppsCache.removeEventListener('onuninstall',
|
||||
this._boundOnApplicationUninstall);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set /resources/permissions_table.json.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Object} permissionTable
|
||||
* @access public
|
||||
*/
|
||||
setPermissionsTable: function pl_setPermissionsTable(permissionTable) {
|
||||
this._permissionTable = permissionTable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the app list when we enter into panel.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access public
|
||||
* @return {Promise}
|
||||
*/
|
||||
refresh: function pl_refresh() {
|
||||
var self = this;
|
||||
this._apps = [];
|
||||
if (this._permissionTableHaveProcessed) {
|
||||
return this.loadApps();
|
||||
} else {
|
||||
return mozApps.getSelf().then(function(app) {
|
||||
return self._initExplicitPermissionsTable(app);
|
||||
}).then(function() {
|
||||
return self.loadApps();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Go to app_permissions_detail panel when user select an app.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Event} evt
|
||||
* @access public
|
||||
*/
|
||||
_onAppChoose: function pl__onAppChoose(evt) {
|
||||
if (evt.target.dataset && evt.target.dataset.appIndex) {
|
||||
SettingsService.navigate('appPermissions-details', {
|
||||
app: this._apps[evt.target.dataset.appIndex],
|
||||
permissionsTable: this._permissionTable
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When new application is installed, we push the app to list, sort them and
|
||||
* rerender the app list.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Event} evt
|
||||
* @access public
|
||||
*/
|
||||
_onApplicationInstall: function pl__onApplicationInstall(evt) {
|
||||
var app = evt.application;
|
||||
this._apps.push(app);
|
||||
this._sortApps();
|
||||
this.renderList();
|
||||
},
|
||||
|
||||
/**
|
||||
* When application is uninstalled, we remove it from list and rerender the
|
||||
* app list.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Event} evt
|
||||
* @access public
|
||||
*/
|
||||
_onApplicationUninstall: function pl__onApplicationUninstall(evt) {
|
||||
var app;
|
||||
var appIndex;
|
||||
this._apps.some(function findApp(anApp, index) {
|
||||
if (anApp.origin === evt.application.origin) {
|
||||
app = anApp;
|
||||
appIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
SettingsService.navigate('appPermissions');
|
||||
this._apps.splice(appIndex, 1);
|
||||
this.renderList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sort the applist by the name of its manifest.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access private
|
||||
*/
|
||||
_sortApps: function pl__sortApps() {
|
||||
this._apps.sort(function alphabeticalSort(app, otherApp) {
|
||||
var manifest = new ManifestHelper(app.manifest ?
|
||||
app.manifest : app.updateManifest);
|
||||
var otherManifest = new ManifestHelper(otherApp.manifest ?
|
||||
otherApp.manifest : otherApp.updateManifest);
|
||||
return manifest.name > otherManifest.name;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Genrate UI template of app item.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access private
|
||||
* @param {Object} itemData
|
||||
* @return {HTMLDivElement}
|
||||
*/
|
||||
_genAppItemTemplate: function pl__genAppItemTemplate(itemData) {
|
||||
var icon = document.createElement('img');
|
||||
var item = document.createElement('li');
|
||||
var link = document.createElement('a');
|
||||
var span = document.createElement('span');
|
||||
span.textContent = itemData.name;
|
||||
span.dir = 'auto';
|
||||
icon.src = itemData.iconSrc;
|
||||
link.dataset.appIndex = itemData.index;
|
||||
link.href = '#';
|
||||
link.classList.add('menu-item');
|
||||
link.appendChild(icon);
|
||||
link.appendChild(span);
|
||||
item.appendChild(link);
|
||||
return item;
|
||||
},
|
||||
|
||||
/**
|
||||
* Genrate UI template of app item.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access public
|
||||
*/
|
||||
renderList: function pl_renderList() {
|
||||
this._listRoot.innerHTML = '';
|
||||
var listFragment = document.createDocumentFragment();
|
||||
this._apps.forEach(function appIterator(app, index) {
|
||||
var manifest = new ManifestHelper(app.manifest ?
|
||||
app.manifest : app.updateManifest);
|
||||
var li = this._genAppItemTemplate({
|
||||
name: manifest.displayName,
|
||||
index: index,
|
||||
iconSrc: this._getBestIconURL(app, manifest.icons)
|
||||
});
|
||||
listFragment.appendChild(li);
|
||||
}.bind(this));
|
||||
this._listRoot.appendChild(listFragment);
|
||||
},
|
||||
|
||||
/**
|
||||
* Genrate explicitCertifiedPermissions table from plainPermissions table
|
||||
* table and "composedPermissions + accessModes" table.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Event} evt
|
||||
* @access private
|
||||
*/
|
||||
_initExplicitPermissionsTable:
|
||||
function pl__initExplicitPermissionsTable(app) {
|
||||
this._currentApp = app;
|
||||
|
||||
var table = this._permissionTable;
|
||||
table.explicitCertifiedPermissions = [];
|
||||
table.plainPermissions.forEach(function plainPermIterator(perm) {
|
||||
if (this._isExplicitPerm(perm)) {
|
||||
table.explicitCertifiedPermissions.push({
|
||||
explicitPermission: perm,
|
||||
permission: perm
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
table.composedPermissions.forEach(function permIterator(perm) {
|
||||
table.accessModes.forEach(function modeIterator(mode) {
|
||||
var composedPerm = perm + '-' + mode;
|
||||
if (this._isExplicitPerm(composedPerm)) {
|
||||
table.explicitCertifiedPermissions.push({
|
||||
explicitPermission: composedPerm,
|
||||
permission: perm
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
this._permissionTableHaveProcessed = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Identify the permission whether is explict or not.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access private
|
||||
* @return {Bool}
|
||||
*/
|
||||
_isExplicitPerm: function pl_isExplicitPerm(perm) {
|
||||
return mozPerms.isExplicit(perm, this._currentApp.manifestURL,
|
||||
this._currentApp.origin, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter explicit apps from moz apps, sort them, and render to screen.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @access public
|
||||
* @return {Promise}
|
||||
*/
|
||||
loadApps: function pl_loadApps() {
|
||||
var self = this;
|
||||
return AppsCache.apps().then(function(apps) {
|
||||
self._loadApps(apps);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterate internal apps and render them on UI.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Object[]} apps
|
||||
* @access private
|
||||
*/
|
||||
_loadApps: function pl__loadApps(apps) {
|
||||
var table = this._permissionTable;
|
||||
apps.forEach(function(app) {
|
||||
var manifest = app.manifest ? app.manifest : app.updateManifest;
|
||||
if (manifest.type != 'certified') {
|
||||
this._apps.push(app);
|
||||
return;
|
||||
}
|
||||
|
||||
var display = table.explicitCertifiedPermissions
|
||||
.some(function iterator(perm) {
|
||||
var permInfo = mozPerms.get(perm.explicitPermission,
|
||||
app.manifestURL, app.origin, false);
|
||||
return permInfo != 'unknown';
|
||||
}.bind(this));
|
||||
|
||||
if (display) {
|
||||
this._apps.push(app);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this._sortApps();
|
||||
this.renderList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get icon URL.
|
||||
*
|
||||
* @memberOf PermissionsList
|
||||
* @param {Object} app
|
||||
* @param {Object} icons
|
||||
* @access private
|
||||
*/
|
||||
_getBestIconURL: function pl__getBestIconURL(app, icons) {
|
||||
if (!icons || !Object.keys(icons).length) {
|
||||
return '../style/images/default.png';
|
||||
}
|
||||
|
||||
// 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 '../style/images/default.png';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_permissions_list() {
|
||||
return new PermissionsList();
|
||||
};
|
||||
});
|
||||
43
gaia/apps/settings/js/panels/app_permissions_list/panel.js
Executable file
43
gaia/apps/settings/js/panels/app_permissions_list/panel.js
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var PermissionList =
|
||||
require('panels/app_permissions_list/app_permissions_list');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
return function ctor_app_permissions_list_panel() {
|
||||
// We use this flag to identify permissions_table.json has been loaded or
|
||||
// not.
|
||||
var permissionsTableHasBeenLoaded = false;
|
||||
var elements = {};
|
||||
var permissionListModule = PermissionList();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {
|
||||
list: panel.querySelector('.app-list')
|
||||
};
|
||||
permissionListModule.init(elements.list);
|
||||
},
|
||||
|
||||
onBeforeShow: function() {
|
||||
if (permissionsTableHasBeenLoaded) {
|
||||
permissionListModule.refresh();
|
||||
} else {
|
||||
LazyLoader.getJSON('/resources/permissions_table.json')
|
||||
.then(function(data) {
|
||||
permissionsTableHasBeenLoaded = true;
|
||||
permissionListModule.setPermissionsTable(data);
|
||||
permissionListModule.refresh();
|
||||
});
|
||||
}
|
||||
permissionListModule.enabled = true;
|
||||
},
|
||||
|
||||
onBeforeHide: function() {
|
||||
permissionListModule.enabled = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
68
gaia/apps/settings/js/panels/app_storage/panel.js
Executable file
68
gaia/apps/settings/js/panels/app_storage/panel.js
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Used to show Storage/App Storage panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var AppStorage = require('modules/app_storage');
|
||||
var StorageHelper = require('modules/storage_helper');
|
||||
|
||||
return function ctor_app_storage_panel() {
|
||||
var _spaceBarElement = null;
|
||||
var _totalSpaceText = null;
|
||||
var _usedSpaceText = null;
|
||||
var _freeSpaceText = null;
|
||||
|
||||
var _updateUsePercentage = function() {
|
||||
_spaceBarElement.value = AppStorage.usedPercentage;
|
||||
};
|
||||
|
||||
var _refreshText = function(element, size) {
|
||||
StorageHelper.showFormatedSize(element,
|
||||
'storageSize', size);
|
||||
};
|
||||
var _updateTotalSize = function() {
|
||||
_refreshText(_totalSpaceText, AppStorage.totalSize);
|
||||
};
|
||||
var _updateUsedSize = function() {
|
||||
_refreshText(_usedSpaceText, AppStorage.usedSize);
|
||||
};
|
||||
var _updateFreeSize = function() {
|
||||
_refreshText(_freeSpaceText, AppStorage.freeSize);
|
||||
};
|
||||
|
||||
var _updateElements = function() {
|
||||
_updateUsePercentage();
|
||||
_updateTotalSize();
|
||||
_updateUsedSize();
|
||||
_updateFreeSize();
|
||||
};
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
_spaceBarElement = panel.querySelector('.apps-space-bar');
|
||||
_totalSpaceText = panel.querySelector('.apps-total-space');
|
||||
_usedSpaceText = panel.querySelector('.apps-used-space');
|
||||
_freeSpaceText = panel.querySelector('.apps-free-space');
|
||||
},
|
||||
|
||||
onBeforeShow: function() {
|
||||
AppStorage.observe('usedPercentage', _updateUsePercentage);
|
||||
AppStorage.observe('totalSize', _updateTotalSize);
|
||||
AppStorage.observe('usedSize', _updateUsedSize);
|
||||
AppStorage.observe('freeSize', _updateFreeSize);
|
||||
|
||||
_updateElements();
|
||||
AppStorage.updateInfo();
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
AppStorage.unobserve('usedPercentage', _updateUsePercentage);
|
||||
AppStorage.unobserve('totalSize', _updateTotalSize);
|
||||
AppStorage.unobserve('usedSize', _updateUsedSize);
|
||||
AppStorage.unobserve('freeSize', _updateFreeSize);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
73
gaia/apps/settings/js/panels/battery/panel.js
Executable file
73
gaia/apps/settings/js/panels/battery/panel.js
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* The battery panel displays battery information provided by Battery.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Battery = require('modules/battery');
|
||||
|
||||
return function ctor_battery_panel() {
|
||||
var _batteryLevelText = null;
|
||||
var _batteryTimeText = null;
|
||||
var _getBatteryTimeL10n = function(type) {
|
||||
type = type === 'charging' ? 'charging': 'discharging';
|
||||
|
||||
var time = Battery[type + 'Time'];
|
||||
var l10nAttrs = {
|
||||
id: 'battery-' + type + '-calculating',
|
||||
args: {}
|
||||
};
|
||||
|
||||
if (time !== Infinity && time > 0) {
|
||||
var timeLeft = new Date(0, 0, 0, 0, 0, time);
|
||||
l10nAttrs.args.hours = timeLeft.getHours();
|
||||
l10nAttrs.args.minutes = timeLeft.getMinutes();
|
||||
|
||||
if (l10nAttrs.args.hours > 0) {
|
||||
l10nAttrs.id = 'battery-' + type + '-hours';
|
||||
} else {
|
||||
l10nAttrs.id = 'battery-' + type + '-minutes';
|
||||
}
|
||||
} else if (time !== Infinity && time === 0) {
|
||||
l10nAttrs.id = 'battery-' + type + '-complete';
|
||||
}
|
||||
|
||||
return l10nAttrs;
|
||||
};
|
||||
var _refreshText = function() {
|
||||
document.l10n.setAttributes(_batteryLevelText,
|
||||
'battery-current-level', { level: Battery.level });
|
||||
|
||||
if (Battery.state === 'unplugged') {
|
||||
var l10nDischarging = _getBatteryTimeL10n('discharging');
|
||||
document.l10n.setAttributes(_batteryTimeText,
|
||||
l10nDischarging.id, l10nDischarging.args);
|
||||
}
|
||||
else {
|
||||
var l10nCharging = _getBatteryTimeL10n('charging');
|
||||
document.l10n.setAttributes(_batteryTimeText,
|
||||
l10nCharging.id, l10nCharging.args);
|
||||
}
|
||||
};
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function bp_onInit(rootElement) {
|
||||
_batteryLevelText = rootElement.querySelector(
|
||||
'#battery-level');
|
||||
_batteryTimeText = rootElement.querySelector(
|
||||
'#battery-time');
|
||||
},
|
||||
onBeforeShow: function bp_onBeforeShow(rootElement) {
|
||||
Battery.observe('level', _refreshText);
|
||||
Battery.observe('state', _refreshText);
|
||||
Battery.observe('chargingTime', _refreshText);
|
||||
Battery.observe('dischargingTime', _refreshText);
|
||||
_refreshText();
|
||||
},
|
||||
onBeforeHide: function bp_onBeforeHide() {
|
||||
Battery.unobserve(_refreshText);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
25
gaia/apps/settings/js/panels/browsing_privacy/browsing_privacy.js
Executable file
25
gaia/apps/settings/js/panels/browsing_privacy/browsing_privacy.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
function BrowsingPrivacy() {}
|
||||
|
||||
/**
|
||||
* Clear browser history.
|
||||
*/
|
||||
BrowsingPrivacy.prototype.clearHistory = function() {
|
||||
navigator.mozSettings.createLock().set({'clear.browser.history': true});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear browser private data.
|
||||
*/
|
||||
BrowsingPrivacy.prototype.clearPrivateData = function() {
|
||||
navigator.mozSettings.createLock().set({
|
||||
'clear.browser.private-data': true
|
||||
});
|
||||
};
|
||||
|
||||
return function() {
|
||||
return new BrowsingPrivacy();
|
||||
};
|
||||
});
|
||||
64
gaia/apps/settings/js/panels/browsing_privacy/panel.js
Executable file
64
gaia/apps/settings/js/panels/browsing_privacy/panel.js
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var BrowsingPrivacy = require('panels/browsing_privacy/browsing_privacy');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
|
||||
var browsingPrivacy = BrowsingPrivacy();
|
||||
var clearHistoryButton, clearPrivateDataButton;
|
||||
|
||||
function onInit(panel) {
|
||||
clearHistoryButton = panel.querySelector('.clear-history-button');
|
||||
clearPrivateDataButton = panel.querySelector('.clear-private-data-button');
|
||||
|
||||
clearHistoryButton.addEventListener('click',
|
||||
handleClearHistoryClick);
|
||||
clearPrivateDataButton.addEventListener('click',
|
||||
handleClearPrivateDataClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clear history button click.
|
||||
*/
|
||||
function handleClearHistoryClick() {
|
||||
DialogService.confirm('confirm-clear-history-desc', {
|
||||
title: 'confirm-clear-history-title',
|
||||
submitButton: { id: 'clear', style: 'danger' },
|
||||
cancelButton: 'cancel'
|
||||
}).then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
return browsingPrivacy.clearHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clear private data button click.
|
||||
*/
|
||||
function handleClearPrivateDataClick() {
|
||||
DialogService.confirm('confirm-clear-cookies-cache-desc', {
|
||||
title: 'confirm-clear-cookies-cache-title',
|
||||
submitButton: { id: 'delete', style: 'danger' },
|
||||
cancelButton: 'cancel'
|
||||
}).then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
return browsingPrivacy.clearPrivateData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onUninit() {
|
||||
clearHistoryButton.removeEventListener('click',
|
||||
handleClearHistoryClick);
|
||||
clearPrivateDataButton.removeEventListener('click',
|
||||
handleClearPrivateDataClick);
|
||||
}
|
||||
|
||||
return function() {
|
||||
return SettingsPanel({
|
||||
onInit: onInit,
|
||||
onUninit: onUninit
|
||||
});
|
||||
};
|
||||
});
|
||||
60
gaia/apps/settings/js/panels/call/call_constant.js
Executable file
60
gaia/apps/settings/js/panels/call/call_constant.js
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
|
||||
const NETWORK_TYPE_CATEGORY = {
|
||||
'gprs': 'gsm',
|
||||
'edge': 'gsm',
|
||||
'umts': 'gsm',
|
||||
'hsdpa': 'gsm',
|
||||
'hsupa': 'gsm',
|
||||
'hspa': 'gsm',
|
||||
'hspa+': 'gsm',
|
||||
'lte': 'gsm',
|
||||
'gsm': 'gsm',
|
||||
'is95a': 'cdma',
|
||||
'is95b': 'cdma',
|
||||
'1xrtt': 'cdma',
|
||||
'evdo0': 'cdma',
|
||||
'evdoa': 'cdma',
|
||||
'evdob': 'cdma',
|
||||
'ehrpd': 'cdma'
|
||||
};
|
||||
|
||||
const CALL_FORWARD_REASON = {
|
||||
'UNCONDITIONAL': 0,
|
||||
'MOBILE_BUSY': 1,
|
||||
'NO_REPLY': 2,
|
||||
'NOT_REACHABLE': 3
|
||||
};
|
||||
|
||||
const CALL_FORWARD_REASON_MAPPING = {
|
||||
'unConditional': CALL_FORWARD_REASON.UNCONDITIONAL,
|
||||
'mobileBusy': CALL_FORWARD_REASON.MOBILE_BUSY,
|
||||
'noReply': CALL_FORWARD_REASON.NO_REPLY,
|
||||
'notReachable': CALL_FORWARD_REASON.NOT_REACHABLE
|
||||
};
|
||||
|
||||
const CALL_FORWARD_ACTION = {
|
||||
'DISABLE': 0,
|
||||
'ENABLE': 1,
|
||||
'QUERY_STATUS': 2,
|
||||
'REGISTRATION': 3,
|
||||
'ERASURE': 4
|
||||
};
|
||||
|
||||
const CLIR_MAPPING = {
|
||||
'CLIR_DEFAULT': 0,
|
||||
'CLIR_INVOCATION': 1,
|
||||
'CLIR_SUPPRESSION': 2
|
||||
};
|
||||
|
||||
const CallConstant = {
|
||||
'NETWORK_TYPE_CATEGORY': NETWORK_TYPE_CATEGORY,
|
||||
'CALL_FORWARD_REASON': CALL_FORWARD_REASON,
|
||||
'CALL_FORWARD_REASON_MAPPING': CALL_FORWARD_REASON_MAPPING,
|
||||
'CALL_FORWARD_ACTION': CALL_FORWARD_ACTION,
|
||||
'CLIR_MAPPING': CLIR_MAPPING
|
||||
};
|
||||
|
||||
return CallConstant;
|
||||
});
|
||||
255
gaia/apps/settings/js/panels/call/call_forwarding.js
Executable file
255
gaia/apps/settings/js/panels/call/call_forwarding.js
Executable file
|
|
@ -0,0 +1,255 @@
|
|||
/**
|
||||
* Make sure all operations would be executed one by one.
|
||||
*
|
||||
* @module call/call_forwarding
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var CallSettingsTaskScheduler =
|
||||
require('panels/call/call_settings_task_scheduler');
|
||||
var Module = require('modules/base/module');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
var CallUtils = require('panels/call/call_utils');
|
||||
var CallConstant = require('panels/call/call_constant');
|
||||
var mozSettings = window.navigator.mozSettings;
|
||||
|
||||
/**
|
||||
* @class CallForwarding
|
||||
* @returns {CallForwarding}
|
||||
*/
|
||||
var CallForwarding = Module.create(function CallForwarding(cardIndex) {
|
||||
this.super(Observable).call(this);
|
||||
|
||||
this._cardIndex = cardIndex;
|
||||
this._cfReasonStates = [0, 0, 0, 0];
|
||||
}).extend(Observable);
|
||||
|
||||
Observable.defineObservableProperty(CallForwarding.prototype, 'state', {
|
||||
readonly: true,
|
||||
value: 'normal'
|
||||
});
|
||||
|
||||
// This will create following observable properties :
|
||||
//
|
||||
// unConditionalNumber
|
||||
// unConditionalEnabled
|
||||
// mobileBusyNumber
|
||||
// mobileBusyEnabled
|
||||
// noReplyNumber
|
||||
// noReplyEnabled
|
||||
// notReachableNumber
|
||||
// notReachableEnabled
|
||||
//
|
||||
Object.keys(CallConstant.CALL_FORWARD_REASON_MAPPING).forEach(
|
||||
(cfOptionName) => {
|
||||
Observable.defineObservableProperty(CallForwarding.prototype,
|
||||
cfOptionName + 'Number', {
|
||||
readonly: true,
|
||||
value: null
|
||||
});
|
||||
|
||||
Observable.defineObservableProperty(CallForwarding.prototype,
|
||||
cfOptionName + 'Enabled', {
|
||||
readonly: true,
|
||||
value: false
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This is an internal function that can help us get the right conn.
|
||||
*
|
||||
* @memberOf CallForwarding
|
||||
* @return {MozMobileConnection}
|
||||
*/
|
||||
CallForwarding.prototype._getConn = function() {
|
||||
var conn = window.navigator.mozMobileConnections[this._cardIndex];
|
||||
if (!conn) {
|
||||
this.throw('We can\'t find the conn object on card ' + this._cardIndex);
|
||||
} else {
|
||||
return conn;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* We can use this function to setCallForwardingValues on each type based
|
||||
* on passing options.key. And in order to make sure operations are safe,
|
||||
* we will put it inside TaskScheduler to help us execute function one by one.
|
||||
*
|
||||
* @memberOf CallForwarding
|
||||
* @param {Object} options
|
||||
* @param {String} options.key
|
||||
* @param {Number} options.number
|
||||
* @param {Boolean} options.enabled
|
||||
* @return {Promise}
|
||||
*/
|
||||
CallForwarding.prototype.setCallForwardingValues = function(options) {
|
||||
this._state = 'requesting';
|
||||
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
var conn = this._getConn(this._cardIndex);
|
||||
var key = options.key;
|
||||
var number = options.number;
|
||||
var enabled = !!options.enabled;
|
||||
|
||||
if (!key) {
|
||||
this._state = 'normal';
|
||||
reject();
|
||||
} else if (this._cfReasonStates[
|
||||
CallConstant.CALL_FORWARD_REASON_MAPPING[key]] === enabled) {
|
||||
// Bails out in case the reason is already enabled/disabled.
|
||||
this._state = 'normal';
|
||||
reject();
|
||||
} else {
|
||||
var mozMobileCFInfo = {};
|
||||
mozMobileCFInfo.action = enabled ?
|
||||
CallConstant.CALL_FORWARD_ACTION.REGISTRATION :
|
||||
CallConstant.CALL_FORWARD_ACTION.DISABLE;
|
||||
mozMobileCFInfo.reason =
|
||||
CallConstant.CALL_FORWARD_REASON_MAPPING[key];
|
||||
mozMobileCFInfo.serviceClass = conn.ICC_SERVICE_CLASS_VOICE;
|
||||
|
||||
// we only have to check phone's number when it is enabled
|
||||
if (enabled && !CallUtils.isPhoneNumberValid(number)) {
|
||||
this._state = 'normal';
|
||||
reject({
|
||||
name: 'callForwardingInvalidNumberError'
|
||||
});
|
||||
} else {
|
||||
mozMobileCFInfo.number = number;
|
||||
mozMobileCFInfo.timeSeconds =
|
||||
mozMobileCFInfo.reason !=
|
||||
CallConstant.CALL_FORWARD_REASON.NO_REPLY ? 0 : 20;
|
||||
CallSettingsTaskScheduler.enqueue({
|
||||
type: 'CALL_FORWARDING',
|
||||
func: () => {
|
||||
return conn.setCallForwardingOption(mozMobileCFInfo);
|
||||
}
|
||||
}).then(() => {
|
||||
this._state = 'normal';
|
||||
resolve({
|
||||
key: key,
|
||||
action: mozMobileCFInfo.action
|
||||
});
|
||||
}).catch(() => {
|
||||
this._state = 'normal';
|
||||
reject({
|
||||
name: 'callForwardingSetError'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* We can use this function to get specific call forwarding option.
|
||||
*
|
||||
* @memberOf CallForwarding
|
||||
* @param {String} optionName
|
||||
* @return {Promise}
|
||||
*/
|
||||
CallForwarding.prototype._getOption = function(optionName) {
|
||||
var conn = this._getConn();
|
||||
|
||||
if (!(optionName in CallConstant.CALL_FORWARD_REASON)) {
|
||||
this.debug('Your optionName may be wrong - ', optionName);
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return conn.getCallForwardingOption(
|
||||
CallConstant.CALL_FORWARD_REASON[optionName]);
|
||||
};
|
||||
|
||||
/**
|
||||
* We can get all call forwarding related options at the same time.
|
||||
*
|
||||
* @memberOf CallForwarding
|
||||
* @return {Promise}
|
||||
*/
|
||||
CallForwarding.prototype.refresh = function() {
|
||||
this._state = 'requesting';
|
||||
|
||||
return CallSettingsTaskScheduler.enqueue({
|
||||
type: 'CALL_FORWARDING',
|
||||
func: () => {
|
||||
return Promise.all([
|
||||
this._getOption('UNCONDITIONAL'),
|
||||
this._getOption('MOBILE_BUSY'),
|
||||
this._getOption('NO_REPLY'),
|
||||
this._getOption('NOT_REACHABLE')
|
||||
]);
|
||||
}
|
||||
}).then((results) => {
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
var storedOptions = {};
|
||||
var cfOptions = {
|
||||
'unConditional': results[0],
|
||||
'mobileBusy': results[1],
|
||||
'noReply': results[2],
|
||||
'notReachable': results[3]
|
||||
};
|
||||
|
||||
Object.keys(cfOptions).forEach(function(cfOptionName) {
|
||||
var rules = cfOptions[cfOptionName];
|
||||
var rule = CallUtils.findActiveVoiceRule(rules);
|
||||
if (rule) {
|
||||
this._cfReasonStates[
|
||||
CallConstant.CALL_FORWARD_REASON_MAPPING[cfOptionName]] = 1;
|
||||
|
||||
this['_' + cfOptionName + 'Number'] = rule.number;
|
||||
this['_' + cfOptionName + 'Enabled'] = true;
|
||||
|
||||
if (cfOptionName === 'unConditional') {
|
||||
// Send the latest query result from carrier to system app
|
||||
storedOptions['ril.cf.carrier.enabled'] = {
|
||||
enabled: true,
|
||||
index: this._cardIndex
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this._cfReasonStates[
|
||||
CallConstant.CALL_FORWARD_REASON_MAPPING[cfOptionName]] = 0;
|
||||
|
||||
this['_' + cfOptionName + 'Number'] = '';
|
||||
this['_' + cfOptionName + 'Enabled'] = false;
|
||||
|
||||
// Send the latest query result from carrier to system app
|
||||
if (cfOptionName === 'unConditional') {
|
||||
storedOptions['ril.cf.carrier.enabled'] = {
|
||||
enabled: false,
|
||||
index: this._cardIndex
|
||||
};
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
mozSettings.createLock().set(storedOptions).then(() => {
|
||||
this._state = 'normal';
|
||||
resolve(cfOptions);
|
||||
}, () => {
|
||||
this._state = 'error';
|
||||
this.error('Something error happened when storing into settings db');
|
||||
reject();
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
}, () => {
|
||||
this._state = 'error';
|
||||
this.error('Something error happened when refresh');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Know whether unconditional call forwarding is set on this simcard.
|
||||
*
|
||||
* @memberOf CallForwarding
|
||||
* @return {Boolean}
|
||||
*/
|
||||
CallForwarding.prototype.isUnconditionalCFOn = function() {
|
||||
return this._cfReasonStates[0] === 1;
|
||||
};
|
||||
|
||||
return CallForwarding;
|
||||
});
|
||||
65
gaia/apps/settings/js/panels/call/call_settings_task_scheduler.js
Executable file
65
gaia/apps/settings/js/panels/call/call_settings_task_scheduler.js
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* This is the wrapper of TaskScheduler for Call Settings use.
|
||||
*
|
||||
* @module call/call_settings_task_scheduler
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var Module = require('modules/base/module');
|
||||
var TaskScheduler = require('modules/base/task_scheduler');
|
||||
|
||||
/**
|
||||
* @class CallSettingsTaskScheduler
|
||||
* @returns {CallSettingsTaskScheduler}
|
||||
*/
|
||||
var CallSettingsTaskScheduler =
|
||||
Module.create(function CallSettingsTaskScheduler() {
|
||||
this.super(TaskScheduler).call(this);
|
||||
}).extend(TaskScheduler);
|
||||
|
||||
const TASK_TYPE = {
|
||||
GENERAL: 0,
|
||||
CALL_FORWARDING: 1,
|
||||
CALL_WAITING: 2,
|
||||
CALLER_ID_PREF: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberOf CallSettingsTaskScheduler
|
||||
* @static
|
||||
*/
|
||||
Object.defineProperty(CallSettingsTaskScheduler, 'TASK_TYPE', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: function() {
|
||||
return TASK_TYPE;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* We will let pre-defined task_type pass here and then pass it
|
||||
* to TaskScheduler.
|
||||
*
|
||||
* @param {Object} task
|
||||
* @param {String} task.type
|
||||
* @param {Function} task.func
|
||||
* @memberOf CallSettingsTaskScheduler
|
||||
* @return {Promise}
|
||||
*/
|
||||
CallSettingsTaskScheduler.prototype.enqueue = function(task) {
|
||||
var {type, func} = task;
|
||||
if (!CallSettingsTaskScheduler.TASK_TYPE[type]) {
|
||||
this.throw('You are enqueuing an unknown type - ', type,
|
||||
'please check again');
|
||||
} else {
|
||||
return TaskScheduler.prototype.enqueue.call(this, {
|
||||
type: type,
|
||||
func: func,
|
||||
cancelable: (type !== 'GENERAL')
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return CallSettingsTaskScheduler();
|
||||
});
|
||||
41
gaia/apps/settings/js/panels/call/call_utils.js
Executable file
41
gaia/apps/settings/js/panels/call/call_utils.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
|
||||
var conns = window.navigator.mozMobileConnections;
|
||||
|
||||
var CallUtils = {
|
||||
/**
|
||||
* Helper function. Check whether the phone number is valid or not.
|
||||
*
|
||||
* @param {String} number The phone number to check.
|
||||
* @return {Boolean} Result.
|
||||
*/
|
||||
isPhoneNumberValid: function(number) {
|
||||
if (number) {
|
||||
var re = /^([\+]*[0-9])+$/;
|
||||
if (re.test(number)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find needed voice rule from passed-in rules. It is possible to have
|
||||
* different rules here, but after checking with Gecko gurus, we only
|
||||
* use "voice rule" now. So, we only need this one.
|
||||
*
|
||||
* @param {Array} rules
|
||||
* @return {Object}
|
||||
*/
|
||||
findActiveVoiceRule: function(rules) {
|
||||
rules = rules || [];
|
||||
return rules.find((rule) => {
|
||||
return (rule.active &&
|
||||
(rule.serviceClass & conns[0].ICC_SERVICE_CLASS_VOICE) !==0);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return CallUtils;
|
||||
});
|
||||
466
gaia/apps/settings/js/panels/call/panel.js
Executable file
466
gaia/apps/settings/js/panels/call/panel.js
Executable file
|
|
@ -0,0 +1,466 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var CallSettingsTaskScheduler =
|
||||
require('panels/call/call_settings_task_scheduler');
|
||||
var CallConstant = require('panels/call/call_constant');
|
||||
var SettingsHelper = require('shared/settings_helper');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var DsdsSettings = require('dsds_settings');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
|
||||
return function ctor_call_panel() {
|
||||
return SettingsPanel({
|
||||
onInit: function(panel, options) {
|
||||
this._cardIndex = options.cardIndex || 0;
|
||||
this._conns = window.navigator.mozMobileConnections;
|
||||
this._conn = this._conns[this._cardIndex];
|
||||
|
||||
var $ = function(selector) {
|
||||
return panel.querySelector(selector);
|
||||
};
|
||||
|
||||
this._elements = {
|
||||
panel: panel,
|
||||
header: $('gaia-header'),
|
||||
rilCallerId: $('#ril-callerId'),
|
||||
fdnDesc: $('#fdnSettings-desc'),
|
||||
alertPanel: $('.cw-alert'),
|
||||
alertPanelSetButton: $('.cw-alert .cw-alert-set'),
|
||||
alertPanelConfirmInput: $('.cw-alert .cw-alert-checkbox'),
|
||||
alertPanelCancelButton: $('.cw-alert .cw-alert-cancel'),
|
||||
alertLabel: $('#menuItem-callWaiting .cw-alert-icon'),
|
||||
callFdnItem: $('#menuItem-callFdn'),
|
||||
callFdnAnchor: $('#menuItem-callFdn a'),
|
||||
callWaitingItem: $('#menuItem-callWaiting'),
|
||||
callWaitingInput: $('#menuItem-callWaiting gaia-switch'),
|
||||
callForwardingItem: $('#menuItem-callForwarding'),
|
||||
callForwardingAnchor: $('#menuItem-callForwarding a'),
|
||||
callBarringItem: $('#menuItem-callBarring'),
|
||||
callerIdItem: $('#menuItem-callerId'),
|
||||
voiceMailItem: $('.menuItem-voicemail'),
|
||||
voiceMailDesc: $('#voiceMail-desc'),
|
||||
voicePrivacyItem: $('#menuItem-voicePrivacyMode'),
|
||||
voicePrivacyInput: $('#menuItem-voicePrivacyMode gaia-switch')
|
||||
};
|
||||
|
||||
// Set the navigation correctly when on a multi ICC card device.
|
||||
if (DsdsSettings.getNumberOfIccSlots() > 1) {
|
||||
this._elements.header.setAttribute('data-href', '#call-iccs');
|
||||
}
|
||||
|
||||
this._bindPanelReadyEvent();
|
||||
this._bindCallForwardingMenuItemClickEvent();
|
||||
this._bindCallFdnMenuItemClickEvent();
|
||||
|
||||
this._addVoiceTypeChangeListeners();
|
||||
|
||||
this._initCallerId();
|
||||
this._initCallWaiting();
|
||||
this._initVoiceMailSettings();
|
||||
this._initVoicePrivacyModeChangeEvent();
|
||||
this._initVoicemailClickEvent();
|
||||
this._initCallWaitingAlertLabelClickEvent();
|
||||
this._initCallWaitingAlertCancelButtonClickEvent();
|
||||
|
||||
CallSettingsTaskScheduler.observe('isLocked', (isLocked) => {
|
||||
this._enableTapOn('callerIdItem', !isLocked);
|
||||
this._enableTapOn('callWaitingInput', !isLocked);
|
||||
});
|
||||
|
||||
this._refreshCallSettingItems();
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
if (typeof options.cardIndex !== 'undefined') {
|
||||
this._cardIndex = options.cardIndex;
|
||||
this._conn = this._conns[options.cardIndex];
|
||||
}
|
||||
|
||||
this._initFDNItem();
|
||||
this._updateNetworkTypeLimitedItemsVisibility(
|
||||
this._conn.voice && this._conn.voice.type);
|
||||
this._refreshCallSettingItems();
|
||||
},
|
||||
|
||||
_bindPanelReadyEvent: function() {
|
||||
window.addEventListener('panelready', (evt) => {
|
||||
switch (evt.detail.current) {
|
||||
case '#call':
|
||||
// No need to refresh the call settings items if navigated from
|
||||
// panels not manipulating call settings.
|
||||
if (evt.detail.previous === '#call-forwarding' ||
|
||||
evt.detail.previous === '#call-cbSettings') {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_bindCallForwardingMenuItemClickEvent: function() {
|
||||
this._elements.callForwardingAnchor.onclick = () => {
|
||||
SettingsService.navigate('call-forwarding', {
|
||||
cardIndex: this._cardIndex
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
_bindCallFdnMenuItemClickEvent: function() {
|
||||
this._elements.callFdnAnchor.onclick = () => {
|
||||
SettingsService.navigate('call-fdnSettings', {
|
||||
cardIndex: this._cardIndex
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
_initCallerId: function() {
|
||||
// We listen for blur events so that way we set the CLIR mode once the
|
||||
// user clicks on the OK button.
|
||||
this._elements.rilCallerId.onblur = () => {
|
||||
var clirMode = CallConstant.CLIR_MAPPING[
|
||||
this._elements.rilCallerId.value];
|
||||
this._conn.setCallingLineIdRestriction(clirMode).then(() => {
|
||||
// If the setting success, system app will sync the value.
|
||||
}, () => {
|
||||
// If the setting fails,
|
||||
// we force sync the value here and update the UI.
|
||||
this._updateCallerIdPreference().then(() => {
|
||||
this._updateCallerIdItemState();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// System app will sync the value 'ril.clirMode' with the carrier
|
||||
navigator.mozSettings.addObserver('ril.clirMode', () => {
|
||||
this._updateCallerIdItemState();
|
||||
});
|
||||
},
|
||||
|
||||
_initCallWaiting: function() {
|
||||
this._elements.alertPanelSetButton.onclick = () => {
|
||||
var checked = this._elements.alertPanelConfirmInput.checked;
|
||||
this._conn.setCallWaitingOption(checked).then(() => {
|
||||
this._elements.alertPanel.hidden = true;
|
||||
this._updateCallWaitingItemState();
|
||||
}, () => {
|
||||
this._elements.alertPanel.hidden = true;
|
||||
this._updateCallWaitingItemState();
|
||||
});
|
||||
};
|
||||
|
||||
// Bind call waiting setting to the input
|
||||
this._elements.callWaitingInput.addEventListener('change', () => {
|
||||
var checked = this._elements.callWaitingInput.checked;
|
||||
this._conn.setCallWaitingOption(checked).then(() => {
|
||||
this._updateCallWaitingItemState();
|
||||
}, () => {
|
||||
this._updateCallWaitingItemState();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_initVoiceMailSettings: function() {
|
||||
// update all voice numbers if necessary
|
||||
SettingsCache.getSettings((results) => {
|
||||
var settings = navigator.mozSettings;
|
||||
var voicemail = navigator.mozVoicemail;
|
||||
var updateVMNumber = false;
|
||||
var numbers = results['ril.iccInfo.mbdn'] || [];
|
||||
|
||||
[].forEach.call(this._conns, (conn, index) => {
|
||||
var number = numbers[index];
|
||||
// If the voicemail number has not been stored into the database yet
|
||||
// we check whether the number is provided by the mozVoicemail API.
|
||||
// In that case we store it into the setting database.
|
||||
if (!number && voicemail) {
|
||||
number = voicemail.getNumber(index);
|
||||
if (number) {
|
||||
updateVMNumber = true;
|
||||
numbers[index] = number;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (updateVMNumber) {
|
||||
settings.createLock().set({
|
||||
'ril.iccInfo.mbdn': numbers
|
||||
}).then(() => {
|
||||
this._updateVoiceMailItemState();
|
||||
settings.addObserver('ril.iccInfo.mbdn', () => {
|
||||
this._updateVoiceMailItemState();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this._updateVoiceMailItemState();
|
||||
settings.addObserver('ril.iccInfo.mbdn', () => {
|
||||
this._updateVoiceMailItemState();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_initFDNItem: function() {
|
||||
var iccId = navigator.mozMobileConnections[this._cardIndex].iccId;
|
||||
if (iccId) {
|
||||
var icc = navigator.mozIccManager.getIccById(iccId);
|
||||
if (icc) {
|
||||
icc.getServiceState('fdn').then((hasFdn) => {
|
||||
this._elements.callFdnItem.hidden = !hasFdn;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_initVoicemailClickEvent: function() {
|
||||
this._elements.voiceMailItem.onclick = () => {
|
||||
DialogService.show('call-voiceMailSettings');
|
||||
};
|
||||
},
|
||||
|
||||
_initCallWaitingAlertLabelClickEvent: function() {
|
||||
this._elements.alertLabel.onclick = () => {
|
||||
this._elements.alertPanelConfirmInput.checked = false;
|
||||
this._elements.alertPanel.hidden = false;
|
||||
};
|
||||
},
|
||||
|
||||
_initVoicePrivacyModeChangeEvent: function() {
|
||||
var defaultVoicePrivacySettings = [].map.call(this._conns, () => {
|
||||
return false;
|
||||
});
|
||||
|
||||
var voicePrivacyHelper = SettingsHelper(
|
||||
'ril.voicePrivacy.enabled', defaultVoicePrivacySettings);
|
||||
|
||||
this._elements.voicePrivacyInput.addEventListener('change', () => {
|
||||
var checked = this._elements.voicePrivacyInput.checked;
|
||||
voicePrivacyHelper.get((values) => {
|
||||
var originalValue = !checked;
|
||||
this._conn.setVoicePrivacyMode(checked).then(() => {
|
||||
values[this._cardIndex] = !originalValue;
|
||||
voicePrivacyHelper.set(values);
|
||||
}, () => {
|
||||
// restore the value if failed.
|
||||
this._elements.voicePrivacyInput.checked = originalValue;
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_initCallWaitingAlertCancelButtonClickEvent: function() {
|
||||
this._elements.alertPanelCancelButton.onclick = () => {
|
||||
this._elements.alertPanel.hidden = true;
|
||||
};
|
||||
},
|
||||
|
||||
_addVoiceTypeChangeListeners: function() {
|
||||
var voiceTypes = [].map.call(this._conns, () => {
|
||||
return null;
|
||||
});
|
||||
|
||||
[].forEach.call(this._conns, (conn, index) => {
|
||||
voiceTypes[index] = conn.voice.type;
|
||||
conn.addEventListener('voicechange', () => {
|
||||
var newType = conn.voice.type;
|
||||
if (index !== this._cardIndex || voiceTypes[index] === newType) {
|
||||
return;
|
||||
}
|
||||
|
||||
voiceTypes[index] = newType;
|
||||
|
||||
this._updateNetworkTypeLimitedItemsVisibility(newType);
|
||||
this._refreshCallSettingItems();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_updateNetworkTypeLimitedItemsVisibility: function(voiceType) {
|
||||
// The following features are limited to GSM types.
|
||||
this._elements.callForwardingItem.hidden =
|
||||
this._elements.callBarringItem.hidden =
|
||||
this._elements.callWaitingItem.hidden =
|
||||
this._elements.callerIdItem.hidden =
|
||||
(CallConstant.NETWORK_TYPE_CATEGORY[voiceType] !== 'gsm');
|
||||
|
||||
// The following feature is limited to CDMA types.
|
||||
this._elements.voicePrivacyItem.hidden =
|
||||
(CallConstant.NETWORK_TYPE_CATEGORY[voiceType] !== 'cdma');
|
||||
},
|
||||
|
||||
_refreshCallSettingItems: function() {
|
||||
this._updateVoiceMailItemState();
|
||||
this._updateFdnStatus();
|
||||
this._updateVoicePrivacyItemState();
|
||||
this._updateCallerIdPreference().then(() => {
|
||||
this._updateCallerIdItemState();
|
||||
});
|
||||
this._updateCallWaitingItemState();
|
||||
},
|
||||
|
||||
_enableTapOn: function(elementName, enable) {
|
||||
var element = this._elements[elementName];
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
element.removeAttribute('aria-disabled');
|
||||
} else {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
}
|
||||
},
|
||||
|
||||
_updateCallerIdPreference: function() {
|
||||
// TODO
|
||||
// move this into separate module like CallForwarding
|
||||
return CallSettingsTaskScheduler.enqueue({
|
||||
type: 'CALLER_ID_PREF',
|
||||
func: () => {
|
||||
return this._conn.getCallingLineIdRestriction();
|
||||
}
|
||||
}).then((result) => {
|
||||
var promise = new Promise((resolve) => {
|
||||
var value = 0; //CLIR_DEFAULT
|
||||
|
||||
// In some legitimates error cases (FdnCheckFailure),
|
||||
// the req.result is undefined. This is fine, we want this,
|
||||
// and in this case we will just display an error message
|
||||
// for all the matching requests.
|
||||
if (result) {
|
||||
switch (result.m) {
|
||||
case 1: // Permanently provisioned
|
||||
case 3: // Temporary presentation disallowed
|
||||
case 4: // Temporary presentation allowed
|
||||
switch (result.n) {
|
||||
case 1: // CLIR invoked, CLIR_INVOCATION
|
||||
case 2: // CLIR suppressed, CLIR_SUPPRESSION
|
||||
case 0: // Network default, CLIR_DEFAULT
|
||||
value = result.n; //'CLIR_INVOCATION'
|
||||
break;
|
||||
default:
|
||||
value = 0; //CLIR_DEFAULT
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0: // Not Provisioned
|
||||
case 2: // Unknown (network error, etc)
|
||||
/* fall through */
|
||||
default:
|
||||
value = 0; //CLIR_DEFAULT
|
||||
break;
|
||||
}
|
||||
|
||||
SettingsCache.getSettings((results) => {
|
||||
var preferences = results['ril.clirMode'] || [0, 0];
|
||||
preferences[this._cardIndex] = value;
|
||||
navigator.mozSettings.createLock().set({
|
||||
'ril.clirMode': preferences
|
||||
}).then(resolve, resolve);
|
||||
});
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
});
|
||||
},
|
||||
|
||||
_updateCallerIdItemState: function() {
|
||||
if (this._elements.callerIdItem.hidden) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
var promise = new Promise((resolve) => {
|
||||
SettingsCache.getSettings((results) => {
|
||||
var preferences = results['ril.clirMode'];
|
||||
var preference = preferences && preferences[this._cardIndex];
|
||||
var value;
|
||||
switch (preference) {
|
||||
case 1: // CLIR invoked
|
||||
value = 'CLIR_INVOCATION';
|
||||
break;
|
||||
case 2: // CLIR suppressed
|
||||
value = 'CLIR_SUPPRESSION';
|
||||
break;
|
||||
case 0: // Network default
|
||||
/* fall through */
|
||||
default:
|
||||
value = 'CLIR_DEFAULT';
|
||||
break;
|
||||
}
|
||||
this._elements.rilCallerId.value = value;
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
},
|
||||
|
||||
_updateCallWaitingItemState: function() {
|
||||
if (this._elements.callWaitingItem.hidden) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
// TODO
|
||||
// move this into separate module like CallForwarding
|
||||
return CallSettingsTaskScheduler.enqueue({
|
||||
type: 'CALL_WAITING',
|
||||
func: () => {
|
||||
return this._conn.getCallWaitingOption();
|
||||
}
|
||||
}).then((enabled) => {
|
||||
this._elements.callWaitingInput.checked = enabled;
|
||||
if (enabled) {
|
||||
this._elements.callWaitingItem.dataset.state = 'on';
|
||||
} else {
|
||||
this._elements.callWaitingItem.dataset.state = 'off';
|
||||
}
|
||||
}).catch(() => {
|
||||
this._elements.callWaitingItem.dataset.state = 'unknown';
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_updateVoiceMailItemState: function() {
|
||||
this._elements.voiceMailDesc.textContent = '';
|
||||
SettingsCache.getSettings((results) => {
|
||||
var numbers = results['ril.iccInfo.mbdn'];
|
||||
var number = numbers[this._cardIndex];
|
||||
if (number) {
|
||||
this._elements.voiceMailDesc.removeAttribute('data-l10n-id');
|
||||
this._elements.voiceMailDesc.textContent = number;
|
||||
} else {
|
||||
this._elements.voiceMailDesc.setAttribute(
|
||||
'data-l10n-id', 'voiceMail-number-notSet');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_updateVoicePrivacyItemState: function() {
|
||||
if (this._elements.voicePrivacyItem.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._conn.getVoicePrivacyMode().then((enabled) => {
|
||||
this._elements.voicePrivacyInput.checked = enabled;
|
||||
}, (error) => {
|
||||
console.log('get voice privacy mode error');
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
|
||||
_updateFdnStatus: function() {
|
||||
var iccId = navigator.mozMobileConnections[this._cardIndex].iccId;
|
||||
if (iccId) {
|
||||
var icc = navigator.mozIccManager.getIccById(iccId);
|
||||
if (icc) {
|
||||
icc.getCardLock('fdn').then((result) => {
|
||||
var enabled = result.enabled;
|
||||
this._elements.fdnDesc.setAttribute('data-l10n-id',
|
||||
enabled ? 'enabled' : 'disabled');
|
||||
this._elements.callForwardingItem.hidden = enabled;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
218
gaia/apps/settings/js/panels/call_barring/call_barring.js
Executable file
218
gaia/apps/settings/js/panels/call_barring/call_barring.js
Executable file
|
|
@ -0,0 +1,218 @@
|
|||
/**
|
||||
* Call Barring Settings
|
||||
* Manage the state of the different services of call barring
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
|
||||
var _cbAction = {
|
||||
CALL_BARRING_BAOC: 0, // BAOC: Barring All Outgoing Calls
|
||||
CALL_BARRING_BOIC: 1, // BOIC: Barring Outgoing International Calls
|
||||
CALL_BARRING_BOICexHC: 2, // BOICexHC: Barring Outgoing International
|
||||
// Calls Except to Home Country
|
||||
CALL_BARRING_BAIC: 3, // BAIC: Barring All Incoming Calls
|
||||
CALL_BARRING_BAICr: 4 // BAICr: Barring All Incoming Calls in Roaming
|
||||
};
|
||||
|
||||
var _cbServiceMapper = {
|
||||
'baoc': _cbAction.CALL_BARRING_BAOC,
|
||||
'boic': _cbAction.CALL_BARRING_BOIC,
|
||||
'boicExhc': _cbAction.CALL_BARRING_BOICexHC,
|
||||
'baic': _cbAction.CALL_BARRING_BAIC,
|
||||
'baicR': _cbAction.CALL_BARRING_BAICr
|
||||
};
|
||||
|
||||
var call_barring_prototype = {
|
||||
// settings
|
||||
baoc: '',
|
||||
boic: '',
|
||||
boicExhc: '',
|
||||
baic: '',
|
||||
baicR: '',
|
||||
// enabled state for the settings
|
||||
baoc_enabled: '',
|
||||
boic_enabled: '',
|
||||
boicExhc_enabled: '',
|
||||
baic_enabled: '',
|
||||
baicR_enabled: '',
|
||||
|
||||
// updatingState
|
||||
updating: false,
|
||||
|
||||
_enable: function(elementArray) {
|
||||
elementArray.forEach(function disable(element) {
|
||||
this[element + '_enabled'] = true;
|
||||
}.bind(this));
|
||||
|
||||
// If barring All Outgoing is set, disable the rest of outgoing calls
|
||||
if (!!this.baoc) {
|
||||
this.boic_enabled = false;
|
||||
this.boicExhc_enabled = false;
|
||||
}
|
||||
// If barring All Incoming is active, disable the rest of incoming calls
|
||||
if (!!this.baic) {
|
||||
this.baicR_enabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
_disable: function(elementArray) {
|
||||
elementArray.forEach(function disable(element) {
|
||||
this[element + '_enabled'] = false;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a request to the RIL for the current state of a specific
|
||||
* call barring option.
|
||||
* @param id Code of the service we want to request the state of
|
||||
* @returns Promise with result/error of the request
|
||||
*/
|
||||
_getRequest: function(api, id) {
|
||||
var callOptions = {
|
||||
'program': id,
|
||||
'serviceClass': api.ICC_SERVICE_CLASS_VOICE
|
||||
};
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Send the request
|
||||
var request = api.getCallBarringOption(callOptions);
|
||||
request.onsuccess = function() {
|
||||
resolve(request.result.enabled);
|
||||
};
|
||||
request.onerror = function() {
|
||||
/* request.error = { name, message } */
|
||||
reject(request.error);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a request to the RIL to change the current state of a specific
|
||||
* call barring option.
|
||||
* @param options - options object with the details of the new state
|
||||
* @param options.program - id of the service to update
|
||||
* @param options.enabled - new state for the service
|
||||
* @param options.password - password introduced by the user
|
||||
* @param options.serviceClass - type of RIL service (voice in this case)
|
||||
*/
|
||||
_setRequest: function(api, options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Send the request
|
||||
var request = api.setCallBarringOption(options);
|
||||
request.onsuccess = function() {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = function() {
|
||||
/* request.error = { name, message } */
|
||||
reject(request.error);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
set: function(api, setting, password) {
|
||||
// Check for updating in progress
|
||||
if (!!this.updating) {
|
||||
return;
|
||||
}
|
||||
// Check for API to be called
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.updating = true;
|
||||
var allElements = [
|
||||
'baoc',
|
||||
'boic',
|
||||
'boicExhc',
|
||||
'baic',
|
||||
'baicR'
|
||||
];
|
||||
self._disable(allElements);
|
||||
// get options
|
||||
var options = {
|
||||
'program': _cbServiceMapper[setting],
|
||||
'enabled': !self[setting],
|
||||
'password': password,
|
||||
'serviceClass': api.ICC_SERVICE_CLASS_VOICE
|
||||
};
|
||||
|
||||
var error = null;
|
||||
self._setRequest(api, options).then(function success() {
|
||||
self[setting] = !self[setting];
|
||||
}).catch(function errored(err) {
|
||||
error = err;
|
||||
}).then(function doAnyways() {
|
||||
self.updating = false;
|
||||
self._enable(allElements);
|
||||
if (!error) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getAll: function(api) {
|
||||
// Check for updating in progress
|
||||
if (!!this.updating) {
|
||||
return;
|
||||
}
|
||||
// Check for API to be called
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for all elements' status
|
||||
var allElements = [
|
||||
'baoc',
|
||||
'boic',
|
||||
'boicExhc',
|
||||
'baic',
|
||||
'baicR'
|
||||
];
|
||||
|
||||
var self = this;
|
||||
self.updating = true;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
self._disable(allElements);
|
||||
|
||||
var setting = 'baoc';
|
||||
self._getRequest(api, _cbServiceMapper[setting]).then(
|
||||
function received(value) {
|
||||
self[setting] = value;
|
||||
setting = 'boic';
|
||||
return self._getRequest(api, _cbServiceMapper[setting]);
|
||||
}).then(function received(value) {
|
||||
self[setting] = value;
|
||||
setting = 'boicExhc';
|
||||
return self._getRequest(api, _cbServiceMapper[setting]);
|
||||
}).then(function received(value) {
|
||||
self[setting] = value;
|
||||
setting = 'baic';
|
||||
return self._getRequest(api, _cbServiceMapper[setting]);
|
||||
}).then(function received(value) {
|
||||
self[setting] = value;
|
||||
setting = 'baicR';
|
||||
return self._getRequest(api, _cbServiceMapper[setting]);
|
||||
}).then(function received(value) {
|
||||
self[setting] = value;
|
||||
}).catch(function errorWhileProcessing(err) {
|
||||
console.error('Error receiving Call Barring status: ' +
|
||||
err.name + ' - ' + err.message);
|
||||
}).then(function afterEverythingDone() {
|
||||
self.updating = false;
|
||||
self._enable(allElements);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var callBarring = Observable(call_barring_prototype);
|
||||
return callBarring;
|
||||
});
|
||||
224
gaia/apps/settings/js/panels/call_barring/panel.js
Executable file
224
gaia/apps/settings/js/panels/call_barring/panel.js
Executable file
|
|
@ -0,0 +1,224 @@
|
|||
/* global DsdsSettings */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var CallBarring = require('panels/call_barring/call_barring');
|
||||
var InputPasscodeScreen = require('panels/call_barring/passcode_dialog');
|
||||
var Toaster = require('shared/toaster');
|
||||
|
||||
return function ctor_call_barring() {
|
||||
var _callBarring = CallBarring;
|
||||
var _passcodeScreen = InputPasscodeScreen();
|
||||
|
||||
var _mobileConnection;
|
||||
var _cbSettings = {};
|
||||
|
||||
var _refresh;
|
||||
var _updating;
|
||||
|
||||
/**
|
||||
* To avoid modifying the setting for the wrong SIM card, it's better to
|
||||
* update the current mobile connection before using it.
|
||||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=910552#c81
|
||||
*/
|
||||
function _updateMobileConnection() {
|
||||
_mobileConnection = window.navigator.mozMobileConnections[
|
||||
DsdsSettings.getIccCardIndexForCallSettings()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage when to update the data
|
||||
*/
|
||||
function refresh_on_load(e) {
|
||||
// Refresh when:
|
||||
// - we load the panel from #call
|
||||
// - we re-load the panel after hide (screen off or change app)
|
||||
// But NOT when:
|
||||
// - we come back from changing the password
|
||||
if (e.detail.current === '#call-cbSettings' &&
|
||||
e.detail.previous === '#call-barring-passcode-change') {
|
||||
_refresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Call Barring item with a new status.
|
||||
* @parameter item DOM 'li' element to update
|
||||
* @parameter newStatus Object with data for the update. Of the form:
|
||||
* {
|
||||
* disabled:[true|false], // optional, new disabled state
|
||||
* checked: [true|false], // optional, new checked state for the input
|
||||
* message: [string] // optional, new message for the description
|
||||
* }
|
||||
*/
|
||||
function _updateCallBarringItem(item, newStatus) {
|
||||
var descText = item.querySelector('details');
|
||||
var input = item.querySelector('gaia-switch');
|
||||
|
||||
// disable the item
|
||||
if (typeof newStatus.disabled === 'boolean') {
|
||||
newStatus.disabled ?
|
||||
item.setAttribute('aria-disabled', true) :
|
||||
item.removeAttribute('aria-disabled');
|
||||
|
||||
if (input) {
|
||||
input.disabled = newStatus.disabled;
|
||||
}
|
||||
}
|
||||
|
||||
// update the input value
|
||||
if (input && typeof newStatus.checked === 'boolean') {
|
||||
input.checked = newStatus.checked;
|
||||
}
|
||||
|
||||
// update the description
|
||||
function inputValue() {
|
||||
return input && input.checked ? 'enabled' : 'disabled';
|
||||
}
|
||||
if (descText) {
|
||||
var text = _updating ? 'callSettingsQuery' : inputValue();
|
||||
document.l10n.setAttributes(descText, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the passcode input screen for the user to introduce the PIN
|
||||
* needed to activate/deactivate a service
|
||||
*/
|
||||
function _callBarringClick(evt) {
|
||||
var input = evt.target;
|
||||
|
||||
// do not change the UI, let it be managed by the data model
|
||||
evt.preventDefault();
|
||||
// Show passcode screen
|
||||
_passcodeScreen.show().then(function confirmed(passcode) {
|
||||
// passcode screen confirmed
|
||||
var inputID = input.parentNode.id;
|
||||
|
||||
var setting = inputID.substring(6);
|
||||
_updateMobileConnection();
|
||||
_callBarring.set(_mobileConnection, setting, passcode).catch(
|
||||
function error(err) {
|
||||
// err = { name, message }
|
||||
var toast;
|
||||
if (err.name === 'GenericFailure') {
|
||||
// show more user friendly string
|
||||
toast = {
|
||||
messageL10nId: 'callBarring-update-generic-error',
|
||||
latency: 2000,
|
||||
useTransition: true
|
||||
};
|
||||
} else {
|
||||
toast = {
|
||||
messageL10nId: 'callBarring-update-item-error',
|
||||
messageL10nArgs: {'error': err.name || 'unknown'},
|
||||
latency: 2000,
|
||||
useTransition: true
|
||||
};
|
||||
}
|
||||
Toaster.showToast(toast);
|
||||
});
|
||||
}).catch(function canceled() {
|
||||
// passcode screen canceled, nothing to do yet
|
||||
});
|
||||
}
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function cb_onInit(panel) {
|
||||
_cbSettings = {
|
||||
baoc: document.getElementById('li-cb-baoc'),
|
||||
boic: document.getElementById('li-cb-boic'),
|
||||
boicExhc: document.getElementById('li-cb-boicExhc'),
|
||||
baic: document.getElementById('li-cb-baic'),
|
||||
baicR: document.getElementById('li-cb-baicR')
|
||||
};
|
||||
|
||||
for (var i in _cbSettings) {
|
||||
_cbSettings[i].querySelector('gaia-switch').
|
||||
addEventListener('click', _callBarringClick);
|
||||
}
|
||||
|
||||
_updateMobileConnection();
|
||||
_passcodeScreen.init();
|
||||
},
|
||||
|
||||
onBeforeShow: function cb_onBeforeShow() {
|
||||
_refresh = true;
|
||||
_updating = false;
|
||||
|
||||
for (var element in _cbSettings) {
|
||||
_callBarring[element] = false;
|
||||
_updateCallBarringItem(_cbSettings[element], {'checked': false});
|
||||
}
|
||||
|
||||
window.addEventListener('panelready', refresh_on_load);
|
||||
|
||||
// Changes on settings value
|
||||
_callBarring.observe('baoc', function(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baoc, {'checked': newValue});
|
||||
});
|
||||
_callBarring.observe('boic', function(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.boic, {'checked': newValue});
|
||||
});
|
||||
_callBarring.observe('boicExhc', function(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.boicExhc, {'checked': newValue});
|
||||
});
|
||||
_callBarring.observe('baic', function(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baic, {'checked': newValue});
|
||||
});
|
||||
_callBarring.observe('baicR', function(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baicR, {'checked': newValue});
|
||||
});
|
||||
|
||||
// Changes on settings availability
|
||||
_callBarring.observe('baoc_enabled', function changed(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baoc, {'disabled': !newValue});
|
||||
});
|
||||
_callBarring.observe('boic_enabled', function changed(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.boic, {'disabled': !newValue});
|
||||
});
|
||||
_callBarring.observe('boicExhc_enabled', function changed(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.boicExhc, {'disabled': !newValue});
|
||||
});
|
||||
_callBarring.observe('baic_enabled', function changed(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baic, {'disabled': !newValue});
|
||||
});
|
||||
_callBarring.observe('baicR_enabled', function changed(newValue) {
|
||||
_updateCallBarringItem(_cbSettings.baicR, {'disabled': !newValue});
|
||||
});
|
||||
|
||||
_callBarring.observe('updating', function changed(newValue) {
|
||||
_updating = newValue;
|
||||
});
|
||||
},
|
||||
|
||||
onShow: function cb_onShow() {
|
||||
if (_refresh) {
|
||||
_updateMobileConnection();
|
||||
_callBarring.getAll(_mobileConnection);
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeHide: function cb_onHide() {
|
||||
window.removeEventListener('panelready', refresh_on_load);
|
||||
|
||||
_callBarring.unobserve('baoc');
|
||||
_callBarring.unobserve('boic');
|
||||
_callBarring.unobserve('boicExhc');
|
||||
_callBarring.unobserve('baic');
|
||||
_callBarring.unobserve('baicR');
|
||||
|
||||
_callBarring.unobserve('baoc_enabled');
|
||||
_callBarring.unobserve('boic_enabled');
|
||||
_callBarring.unobserve('boicExhc_enabled');
|
||||
_callBarring.unobserve('baic_enabled');
|
||||
_callBarring.unobserve('baicR_enabled');
|
||||
|
||||
_callBarring.unobserve('updating');
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
109
gaia/apps/settings/js/panels/call_barring/passcode_dialog.js
Executable file
109
gaia/apps/settings/js/panels/call_barring/passcode_dialog.js
Executable file
|
|
@ -0,0 +1,109 @@
|
|||
/* exported InputPasscodeScreen */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var InputPasscodeScreen = function() {
|
||||
const PIN_SIZE = 4;
|
||||
|
||||
var panel,
|
||||
container,
|
||||
input,
|
||||
btnOK,
|
||||
btnCancel;
|
||||
var _passcodeDigits,
|
||||
_passcodeBuffer;
|
||||
|
||||
function _getInputKey(evt) {
|
||||
var code = evt.charCode;
|
||||
if (code !== 0 && (code < 0x30 || code > 0x39)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var key = String.fromCharCode(code);
|
||||
|
||||
if (evt.charCode === 0) { // Deletion
|
||||
if (_passcodeBuffer.length > 0) {
|
||||
_passcodeBuffer = _passcodeBuffer.substring(0,
|
||||
_passcodeBuffer.length - 1);
|
||||
}
|
||||
} else if (_passcodeBuffer.length < PIN_SIZE) {
|
||||
_passcodeBuffer += key;
|
||||
}
|
||||
|
||||
_updateUI();
|
||||
}
|
||||
|
||||
function _updateUI() {
|
||||
for (var i = 0; i < PIN_SIZE; i++) {
|
||||
if (i < _passcodeBuffer.length) {
|
||||
_passcodeDigits[i].dataset.dot = true;
|
||||
} else {
|
||||
delete _passcodeDigits[i].dataset.dot;
|
||||
}
|
||||
}
|
||||
|
||||
btnOK.disabled = _passcodeBuffer.length === PIN_SIZE ? false : true;
|
||||
}
|
||||
|
||||
function _init() {
|
||||
panel = document.getElementById('cb-passcode');
|
||||
container = panel.querySelector('.passcode-container');
|
||||
input = panel.querySelector('.passcode-input');
|
||||
_passcodeDigits = panel.querySelectorAll('.passcode-digit');
|
||||
_passcodeBuffer = '';
|
||||
|
||||
btnOK = document.getElementById('cb-passcode-ok-btn');
|
||||
btnCancel = document.getElementById('cb-passcode-cancel-btn');
|
||||
|
||||
container.addEventListener('click', function(evt) {
|
||||
input.focus();
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
||||
input.addEventListener('keypress', _getInputKey);
|
||||
}
|
||||
|
||||
function okClicked() {
|
||||
if (_passcodeBuffer.length === 4) {
|
||||
var password = _passcodeBuffer;
|
||||
_closePanel();
|
||||
/*jshint validthis: true */
|
||||
this.resolve(password);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelClicked() {
|
||||
_closePanel();
|
||||
/*jshint validthis: true */
|
||||
this.reject();
|
||||
}
|
||||
|
||||
function _showPanel() {
|
||||
return new Promise((resolve, reject) => {
|
||||
panel.hidden = false;
|
||||
input.focus();
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
|
||||
btnOK.addEventListener('click', okClicked.bind(this));
|
||||
btnCancel.addEventListener('click', cancelClicked.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
function _closePanel() {
|
||||
btnOK.removeEventListener('click', okClicked);
|
||||
btnCancel.removeEventListener('click', cancelClicked);
|
||||
_passcodeBuffer = '';
|
||||
_updateUI();
|
||||
panel.hidden = true;
|
||||
}
|
||||
|
||||
return {
|
||||
init: _init,
|
||||
show: _showPanel
|
||||
};
|
||||
};
|
||||
|
||||
return InputPasscodeScreen;
|
||||
});
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* export ChangePasscodeScreen */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ChangePasscodeScreen = function() {
|
||||
|
||||
/**
|
||||
* Makes a RIL request to change the passcode.
|
||||
* @param api - api object mobileConnection to be used for the call.
|
||||
* @param pinData - data info related to the PIN code.
|
||||
* @param pinData.pin - current passcode
|
||||
* @param pinData.newPin - new passcode
|
||||
*/
|
||||
function _changeCallBarringPasscode(api, pinData) {
|
||||
return new Promise(function finished(resolve, reject) {
|
||||
var request = api.changeCallBarringPassword(pinData);
|
||||
request.onsuccess = function() {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = function() {
|
||||
/* request.error = { name, message } */
|
||||
reject(request.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
change: _changeCallBarringPasscode
|
||||
};
|
||||
};
|
||||
|
||||
return ChangePasscodeScreen;
|
||||
});
|
||||
201
gaia/apps/settings/js/panels/call_barring_passcode_change/panel.js
Executable file
201
gaia/apps/settings/js/panels/call_barring_passcode_change/panel.js
Executable file
|
|
@ -0,0 +1,201 @@
|
|||
/* global DsdsSettings */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var ChangePasscodeScreen =
|
||||
require('panels/call_barring_passcode_change/call_barring_passcode_change');
|
||||
|
||||
return function ctor_call_barring_passcode_change() {
|
||||
var passcodeChange = ChangePasscodeScreen();
|
||||
|
||||
var _mobileConnection,
|
||||
_voiceServiceClassMask;
|
||||
|
||||
var _passcodePanel,
|
||||
_passcodeHeader,
|
||||
_passcodeInput,
|
||||
_passcodeError,
|
||||
_passcodeDigits,
|
||||
_passcodeContainer,
|
||||
_changePasscodeButton;
|
||||
|
||||
var _MODE;
|
||||
var _settings = {
|
||||
pin: '',
|
||||
newPin: ''
|
||||
};
|
||||
var _passcodeBuffer = '';
|
||||
var _checkingLength = {
|
||||
'new': 8,
|
||||
'edit': 4
|
||||
};
|
||||
|
||||
function _changeMode(mode) {
|
||||
_passcodePanel.dataset.mode = _MODE = mode;
|
||||
_hideErrorMessage();
|
||||
}
|
||||
|
||||
function _getInputKey(evt) {
|
||||
if (_passcodeBuffer === '') {
|
||||
_hideErrorMessage();
|
||||
}
|
||||
|
||||
var code = evt.charCode;
|
||||
if (code !== 0 && (code < 0x30 || code > 0x39)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var key = String.fromCharCode(code);
|
||||
if (code === 0) { // delete
|
||||
if (_passcodeBuffer.length > 0) {
|
||||
_passcodeBuffer = _passcodeBuffer.substring(0,
|
||||
_passcodeBuffer.length - 1);
|
||||
if (_passcodePanel.dataset.passcodeStatus === 'success') {
|
||||
_resetPasscodeStatus();
|
||||
}
|
||||
}
|
||||
} else if (_passcodeBuffer.length < 8) {
|
||||
_passcodeBuffer += key;
|
||||
}
|
||||
|
||||
_updatePasscodeUI();
|
||||
_enablePasscode();
|
||||
}
|
||||
|
||||
function _enablePasscode() {
|
||||
if (_passcodeBuffer.length === _checkingLength[_MODE]) {
|
||||
switch (_MODE) {
|
||||
case 'edit':
|
||||
if (_checkPasscode()) {
|
||||
_passcodeBuffer = '';
|
||||
_updatePasscodeUI();
|
||||
_changeMode('new');
|
||||
} else {
|
||||
_passcodeBuffer = '';
|
||||
}
|
||||
break;
|
||||
case 'new':
|
||||
var passcode = _passcodeBuffer.substring(0, 4);
|
||||
var passcodeToConfirm = _passcodeBuffer.substring(4, 8);
|
||||
if (passcode === passcodeToConfirm) {
|
||||
_settings.newPin = passcode;
|
||||
_enableButton();
|
||||
} else {
|
||||
_passcodeBuffer = '';
|
||||
_showErrorMessage('mismatch');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _showErrorMessage(type) {
|
||||
_passcodePanel.dataset.passcodeStatus = 'error';
|
||||
var tag = 'callBarring-passcode-error-' + type || 'generic';
|
||||
document.l10n.setAttributes(_passcodeError, tag);
|
||||
}
|
||||
|
||||
function _hideErrorMessage() {
|
||||
_passcodePanel.dataset.passcodeStatus = '';
|
||||
}
|
||||
|
||||
function _resetPasscodeStatus() {
|
||||
_passcodePanel.dataset.passcodeStatus = '';
|
||||
}
|
||||
|
||||
function _enableButton() {
|
||||
_passcodePanel.dataset.passcodeStatus = 'success';
|
||||
}
|
||||
|
||||
function _updatePasscodeUI() {
|
||||
for (var i = 0; i < 8; i++) {
|
||||
if (i < _passcodeBuffer.length) {
|
||||
_passcodeDigits[i].dataset.dot = true;
|
||||
} else {
|
||||
delete _passcodeDigits[i].dataset.dot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _resetScreen() {
|
||||
_passcodeBuffer = '';
|
||||
_changeMode('edit');
|
||||
_updatePasscodeUI();
|
||||
}
|
||||
|
||||
function _checkPasscode() {
|
||||
if (_passcodeBuffer.length === 4) {
|
||||
_settings.pin = _passcodeBuffer;
|
||||
_hideErrorMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the passcode change screen
|
||||
*/
|
||||
function _changePassword() {
|
||||
passcodeChange.change(_mobileConnection, _settings)
|
||||
.then(function success() {
|
||||
// exit
|
||||
SettingsService.back();
|
||||
}).catch(function error(err) {
|
||||
// back to first screen
|
||||
_resetScreen();
|
||||
|
||||
// show error
|
||||
_showErrorMessage('incorrect');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function cb_onInit(panel) {
|
||||
_mobileConnection = window.navigator.mozMobileConnections[
|
||||
DsdsSettings.getIccCardIndexForCallSettings()
|
||||
];
|
||||
_voiceServiceClassMask = _mobileConnection.ICC_SERVICE_CLASS_VOICE;
|
||||
|
||||
_passcodePanel =
|
||||
document.getElementById('call-barring-passcode-change');
|
||||
_passcodeHeader = _passcodePanel.querySelector('gaia-header');
|
||||
_passcodeInput = _passcodePanel.querySelector('.passcode-input');
|
||||
_passcodeError = _passcodePanel.querySelector('.passcode-error');
|
||||
_passcodeDigits =
|
||||
_passcodePanel.querySelectorAll('.passcode-digit');
|
||||
_passcodeContainer =
|
||||
_passcodePanel.querySelector('.passcode-container');
|
||||
_changePasscodeButton =
|
||||
_passcodePanel.querySelector('.passcode-change');
|
||||
|
||||
_passcodeInput.addEventListener('keypress', _getInputKey);
|
||||
_changePasscodeButton.addEventListener('click', _changePassword);
|
||||
|
||||
// restore focus by touching the container around the pseudo-input.
|
||||
_passcodeContainer.addEventListener('click',
|
||||
function inputClick(evt) {
|
||||
_passcodeInput.focus();
|
||||
_hideErrorMessage();
|
||||
evt.preventDefault();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
onBeforeShow: function cb_beforeShow() {
|
||||
_changeMode('edit');
|
||||
},
|
||||
|
||||
onShow: function cb_onShow() {
|
||||
_passcodeInput.focus();
|
||||
},
|
||||
|
||||
onBeforeHide: function cb_onHide() {
|
||||
_resetScreen();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
204
gaia/apps/settings/js/panels/call_fdn_list/panel.js
Executable file
204
gaia/apps/settings/js/panels/call_fdn_list/panel.js
Executable file
|
|
@ -0,0 +1,204 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DsdsSettings = require('dsds_settings');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var FdnContext = require('modules/fdn_context');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
|
||||
return function ctor_settings_panel() {
|
||||
return SettingsPanel({
|
||||
_currentContact: null,
|
||||
|
||||
onInit: function(panel) {
|
||||
this._elements = {};
|
||||
this._elements.fdnContactButton = panel.querySelector('.fdnContact');
|
||||
this._elements.fdnActionMenu =
|
||||
panel.querySelector('.call-fdnList-action');
|
||||
this._elements.fdnActionMenuName =
|
||||
panel.querySelector('.fdnAction-name');
|
||||
this._elements.fdnActionMenuNumber =
|
||||
panel.querySelector('.fdnAction-number');
|
||||
this._elements.fdnActionMenuCall =
|
||||
panel.querySelector('.fdnAction-call');
|
||||
this._elements.fdnActionMenuEdit =
|
||||
panel.querySelector('.fdnAction-edit');
|
||||
this._elements.fdnActionMenuRemove =
|
||||
panel.querySelector('.fdnAction-delete');
|
||||
this._elements.contactsContainer =
|
||||
panel.querySelector('.fdn-contactsContainer');
|
||||
|
||||
this._bindEvents();
|
||||
},
|
||||
|
||||
onBeforeShow: function() {
|
||||
this._renderAuthorizedNumbers();
|
||||
},
|
||||
|
||||
_bindEvents: function() {
|
||||
// add FDN contact
|
||||
this._elements.fdnContactButton.onclick = () => {
|
||||
DialogService.show('call-fdnList-add', {
|
||||
mode: 'add'
|
||||
}).then((result) => {
|
||||
var type = result.type;
|
||||
var value = result.value;
|
||||
if (type === 'submit') {
|
||||
this._updateContact('add', {
|
||||
name: value.name,
|
||||
number: value.number
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// edit FDN contact
|
||||
this._elements.fdnActionMenuEdit.onclick = () => {
|
||||
// hide action menu first
|
||||
this._hideActionMenu();
|
||||
|
||||
// then show dialog
|
||||
DialogService.show('call-fdnList-add', {
|
||||
name: this._currentContact.name,
|
||||
number: this._currentContact.number,
|
||||
mode: 'edit'
|
||||
}).then((result) => {
|
||||
var type = result.type;
|
||||
var value = result.value;
|
||||
if (type === 'submit') {
|
||||
this._updateContact('edit', {
|
||||
name: value.name,
|
||||
number: value.number
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// remove FDN contact
|
||||
this._elements.fdnActionMenuRemove.onclick = () => {
|
||||
this._hideActionMenu();
|
||||
this._updateContact('remove');
|
||||
};
|
||||
|
||||
// call that fdn
|
||||
this._elements.fdnActionMenuCall.onclick = () => {
|
||||
var activity = new window.MozActivity({
|
||||
name: 'dial',
|
||||
data: {
|
||||
type: 'webtelephony/number',
|
||||
number: this._currentContact.number
|
||||
}
|
||||
});
|
||||
|
||||
activity.onerror = () => {
|
||||
console.error('we are not able to call mozActivity to dialer with' +
|
||||
' number ' + this._currentContact.number);
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* we will render all registered FDN numbers on screen.
|
||||
*
|
||||
* @type {Function}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_renderAuthorizedNumbers: function() {
|
||||
this._elements.contactsContainer.innerHTML = '';
|
||||
var cardIndex = DsdsSettings.getIccCardIndexForCallSettings();
|
||||
return FdnContext.getContacts(cardIndex).then((contacts) => {
|
||||
for (var i = 0, l = contacts.length; i < l; i++) {
|
||||
var li = this._renderFdnContact(contacts[i]);
|
||||
this._elements.contactsContainer.appendChild(li);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* render needed UI for each contact item.
|
||||
*
|
||||
* @type {Function}
|
||||
* @param {Object} contact
|
||||
*/
|
||||
_renderFdnContact: function(contact) {
|
||||
var li = document.createElement('li');
|
||||
var nameContainer = document.createElement('span');
|
||||
var numberContainer = document.createElement('small');
|
||||
|
||||
nameContainer.dir = 'auto';
|
||||
nameContainer.textContent = contact.name;
|
||||
numberContainer.dir = 'auto';
|
||||
numberContainer.textContent = contact.number;
|
||||
li.appendChild(numberContainer);
|
||||
li.appendChild(nameContainer);
|
||||
|
||||
li.onclick = () => {
|
||||
this._showActionMenu(contact);
|
||||
};
|
||||
|
||||
return li;
|
||||
},
|
||||
|
||||
/**
|
||||
* show specific contact on the menu.
|
||||
*
|
||||
* @type {Function}
|
||||
* @param {Object} contact
|
||||
*/
|
||||
_showActionMenu: function(contact) {
|
||||
this._currentContact = contact;
|
||||
this._elements.fdnActionMenuName.textContent = contact.name;
|
||||
this._elements.fdnActionMenuNumber.textContent = contact.number;
|
||||
this._elements.fdnActionMenu.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* hide the whole action menu.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
_hideActionMenu: function() {
|
||||
this._elements.fdnActionMenu.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* update information on each contact item based on passed in parameters.
|
||||
*
|
||||
* @type {Function}
|
||||
* @param {String} action
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_updateContact: function(action, options) {
|
||||
// `action' is either `add', `edit' or `remove': these three actions all
|
||||
// rely on the same mozIccManager.updateContact() method.
|
||||
options = options || {};
|
||||
var cardIndex = DsdsSettings.getIccCardIndexForCallSettings();
|
||||
var name = options.name;
|
||||
var number = options.number;
|
||||
|
||||
var contact = FdnContext.createAction(action, {
|
||||
cardIndex: cardIndex,
|
||||
contact: {
|
||||
id: this._currentContact && this._currentContact.id,
|
||||
name: name,
|
||||
number: number
|
||||
}
|
||||
});
|
||||
|
||||
return DialogService.show('simpin-dialog', {
|
||||
method: 'get_pin2',
|
||||
cardIndex: cardIndex,
|
||||
pinOptions: {
|
||||
fdnContact: contact
|
||||
}
|
||||
}).then((result) => {
|
||||
var type = result.type;
|
||||
if (type === 'submit') {
|
||||
this._renderAuthorizedNumbers();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
66
gaia/apps/settings/js/panels/call_fdn_list_add/panel.js
Executable file
66
gaia/apps/settings/js/panels/call_fdn_list_add/panel.js
Executable file
|
|
@ -0,0 +1,66 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
|
||||
return function ctor_call_fdn_list_add() {
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
this._submitable = false;
|
||||
this._mode = 'add';
|
||||
|
||||
this._elements = {};
|
||||
this._elements.fdnNameInput = panel.querySelector('.fdnContact-name');
|
||||
this._elements.fdnNumberInput =
|
||||
panel.querySelector('.fdnContact-number');
|
||||
this._elements.fdnContactTitle =
|
||||
panel.querySelector('.fdnContact-title');
|
||||
this._elements.submitButton = panel.querySelector('.fdnContact-submit');
|
||||
|
||||
// We have to make sure submit Button is clickable
|
||||
this._elements.fdnNameInput.oninput =
|
||||
this._checkContactInputs.bind(this);
|
||||
this._elements.fdnNumberInput.oninput =
|
||||
this._checkContactInputs.bind(this);
|
||||
},
|
||||
onBeforeShow: function(panel, options) {
|
||||
this._mode = options.mode || 'add';
|
||||
this._elements.fdnNameInput.value = options.name || '';
|
||||
this._elements.fdnNumberInput.value = options.number || '';
|
||||
|
||||
if (this._mode === 'add') {
|
||||
this._elements.fdnContactTitle.setAttribute('data-l10n-id',
|
||||
'fdnAction-add');
|
||||
} else {
|
||||
this._elements.fdnContactTitle.setAttribute('data-l10n-id',
|
||||
'fdnAction-edit-header');
|
||||
}
|
||||
|
||||
// make sure when onShow, the state of submitButton is okay
|
||||
this._checkContactInputs();
|
||||
},
|
||||
onShow: function() {
|
||||
this._elements.fdnNameInput.focus();
|
||||
},
|
||||
onSubmit: function() {
|
||||
if (this._submitable) {
|
||||
return Promise.resolve({
|
||||
name: this._elements.fdnNameInput.value,
|
||||
number: this._elements.fdnNumberInput.value
|
||||
});
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
},
|
||||
_checkContactInputs: function() {
|
||||
if (this._elements.fdnNameInput.value === '' ||
|
||||
this._elements.fdnNumberInput.value === '') {
|
||||
this._submitable = false;
|
||||
} else {
|
||||
this._submitable = true;
|
||||
}
|
||||
this._elements.submitButton.disabled = !this._submitable;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
102
gaia/apps/settings/js/panels/call_fdn_settings/panel.js
Executable file
102
gaia/apps/settings/js/panels/call_fdn_settings/panel.js
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
|
||||
return function ctor_call_fdn_settings_panel() {
|
||||
return SettingsPanel({
|
||||
onInit: function(panel, options) {
|
||||
this._cardIndex = options.cardIndex || 0;
|
||||
this._conns = window.navigator.mozMobileConnections;
|
||||
this._conn = this._conns[this._cardIndex];
|
||||
|
||||
this._elements = {
|
||||
panel: panel,
|
||||
simFdnDesc: panel.querySelector('.fdn-enabled details'),
|
||||
resetPin2Item: panel.querySelector('.fdn-resetPIN2'),
|
||||
simFdnCheckBox: panel.querySelector('.fdn-enabled gaia-switch'),
|
||||
resetPin2Button: panel.querySelector('.fdn-resetPIN2 button')
|
||||
};
|
||||
|
||||
this._elements.simFdnCheckBox.addEventListener('change',
|
||||
this._showToggleFdnDialog.bind(this));
|
||||
this._elements.resetPin2Button.onclick =
|
||||
this._showChangePin2Dialog.bind(this);
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
if (typeof options.cardIndex !== 'undefined') {
|
||||
this._cardIndex = options.cardIndex;
|
||||
this._conn = this._conns[this._cardIndex];
|
||||
}
|
||||
|
||||
var iccObj = this._getCurrentIccObj();
|
||||
if (iccObj) {
|
||||
iccObj.oncardstatechange = this._updateFdnStatus.bind(this);
|
||||
}
|
||||
|
||||
this._updateFdnStatus();
|
||||
},
|
||||
|
||||
_showToggleFdnDialog: function() {
|
||||
var action = this._elements.simFdnCheckBox.checked ?
|
||||
'enable_fdn' : 'disable_fdn';
|
||||
var iccObj = this._getCurrentIccObj();
|
||||
if (iccObj) {
|
||||
if (iccObj.cardState === 'puk2Required') {
|
||||
action = 'unlock_puk2';
|
||||
}
|
||||
|
||||
return DialogService.show('simpin-dialog', {
|
||||
method: action,
|
||||
cardIndex: this._cardIndex
|
||||
}).then(() => {
|
||||
// we will update fdn status no matter how
|
||||
this._updateFdnStatus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_showChangePin2Dialog: function() {
|
||||
DialogService.show('simpin-dialog', {
|
||||
method: 'change_pin2',
|
||||
cardIndex: this._cardIndex
|
||||
});
|
||||
},
|
||||
|
||||
_updateFdnStatus: function() {
|
||||
var iccObj = this._getCurrentIccObj();
|
||||
if (iccObj) {
|
||||
return iccObj.getCardLock('fdn').then((result) => {
|
||||
var enabled = result.enabled;
|
||||
this._elements.simFdnDesc.setAttribute('data-l10n-id',
|
||||
enabled ? 'enabled' : 'disabled');
|
||||
this._elements.simFdnCheckBox.disabled = false;
|
||||
this._elements.simFdnCheckBox.checked = enabled;
|
||||
this._elements.resetPin2Item.hidden = !enabled;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_getCurrentIccObj: function() {
|
||||
var iccId;
|
||||
var iccObj;
|
||||
|
||||
if (this._conn) {
|
||||
iccId = this._conn.iccId;
|
||||
if (iccId) {
|
||||
iccObj = window.navigator.mozIccManager.getIccById(iccId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!iccObj) {
|
||||
console.log('We can\'t find related iccObj in card - ',
|
||||
this._cardIndex);
|
||||
}
|
||||
|
||||
return iccObj;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
270
gaia/apps/settings/js/panels/call_forwarding/panel.js
Executable file
270
gaia/apps/settings/js/panels/call_forwarding/panel.js
Executable file
|
|
@ -0,0 +1,270 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var CallConstant = require('panels/call/call_constant');
|
||||
var CallForwarding = require('panels/call/call_forwarding');
|
||||
var CallUtils = require('panels/call/call_utils');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
|
||||
return function ctor_call_forwarding() {
|
||||
return SettingsPanel({
|
||||
onInit: function(panel, options) {
|
||||
this._elements = {};
|
||||
this._elements.panel = panel;
|
||||
|
||||
this._cardIndex = options.cardIndex || 0;
|
||||
this._conns = window.navigator.mozMobileConnections;
|
||||
this._conn = this._conns[this._cardIndex];
|
||||
|
||||
// We will keep each callForwarding as internal variable
|
||||
this._callForwardings = [null, null];
|
||||
|
||||
var $ = function(selector) {
|
||||
return panel.querySelector(selector);
|
||||
};
|
||||
|
||||
this._elements = {
|
||||
liCfuDesc: $('#li-cfu-desc'),
|
||||
liCfmbDesc: $('#li-cfmb-desc'),
|
||||
liCfnRepDesc: $('#li-cfnrep-desc'),
|
||||
liCfnReaDesc: $('#li-cfnrea-desc'),
|
||||
cfuDesc: $('#cfu-desc'),
|
||||
cfmbDesc: $('#cfmb-desc'),
|
||||
cfnRepDesc: $('#cfnrep-desc'),
|
||||
cfnReaDesc: $('#cfnrea-desc'),
|
||||
mobileBusyMenuItem: $('.callForwardingMobileBusy'),
|
||||
noReplyMenuItem: $('.callForwardingNoReply'),
|
||||
notReachableMenuItem: $('.callForwardingNotReachable'),
|
||||
unConditionalMenuItem: $('.callForwardingUnconditional')
|
||||
};
|
||||
|
||||
this._cfOptionNameMapping = {
|
||||
'unConditional': 'cfuDesc',
|
||||
'mobileBusy': 'cfmbDesc',
|
||||
'noReply': 'cfnRepDesc',
|
||||
'notReachable': 'cfnReaDesc'
|
||||
};
|
||||
|
||||
this._bindMenuItemsClickEvent();
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
if (typeof options.cardIndex !== 'undefined') {
|
||||
this._cardIndex = options.cardIndex;
|
||||
this._conn = this._conns[this._cardIndex];
|
||||
}
|
||||
|
||||
if (this._callForwardings[this._cardIndex] === null) {
|
||||
this._callForwardings[this._cardIndex] =
|
||||
CallForwarding(this._cardIndex);
|
||||
}
|
||||
|
||||
this._bindCallForwardingObservers();
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
this._unbindCallForwardingObservers();
|
||||
},
|
||||
|
||||
_bindCallForwardingObservers: function() {
|
||||
var cf = this._getCallForwarding();
|
||||
|
||||
cf.observe('state', (state) => {
|
||||
if (state === 'normal') {
|
||||
this._lockMenuItems(false);
|
||||
|
||||
// we have to force updating this information because
|
||||
// some numbers will not be changed, there is no way
|
||||
// to trigger every cf.observe()
|
||||
Object.keys(CallConstant.CALL_FORWARD_REASON_MAPPING).forEach(
|
||||
(cfOptionName) => {
|
||||
var selector = this._cfOptionNameMapping[cfOptionName];
|
||||
var element = this._elements[selector];
|
||||
var number = cf[cfOptionName + 'Number'];
|
||||
this._updateNumberInfoOnElement(element, number);
|
||||
});
|
||||
} else if (state === 'requesting') {
|
||||
this._lockMenuItems(true);
|
||||
this._updateAllDescInfoWithL10nId('callSettingsQuery');
|
||||
} else if (state === 'error') {
|
||||
this._lockMenuItems(false);
|
||||
this._updateAllDescInfoWithL10nId('callSettingsQueryError');
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(CallConstant.CALL_FORWARD_REASON_MAPPING).forEach(
|
||||
(cfOptionName) => {
|
||||
var selector = this._cfOptionNameMapping[cfOptionName];
|
||||
var element = this._elements[selector];
|
||||
|
||||
cf.observe(cfOptionName + 'Number', (number) => {
|
||||
this._updateNumberInfoOnElement(element, number);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_updateNumberInfoOnElement: function(element, number) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (number) {
|
||||
document.l10n.setAttributes(element,
|
||||
'callForwardingForwardingVoiceToNumber', {
|
||||
number: number
|
||||
});
|
||||
} else {
|
||||
element.setAttribute('data-l10n-id',
|
||||
'callForwardingNotForwarding');
|
||||
}
|
||||
},
|
||||
|
||||
_unbindCallForwardingObservers: function() {
|
||||
var cf = this._getCallForwarding();
|
||||
cf.unobserve('state');
|
||||
Object.keys(CallConstant.CALL_FORWARD_REASON_MAPPING).forEach(
|
||||
(cfOptionName) => {
|
||||
cf.unobserve(cfOptionName + 'Number');
|
||||
});
|
||||
},
|
||||
|
||||
_getCallForwarding: function() {
|
||||
var cf = this._callForwardings[this._cardIndex];
|
||||
if (!cf) {
|
||||
console.log('can\'t find related call forwarding instance ' +
|
||||
'at cardIndex - ', this._cardIndex);
|
||||
} else {
|
||||
return cf;
|
||||
}
|
||||
},
|
||||
|
||||
_updateAllDescInfoWithL10nId: function(l10nId) {
|
||||
this._elements.cfuDesc.setAttribute('data-l10n-id', l10nId);
|
||||
this._elements.cfmbDesc.setAttribute('data-l10n-id', l10nId);
|
||||
this._elements.cfnRepDesc.setAttribute('data-l10n-id', l10nId);
|
||||
this._elements.cfnReaDesc.setAttribute('data-l10n-id', l10nId);
|
||||
},
|
||||
|
||||
_bindMenuItemsClickEvent: function() {
|
||||
var mapping = {
|
||||
mobileBusy: {
|
||||
menuItem: this._elements.mobileBusyMenuItem,
|
||||
panelId: 'call-cf-mobile-busy-settings'
|
||||
},
|
||||
noReply: {
|
||||
menuItem: this._elements.noReplyMenuItem,
|
||||
panelId: 'call-cf-no-reply-settings'
|
||||
},
|
||||
notReachable: {
|
||||
menuItem: this._elements.notReachableMenuItem,
|
||||
panelId: 'call-cf-not-reachable-settings'
|
||||
},
|
||||
unConditional: {
|
||||
menuItem: this._elements.unConditionalMenuItem,
|
||||
panelId: 'call-cf-unconditional-settings'
|
||||
}
|
||||
};
|
||||
|
||||
var onclick = (panelId, cfOptionName) => {
|
||||
var cf = this._getCallForwarding();
|
||||
var number = cf[cfOptionName + 'Number'];
|
||||
var enabled = cf[cfOptionName + 'Enabled'];
|
||||
|
||||
// Reflect related settings value on the panel
|
||||
DialogService.show(panelId, {
|
||||
number: number,
|
||||
enabled: enabled
|
||||
}).then((result) => {
|
||||
var type = result.type;
|
||||
var value = result.value || {};
|
||||
var returnNumber = value.number || '';
|
||||
var returnEnabled = value.enabled || false;
|
||||
|
||||
if (type === 'submit') {
|
||||
this._getCallForwarding().setCallForwardingValues({
|
||||
key: cfOptionName,
|
||||
enabled: returnEnabled,
|
||||
number: returnNumber
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
var {key, action} = result;
|
||||
this._refresh().then((options) => {
|
||||
var rules = options[key];
|
||||
var messageL10nId =
|
||||
this._getSetCallForwardingOptionResult(rules, action);
|
||||
DialogService.alert(messageL10nId, {
|
||||
title: 'callForwardingConfirmTitle',
|
||||
submitButton: 'continue'
|
||||
});
|
||||
});
|
||||
}
|
||||
}, (error) => {
|
||||
if (error) {
|
||||
DialogService.alert(error.name, {
|
||||
title: 'callForwardingConfirmTitle',
|
||||
submitButtonText: 'continue'
|
||||
});
|
||||
this._refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
for (var cfOptionName in mapping) {
|
||||
var item = mapping[cfOptionName];
|
||||
var panelId = item.panelId;
|
||||
var menuItem = item.menuItem;
|
||||
menuItem.addEventListener('click',
|
||||
onclick.bind(null, panelId, cfOptionName));
|
||||
}
|
||||
},
|
||||
|
||||
_lockMenuItems: function(isLocked) {
|
||||
// After that, update 'Call Forwarding' submenu items
|
||||
var elements = [
|
||||
'liCfuDesc',
|
||||
'liCfmbDesc',
|
||||
'liCfnRepDesc',
|
||||
'liCfnReaDesc'
|
||||
];
|
||||
|
||||
elements.forEach(function(id) {
|
||||
var element = this._elements[id];
|
||||
if (isLocked) {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
} else {
|
||||
element.removeAttribute('aria-disabled');
|
||||
// If unconditional call forwarding is on we
|
||||
// keep disabled the other panels.
|
||||
if (this._getCallForwarding().isUnconditionalCFOn() &&
|
||||
id !== 'liCfuDesc') {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
_refresh: function(checkCallForwardingOption, reason, action) {
|
||||
return this._getCallForwarding().refresh();
|
||||
},
|
||||
|
||||
_getSetCallForwardingOptionResult: function(rules, action) {
|
||||
var rule = CallUtils.findActiveVoiceRule(rules);
|
||||
var l10nId;
|
||||
|
||||
if (rule) {
|
||||
l10nId = (action === CallConstant.CALL_FORWARD_ACTION.DISABLE) ?
|
||||
'callForwardingSetForbidden' : 'callForwardingSetSuccess';
|
||||
return l10nId;
|
||||
} else {
|
||||
l10nId = (action === CallConstant.CALL_FORWARD_ACTION.REGISTRATION) ?
|
||||
'callForwardingSetError' : 'callForwardingSetSuccess';
|
||||
return l10nId;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
28
gaia/apps/settings/js/panels/call_forwarding_details/panel.js
Executable file
28
gaia/apps/settings/js/panels/call_forwarding_details/panel.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
|
||||
return function ctor_call_forwarding_details() {
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
this._elements = {};
|
||||
this._elements.telInput = panel.querySelector('input[type="tel"]');
|
||||
this._elements.checkboxInput = panel.querySelector('gaia-switch');
|
||||
},
|
||||
onBeforeShow: function(panel, options) {
|
||||
this._elements.telInput.value = options.number || '';
|
||||
this._elements.checkboxInput.checked = !!options.enabled;
|
||||
},
|
||||
onShow: function() {
|
||||
this._elements.telInput.focus();
|
||||
},
|
||||
onSubmit: function() {
|
||||
return Promise.resolve({
|
||||
number: this._elements.telInput.value,
|
||||
enabled: this._elements.checkboxInput.checked
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
280
gaia/apps/settings/js/panels/call_iccs/call_icc_handler.js
Executable file
280
gaia/apps/settings/js/panels/call_iccs/call_icc_handler.js
Executable file
|
|
@ -0,0 +1,280 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DsdsSettings = require('dsds_settings');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
|
||||
/**
|
||||
* Singleton object that helps to populate and manage the 'Select a SIM card'
|
||||
* panel in the call settings panel.
|
||||
*/
|
||||
var CallIccHandler = (function() {
|
||||
|
||||
/** Card state mapping const. */
|
||||
var CARDSTATE_MAPPING = {
|
||||
'pinRequired' : 'simCardLockedMsg',
|
||||
'pukRequired' : 'simCardLockedMsg',
|
||||
'permanentBlocked': 'simCardBlockedMsg',
|
||||
'networkLocked' : 'simLockedPhone',
|
||||
'serviceProviderLocked' : 'simLockedPhone',
|
||||
'corporateLocked' : 'simLockedPhone',
|
||||
'network1Locked' : 'simLockedPhone',
|
||||
'network2Locked' : 'simLockedPhone',
|
||||
'hrpdNetworkLocked' : 'simLockedPhone',
|
||||
'ruimCorporateLocked' : 'simLockedPhone',
|
||||
'ruimServiceProviderLocked' : 'simLockedPhone',
|
||||
'unknown' : 'unknownSimCardState',
|
||||
'illegal' : 'simCardIllegal',
|
||||
'absent' : 'noSimCard',
|
||||
'null' : 'simCardNotReady',
|
||||
'ready': ''
|
||||
};
|
||||
|
||||
var _settings = window.navigator.mozSettings;
|
||||
var _iccManager = window.navigator.mozIccManager;
|
||||
var _mobileConnections = null;
|
||||
var _telephony = window.navigator.mozTelephony;
|
||||
|
||||
/** Array of menu item ids. */
|
||||
var _menuItemIds = ['menuItem-call-sim1', 'menuItem-call-sim2'];
|
||||
|
||||
/** Array of menu item descriptions. */
|
||||
var _menuItemDescriptions = [];
|
||||
|
||||
/**
|
||||
* Holds the ICC code from the ICC card that might be active during
|
||||
* the time the settings app is open. We need to keep this value in order to
|
||||
* disable the call settings from the ICC card not being the active one for
|
||||
* the current call. It will be used for multi ICC card devices only.
|
||||
*/
|
||||
var _iccIdOnCall = null;
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*/
|
||||
function ihfcs_init() {
|
||||
_mobileConnections = window.navigator.mozMobileConnections;
|
||||
if (!_settings || !_mobileConnections || !_iccManager || !_telephony) {
|
||||
return;
|
||||
}
|
||||
if (DsdsSettings.getNumberOfIccSlots() === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
function addListeners(iccId) {
|
||||
var eventHandler = ihfcs_showICCCardDetails.bind(null, iccId);
|
||||
var iccCard = _iccManager.getIccById(iccId);
|
||||
if (!iccCard) {
|
||||
return;
|
||||
}
|
||||
iccCard.addEventListener('cardstatechange', eventHandler);
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
return;
|
||||
}
|
||||
mobileConnection.addEventListener('datachange', eventHandler);
|
||||
mobileConnection.addEventListener('radiostatechange', eventHandler);
|
||||
}
|
||||
|
||||
_menuItemIds.forEach(function forEachFunction(id) {
|
||||
var selector = document.getElementById(id);
|
||||
_menuItemDescriptions[_menuItemIds.indexOf(id)] =
|
||||
selector.querySelector('small');
|
||||
});
|
||||
|
||||
_menuItemIds.forEach(function forEachFunction(id) {
|
||||
var selector = document.getElementById(id);
|
||||
var element = selector.querySelector('a');
|
||||
element.addEventListener('click', function eventListenerHandler() {
|
||||
var cardIndex = _menuItemIds.indexOf(id);
|
||||
DsdsSettings.setIccCardIndexForCallSettings(cardIndex);
|
||||
SettingsService.navigate('call', {
|
||||
cardIndex: cardIndex
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var numberOfIccCards = _mobileConnections.length;
|
||||
for (var i = 0; i < numberOfIccCards; i++) {
|
||||
var mobileConnection = _mobileConnections[i];
|
||||
if (!mobileConnection.iccId) {
|
||||
// TODO: this could mean there is no ICC card or the ICC card is
|
||||
// locked. If locked we would need to figure out how to check the
|
||||
// current card state.
|
||||
ihfcs_disableItems(_menuItemIds[i], true);
|
||||
continue;
|
||||
}
|
||||
ihfcs_showICCCardDetails(mobileConnection.iccId);
|
||||
addListeners(mobileConnection.iccId);
|
||||
}
|
||||
|
||||
_iccManager.addEventListener('iccdetected',
|
||||
function iccDetectedHandler(evt) {
|
||||
var iccId = evt.iccId;
|
||||
ihfcs_showICCCardDetails(iccId);
|
||||
addListeners(iccId);
|
||||
});
|
||||
|
||||
_iccManager.addEventListener('iccundetected',
|
||||
function iccUndetectedHandler(evt) {
|
||||
var iccId = evt.iccId;
|
||||
var eventHandler = ihfcs_showICCCardDetails.bind(null, iccId);
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
return;
|
||||
}
|
||||
mobileConnection.removeEventListener('datachange', eventHandler);
|
||||
mobileConnection.removeEventListener('radiostatechange',
|
||||
eventHandler);
|
||||
});
|
||||
|
||||
function callsChangedHandler() {
|
||||
if (!_telephony.active && !_iccIdOnCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_telephony.active) {
|
||||
_iccIdOnCall = _mobileConnections[_telephony.active.serviceId].iccId;
|
||||
} else {
|
||||
_iccIdOnCall = null;
|
||||
}
|
||||
|
||||
var numberOfIccCards = _mobileConnections.length;
|
||||
for (var i = 0; i < numberOfIccCards; i++) {
|
||||
var mobileConnection = _mobileConnections[i];
|
||||
if (mobileConnection.iccId) {
|
||||
ihfcs_showICCCardDetails(mobileConnection.iccId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the call settings from the ICC card not being the active one
|
||||
// for the current call (if any).
|
||||
callsChangedHandler();
|
||||
_telephony.addEventListener('callschanged', callsChangedHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show some details (card state or carrier) of the ICC card.
|
||||
*
|
||||
* @param {String} iccId ICC id.
|
||||
*/
|
||||
function ihfcs_showICCCardDetails(iccId) {
|
||||
var iccCardIndex = ihfcs_getIccCardIndex(iccId);
|
||||
var desc = _menuItemDescriptions[iccCardIndex];
|
||||
|
||||
desc.style.fontStyle = 'italic';
|
||||
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
desc.textContent = '';
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mobileConnection.radioState !== 'enabled') {
|
||||
// Airplane is enabled. Well, radioState property could be changing but
|
||||
// let's disable the items during the transitions also.
|
||||
desc.setAttribute('data-l10n-id', CARDSTATE_MAPPING['null']);
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
if (mobileConnection.radioState === 'enabled') {
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
desc.textContent = '';
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], false);
|
||||
}
|
||||
|
||||
var iccCard = _iccManager.getIccById(iccId);
|
||||
if (!iccCard) {
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
desc.textContent = '';
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
var cardState = iccCard.cardState;
|
||||
if (cardState !== 'ready') {
|
||||
desc.setAttribute('data-l10n-id',
|
||||
CARDSTATE_MAPPING[cardState || 'null']);
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
desc.style.fontStyle = 'normal';
|
||||
|
||||
var network = mobileConnection.voice.network;
|
||||
var iccInfo = iccCard.iccInfo;
|
||||
var carrier = network ? (network.shortName || network.longName) : null;
|
||||
|
||||
if (carrier && iccInfo && iccInfo.isDisplaySpnRequired && iccInfo.spn) {
|
||||
if (iccInfo.isDisplayNetworkNameRequired && carrier !== iccInfo.spn) {
|
||||
carrier = carrier + ' ' + iccInfo.spn;
|
||||
} else {
|
||||
carrier = iccInfo.spn;
|
||||
}
|
||||
}
|
||||
desc.textContent = carrier;
|
||||
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex],
|
||||
_iccIdOnCall && (_iccIdOnCall !== iccId) ?
|
||||
true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the items listed in the panel in order to avoid user interaction.
|
||||
*
|
||||
* @param {String} id Element id from the element to disable.
|
||||
* @param {Boolean} disable This flag tells the function what to do.
|
||||
*/
|
||||
function ihfcs_disableItems(id, disable) {
|
||||
var element = document.getElementById(id);
|
||||
if (disable) {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
} else {
|
||||
element.removeAttribute('aria-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Return the index of the ICC card given the ICC code in
|
||||
* the ICC card.
|
||||
*
|
||||
* @param {String} iccId The iccId code form the ICC card.
|
||||
*
|
||||
* @return {Numeric} The index.
|
||||
*/
|
||||
function ihfcs_getIccCardIndex(iccId) {
|
||||
for (var i = 0; i < _mobileConnections.length; i++) {
|
||||
if (_mobileConnections[i].iccId === iccId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Return the mozMobileConnection for the ICC card given
|
||||
* the ICC code in the ICC card.
|
||||
*
|
||||
* @param {String} iccId The iccId code form the ICC card.
|
||||
*
|
||||
* @return {mozMobileConnection} mozMobileConnection object.
|
||||
*/
|
||||
function ihfcs_getMobileConnectionFromIccId(iccId) {
|
||||
for (var i = 0; i < _mobileConnections.length; i++) {
|
||||
if (_mobileConnections[i].iccId === iccId) {
|
||||
return _mobileConnections[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
init: ihfcs_init
|
||||
};
|
||||
})();
|
||||
|
||||
return CallIccHandler;
|
||||
});
|
||||
14
gaia/apps/settings/js/panels/call_iccs/panel.js
Executable file
14
gaia/apps/settings/js/panels/call_iccs/panel.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var CallIccHandler = require('panels/call_iccs/call_icc_handler');
|
||||
|
||||
return function ctor_call_iccs() {
|
||||
return SettingsPanel({
|
||||
onInit: function() {
|
||||
CallIccHandler.init();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
54
gaia/apps/settings/js/panels/call_voice_mail_settings/panel.js
Executable file
54
gaia/apps/settings/js/panels/call_voice_mail_settings/panel.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
|
||||
return function ctor_call_voice_mail_settings() {
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
this._currentSimIndex = 0;
|
||||
this._elements = {
|
||||
vmNumberInput: panel.querySelector('.vm-number')
|
||||
};
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
// TODO
|
||||
// remove this later
|
||||
this._currentSimIndex =
|
||||
window.DsdsSettings.getIccCardIndexForCallSettings();
|
||||
|
||||
return this._getVoicemailNumber().then((number) => {
|
||||
this._elements.vmNumberInput.value = number;
|
||||
});
|
||||
},
|
||||
onShow: function() {
|
||||
// For better UX
|
||||
var cursorPos = this._elements.vmNumberInput.value.length;
|
||||
this._elements.vmNumberInput.focus();
|
||||
this._elements.vmNumberInput.setSelectionRange(0, cursorPos);
|
||||
},
|
||||
onSubmit: function() {
|
||||
// We have to set the mbdn value back to db
|
||||
SettingsCache.getSettings((results) => {
|
||||
var numbers = results['ril.iccInfo.mbdn'] || [];
|
||||
numbers[this._currentSimIndex] = this._elements.vmNumberInput.value;
|
||||
navigator.mozSettings.createLock().set({
|
||||
'ril.iccInfo.mbdn': numbers
|
||||
});
|
||||
});
|
||||
},
|
||||
_getVoicemailNumber: function() {
|
||||
var self = this;
|
||||
var promise = new Promise(function(resolve) {
|
||||
SettingsCache.getSettings(function(results) {
|
||||
var numbers = results['ril.iccInfo.mbdn'];
|
||||
var number = numbers && numbers[self._currentSimIndex];
|
||||
resolve(number || '');
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
342
gaia/apps/settings/js/panels/carrier/carrier.js
Executable file
342
gaia/apps/settings/js/panels/carrier/carrier.js
Executable file
|
|
@ -0,0 +1,342 @@
|
|||
/* global DsdsSettings, IccHandlerForCarrierSettings */
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
/**
|
||||
* Singleton object that handles some cell and data settings.
|
||||
*/
|
||||
var Carrier = (function() {
|
||||
var DATA_KEY = 'ril.data.enabled';
|
||||
var DATA_ROAMING_KEY = 'ril.data.roaming_enabled';
|
||||
|
||||
var _settings;
|
||||
var _mobileConnections;
|
||||
var _iccManager;
|
||||
|
||||
/** mozMobileConnection instance the panel settings rely on */
|
||||
var _mobileConnection = null;
|
||||
/** Flag */
|
||||
var _restartingDataConnection = false;
|
||||
|
||||
var _elements = {
|
||||
detailHeader:
|
||||
document.querySelector('#carrier-detail gaia-header h1'),
|
||||
carrierInfo:
|
||||
document.querySelector('#carrier .carrier-info'),
|
||||
advancedSettings:
|
||||
document.querySelector('#carrier .carrier-advancedSettings'),
|
||||
advancedSettingsList:
|
||||
document.querySelectorAll('#carrier .carrier-advancedSettings li'),
|
||||
simSettings:
|
||||
document.querySelector('#carrier .carrier-simSettings'),
|
||||
dataToggle:
|
||||
document.querySelector('#menuItem-enableDataCall gaia-switch'),
|
||||
dataRoamingToggle:
|
||||
document.querySelector('#menuItem-enableDataRoaming gaia-switch'),
|
||||
desc:
|
||||
document.getElementById('dataNetwork-desc')
|
||||
};
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*/
|
||||
function cs_init() {
|
||||
_settings = window.navigator.mozSettings;
|
||||
_mobileConnections = window.navigator.mozMobileConnections;
|
||||
_iccManager = window.navigator.mozIccManager;
|
||||
if (!_settings || !_mobileConnections || !_iccManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the mozMobileConnection instace for this ICC card.
|
||||
_mobileConnection = _mobileConnections[
|
||||
DsdsSettings.getIccCardIndexForCellAndDataSettings()
|
||||
];
|
||||
if (!_mobileConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show carrier name.
|
||||
cs_showCarrierName();
|
||||
|
||||
// Set the navigation correctly when on a multi ICC card device.
|
||||
cs_initIccsUI();
|
||||
|
||||
cs_initDataToggles();
|
||||
|
||||
window.addEventListener('panelready', function(e) {
|
||||
// Get the mozMobileConnection instace for this ICC card.
|
||||
_mobileConnection = _mobileConnections[
|
||||
DsdsSettings.getIccCardIndexForCellAndDataSettings()
|
||||
];
|
||||
if (!_mobileConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentHash = e.detail.current;
|
||||
if (currentHash === '#carrier') {
|
||||
// Show carrier name.
|
||||
cs_showCarrierName();
|
||||
return;
|
||||
} else if (currentHash === '#carrier-detail') {
|
||||
document.l10n.setAttributes(
|
||||
_elements.detailHeader,
|
||||
'simSettingsWithIndex',
|
||||
{ index: DsdsSettings.getIccCardIndexForCellAndDataSettings() + 1 });
|
||||
}
|
||||
|
||||
if (!currentHash.startsWith('#carrier-')) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cs_initIccsUI() {
|
||||
var isMultiSim = DsdsSettings.getNumberOfIccSlots() > 1;
|
||||
if (isMultiSim) {
|
||||
LazyLoader.load([
|
||||
'/js/panels/carrier/carrier_iccs.js'
|
||||
], function() {
|
||||
IccHandlerForCarrierSettings.init();
|
||||
});
|
||||
}
|
||||
_elements.carrierInfo.hidden = isMultiSim;
|
||||
_elements.advancedSettings.hidden = isMultiSim;
|
||||
_elements.simSettings.hidden = !isMultiSim;
|
||||
}
|
||||
|
||||
function cs_initDataToggles() {
|
||||
function updateDataRoamingToggle(dataEnabled) {
|
||||
if (dataEnabled) {
|
||||
_elements.dataRoamingToggle.disabled = false;
|
||||
} else {
|
||||
_elements.dataRoamingToggle.disabled = true;
|
||||
_elements.dataRoamingToggle.checked = false;
|
||||
_elements.dataRoamingToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
function getDataEnabled() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var transaction = _settings.createLock();
|
||||
var req = transaction.get(DATA_KEY);
|
||||
req.onsuccess = function() {
|
||||
resolve(req.result[DATA_KEY]);
|
||||
};
|
||||
req.onerror = function() {
|
||||
resolve(false);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var isSimCardInserted = Array.prototype.some.call(
|
||||
navigator.mozMobileConnections,
|
||||
function(connection) {
|
||||
return connection.iccId;
|
||||
}
|
||||
);
|
||||
if (!isSimCardInserted) {
|
||||
_elements.dataToggle.disabled = true;
|
||||
_elements.dataRoamingToggle.disabled = true;
|
||||
Array.prototype.forEach.call(_elements.advancedSettingsList,
|
||||
(element) => {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getDataEnabled().then(function(dataEnabled) {
|
||||
dataEnabled = isSimCardInserted && dataEnabled;
|
||||
updateDataRoamingToggle(dataEnabled);
|
||||
});
|
||||
// We need to disable data roaming when data connection is disabled.
|
||||
_settings.addObserver(DATA_KEY, function observerCb(event) {
|
||||
_elements.dataToggle.checked = event.settingValue;
|
||||
if (_restartingDataConnection) {
|
||||
return;
|
||||
}
|
||||
updateDataRoamingToggle(event.settingValue);
|
||||
});
|
||||
|
||||
// Init warnings the user sees before enabling data calls and roaming.
|
||||
// The function also registers handlers for the changes of the toggles.
|
||||
cs_initWarning(DATA_KEY,
|
||||
_elements.dataToggle,
|
||||
isSimCardInserted,
|
||||
'dataConnection-warning-head',
|
||||
'dataConnection-warning-message');
|
||||
cs_initWarning(DATA_ROAMING_KEY,
|
||||
_elements.dataRoamingToggle,
|
||||
isSimCardInserted,
|
||||
'dataRoaming-warning-head',
|
||||
'dataRoaming-warning-message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the carrier name in the ICC card.
|
||||
*/
|
||||
function cs_showCarrierName() {
|
||||
if (DsdsSettings.getNumberOfIccSlots() > 1) {
|
||||
// We don't do anything here when the device support dsds.
|
||||
return;
|
||||
}
|
||||
var iccCard = _iccManager.getIccById(_mobileConnection.iccId);
|
||||
var network = _mobileConnection.voice.network;
|
||||
var iccInfo = iccCard && iccCard.iccInfo;
|
||||
var carrier = network ? (network.shortName || network.longName) : null;
|
||||
|
||||
if (carrier && iccInfo && iccInfo.isDisplaySpnRequired && iccInfo.spn) {
|
||||
if (iccInfo.isDisplayNetworkNameRequired && carrier !== iccInfo.spn) {
|
||||
carrier = carrier + ' ' + iccInfo.spn;
|
||||
} else {
|
||||
carrier = iccInfo.spn;
|
||||
}
|
||||
}
|
||||
if (iccCard) {
|
||||
_elements.desc.textContent = carrier;
|
||||
} else {
|
||||
_elements.desc.setAttribute('data-l10n-id', 'noSimCard');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init a warning dialog.
|
||||
*
|
||||
* @param {String} settingKey The key of the setting.
|
||||
* @param {Object} input The input element.
|
||||
* @param {Boolean} isSimCardInserted
|
||||
* True if there is a SIM card inserted, otherwise.
|
||||
* @param {String} l10n id of the title.
|
||||
* @param {String} l10n id of the message.
|
||||
*/
|
||||
function cs_initWarning(settingKey,
|
||||
input,
|
||||
isSimCardInserted,
|
||||
titleL10nId,
|
||||
messageL10nId) {
|
||||
var warningDialogEnabledKey = settingKey + '.warningDialog.enabled';
|
||||
|
||||
/**
|
||||
* Figure out whether the warning is enabled or not.
|
||||
*
|
||||
* @param {Function} callback Callback function to be called once the
|
||||
* work is done.
|
||||
*/
|
||||
function getWarningEnabled(callback) {
|
||||
var request = _settings.createLock().get(warningDialogEnabledKey);
|
||||
|
||||
request.onsuccess = function onSuccessHandler() {
|
||||
var warningEnabled = request.result[warningDialogEnabledKey];
|
||||
if (warningEnabled === null) {
|
||||
warningEnabled = true;
|
||||
}
|
||||
if (callback) {
|
||||
callback(warningEnabled);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the setting into the settings database.
|
||||
*
|
||||
* @param {Boolean} state State to be stored.
|
||||
*/
|
||||
function setState(state) {
|
||||
var cset = {};
|
||||
cset[settingKey] = !!state;
|
||||
_settings.createLock().set(cset);
|
||||
}
|
||||
|
||||
function getState(callback) {
|
||||
var request = _settings.createLock().get(settingKey);
|
||||
request.onsuccess = function onSuccessHandler() {
|
||||
if (callback) {
|
||||
callback(request.result[settingKey]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setWarningDialogState(state) {
|
||||
var cset = {};
|
||||
cset[warningDialogEnabledKey] = !!state;
|
||||
_settings.createLock().set(cset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Handler to be called once the user click on the
|
||||
* accept button form the warning dialog.
|
||||
*/
|
||||
function onSubmit() {
|
||||
setWarningDialogState(false);
|
||||
setState(true);
|
||||
input.checked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Handler to be called once the user click on the
|
||||
* cancel button form the warning dialog.
|
||||
*/
|
||||
function onReset() {
|
||||
setWarningDialogState(true);
|
||||
setState(false);
|
||||
input.checked = false;
|
||||
}
|
||||
|
||||
// Initialize the state of the input.
|
||||
getState(function(enabled) {
|
||||
input.checked = isSimCardInserted && enabled;
|
||||
|
||||
// Register an observer to monitor setting changes.
|
||||
input.addEventListener('change', function() {
|
||||
var enabled = this.checked;
|
||||
getWarningEnabled(function getWarningEnabledCb(warningEnabled) {
|
||||
if (warningEnabled) {
|
||||
if (enabled) {
|
||||
require(['modules/dialog_service'], function(DialogService) {
|
||||
DialogService.confirm(messageL10nId, {
|
||||
title: titleL10nId,
|
||||
submitButton: 'turnOn',
|
||||
cancelButton: 'notNow'
|
||||
}).then(function(result) {
|
||||
var type = result.type;
|
||||
if (type === 'submit') {
|
||||
onSubmit();
|
||||
} else {
|
||||
onReset();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setState(enabled);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize the visibility of the warning message.
|
||||
getWarningEnabled(function getWarningEnabledCb(warningEnabled) {
|
||||
if (warningEnabled) {
|
||||
var request = _settings.createLock().get(settingKey);
|
||||
request.onsuccess = function onSuccessCb() {
|
||||
var enabled = false;
|
||||
if (request.result[settingKey] !== undefined) {
|
||||
enabled = request.result[settingKey];
|
||||
}
|
||||
if (enabled) {
|
||||
setWarningDialogState(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: cs_init
|
||||
};
|
||||
})();
|
||||
|
||||
return Carrier;
|
||||
});
|
||||
261
gaia/apps/settings/js/panels/carrier/carrier_iccs.js
Executable file
261
gaia/apps/settings/js/panels/carrier/carrier_iccs.js
Executable file
|
|
@ -0,0 +1,261 @@
|
|||
/* global DsdsSettings */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Singleton object that helps to populate and manage the 'Select a SIM card'
|
||||
* panel in the cell and data settings panel.
|
||||
*/
|
||||
window.IccHandlerForCarrierSettings = (function() {
|
||||
/** Card state mapping const. */
|
||||
var CARDSTATE_MAPPING = {
|
||||
'pinRequired' : 'simCardLockedMsg',
|
||||
'pukRequired' : 'simCardLockedMsg',
|
||||
'permanentBlocked': 'simCardBlockedMsg',
|
||||
'networkLocked' : 'simLockedPhone',
|
||||
'serviceProviderLocked' : 'simLockedPhone',
|
||||
'corporateLocked' : 'simLockedPhone',
|
||||
'network1Locked' : 'simLockedPhone',
|
||||
'network2Locked' : 'simLockedPhone',
|
||||
'hrpdNetworkLocked' : 'simLockedPhone',
|
||||
'ruimCorporateLocked' : 'simLockedPhone',
|
||||
'ruimServiceProviderLocked' : 'simLockedPhone',
|
||||
'unknown' : 'unknownSimCardState',
|
||||
'illegal' : 'simCardIllegal',
|
||||
'absent' : 'noSimCard',
|
||||
'null' : 'simCardNotReady',
|
||||
'ready': ''
|
||||
};
|
||||
|
||||
var DATA_TYPE_MAPPING = {
|
||||
'lte' : '4G LTE',
|
||||
'ehrpd': '4G CDMA',
|
||||
'hspa+': '3.5G HSPA+',
|
||||
'hsdpa': '3.5G HSDPA',
|
||||
'hsupa': '3.5G HSDPA',
|
||||
'hspa' : '3.5G HSDPA',
|
||||
'evdo0': 'EVDO',
|
||||
'evdoa': 'EVDO',
|
||||
'evdob': 'EVDO',
|
||||
'1xrtt': '1xRTT',
|
||||
'umts' : '3G UMTS',
|
||||
'edge' : '2G EDGE',
|
||||
'is95a': '1xRTT',
|
||||
'is95b': '1xRTT',
|
||||
'gprs' : '2G GPRS'
|
||||
};
|
||||
|
||||
var _settings = window.navigator.mozSettings;
|
||||
var _iccManager = window.navigator.mozIccManager;
|
||||
var _mobileConnections = null;
|
||||
|
||||
|
||||
/** Array of menu item ids. */
|
||||
var _menuItemIds = ['menuItem-carrier-sim1', 'menuItem-carrier-sim2'];
|
||||
|
||||
/** Array of menu item descriptions. */
|
||||
var _menuItemDescriptions = [];
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*/
|
||||
function ihfcs_init() {
|
||||
_mobileConnections = window.navigator.mozMobileConnections;
|
||||
if (!_settings || !_mobileConnections || !_iccManager) {
|
||||
return;
|
||||
}
|
||||
if (DsdsSettings.getNumberOfIccSlots() === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
function addListeners(iccId) {
|
||||
var eventHandler = ihfcs_showICCCardDetails.bind(null, iccId);
|
||||
var iccCard = _iccManager.getIccById(iccId);
|
||||
if (!iccCard) {
|
||||
return;
|
||||
}
|
||||
iccCard.addEventListener('cardstatechange', eventHandler);
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
return;
|
||||
}
|
||||
mobileConnection.addEventListener('datachange', eventHandler);
|
||||
mobileConnection.addEventListener('radiostatechange', eventHandler);
|
||||
}
|
||||
|
||||
_menuItemIds.forEach(function forEachFunction(id) {
|
||||
var selector = document.getElementById(id);
|
||||
_menuItemDescriptions[_menuItemIds.indexOf(id)] =
|
||||
selector.querySelector('small');
|
||||
});
|
||||
|
||||
_menuItemIds.forEach(function forEachFunction(id) {
|
||||
var selector = document.getElementById(id);
|
||||
var element = selector.querySelector('a');
|
||||
element.addEventListener('click', function eventListenerHandler() {
|
||||
DsdsSettings.setIccCardIndexForCellAndDataSettings(
|
||||
_menuItemIds.indexOf(id)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
var numberOfIccCards = _mobileConnections.length;
|
||||
for (var i = 0; i < numberOfIccCards; i++) {
|
||||
var mobileConnection = _mobileConnections[i];
|
||||
if (!mobileConnection.iccId) {
|
||||
// TODO: this could mean there is no ICC card or the ICC card is
|
||||
// locked. If locked we would need to figure out how to check the
|
||||
// current card state.
|
||||
ihfcs_disableItems(_menuItemIds[i], true);
|
||||
continue;
|
||||
}
|
||||
ihfcs_showICCCardDetails(mobileConnection.iccId);
|
||||
addListeners(mobileConnection.iccId);
|
||||
}
|
||||
|
||||
_iccManager.addEventListener('iccdetected',
|
||||
function iccDetectedHandler(evt) {
|
||||
var iccId = evt.iccId;
|
||||
ihfcs_showICCCardDetails(iccId);
|
||||
addListeners(iccId);
|
||||
});
|
||||
|
||||
_iccManager.addEventListener('iccundetected',
|
||||
function iccUndetectedHandler(evt) {
|
||||
var iccId = evt.iccId;
|
||||
var eventHandler = ihfcs_showICCCardDetails.bind(null, iccId);
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
return;
|
||||
}
|
||||
mobileConnection.removeEventListener('datachange', eventHandler);
|
||||
mobileConnection.removeEventListener('radiostatechange', eventHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show some details (card state or carrier) of the ICC card.
|
||||
*
|
||||
* @param {String} iccId ICC id.
|
||||
*/
|
||||
function ihfcs_showICCCardDetails(iccId) {
|
||||
var iccCardIndex = ihfcs_getIccCardIndex(iccId);
|
||||
var desc = _menuItemDescriptions[iccCardIndex];
|
||||
|
||||
desc.style.fontStyle = 'italic';
|
||||
|
||||
var mobileConnection = ihfcs_getMobileConnectionFromIccId(iccId);
|
||||
if (!mobileConnection) {
|
||||
desc.textContent = '';
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mobileConnection.radioState !== 'enabled') {
|
||||
// Airplane is enabled. Well, radioState property could be changing but
|
||||
// let's disable the items during the transitions also.
|
||||
desc.setAttribute('data-l10n-id', CARDSTATE_MAPPING['null']);
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
if (mobileConnection.radioState === 'enabled') {
|
||||
desc.textContent = '';
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], false);
|
||||
}
|
||||
|
||||
var iccCard = _iccManager.getIccById(iccId);
|
||||
if (!iccCard) {
|
||||
desc.textContent = '';
|
||||
desc.removeAttribute('data-l10n-id');
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
var cardState = iccCard.cardState;
|
||||
if (cardState !== 'ready') {
|
||||
desc.setAttribute('data-l10n-id',
|
||||
CARDSTATE_MAPPING[cardState || 'null']);
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], true);
|
||||
return;
|
||||
}
|
||||
|
||||
desc.style.fontStyle = 'normal';
|
||||
|
||||
var network = mobileConnection.voice.network;
|
||||
var iccInfo = iccCard.iccInfo;
|
||||
var carrier = network ? (network.shortName || network.longName) : null;
|
||||
|
||||
if (carrier && iccInfo && iccInfo.isDisplaySpnRequired && iccInfo.spn) {
|
||||
if (iccInfo.isDisplayNetworkNameRequired && carrier !== iccInfo.spn) {
|
||||
carrier = carrier + ' ' + iccInfo.spn;
|
||||
} else {
|
||||
carrier = iccInfo.spn;
|
||||
}
|
||||
}
|
||||
desc.textContent = carrier;
|
||||
|
||||
var dataType = (mobileConnection.data.connected &&
|
||||
mobileConnection.data.type) ?
|
||||
DATA_TYPE_MAPPING[mobileConnection.data.type] :
|
||||
'';
|
||||
if (dataType) {
|
||||
desc.textContent += ' - ' + dataType;
|
||||
}
|
||||
|
||||
ihfcs_disableItems(_menuItemIds[iccCardIndex], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the items listed in the panel in order to avoid user interaction.
|
||||
*
|
||||
* @param {String} id Element id from the element to disable.
|
||||
* @param {Boolean} disable This flag tells the function what to do.
|
||||
*/
|
||||
function ihfcs_disableItems(id, disable) {
|
||||
var element = document.getElementById(id);
|
||||
if (disable) {
|
||||
element.setAttribute('aria-disabled', true);
|
||||
} else {
|
||||
element.removeAttribute('aria-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Return the index of the ICC card given the ICC code in the
|
||||
* ICC card.
|
||||
*
|
||||
* @param {String} iccId The iccId code form the ICC card.
|
||||
*
|
||||
* @return {Numeric} The index.
|
||||
*/
|
||||
function ihfcs_getIccCardIndex(iccId) {
|
||||
for (var i = 0; i < _mobileConnections.length; i++) {
|
||||
if (_mobileConnections[i].iccId === iccId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Return the mozMobileConnection for the ICC card given the
|
||||
* ICC code in the ICC card.
|
||||
*
|
||||
* @param {String} iccId The iccId code form the ICC card.
|
||||
*
|
||||
* @return {mozMobileConnection} mozMobileConnection object.
|
||||
*/
|
||||
function ihfcs_getMobileConnectionFromIccId(iccId) {
|
||||
for (var i = 0; i < _mobileConnections.length; i++) {
|
||||
if (_mobileConnections[i].iccId === iccId) {
|
||||
return _mobileConnections[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
init: ihfcs_init
|
||||
};
|
||||
})();
|
||||
18
gaia/apps/settings/js/panels/carrier/panel.js
Executable file
18
gaia/apps/settings/js/panels/carrier/panel.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Used to show Cellular and Data panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Carrier = require('panels/carrier/carrier');
|
||||
|
||||
return function ctor_media_storage_panel() {
|
||||
return SettingsPanel({
|
||||
onInit: function() {
|
||||
//XXX should further refactor the panel
|
||||
Carrier.init();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
279
gaia/apps/settings/js/panels/date_time/panel.js
Executable file
279
gaia/apps/settings/js/panels/date_time/panel.js
Executable file
|
|
@ -0,0 +1,279 @@
|
|||
/**
|
||||
* Used to show Personalization/Date & Time panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DateTime = require('modules/date_time');
|
||||
var tzSelect = require('shared/tz_select');
|
||||
|
||||
var _debug = false;
|
||||
var debug = function() {};
|
||||
if (_debug) {
|
||||
debug = function btam_debug(msg) {
|
||||
console.log('--> [DateTime]: ' + msg);
|
||||
};
|
||||
}
|
||||
|
||||
return function ctor_date_time_panel() {
|
||||
var HOUR_12 = 'ampm';
|
||||
var HOUR_24 = '24';
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
this._elements = {
|
||||
timeAutoSwitch: panel.querySelector('.time-auto'),
|
||||
timezoneRegion: panel.querySelector('.timezone-region'),
|
||||
timezoneCity: panel.querySelector('.timezone-city'),
|
||||
datePicker: panel.querySelector('.date-picker'),
|
||||
timePicker: panel.querySelector('.time-picker'),
|
||||
clockDate: panel.querySelector('.clock-date'),
|
||||
clockTime: panel.querySelector('.clock-time'),
|
||||
timeManual: panel.querySelector('.time-manual'),
|
||||
timezone: panel.querySelector('.timezone'),
|
||||
timezonePickers: [].slice.apply(
|
||||
panel.querySelectorAll('.timezone-picker')),
|
||||
timezoneInfo: panel.querySelector('.timezone-info'),
|
||||
timezoneInfoText: panel.querySelector('.timezone-info-text'),
|
||||
timeFormat: panel.querySelector('.timeformat'),
|
||||
timeFormatAutoSwitch:
|
||||
panel.querySelector('.time-format-auto gaia-switch'),
|
||||
timeFormatSwitch: panel.querySelector('.time-format-time')
|
||||
};
|
||||
|
||||
// update date/clock periodically
|
||||
this._boundSetDate = () => {
|
||||
this._elements.clockDate.textContent = DateTime.date;
|
||||
};
|
||||
|
||||
this._boundSetTime = () => {
|
||||
this._elements.clockTime.textContent = DateTime.time;
|
||||
};
|
||||
|
||||
this._boundSetTimezoneInfo = () => {
|
||||
// Only display the timezone info when auto time is enabled.
|
||||
var info = DateTime.clockAutoEnabled ? DateTime.timezone : '';
|
||||
debug('Timezone: ' + info);
|
||||
this._elements.timezoneInfoText.textContent = info;
|
||||
};
|
||||
|
||||
// Reset the timezone to the previous user selected value
|
||||
this._boundSetSelectedTimeZone = (selected) => {
|
||||
DateTime.setUserSelectedTimezone(selected);
|
||||
};
|
||||
|
||||
this._boundUpdateUI = this._updateUI.bind(this);
|
||||
this._boundRenderTimeFormat = this._renderTimeFormat.bind(this);
|
||||
this._boundDatePickerChange = this._datePickerChange.bind(this);
|
||||
this._boundTimePickerChange = this._timePickerChange.bind(this);
|
||||
this._boundTimeFormatAutoChange = () => {
|
||||
var checked = this._elements.timeFormatAutoSwitch.checked;
|
||||
|
||||
if (checked) {
|
||||
DateTime.setCurrentHour12(null);
|
||||
} else {
|
||||
var isHour12 = DateTime.getHour12ForCurrentLocale();
|
||||
DateTime.setCurrentHour12(isHour12);
|
||||
}
|
||||
};
|
||||
|
||||
this._boundTimeFormatChange = () => {
|
||||
var value = (this._elements.timeFormatSwitch.value === HOUR_12);
|
||||
DateTime.setCurrentHour12(value);
|
||||
};
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
DateTime.observe('date', this._boundSetDate);
|
||||
DateTime.observe('time', this._boundSetTime);
|
||||
DateTime.observe('timezone', this._boundSetTimezoneInfo);
|
||||
DateTime.observe('clockAutoEnabled', this._boundUpdateUI);
|
||||
DateTime.observe('clockAutoAvailable', this._boundUpdateUI);
|
||||
DateTime.observe('timezoneAutoAvailable', this._boundUpdateUI);
|
||||
DateTime.observe('currentHour12', this._boundRenderTimeFormat);
|
||||
DateTime.observe('userSelectedTimezone',
|
||||
this._boundSetSelectedTimeZone);
|
||||
|
||||
this._elements.datePicker.addEventListener('input',
|
||||
this._boundDatePickerChange);
|
||||
this._elements.timePicker.addEventListener('input',
|
||||
this._boundTimePickerChange);
|
||||
this._elements.timeFormatSwitch.addEventListener('change',
|
||||
this._boundTimeFormatChange);
|
||||
this._elements.timeFormatAutoSwitch.addEventListener('change',
|
||||
this._boundTimeFormatAutoChange);
|
||||
|
||||
this._renderTimeZone();
|
||||
this._boundSetDate();
|
||||
this._boundSetTime();
|
||||
this._boundSetTimezoneInfo();
|
||||
if (DateTime.userSelectedTimezone && !DateTime.clockAutoEnabled) {
|
||||
this._boundSetSelectedTimeZone(DateTime.userSelectedTimezone);
|
||||
}
|
||||
this._renderTimeFormat();
|
||||
this._boundUpdateUI();
|
||||
|
||||
document.addEventListener('DOMRetranslated', this);
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
DateTime.unobserve('date', this._boundSetDate);
|
||||
DateTime.unobserve('time', this._boundSetTime);
|
||||
DateTime.unobserve('timezone', this._boundSetTimezoneInfo);
|
||||
DateTime.unobserve('clockAutoEnabled', this._boundUpdateUI);
|
||||
DateTime.unobserve('clockAutoAvailable', this._boundUpdateUI);
|
||||
DateTime.unobserve('timezoneAutoAvailable', this._boundUpdateUI);
|
||||
DateTime.unobserve('currentHour12', this._boundRenderTimeFormat);
|
||||
DateTime.unobserve('userSelectedTimezone',
|
||||
this._boundSetSelectedTimeZone);
|
||||
|
||||
this._elements.datePicker.removeEventListener('input',
|
||||
this._boundDatePickerChange);
|
||||
this._elements.timePicker.removeEventListener('input',
|
||||
this._boundTimePickerChange);
|
||||
this._elements.timeFormatSwitch.removeEventListener('change',
|
||||
this._boundTimeFormatChange);
|
||||
this._elements.timeFormatAutoSwitch.removeEventListener('change',
|
||||
this._boundTimeFormatAutoChange);
|
||||
|
||||
document.removeEventListener('DOMRetranslated', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* monitor time.timezone changes, see /shared/js/tz_select.js
|
||||
*/
|
||||
_renderTimeZone: function dt_renderTimeZone() {
|
||||
var noOp = function() {};
|
||||
tzSelect(this._elements.timezoneRegion, this._elements.timezoneCity,
|
||||
noOp, noOp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update DatePicker value
|
||||
*/
|
||||
_datePickerChange: function dt_datePickerChange() {
|
||||
DateTime.setTime('date', this._elements.datePicker.value);
|
||||
// Clean up the value of picker once we get date set by the user.
|
||||
// It will get new date according system time when pop out again.
|
||||
this._elements.datePicker.value = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Update TimePicker value
|
||||
*/
|
||||
_timePickerChange: function dt_timePickerChange() {
|
||||
DateTime.setTime('time', this._elements.timePicker.value);
|
||||
// Clean up the value of picker once we get time set by the user.
|
||||
// It will get new time according system time when pop out again.
|
||||
this._elements.timePicker.value = '';
|
||||
},
|
||||
|
||||
handleEvent: function dt_handleEvent(evt) {
|
||||
switch (evt.type) {
|
||||
case 'DOMRetranslated':
|
||||
this._renderTimeZone();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update Panel UI elements
|
||||
*/
|
||||
_updateUI: function dt_updateUI() {
|
||||
this._elements.timeAutoSwitch.dataset.state =
|
||||
DateTime.clockAutoEnabled ? 'auto' : 'manual';
|
||||
// There are three possible combinations:
|
||||
// - clockAutoAvailable is true, timezoneAutoAvailable is true
|
||||
// - clockAutoAvailable is false, timezoneAutoAvailable is false
|
||||
// - clockAutoAvailable is true, timezoneAutoAvailable is false
|
||||
// We show the auto time switch only when clockAutoAvailable is true.
|
||||
this._elements.timeAutoSwitch.hidden = !DateTime.clockAutoAvailable;
|
||||
|
||||
// DataTime.clockAutoEnabled is a user preference and in some cases it
|
||||
// can be true while DateTime.clockAutoAvailable is false. The reason is
|
||||
// the device may not connect to the network to retrieve the correct
|
||||
// time automatically after startup. That being said, we should always
|
||||
// check both `DateTime.clockAutoEnabled` and
|
||||
// `DateTime.clockAutoAvailable` to determine whether the device is in
|
||||
// the auto time mode.
|
||||
var autoTimeMode =
|
||||
DateTime.clockAutoEnabled && DateTime.clockAutoAvailable;
|
||||
|
||||
this._elements.datePicker.disabled = autoTimeMode;
|
||||
this._elements.timePicker.disabled = autoTimeMode;
|
||||
this._elements.timezoneRegion.disabled =
|
||||
DateTime.timezoneAutoAvailable && autoTimeMode;
|
||||
this._elements.timezoneCity.disabled =
|
||||
DateTime.timezoneAutoAvailable && autoTimeMode;
|
||||
|
||||
// XXX: Force to trigger the selector change so that tz_select is able
|
||||
// to write the previous user-selected value back to time.timezone.
|
||||
if (!DateTime.clockAutoEnabled) {
|
||||
this._elements.timezoneRegion.dispatchEvent(new Event('change'));
|
||||
this._elements.timezoneCity.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
if (autoTimeMode) {
|
||||
this._elements.timeManual.classList.add('disabled');
|
||||
this._elements.timezonePickers.forEach((picker) => {
|
||||
picker.hidden = DateTime.timezoneAutoAvailable;
|
||||
});
|
||||
debug('force update timezone string');
|
||||
this._boundSetTimezoneInfo();
|
||||
this._elements.timezoneInfo.hidden = !DateTime.timezoneAutoAvailable;
|
||||
} else {
|
||||
this._elements.timeManual.classList.remove('disabled');
|
||||
this._elements.timezonePickers.forEach((picker) => {
|
||||
picker.hidden = false;
|
||||
});
|
||||
this._elements.timezoneInfo.hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update TimeFormat selector
|
||||
*/
|
||||
_renderTimeFormat: function dt_renderTimeFormat() {
|
||||
var hour12Value = DateTime.currentHour12;
|
||||
if (hour12Value === null) {
|
||||
hour12Value = undefined;
|
||||
}
|
||||
if (hour12Value === undefined) {
|
||||
this._elements.timeFormatAutoSwitch.checked = true;
|
||||
this._elements.timeFormat.classList.add('disabled');
|
||||
} else {
|
||||
this._elements.timeFormatAutoSwitch.checked = false;
|
||||
this._elements.timeFormat.classList.remove('disabled');
|
||||
}
|
||||
// restore default selection
|
||||
if (hour12Value === undefined) {
|
||||
hour12Value = DateTime.getHour12ForCurrentLocale();
|
||||
}
|
||||
|
||||
var selectedItem = 0;
|
||||
if (hour12Value === false) {
|
||||
selectedItem = 1;
|
||||
}
|
||||
|
||||
var options = [{
|
||||
attr: 'hour-12', // 2:00PM
|
||||
value: HOUR_12,
|
||||
}, {
|
||||
attr: 'hour-24', // 14:00
|
||||
value: HOUR_24
|
||||
}];
|
||||
|
||||
var obj = this._elements.timeFormatSwitch;
|
||||
while(obj.options.length) { // clean options
|
||||
obj.remove(0);
|
||||
}
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
var option = document.createElement('option');
|
||||
option.setAttribute('data-l10n-id', options[i].attr);
|
||||
option.selected = (selectedItem === i);
|
||||
option.value = options[i].value;
|
||||
obj.appendChild(option);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
181
gaia/apps/settings/js/panels/developer/developer.js
Executable file
181
gaia/apps/settings/js/panels/developer/developer.js
Executable file
|
|
@ -0,0 +1,181 @@
|
|||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*
|
||||
* @module developer/developer
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var AppsCache = require('modules/apps_cache');
|
||||
var ScreenLayout = require('shared/screen_layout');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var HomescreenSettings = require('shared/homescreens/homescreen_settings');
|
||||
|
||||
const DEVTOOLS_UNRESTRICTED_KEY = 'devtools.unrestricted';
|
||||
|
||||
/**
|
||||
* @alias module:developer/developer
|
||||
* @class Developer
|
||||
* @returns {Developer}
|
||||
*/
|
||||
var Developer = function() {
|
||||
this._elements = null;
|
||||
};
|
||||
|
||||
Developer.prototype = {
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Developer.prototype
|
||||
* @param {HTMLElement} elements
|
||||
*/
|
||||
init: function d_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._elements.ftuLauncher.addEventListener('click', this._launchFTU);
|
||||
|
||||
// hide software home button whenever the device has no hardware
|
||||
// home button
|
||||
if (!ScreenLayout.getCurrentLayout('hardwareHomeButton')) {
|
||||
this._elements.softwareHomeButton.style.display = 'none';
|
||||
// always set homegesture enabled on tablet, so hide the setting
|
||||
if (!ScreenLayout.getCurrentLayout('tiny')) {
|
||||
this._elements.homegesture.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.mozPower) {
|
||||
this._elements.resetSwitch.disabled = false;
|
||||
this._elements.resetSwitch.addEventListener('click', event => {
|
||||
this._resetDevice();
|
||||
// The switch is updated based on the setting.
|
||||
event.preventDefault();
|
||||
});
|
||||
} else {
|
||||
// disable button if mozPower is undefined or can't be used
|
||||
this._elements.resetSwitch.disabled = true;
|
||||
}
|
||||
|
||||
// Listen to home screen paging setting
|
||||
HomescreenSettings.addEventListener('updated', e => {
|
||||
var prop = e.target;
|
||||
if (prop.name === 'grid.paging') {
|
||||
this._homescreenPagingEnabled = prop.value;
|
||||
this._elements.homescreenPaging.checked = prop.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Listen to checked/unchecked
|
||||
this._elements.homescreenPaging.addEventListener('change', () => {
|
||||
this._setHomescreenPaging(this._elements.homescreenPaging.checked);
|
||||
});
|
||||
|
||||
// Set the default value.
|
||||
HomescreenSettings.get('grid.paging').then(value => {
|
||||
this._homescreenPagingEnabled = value;
|
||||
this._elements.homescreenPaging.checked = value;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* launch FTU app.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf Developer.prototype
|
||||
*/
|
||||
_launchFTU: function d__launchFTU() {
|
||||
var key = 'ftu.manifestURL';
|
||||
var req = navigator.mozSettings.createLock().get(key);
|
||||
req.onsuccess = function ftuManifest() {
|
||||
var ftuManifestURL = req.result[key];
|
||||
|
||||
// fallback if no settings present
|
||||
if (!ftuManifestURL) {
|
||||
ftuManifestURL = document.location.protocol +
|
||||
'//ftu.gaiamobile.org' +
|
||||
(location.port ? (':' + location.port) : '') +
|
||||
'/manifest.webapp';
|
||||
}
|
||||
|
||||
var ftuApp = null;
|
||||
AppsCache.apps().then(function(apps) {
|
||||
for (var i = 0; i < apps.length && ftuApp === null; i++) {
|
||||
var app = apps[i];
|
||||
if (app.manifestURL === ftuManifestURL) {
|
||||
ftuApp = app;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftuApp) {
|
||||
ftuApp.launch();
|
||||
} else {
|
||||
DialogService.alert('no-ftu', {title: 'no-ftu'});
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* popup warning dialog.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf Developer.prototype
|
||||
*/
|
||||
_resetDevice: function d__resetDevice() {
|
||||
SettingsCache.getSettings(results => {
|
||||
var unrestricted = results[DEVTOOLS_UNRESTRICTED_KEY];
|
||||
if (unrestricted) {
|
||||
DialogService.confirm('reset-devtools-warning-body', {
|
||||
title: 'reset-devtools-warning-title',
|
||||
submitButton: 'factory-reset',
|
||||
cancelButton: 'cancel'
|
||||
}).then((result) => {
|
||||
var type = result.type;
|
||||
if (type === 'submit') {
|
||||
this._wipe('normal');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
DialogService.show('full-developer-mode-warning').then((result) => {
|
||||
if (result.type === 'submit' &&
|
||||
result.value === 'final_warning') {
|
||||
DialogService.show('full-developer-mode-final-warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset and enable full DevTools access.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf Developer.prototype
|
||||
*/
|
||||
_wipe: function about__wipe(reason) {
|
||||
var power = navigator.mozPower;
|
||||
if (!power) {
|
||||
console.error('Cannot get mozPower');
|
||||
return;
|
||||
}
|
||||
if (!power.factoryReset) {
|
||||
console.error('Cannot invoke mozPower.factoryReset()');
|
||||
return;
|
||||
}
|
||||
power.factoryReset(reason);
|
||||
},
|
||||
|
||||
_setHomescreenPaging: function d__setHomescrenPaging(paging) {
|
||||
if (this._homescreenPagingEnabled === paging) {
|
||||
return;
|
||||
}
|
||||
HomescreenSettings.put('grid.paging', paging);
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_developer_panel() {
|
||||
return new Developer();
|
||||
};
|
||||
});
|
||||
26
gaia/apps/settings/js/panels/developer/panel.js
Executable file
26
gaia/apps/settings/js/panels/developer/panel.js
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Used to show Device/developer panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Developer = require('panels/developer/developer');
|
||||
|
||||
return function ctor_developer_panel() {
|
||||
var developer = Developer();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var elements = {
|
||||
resetSwitch: panel.querySelector('.reset-devtools'),
|
||||
ftuLauncher: panel.querySelector('.ftuLauncher'),
|
||||
softwareHomeButton: panel.querySelector('.software-home-button'),
|
||||
homegesture: panel.querySelector('.homegesture'),
|
||||
homescreenPaging: panel.querySelector('.homescreen-paging')
|
||||
};
|
||||
developer.init(elements);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
48
gaia/apps/settings/js/panels/developer_hud/developer_hud.js
Executable file
48
gaia/apps/settings/js/panels/developer_hud/developer_hud.js
Executable file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
|
||||
/**
|
||||
* @alias module:developer_hud/developer_hud
|
||||
* @class DeveloperHud
|
||||
* @returns {DeveloperHud}
|
||||
*/
|
||||
var DeveloperHud = function() {
|
||||
this._elements = {};
|
||||
};
|
||||
|
||||
DeveloperHud.prototype = {
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DeveloperHud.prototype
|
||||
* @param {Object} elements
|
||||
*/
|
||||
init: function about_init(elements) {
|
||||
this._elements = elements;
|
||||
SettingsListener.observe('devtools.overlay', false,
|
||||
(enabled) => {
|
||||
[].forEach.call(this._elements.widgets, function(widget) {
|
||||
widget.classList.toggle('disabled', !enabled);
|
||||
});
|
||||
});
|
||||
|
||||
SettingsListener.observe('hud.appmemory', false,
|
||||
(enabled) => {
|
||||
[].forEach.call(this._elements.items, function(item) {
|
||||
item.parentElement.parentElement.classList.toggle('disabled',
|
||||
!enabled);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_developer_hud_panel() {
|
||||
return new DeveloperHud();
|
||||
};
|
||||
});
|
||||
23
gaia/apps/settings/js/panels/developer_hud/panel.js
Executable file
23
gaia/apps/settings/js/panels/developer_hud/panel.js
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Used to show Device/developer hud panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DeveloperHud = require('panels/developer_hud/developer_hud');
|
||||
|
||||
return function ctor_developer_hud_panel() {
|
||||
var developerHud = DeveloperHud();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var elements = {
|
||||
widgets: panel.querySelectorAll('.hud-widgets'),
|
||||
items: panel.querySelectorAll('.memory-item')
|
||||
};
|
||||
developerHud.init(elements);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global SettingsListener */
|
||||
|
||||
/**
|
||||
* On B2G we cannot directly show the content of about:* pages with access to
|
||||
* privileged code. We already have an about:serviceworkers chrome page showing
|
||||
* the list of installed service workers on Desktop. Unfortunately we cannot
|
||||
* access it from Gaia. To access this information, we need to query the
|
||||
* platform from the System app. And to show it we chose to add a new panel
|
||||
* inside the existing "Developer" panel in the Settings app. The communication
|
||||
* with the Settings app is done via Inter App Communication API.
|
||||
*
|
||||
* From this panel we will be sending IAC messages to the System app to request
|
||||
* the list of registered service workers and to request actions like updating
|
||||
* or unregistering them. The System app will proxy this IAC messages to the
|
||||
* platform with no modification.
|
||||
*
|
||||
* The high level picture is the following:
|
||||
*
|
||||
* Settings App
|
||||
* |
|
||||
* |- IAC
|
||||
* |
|
||||
* System App
|
||||
* |
|
||||
* |- Chrome/Content events
|
||||
* |
|
||||
* Gecko (ServiceWorkersManager accessed from a B2G dedicated component)
|
||||
*/
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @alias module:developer_service_workers/developer_service_workers
|
||||
* @class DeveloperServiceWorkers
|
||||
* @returns {DeveloperServiceWorkers}
|
||||
*/
|
||||
var DeveloperServiceWorkers = function() {
|
||||
this._elements = {};
|
||||
};
|
||||
|
||||
DeveloperServiceWorkers.prototype = {
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DeveloperServiceWorkers.prototype
|
||||
* @param {Object} elements
|
||||
*/
|
||||
init: function(elements) {
|
||||
this._elements = elements;
|
||||
this.listeners = {};
|
||||
this.serviceWorkersCount = 0;
|
||||
SettingsListener.observe('dom.serviceWorkers.interception.enabled', true,
|
||||
(enabled) => {
|
||||
this._elements.serviceWorkersInterceptionEnabled.checked = enabled;
|
||||
});
|
||||
SettingsListener.observe('dom.serviceWorkers.testing.enabled', false,
|
||||
(enabled) => {
|
||||
this._elements.serviceWorkersTestingEnabled.checked = enabled;
|
||||
});
|
||||
},
|
||||
|
||||
onInterceptionCheckboxClick: function() {
|
||||
var checkbox = this._elements.serviceWorkersInterceptionEnabled;
|
||||
navigator.mozSettings.createLock().set({
|
||||
'dom.serviceWorkers.interception.enabled': checkbox.checked
|
||||
});
|
||||
},
|
||||
|
||||
onTestingCheckboxClick: function() {
|
||||
var checkbox = this._elements.serviceWorkersTestingEnabled;
|
||||
navigator.mozSettings.createLock().set({
|
||||
'dom.serviceWorkers.testing.enabled': checkbox.checked
|
||||
});
|
||||
},
|
||||
|
||||
renderServiceWorkerInfo: function(serviceWorkersInfo) {
|
||||
var principal = serviceWorkersInfo.principal;
|
||||
var scope = serviceWorkersInfo.scope;
|
||||
|
||||
var list = this._elements.serviceWorkersList;
|
||||
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.dataset.scope = scope;
|
||||
|
||||
var header = document.createElement('header');
|
||||
var headerContent = document.createElement('h2');
|
||||
headerContent.textContent = principal.origin;
|
||||
header.appendChild(headerContent);
|
||||
div.appendChild(header);
|
||||
|
||||
[{
|
||||
l10nId: 'service-worker-scope',
|
||||
textContent: serviceWorkersInfo.scope
|
||||
}, {
|
||||
l10nId: 'service-worker-script-spec',
|
||||
textContent: serviceWorkersInfo.scriptSpec
|
||||
}].forEach(property => {
|
||||
var li = document.createElement('li');
|
||||
var span = document.createElement('span');
|
||||
span.dataset.l10nId = property.l10nId;
|
||||
li.appendChild(span);
|
||||
var small = document.createElement('small');
|
||||
small.textContent = property.textContent;
|
||||
li.appendChild(small);
|
||||
div.appendChild(li);
|
||||
});
|
||||
|
||||
// We store the listeners so we can remove them in case that
|
||||
// we unregister the service worker.
|
||||
if (!this.listeners[scope]) {
|
||||
var self = this;
|
||||
this.listeners[scope] = {
|
||||
update: function() {
|
||||
self.update(principal, scope);
|
||||
},
|
||||
unregister: function() {
|
||||
self.unregister(principal, scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var update = document.createElement('li');
|
||||
var updateButton = document.createElement('button');
|
||||
updateButton.dataset.l10nId = 'service-worker-update';
|
||||
updateButton.addEventListener('click', this.listeners[scope].update);
|
||||
update.appendChild(updateButton);
|
||||
div.appendChild(update);
|
||||
|
||||
var unregister = document.createElement('li');
|
||||
var unregisterButton = document.createElement('button');
|
||||
unregisterButton.dataset.l10nId = 'service-worker-unregister';
|
||||
unregisterButton.addEventListener('click',
|
||||
this.listeners[scope].unregister);
|
||||
unregister.appendChild(unregisterButton);
|
||||
div.appendChild(unregister);
|
||||
|
||||
fragment.appendChild(div);
|
||||
list.appendChild(fragment);
|
||||
|
||||
this._elements.serviceWorkersListSection.classList.remove('hidden');
|
||||
this._elements.serviceWorkersEmptySection.classList.add('hidden');
|
||||
},
|
||||
|
||||
renderNoServiceWorkerInfo: function() {
|
||||
this._elements.serviceWorkersListSection.classList.add('hidden');
|
||||
this._elements.serviceWorkersEmptySection.classList.remove('hidden');
|
||||
},
|
||||
|
||||
connect: function() {
|
||||
if (this._port) {
|
||||
return Promise.resolve(this._port);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
navigator.mozApps.getSelf().onsuccess = event => {
|
||||
var app = event.target.result;
|
||||
app.connect('about-service-workers').then(ports => {
|
||||
if (!ports || !ports.length) {
|
||||
return reject();
|
||||
}
|
||||
this._port = ports[0];
|
||||
resolve(this._port);
|
||||
}).catch(reject);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
iacRequest: function(request) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connect().then(port => {
|
||||
var id = require('shared/uuid')();
|
||||
request.id = id;
|
||||
port.postMessage(request);
|
||||
port.onmessage = event => {
|
||||
var message = event.data;
|
||||
if (!message || (message.id != id)) {
|
||||
return;
|
||||
}
|
||||
resolve(message.result);
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.iacRequest({
|
||||
name: 'init'
|
||||
}).then(result => {
|
||||
var registrations = result.registrations;
|
||||
if (!result || !result.registrations) {
|
||||
this.renderNoServiceWorkerInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
this.serviceWorkersCount = registrations.length;
|
||||
if (!this.serviceWorkersCount) {
|
||||
this.renderNoServiceWorkerInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
this._elements.serviceWorkersList.innerHTML = '';
|
||||
|
||||
for (var i = 0; i < registrations.length; i++) {
|
||||
this.renderServiceWorkerInfo(registrations[i]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
update: function(principal, scope) {
|
||||
this.iacRequest({
|
||||
name: 'update',
|
||||
principal: principal,
|
||||
scope: scope
|
||||
}).catch(e => {
|
||||
console.error('Could not update service worker ' + e);
|
||||
});
|
||||
},
|
||||
|
||||
unregister: function(principal, scope) {
|
||||
this.iacRequest({
|
||||
name: 'unregister',
|
||||
principal: principal,
|
||||
scope: scope
|
||||
}).then(result => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
this.doUnregister(scope);
|
||||
}).catch(e => {
|
||||
console.error('Could not unregister service worker ' + e);
|
||||
});
|
||||
},
|
||||
|
||||
doUnregister: function(scope) {
|
||||
var element = document.querySelector('[data-scope="' + scope + '"]');
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.parentElement.removeChild(element);
|
||||
|
||||
var listeners = this.listeners[scope];
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listeners.update) {
|
||||
window.removeEventListener('click', listeners.update);
|
||||
}
|
||||
|
||||
if(listeners.unregister) {
|
||||
window.removeEventListener('click', listeners.unregister);
|
||||
}
|
||||
|
||||
this.serviceWorkersCount--;
|
||||
if (!this.serviceWorkersCount) {
|
||||
this.renderNoServiceWorkerInfo();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function() {
|
||||
return new DeveloperServiceWorkers();
|
||||
};
|
||||
});
|
||||
49
gaia/apps/settings/js/panels/developer_service_workers/panel.js
Executable file
49
gaia/apps/settings/js/panels/developer_service_workers/panel.js
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Used to show Device/Service Workers panel
|
||||
*/
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DeveloperServiceWorkers =
|
||||
require('panels/developer_service_workers/developer_service_workers');
|
||||
|
||||
return function ctor_developer_service_workers_panel() {
|
||||
var developerServiceWorkers= DeveloperServiceWorkers();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var elements = {
|
||||
serviceWorkersListSection:
|
||||
panel.querySelector('#service-workers-list-section'),
|
||||
serviceWorkersList: panel.querySelector('#service-workers-list'),
|
||||
serviceWorkersEmptySection:
|
||||
panel.querySelector('#service-workers-empty-section'),
|
||||
serviceWorkersInterceptionEnabled:
|
||||
panel.querySelector('#service-workers-interception-enabled'),
|
||||
serviceWorkersTestingEnabled:
|
||||
panel.querySelector('#service-workers-testing-enabled')
|
||||
};
|
||||
developerServiceWorkers.init(elements);
|
||||
elements.serviceWorkersInterceptionEnabled.addEventListener(
|
||||
'click',
|
||||
developerServiceWorkers.onInterceptionCheckboxClick
|
||||
.bind(developerServiceWorkers)
|
||||
);
|
||||
elements.serviceWorkersTestingEnabled.addEventListener(
|
||||
'click',
|
||||
developerServiceWorkers.onTestingCheckboxClick
|
||||
.bind(developerServiceWorkers)
|
||||
);
|
||||
},
|
||||
onShow: function() {
|
||||
developerServiceWorkers.refresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
81
gaia/apps/settings/js/panels/display/display.js
Executable file
81
gaia/apps/settings/js/panels/display/display.js
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Handle display panel functionality, which will change the value of
|
||||
* screen.automatic-brightness of settings.
|
||||
*
|
||||
* @module display/display
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SliderHandler = require('panels/display/slider_handler');
|
||||
|
||||
const AUTO_BRIGHTNESS_SETTING = 'screen.automatic-brightness';
|
||||
const SCREEN_BRIGHTNESS = 'screen.brightness';
|
||||
/**
|
||||
* @alias module:display/display
|
||||
* @class Display
|
||||
* @returns {Display}
|
||||
*/
|
||||
var Display = function() {
|
||||
this.elements = null;
|
||||
};
|
||||
|
||||
Display.prototype = {
|
||||
/**
|
||||
* Init Display module with doms and data of device-features.json.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Display.prototype
|
||||
* @param {HTMLElement} elements
|
||||
* @param {Object} data
|
||||
* content of resources/device-features.json.
|
||||
*/
|
||||
init: function d_init(elements, data) {
|
||||
this.elements = elements;
|
||||
this.initBrightnessItems(data);
|
||||
SliderHandler().init(
|
||||
elements.brightnessManualInput,
|
||||
SCREEN_BRIGHTNESS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decide whether to show brightnessAuto and brightnessManual options.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Display.prototype
|
||||
* @param {Object} data
|
||||
* content of resources/device-features.json.
|
||||
*/
|
||||
initBrightnessItems: function d_init_brightness_items(data) {
|
||||
var brightnessAuto = this.elements.brightnessAuto;
|
||||
var brightnessAutoCheckbox = this.elements.brightnessAutoCheckbox;
|
||||
var brightnessManual = this.elements.brightnessManual;
|
||||
var brightnessManualInput = this.elements.brightnessManualInput;
|
||||
|
||||
if (data.ambientLight) {
|
||||
brightnessAuto.hidden = false;
|
||||
// Observe auto brightness setting
|
||||
SettingsListener.observe(AUTO_BRIGHTNESS_SETTING, false,
|
||||
function(value) {
|
||||
brightnessAutoCheckbox.checked = value;
|
||||
brightnessManual.hidden = value;
|
||||
}.bind(this));
|
||||
} else {
|
||||
brightnessAuto.hidden = true;
|
||||
brightnessManual.hidden = false;
|
||||
var cset = {};
|
||||
cset[AUTO_BRIGHTNESS_SETTING] = false;
|
||||
SettingsListener.getSettingsLock().set(cset);
|
||||
}
|
||||
// Observe screen brightness setting
|
||||
SettingsListener.observe(SCREEN_BRIGHTNESS, 0.5, function(value) {
|
||||
brightnessManualInput.value = value;
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_display() {
|
||||
return new Display();
|
||||
};
|
||||
});
|
||||
34
gaia/apps/settings/js/panels/display/panel.js
Executable file
34
gaia/apps/settings/js/panels/display/panel.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* The display panel allow user to modify timeout forscreen-off, brightness.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DisplayModule = require('panels/display/display');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var displayElements = {};
|
||||
|
||||
return function ctor_display_panel() {
|
||||
var display = DisplayModule();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function dp_onInit(panel) {
|
||||
displayElements = {
|
||||
brightnessManual: panel.querySelector('.brightness-manual'),
|
||||
brightnessManualInput:
|
||||
panel.querySelector('.brightness-manual input'),
|
||||
brightnessAuto: panel.querySelector('.brightness-auto'),
|
||||
brightnessAutoCheckbox:
|
||||
panel.querySelector('.brightness-auto gaia-checkbox')
|
||||
};
|
||||
|
||||
LazyLoader.getJSON('/resources/device-features.json')
|
||||
.then(function(data) {
|
||||
display.init(displayElements, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
129
gaia/apps/settings/js/panels/display/slider_handler.js
Executable file
129
gaia/apps/settings/js/panels/display/slider_handler.js
Executable file
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
const INTERVAL = 300;
|
||||
|
||||
var SliderHandler = function() {
|
||||
this._element = null;
|
||||
this._key = '';
|
||||
this._isFirstInput = false;
|
||||
this._intervalID = null;
|
||||
};
|
||||
|
||||
SliderHandler.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*
|
||||
* The sliders listen to input, touchstart and touchend events to fit
|
||||
* the ux requirements, and when the user tap or drag the sliders, the
|
||||
* sequence of the events is:
|
||||
* touchstart -> input -> input(more if dragging) -> touchend -> input
|
||||
*
|
||||
* @access public
|
||||
* @memberOf SliderHandler.prototype
|
||||
* @param {Object} element html elements
|
||||
* @param {String} settings key
|
||||
*/
|
||||
init: function d_init(element, key) {
|
||||
this._element = element;
|
||||
this._key = key;
|
||||
|
||||
this._boundSetSliderValue = function(value) {
|
||||
this._setSliderValue(value);
|
||||
}.bind(this);
|
||||
|
||||
// We can't use the input change event because it does not fire
|
||||
// when the slider is being dragged, and will only fire when released.
|
||||
this._element.addEventListener('touchstart',
|
||||
this._touchStartHandler.bind(this));
|
||||
this._element.addEventListener('input',
|
||||
this._inputHandler.bind(this));
|
||||
this._element.addEventListener('change',
|
||||
this._touchEndHandler.bind(this));
|
||||
this._element.addEventListener('touchend',
|
||||
this._touchEndHandler.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Change slider's value
|
||||
*
|
||||
* @access private
|
||||
* @memberOf SliderHandler.prototype
|
||||
* @param {Number} value slider value
|
||||
*/
|
||||
_setSliderValue: function d_setSliderValue(value) {
|
||||
this._element.value = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle touchstart event
|
||||
*
|
||||
* @access private
|
||||
* @memberOf SliderHandler.prototype
|
||||
*/
|
||||
_touchStartHandler: function d_touchStartHandler(event) {
|
||||
this._isFirstInput = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update setting
|
||||
*
|
||||
* @access private
|
||||
* @memberOf SliderHandler.prototype
|
||||
*/
|
||||
_updateSetting: function d_updateSetting() {
|
||||
var value = this._element.valueAsNumber;
|
||||
if (!Number.isFinite(value)) return;
|
||||
var settingObject = {};
|
||||
settingObject[this._key] = value;
|
||||
|
||||
// Only set the new value if it does not equal to the previous one
|
||||
if (value !== this._previous) {
|
||||
navigator.mozSettings.createLock().set(settingObject);
|
||||
this._previous = value;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle input event
|
||||
*
|
||||
* The mozSettings api is not designed to call rapidly, so we use
|
||||
* setInterval() as a timer to ease the number of calling, or we
|
||||
* will see the queued callbacks try to update the slider's value
|
||||
* which we are unable to avoid and make bad ux for the users.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf SliderHandler.prototype
|
||||
*/
|
||||
_inputHandler: function d_inputHandler(event) {
|
||||
if (!this._intervalID) {
|
||||
this._isFirstInput = false;
|
||||
this._updateSetting();
|
||||
this._intervalID =
|
||||
setInterval(this._updateSetting.bind(this), INTERVAL);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle touchend event
|
||||
*
|
||||
* @access private
|
||||
* @memberOf SliderHandler.prototype
|
||||
*/
|
||||
_touchEndHandler: function d_touchEndHandler(event) {
|
||||
clearInterval(this._intervalID);
|
||||
this._intervalID = null;
|
||||
this._updateSetting();
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_sliderHandler() {
|
||||
return new SliderHandler();
|
||||
};
|
||||
});
|
||||
78
gaia/apps/settings/js/panels/do_not_track/do_not_track.js
Executable file
78
gaia/apps/settings/js/panels/do_not_track/do_not_track.js
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Handle Do not track panel functionality.
|
||||
* Including key migration and carry value for compatibility.
|
||||
*
|
||||
* @module DoNotTrack
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
|
||||
var settings = navigator.mozSettings;
|
||||
/**
|
||||
* ENABLED_KEY: "privacy.donottrackheader.enabled"
|
||||
* VALUE_KEY "privacy.donottrackheader.value"
|
||||
*
|
||||
* ENABLED_KEY VALUE_KEY
|
||||
* Do not track: true 1
|
||||
* Track: true 0
|
||||
* No preference: false N/A
|
||||
*/
|
||||
var kEnabledKey = 'privacy.donottrackheader.enabled';
|
||||
var kValueKey = 'privacy.donottrackheader.value';
|
||||
|
||||
var enabledMap = {
|
||||
'0': true, // Track
|
||||
'1': true, // Do not track
|
||||
'-1': false // No preference
|
||||
};
|
||||
|
||||
var DoNotTrack = function() {};
|
||||
|
||||
DoNotTrack.prototype = {
|
||||
/*
|
||||
* If 'privacy.donottrackheader.value' was not defined then
|
||||
* we're upgrading from 1.1 which did not have this key.
|
||||
* Set it according to the contents of
|
||||
* 'privacy.donottrackheader.enabled'.
|
||||
*/
|
||||
keyMigration: function dnt_migration() {
|
||||
SettingsCache.getSettings(function(results) {
|
||||
if (results[kValueKey] === undefined) {
|
||||
var value = '-1';
|
||||
if (results[kEnabledKey] !== undefined) {
|
||||
value = results[kEnabledKey] ? '1' : '-1';
|
||||
}
|
||||
settings.createLock().set({ kValueKey: value });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* if 'privacy.donottrackheader.value' is changed,
|
||||
* carry the value to 'privacy.donottrackheader.enabled'.
|
||||
*/
|
||||
carryKeyChange: function carryKeyChange() {
|
||||
settings.addObserver(kValueKey, function(result) {
|
||||
var enabled = enabledMap[result.settingValue];
|
||||
if (enabled === undefined) {
|
||||
console.warn('Invalid ' + kValueKey + ' value ' +
|
||||
result.settingValue);
|
||||
return;
|
||||
}
|
||||
|
||||
var cset = {};
|
||||
cset[kEnabledKey] = enabled;
|
||||
var request = settings.createLock().set(cset);
|
||||
request.onerror = function set_onerror() {
|
||||
console.warn('Set ' + kEnabledKey + ' failed');
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_do_not_track() {
|
||||
return new DoNotTrack();
|
||||
};
|
||||
});
|
||||
20
gaia/apps/settings/js/panels/do_not_track/panel.js
Executable file
20
gaia/apps/settings/js/panels/do_not_track/panel.js
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Used to show Privacy/Do Not Track panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var DoNotTrackModule = require('panels/do_not_track/do_not_track');
|
||||
|
||||
return function ctor_do_not_track_panel() {
|
||||
var doNotTrack = DoNotTrackModule();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
doNotTrack.keyMigration();
|
||||
doNotTrack.carryKeyChange();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
31
gaia/apps/settings/js/panels/feedback_choose/panel.js
Executable file
31
gaia/apps/settings/js/panels/feedback_choose/panel.js
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
|
||||
return function ctor_choose_feedback_panel() {
|
||||
var feedbackHappy;
|
||||
var feedbackSad;
|
||||
|
||||
function navigateToSendfeedbackPanel(evt) {
|
||||
SettingsService.navigate('improveBrowserOS-sendFeedback', {
|
||||
feel: evt.currentTarget.id
|
||||
});
|
||||
}
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
feedbackHappy = panel.querySelector('#feedback-happy');
|
||||
feedbackSad = panel.querySelector('#feedback-sad');
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
feedbackHappy.addEventListener('click', navigateToSendfeedbackPanel);
|
||||
feedbackSad.addEventListener('click', navigateToSendfeedbackPanel);
|
||||
},
|
||||
onBeforeHide: function() {
|
||||
feedbackHappy.removeEventListener('click', navigateToSendfeedbackPanel);
|
||||
feedbackSad.removeEventListener('click', navigateToSendfeedbackPanel);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
187
gaia/apps/settings/js/panels/feedback_send/feedback_send.js
Executable file
187
gaia/apps/settings/js/panels/feedback_send/feedback_send.js
Executable file
|
|
@ -0,0 +1,187 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
require('shared/async_storage');
|
||||
|
||||
var SendFeedback = function(){};
|
||||
SendFeedback.prototype = {
|
||||
_SettingsCache: SettingsCache,
|
||||
_SettingsService: SettingsService,
|
||||
|
||||
init: function(elements) {
|
||||
this.elements = elements;
|
||||
this.options = {};
|
||||
this._sendData = {
|
||||
product: 'Firefox OS',
|
||||
platform: 'Firefox OS'
|
||||
};
|
||||
this._showEmail = false;
|
||||
},
|
||||
|
||||
_isHappy: function() {
|
||||
var happy = this.options && this.options.feel;
|
||||
return happy === 'feedback-happy';
|
||||
},
|
||||
|
||||
updateTitle: function() {
|
||||
var id = 'feedback_whyfeel_' + (this._isHappy() ? 'happy' : 'sad');
|
||||
this.elements.title.setAttribute('data-l10n-id', id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get previous inputs from asyncStorage.
|
||||
*/
|
||||
getPreviousInputs: function() {
|
||||
window.asyncStorage.getItem('feedback', function(value) {
|
||||
this._inputData = value || {};
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
keepAllInputs: function() {
|
||||
window.asyncStorage.setItem('feedback', this._inputData);
|
||||
},
|
||||
|
||||
get _inputData() {
|
||||
return {
|
||||
description: this.elements.description.value,
|
||||
email: this.elements.emailInput.value,
|
||||
emailEnable: this._showEmail
|
||||
};
|
||||
},
|
||||
|
||||
set _inputData(data) {
|
||||
this.elements.description.value = data.description || '';
|
||||
this.elements.emailInput.value = data.email || '';
|
||||
this._showEmail = !data.emailEnable;
|
||||
this.enableEmail();
|
||||
},
|
||||
|
||||
send: function() {
|
||||
this.elements.sendBtn.disabled = true;
|
||||
if (!navigator.onLine) {
|
||||
this._messageHandler('connect-error');
|
||||
return;
|
||||
}
|
||||
var emailBar = this.elements.emailColumn;
|
||||
var emailInput = this.elements.emailInput;
|
||||
var contextInput = this.elements.description;
|
||||
if (contextInput.textLength === 0) {
|
||||
this._messageHandler('empty-comment');
|
||||
return;
|
||||
} else {
|
||||
this._sendData.description = contextInput.value;
|
||||
}
|
||||
|
||||
if (!emailBar.hidden) {
|
||||
this._sendData.email = emailInput.value;
|
||||
} else {
|
||||
delete this._sendData.email;
|
||||
}
|
||||
|
||||
if (!emailBar.hidden &&
|
||||
(!emailInput.value.length ||
|
||||
!emailInput.validity.valid)) {
|
||||
this._messageHandler('wrong-email');
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSetting = this._SettingsCache.cache;
|
||||
var feedbackUrl = currentSetting['feedback.url'];
|
||||
this._sendData.version =
|
||||
currentSetting['deviceinfo.os'];
|
||||
this._sendData.device =
|
||||
currentSetting['deviceinfo.hardware'];
|
||||
this._sendData.locale =
|
||||
currentSetting['language.current'];
|
||||
this._sendData.happy = this._isHappy();
|
||||
|
||||
this._xhr = new XMLHttpRequest({mozSystem: true});
|
||||
this._xhr.open('POST', feedbackUrl, true);
|
||||
this._xhr.setRequestHeader(
|
||||
'Content-type', 'application/json');
|
||||
this._xhr.timeout = 5000;
|
||||
this._xhr.onload =
|
||||
this._responseHandler.bind(this);
|
||||
this._xhr.ontimeout = function() {
|
||||
this._messageHandler('timeout');
|
||||
}.bind(this);
|
||||
this._xhr.send(JSON.stringify(this._sendData));
|
||||
},
|
||||
|
||||
/**
|
||||
* Show email input column if use click the checkbox.
|
||||
*/
|
||||
enableEmail: function() {
|
||||
var original = this._showEmail;
|
||||
this._showEmail = !original;
|
||||
this.elements.emailEnable.checked = !original;
|
||||
this.elements.emailColumn.hidden = original;
|
||||
},
|
||||
|
||||
back: function() {
|
||||
this.keepAllInputs();
|
||||
this._SettingsService.navigate('improveBrowserOS-chooseFeedback');
|
||||
},
|
||||
|
||||
_responseHandler: function() {
|
||||
switch (this._xhr.status) {
|
||||
case 201:
|
||||
this._messageHandler('success');
|
||||
break;
|
||||
case 429:
|
||||
// this status is trying to prevent users keep sending the same
|
||||
// message, but from server aspect, it means success.
|
||||
this._messageHandler('success');
|
||||
break;
|
||||
case 404:
|
||||
this._messageHandler('server-off');
|
||||
break;
|
||||
case 400:
|
||||
this._messageHandler('unknown-error');
|
||||
break;
|
||||
default:
|
||||
this._messageHandler('unknown-error');
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_messageHandler: function(type) {
|
||||
if (type === 'success') {
|
||||
this._openDoneDialog();
|
||||
} else {
|
||||
this.keepAllInputs();
|
||||
this._openAlertDialog(type);
|
||||
}
|
||||
this.elements.sendBtn.disabled = false;
|
||||
},
|
||||
|
||||
_openAlertDialog: function(type) {
|
||||
DialogService.alert({
|
||||
id: 'feedback-errormessage-' + type
|
||||
}, {
|
||||
submitButton: { id: 'ok', style: 'full' }
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Once the data is sent successfully and user click 'done' button,
|
||||
* we'll go back to improveBrowserOS panel.
|
||||
*/
|
||||
_openDoneDialog: function() {
|
||||
DialogService.alert({
|
||||
id: 'feedback-complete-msg'
|
||||
}, {
|
||||
title: 'sendFeedbackTitle',
|
||||
submitButton: { id: 'done', style: 'full' }
|
||||
}).then(function() {
|
||||
this._SettingsService.navigate('improveBrowserOS');
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
return function ctor_send_feedback() {
|
||||
return new SendFeedback();
|
||||
};
|
||||
});
|
||||
62
gaia/apps/settings/js/panels/feedback_send/panel.js
Executable file
62
gaia/apps/settings/js/panels/feedback_send/panel.js
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SendFeedback = require('panels/feedback_send/feedback_send');
|
||||
|
||||
return function ctor_sendFeedbackPanel() {
|
||||
var elements = {};
|
||||
var sendFeedback = SendFeedback();
|
||||
var eventMapping = [
|
||||
{ elementName: 'sendBtn', eventType: 'click', methodName: 'send' },
|
||||
{ elementName: 'emailEnable', eventType: 'change',
|
||||
methodName: 'enableEmail' },
|
||||
{ elementName: 'header', eventType: 'action', methodName: 'back' }
|
||||
];
|
||||
|
||||
function bindEvents(elements) {
|
||||
eventMapping.forEach(function(map) {
|
||||
map.method = sendFeedback[map.methodName].bind(sendFeedback);
|
||||
elements[map.elementName].addEventListener(map.eventType,
|
||||
map.method);
|
||||
});
|
||||
}
|
||||
|
||||
function unbindEvents(elements) {
|
||||
eventMapping.forEach(function(map) {
|
||||
if (!map.method) {
|
||||
return;
|
||||
}
|
||||
elements[map.elementName].removeEventListener(map.eventType,
|
||||
map.method);
|
||||
});
|
||||
}
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {
|
||||
title: panel.querySelector('#feedback-title'),
|
||||
description: panel.querySelector('#feedback-description'),
|
||||
emailInput: panel.querySelector('#feedback-email'),
|
||||
emailColumn: panel.querySelector('#feedback-emailbar'),
|
||||
emailEnable: panel.querySelector('#email-enable'),
|
||||
sendBtn: panel.querySelector('#feedback-send-btn'),
|
||||
header: panel.querySelector('#feedback-header')
|
||||
};
|
||||
sendFeedback.init(elements);
|
||||
},
|
||||
onBeforeShow: function(panel, options) {
|
||||
bindEvents(elements);
|
||||
sendFeedback.options = options;
|
||||
sendFeedback.updateTitle();
|
||||
sendFeedback.getPreviousInputs();
|
||||
},
|
||||
onBeforeHide: function() {
|
||||
unbindEvents(elements);
|
||||
if (document.hidden) {
|
||||
sendFeedback.keepAllInputs();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
186
gaia/apps/settings/js/panels/findmydevice/findmydevice.js
Executable file
186
gaia/apps/settings/js/panels/findmydevice/findmydevice.js
Executable file
|
|
@ -0,0 +1,186 @@
|
|||
/* global IAC_API_WAKEUP_REASON_TRY_DISABLE */
|
||||
|
||||
'use strict';
|
||||
|
||||
define(function(require) {
|
||||
var SettingsUtils = require('modules/settings_utils');
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsHelper = require('shared/settings_helper');
|
||||
var wakeUpFindMyDevice = require('shared/findmydevice_iac_api');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var FindMyDevice = function() {
|
||||
// When the FxA login callback is called, we need to know if the
|
||||
// login process began with the user clicking our login button
|
||||
// since in that case we also want to enable Find My Device if it's
|
||||
// not already registered.
|
||||
this._interactiveLogin = false;
|
||||
|
||||
this._enabledHelper = SettingsHelper('findmydevice.enabled');
|
||||
this._loggedInHelper = SettingsHelper('findmydevice.logged-in');
|
||||
this._registeredHelper = SettingsHelper('findmydevice.registered');
|
||||
|
||||
this._boundSetTracked = this._boundSetTracked ||
|
||||
this._setTracked.bind(this);
|
||||
this._boundSetEnabled = this._boundSetEnabled ||
|
||||
this._setEnabled.bind(this);
|
||||
this._boundSetCanDisable = this._boundSetCanDisable ||
|
||||
this._setCanDisable.bind(this);
|
||||
this._boundLoginClick = this._boundLoginClick ||
|
||||
this._onLoginClick.bind(this);
|
||||
this._boundCheckboxChanged = this._boundCheckboxChanged ||
|
||||
this._onCheckboxChanged.bind(this);
|
||||
};
|
||||
|
||||
FindMyDevice.prototype = {
|
||||
onInit: function fmd_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
// The app name may overflow the header width in some locales; try to
|
||||
// shrink it. Bug 1087441
|
||||
SettingsUtils.runHeaderFontFit(this._elements.header);
|
||||
|
||||
LazyLoader.getJSON('/resources/findmydevice.json').then((data) => {
|
||||
SettingsListener.observe('findmydevice.logged-in', false,
|
||||
this._togglePanel.bind(this));
|
||||
|
||||
navigator.mozId.watch({
|
||||
wantIssuer: 'firefox-accounts',
|
||||
audience: data.api_url,
|
||||
onlogin: this._onChangeLoginState.bind(this, true),
|
||||
onlogout: this._onChangeLoginState.bind(this, false),
|
||||
onready: () => {
|
||||
this._elements.loginButton.removeAttribute('disabled');
|
||||
console.log('Find My Device: onready fired');
|
||||
},
|
||||
onerror: (err) => {
|
||||
console.error('Find My Device: onerror fired: ' + err);
|
||||
this._interactiveLogin = false;
|
||||
this._elements.loginButton.removeAttribute('disabled');
|
||||
var errorName = JSON.parse(err).name;
|
||||
if (errorName !== 'OFFLINE') {
|
||||
if (errorName === 'UNVERIFIED_ACCOUNT') {
|
||||
this._elements.unverifiedError.hidden = false;
|
||||
this._elements.login.hidden = true;
|
||||
}
|
||||
this._loggedInHelper.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
onBeforeShow: function fmd_onBeforeShow() {
|
||||
SettingsListener.observe('findmydevice.tracking', false,
|
||||
this._boundSetTracked);
|
||||
SettingsListener.observe('findmydevice.enabled', false,
|
||||
this._boundSetEnabled);
|
||||
SettingsListener.observe('findmydevice.can-disable', true,
|
||||
this._boundSetCanDisable);
|
||||
|
||||
this._elements.loginButton.addEventListener('click',
|
||||
this._boundLoginClick);
|
||||
this._elements.checkbox.addEventListener('change',
|
||||
this._boundCheckboxChanged);
|
||||
},
|
||||
onBeforeHide: function fmd_onBeforeHide() {
|
||||
SettingsListener.unobserve('findmydevice.tracking',
|
||||
this._boundSetTracked);
|
||||
SettingsListener.unobserve('findmydevice.enabled',
|
||||
this._boundSetEnabled);
|
||||
SettingsListener.unobserve('findmydevice.can-disable',
|
||||
this._boundSetCanDisable);
|
||||
|
||||
this._elements.loginButton.removeEventListener('click',
|
||||
this._boundLoginClick);
|
||||
this._elements.checkbox.removeEventListener('change',
|
||||
this._boundCheckboxChanged);
|
||||
},
|
||||
|
||||
_onLoginClick: function fmd_on_login_click(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this._elements.loginButton.disabled) {
|
||||
return;
|
||||
}
|
||||
if (!window.navigator.onLine) {
|
||||
document.l10n.formatValue('findmydevice-enable-network').then(
|
||||
(msg) => window.alert(msg));
|
||||
return;
|
||||
}
|
||||
this._interactiveLogin = true;
|
||||
navigator.mozId.request({
|
||||
oncancel: () => {
|
||||
this._interactiveLogin = false;
|
||||
console.log('Find My Device: oncancel fired');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_setEnabled: function fmd_set_enabled(value) {
|
||||
this._elements.checkbox.checked = value;
|
||||
this._elements.status.hidden = !value;
|
||||
},
|
||||
|
||||
_setTracked: function fmd_set_tracked(value) {
|
||||
this._elements.status.setAttribute('data-l10n-id',
|
||||
value ? 'findmydevice-active-tracking' : 'findmydevice-not-tracking');
|
||||
},
|
||||
|
||||
_setCanDisable: function fmd_set_can_disable(value) {
|
||||
this._elements.checkbox.disabled = !value;
|
||||
},
|
||||
|
||||
_togglePanel: function fmd_toggle_panel(loggedIn) {
|
||||
this._elements.signin.hidden = loggedIn;
|
||||
this._elements.settings.hidden = !loggedIn;
|
||||
},
|
||||
|
||||
_onChangeLoginState: function fmd_on_change_login_state(loggedIn) {
|
||||
console.log('settings, logged in: ' + loggedIn);
|
||||
|
||||
if (this._interactiveLogin) {
|
||||
this._registeredHelper.get(registered => {
|
||||
if (!registered) {
|
||||
this._enabledHelper.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._interactiveLogin = false;
|
||||
|
||||
// Bug 1164713: Force logged in status in case of stale setting value
|
||||
this._loggedInHelper.set(loggedIn);
|
||||
|
||||
this._elements.unverifiedError.hidden = true;
|
||||
this._elements.login.hidden = false;
|
||||
},
|
||||
|
||||
_onCheckboxChanged: function fmd_on_checkbox_changed(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!window.navigator.onLine) {
|
||||
// XXX(ggp) do this later so that the visual change in the
|
||||
// checkbox is properly prevented.
|
||||
// formatValue is asynchronous so we don't need an artificial
|
||||
// setTimeout here.
|
||||
document.l10n.formatValue('findmydevice-enable-network').then(
|
||||
msg => window.alert(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
this._elements.checkbox.disabled = true;
|
||||
|
||||
if (this._elements.checkbox.checked === false) {
|
||||
wakeUpFindMyDevice(IAC_API_WAKEUP_REASON_TRY_DISABLE);
|
||||
} else {
|
||||
this._enabledHelper.set(true, () => {
|
||||
this._elements.checkbox.disabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_findmydevice() {
|
||||
return new FindMyDevice();
|
||||
};
|
||||
});
|
||||
34
gaia/apps/settings/js/panels/findmydevice/panel.js
Executable file
34
gaia/apps/settings/js/panels/findmydevice/panel.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
define(function(require) {
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var FindMyDevice = require('panels/findmydevice/findmydevice');
|
||||
|
||||
return function ctor_findmydevice_panel() {
|
||||
var elements;
|
||||
var findmydevice = FindMyDevice();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {
|
||||
header: panel.querySelector('gaia-header'),
|
||||
login: panel.querySelector('.findmydevice-login'),
|
||||
loginButton: panel.querySelector('.findmydevice-login > button'),
|
||||
unverifiedError: panel.querySelector(
|
||||
'.findmydevice-fxa-unverified-error'),
|
||||
checkbox: panel.querySelector('.findmydevice-enabled gaia-switch'),
|
||||
status: panel.querySelector('.findmydevice-tracking'),
|
||||
signin: panel.querySelector('.findmydevice-signin'),
|
||||
settings: panel.querySelector('.findmydevice-settings')
|
||||
};
|
||||
findmydevice.onInit(elements);
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
findmydevice.onBeforeShow();
|
||||
},
|
||||
onBeforeHide: function() {
|
||||
findmydevice.onBeforeHide();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
155
gaia/apps/settings/js/panels/firefox_accounts/firefox_accounts.js
Executable file
155
gaia/apps/settings/js/panels/firefox_accounts/firefox_accounts.js
Executable file
|
|
@ -0,0 +1,155 @@
|
|||
'use strict';
|
||||
|
||||
define(function(require) {
|
||||
var normalizer = require('shared/text_normalizer');
|
||||
var fxaHelper = require('shared/fxa_iac_client');
|
||||
var dialogService = require('modules/dialog_service');
|
||||
|
||||
var FirefoxAccounts = function() {
|
||||
this._boundRefreshStatus =
|
||||
this._boundRefreshStatus || this.refreshStatus.bind(this);
|
||||
};
|
||||
|
||||
FirefoxAccounts.prototype = {
|
||||
onInit: function fxa_init(elements) {
|
||||
this._elements = elements;
|
||||
},
|
||||
|
||||
onBeforeShow: function fxa_beforeShow() {
|
||||
fxaHelper.addEventListener('onlogin', this._boundRefreshStatus);
|
||||
fxaHelper.addEventListener('onverified', this._boundRefreshStatus);
|
||||
fxaHelper.addEventListener('onlogout', this._boundRefreshStatus);
|
||||
this._boundRefreshStatus();
|
||||
},
|
||||
|
||||
onBeforeHide: function fxa_beforeHide() {
|
||||
fxaHelper.removeEventListener('onlogin', this._boundRefreshStatus);
|
||||
fxaHelper.removeEventListener('onverified', this._boundRefreshStatus);
|
||||
fxaHelper.removeEventListener('onlogout', this._boundRefreshStatus);
|
||||
},
|
||||
|
||||
refreshStatus: function fxa_refreshStatus() {
|
||||
fxaHelper.getAccount(
|
||||
this._onFxAccountStateChange.bind(this),
|
||||
this._onFxAccountError.bind(this));
|
||||
},
|
||||
|
||||
// if e == null, user is logged out.
|
||||
// if e.verified, user is logged in & verified.
|
||||
// if !e.verified, user is logged in & unverified.
|
||||
_onFxAccountStateChange: function fxa_onFxAccountStateChange(e) {
|
||||
var email = e ? normalizer.escapeHTML(e.email) : '';
|
||||
if (!e) {
|
||||
this._hideLoggedInPanel();
|
||||
this._hideUnverifiedPanel();
|
||||
this._showLoggedOutPanel();
|
||||
} else if (e.verified) {
|
||||
this._hideLoggedOutPanel();
|
||||
this._hideUnverifiedPanel();
|
||||
this._showLoggedInPanel(email);
|
||||
} else {
|
||||
this._hideLoggedOutPanel();
|
||||
this._hideLoggedInPanel();
|
||||
this._showUnverifiedPanel(email);
|
||||
}
|
||||
},
|
||||
|
||||
_onFxAccountError: function fxa_onFxAccountError(err) {
|
||||
console.error('FxaPanel: Error getting Firefox Account: ' + err.error);
|
||||
},
|
||||
|
||||
_hideLoggedOutPanel: function fxa_hideLoggedOutPanel() {
|
||||
this._elements.loginBtn.onclick = null;
|
||||
this._elements.loggedOutPanel.hidden = true;
|
||||
},
|
||||
|
||||
_showLoggedOutPanel: function fxa_showLoggedOutPanel() {
|
||||
this._elements.loginBtn.onclick = this._onLoginClick.bind(this);
|
||||
this._elements.loggedOutPanel.hidden = false;
|
||||
},
|
||||
|
||||
_hideLoggedInPanel: function fxa_hideLoggedInPanel() {
|
||||
this._elements.loggedInPanel.hidden = true;
|
||||
this._elements.loggedInEmail.textContent = '';
|
||||
this._elements.logoutBtn.onclick = null;
|
||||
},
|
||||
|
||||
_showLoggedInPanel: function fxa_showLoggedInPanel(email) {
|
||||
document.l10n.setAttributes(
|
||||
this._elements.loggedInEmail, 'fxa-logged-in-text', {
|
||||
email: email
|
||||
});
|
||||
this._elements.loggedInPanel.hidden = false;
|
||||
this._elements.logoutBtn.onclick = this._onLogoutClick.bind(this);
|
||||
},
|
||||
|
||||
_hideUnverifiedPanel: function fxa_hideUnverifiedPanel() {
|
||||
this._elements.unverifiedPanel.hidden = true;
|
||||
this._elements.unverifiedEmail.textContent = '';
|
||||
this._elements.cancelBtn.onclick = null;
|
||||
if (this._elements.resendLink) {
|
||||
this._elements.resendLink.onclick = null;
|
||||
}
|
||||
},
|
||||
|
||||
_showUnverifiedPanel: function fxa_showUnverifiedPanel(email) {
|
||||
this._elements.unverifiedPanel.hidden = false;
|
||||
this._elements.cancelBtn.onclick = this._onLogoutClick.bind(this);
|
||||
document.l10n.setAttributes(
|
||||
this._elements.unverifiedEmail,
|
||||
'fxa-verification-email-sent-msg',
|
||||
{email: email}
|
||||
);
|
||||
this._elements.resendLink.onclick = this._onResendClick.bind(this);
|
||||
},
|
||||
|
||||
_onLogoutClick: function fxa_onLogoutClick(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
fxaHelper.logout(
|
||||
this._onFxAccountStateChange.bind(this),
|
||||
this._onFxAccountError.bind(this));
|
||||
},
|
||||
|
||||
_onResendClick: function fxa__onResendClick(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (e.target.classList.contains('disabled')) {
|
||||
return;
|
||||
}
|
||||
fxaHelper.getAccount((accts) => {
|
||||
var email = accts && accts.email;
|
||||
if (!email) {
|
||||
return this._onFxAccountStateChange(accts);
|
||||
}
|
||||
fxaHelper.resendVerificationEmail(
|
||||
email, this._onResend.bind(this, email),
|
||||
this._onFxAccountError.bind(this));
|
||||
}, this._onFxAccountError.bind(this));
|
||||
},
|
||||
|
||||
_onResend: function fxa__onResend(email) {
|
||||
dialogService.alert({
|
||||
id: 'fxa-resend-alert',
|
||||
args: { email: email }
|
||||
});
|
||||
// disable link for 60 seconds, then reenable
|
||||
this._elements.resendLink.classList.add('disabled');
|
||||
setTimeout(() => {
|
||||
this._elements.resendLink.classList.remove('disabled');
|
||||
}, 60000);
|
||||
},
|
||||
|
||||
_onLoginClick: function fxa_onLoginClick(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
fxaHelper.openFlow(
|
||||
this._onFxAccountStateChange.bind(this),
|
||||
this._onFxAccountError.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_firefox_accounts() {
|
||||
return new FirefoxAccounts();
|
||||
};
|
||||
});
|
||||
36
gaia/apps/settings/js/panels/firefox_accounts/panel.js
Executable file
36
gaia/apps/settings/js/panels/firefox_accounts/panel.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
define(function(require) {
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var FirefoxAccounts = require('panels/firefox_accounts/firefox_accounts');
|
||||
|
||||
return function ctor_firefox_account_panel() {
|
||||
var elements;
|
||||
var firefoxAccounts = FirefoxAccounts();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {
|
||||
fxaContainer: document.getElementById('fxa'),
|
||||
loggedOutPanel: document.getElementById('fxa-logged-out'),
|
||||
loggedInPanel: document.getElementById('fxa-logged-in'),
|
||||
unverifiedPanel: document.getElementById('fxa-unverified'),
|
||||
resendEmail: document.getElementById('fxa-resend-email'),
|
||||
cancelBtn: document.getElementById('fxa-cancel-confirmation'),
|
||||
loginBtn: document.getElementById('fxa-login'),
|
||||
logoutBtn: document.getElementById('fxa-logout'),
|
||||
loggedInEmail: document.getElementById('fxa-logged-in-text'),
|
||||
unverifiedEmail: document.getElementById('fxa-unverified-text'),
|
||||
resendLink: document.getElementById('fxa-resend')
|
||||
};
|
||||
firefoxAccounts.onInit(elements);
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
firefoxAccounts.onBeforeShow();
|
||||
},
|
||||
onBeforeHide: function() {
|
||||
firefoxAccounts.onBeforeHide();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
381
gaia/apps/settings/js/panels/firefox_sync/firefox_sync.js
Executable file
381
gaia/apps/settings/js/panels/firefox_sync/firefox_sync.js
Executable file
|
|
@ -0,0 +1,381 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global ERROR_DIALOG_CLOSED_BY_USER */
|
||||
/* global ERROR_INVALID_SYNC_ACCOUNT */
|
||||
/* global ERROR_NO_KEY_FETCH_TOKEN */
|
||||
/* global ERROR_OFFLINE */
|
||||
/* global ERROR_SYNC_APP_KILLED */
|
||||
/* global ERROR_UNVERIFIED_ACCOUNT */
|
||||
/* global ERROR_UNKNOWN */
|
||||
/* global LazyLoader */
|
||||
/* global mozIntl */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SyncManagerBridge = require('modules/sync_manager_bridge');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
|
||||
const LOGGED_OUT_SCREEN = 'loggedout';
|
||||
const LOGGED_IN_SCREEN = 'loggedin';
|
||||
|
||||
const ELEMENTS = [{
|
||||
screen: LOGGED_OUT_SCREEN,
|
||||
selector: '.fxsync-login',
|
||||
event: 'click',
|
||||
listener: 'enable'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-logout',
|
||||
event: 'click',
|
||||
listener: 'disable'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-user'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-sync-now',
|
||||
event: 'click',
|
||||
listener: 'sync'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-tos',
|
||||
event: 'click',
|
||||
listener: 'openTos'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-privacy',
|
||||
event: 'click',
|
||||
listener: 'openPrivacy'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-collections-bookmarks'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-collections-history'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-collections-passwords'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-last-sync'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-unverified'
|
||||
}, {
|
||||
screen: LOGGED_IN_SCREEN,
|
||||
selector: '.fxsync-empty-account'
|
||||
}];
|
||||
|
||||
function getElementName(str) {
|
||||
str = str.toLowerCase().replace('.fxsync-', '');
|
||||
return str.replace(/-([a-z])/g, g => {
|
||||
return g[1].toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function FirefoxSyncPanel(panel, screens) {
|
||||
this.panel = panel;
|
||||
this.screens = screens;
|
||||
this.elements = {};
|
||||
this.currentScreen = null;
|
||||
this.collections = new Map();
|
||||
['sync.collections.bookmarks.enabled',
|
||||
'sync.collections.history.enabled',
|
||||
'sync.collections.passwords.enabled'].forEach(setting => {
|
||||
SettingsListener.observe(setting, true, enabled => {
|
||||
enabled ? this.collections.set(setting, enabled)
|
||||
: this.collections.delete(setting);
|
||||
this.maybeEnableSyncNow();
|
||||
});
|
||||
});
|
||||
|
||||
SyncManagerBridge.onsyncchange = this.onsyncchange.bind(this);
|
||||
}
|
||||
|
||||
FirefoxSyncPanel.prototype = {
|
||||
refresh() {
|
||||
SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));
|
||||
},
|
||||
|
||||
clean() {
|
||||
window.clearTimeout(this.timeout);
|
||||
this.timeout = undefined;
|
||||
window.removeEventListener('offline', this.onlineListener);
|
||||
window.removeEventListener('online', this.onlineListener);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is where most part of the panel logic lives.
|
||||
*
|
||||
* Here we handle Sync lifecycle events coming from the SyncManager
|
||||
* through the SyncMangerBridge. We react to these events by
|
||||
* changing the screen depending on the state.
|
||||
*
|
||||
* We have two possible different screens:
|
||||
* 1. Logged out screen. We simply show the user what Sync is and
|
||||
* allow her to start using it or to keep walking.
|
||||
* 2. Logged in screen. We show to the user several different options:
|
||||
* + The user owning the Sync account. If the account belongs to a
|
||||
* verified user, we are fine, if not, we show a warning asking the
|
||||
* user to verify her account and we disable all the options except
|
||||
* the one to disable sync.
|
||||
* + A button to disable sync. Clicking it will not log out from
|
||||
* FxA, it will simply disable sync on the device.
|
||||
* + A button to start a sync on demand. Clicking it will disable the
|
||||
* button and the collections switches while the synchronization is
|
||||
* being performaned. Once the sync is done successfully, we enable
|
||||
* all the options back and refresh the last sync time.
|
||||
* We disable this button if no collection is selected.
|
||||
* + Last sync time. Show the last time a successful synchronization
|
||||
* was done. If no previous synchronization exists for that login,
|
||||
* we don't show this label.
|
||||
* + Collections switches. Allow the user to select which collections
|
||||
* she wants to keep synchronized. If no collection is selected, the
|
||||
* synchronization will be disabled.
|
||||
* + ToS and Privacy links.
|
||||
*/
|
||||
onsyncchange(message) {
|
||||
if (!message || !message.state) {
|
||||
throw new Error('Missing sync state');
|
||||
}
|
||||
switch (message.state) {
|
||||
case 'disabled':
|
||||
this.showScreen(LOGGED_OUT_SCREEN);
|
||||
this.hideEnabling();
|
||||
this.clean();
|
||||
break;
|
||||
case 'enabling':
|
||||
this.showScreen(LOGGED_OUT_SCREEN);
|
||||
this.showEnabling();
|
||||
break;
|
||||
case 'enabled':
|
||||
this.hideEnabling();
|
||||
this.showScreen(LOGGED_IN_SCREEN);
|
||||
this.showSyncNow();
|
||||
this.showUser(message.user);
|
||||
this.showLastSync(message.lastSync);
|
||||
if (message.error) {
|
||||
LazyLoader.load('shared/js/sync/errors.js', () => {
|
||||
if (message.error == ERROR_INVALID_SYNC_ACCOUNT) {
|
||||
this.showEmptyAccount(message.user);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.hideEmptyAccount();
|
||||
}
|
||||
break;
|
||||
case 'syncing':
|
||||
this.showScreen(LOGGED_IN_SCREEN);
|
||||
// The user may enter in the panel while we are in the syncing
|
||||
// state. In that case, we also need to show the user because
|
||||
// the enabled state was not handled yet.
|
||||
this.showUser(message.user);
|
||||
this.showSyncing();
|
||||
break;
|
||||
case 'errored':
|
||||
LazyLoader.load('shared/js/sync/errors.js', () => {
|
||||
if (message.error == ERROR_UNVERIFIED_ACCOUNT) {
|
||||
this.showScreen(LOGGED_IN_SCREEN);
|
||||
this.showUnverified(message.user);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.error == ERROR_INVALID_SYNC_ACCOUNT) {
|
||||
this.showScreen(LOGGED_IN_SCREEN);
|
||||
this.showEmptyAccount(message.user);
|
||||
return;
|
||||
}
|
||||
|
||||
const IGNORED_ERRORS = [
|
||||
ERROR_DIALOG_CLOSED_BY_USER,
|
||||
ERROR_SYNC_APP_KILLED
|
||||
];
|
||||
|
||||
if (IGNORED_ERRORS.indexOf(message.error) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var errorMsg = ERROR_UNKNOWN;
|
||||
var title;
|
||||
|
||||
const KNOWN_ERRORS = [
|
||||
ERROR_OFFLINE,
|
||||
ERROR_NO_KEY_FETCH_TOKEN
|
||||
];
|
||||
|
||||
if (KNOWN_ERRORS.indexOf(message.error) > -1) {
|
||||
title = message.error;
|
||||
errorMsg = message.error + '-explanation';
|
||||
}
|
||||
|
||||
DialogService.alert(errorMsg, {
|
||||
title: title
|
||||
}).then(() => {
|
||||
this.showScreen(LOGGED_OUT_SCREEN);
|
||||
this.clean();
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
addListener(element, event, listener) {
|
||||
if (!element || !event || !listener) {
|
||||
return;
|
||||
}
|
||||
element.addEventListener(event, this[listener]);
|
||||
},
|
||||
|
||||
removeListener(element, event, listener) {
|
||||
if (!element || !event || !listener) {
|
||||
return;
|
||||
}
|
||||
element.removeEventListener(event, this[listener]);
|
||||
},
|
||||
|
||||
loadElements(screen) {
|
||||
ELEMENTS.forEach(element => {
|
||||
var name = getElementName(element.selector);
|
||||
if (element.screen == screen) {
|
||||
this.elements[name] = this.panel.querySelector(element.selector);
|
||||
this.addListener(this.elements[name],
|
||||
element.event, element.listener);
|
||||
return;
|
||||
}
|
||||
this.removeListener(this.elements[name],
|
||||
element.event, element.listener);
|
||||
this.elements[name] = null;
|
||||
});
|
||||
},
|
||||
|
||||
showScreen(screen) {
|
||||
if (this.currentScreen == screen) {
|
||||
return;
|
||||
}
|
||||
this.currentScreen = screen;
|
||||
this.screens.loggedIn.hidden = (screen != LOGGED_IN_SCREEN);
|
||||
this.screens.loggedOut.hidden = (screen != LOGGED_OUT_SCREEN);
|
||||
this.loadElements(screen);
|
||||
},
|
||||
|
||||
showEnabling() {
|
||||
this.elements.login.disabled = true;
|
||||
this.elements.login.dataset.l10nId = 'fxsync-signing';
|
||||
},
|
||||
|
||||
hideEnabling() {
|
||||
// It is possible that we go from enabled to syncing and viceversa.
|
||||
// In that case the login button won't be available anymore, so we
|
||||
// just bail out.
|
||||
if (!this.elements.login) {
|
||||
return;
|
||||
}
|
||||
this.elements.login.disabled = false;
|
||||
this.elements.login.dataset.l10nId = 'fxsync-get-started';
|
||||
},
|
||||
|
||||
showSyncing() {
|
||||
this.elements.syncNow.dataset.l10nId = 'fxsync-syncing';
|
||||
this.disableSyncNowAndCollections(true);
|
||||
},
|
||||
|
||||
maybeEnableSyncNow() {
|
||||
if (!this.elements.syncNow) {
|
||||
return;
|
||||
}
|
||||
this.elements.syncNow.disabled = (this.collections.size <= 0) ||
|
||||
!navigator.onLine;
|
||||
},
|
||||
|
||||
showSyncNow() {
|
||||
this.elements.syncNow.dataset.l10nId = 'fxsync-sync-now';
|
||||
this.disableSyncNowAndCollections(false);
|
||||
this.elements.unverified.hidden = true;
|
||||
this.maybeEnableSyncNow();
|
||||
this.onlineListener = this.maybeEnableSyncNow.bind(this);
|
||||
window.addEventListener('offline', this.onlineListener);
|
||||
window.addEventListener('online', this.onlineListener);
|
||||
},
|
||||
|
||||
showUser(email) {
|
||||
this.elements.user.textContent = email;
|
||||
},
|
||||
|
||||
showUnverified(email) {
|
||||
this.showUser(email);
|
||||
this.showLastSync();
|
||||
this.disableSyncNowAndCollections(true);
|
||||
this.elements.unverified.hidden = false;
|
||||
},
|
||||
|
||||
showEmptyAccount(email) {
|
||||
this.showUser(email);
|
||||
this.showLastSync();
|
||||
this.disableSyncNowAndCollections(false);
|
||||
this.elements.emptyAccount.hidden = false;
|
||||
},
|
||||
|
||||
hideEmptyAccount() {
|
||||
this.elements.emptyAccount.hidden = true;
|
||||
},
|
||||
|
||||
showLastSync(time) {
|
||||
if (!time) {
|
||||
this.elements.lastSync.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
var formatter = mozIntl._gaia.RelativeDate(navigator.languages, {
|
||||
style: 'short'
|
||||
});
|
||||
formatter.format(time).then(relDate => {
|
||||
var selector = 'span[data-l10n-id=fxsync-last-synced]';
|
||||
var lastSync = this.elements.lastSync.querySelector(selector);
|
||||
document.l10n.setAttributes(lastSync, 'fxsync-last-synced', {
|
||||
when: relDate
|
||||
});
|
||||
this.elements.lastSync.classList.remove('hidden');
|
||||
});
|
||||
|
||||
this.timeout = window.setTimeout(() => {
|
||||
this.showLastSync(time);
|
||||
}, 60 * 1000);
|
||||
},
|
||||
|
||||
disableSyncNowAndCollections(disabled) {
|
||||
['syncNow',
|
||||
'collectionsBookmarks',
|
||||
'collectionsHistory',
|
||||
'collectionsPasswords'].forEach(name => {
|
||||
this.elements[name].disabled = disabled;
|
||||
});
|
||||
},
|
||||
|
||||
enable() {
|
||||
SyncManagerBridge.enable();
|
||||
},
|
||||
|
||||
disable() {
|
||||
SyncManagerBridge.disable();
|
||||
},
|
||||
|
||||
sync() {
|
||||
SyncManagerBridge.sync();
|
||||
},
|
||||
|
||||
openTos() {
|
||||
window.open('https://accounts.firefox.com/legal/terms');
|
||||
},
|
||||
|
||||
openPrivacy() {
|
||||
window.open('https://accounts.firefox.com/legal/privacy');
|
||||
}
|
||||
};
|
||||
|
||||
return (panel, screens) => {
|
||||
return new FirefoxSyncPanel(panel, screens);
|
||||
};
|
||||
});
|
||||
39
gaia/apps/settings/js/panels/firefox_sync/panel.js
Executable file
39
gaia/apps/settings/js/panels/firefox_sync/panel.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsUtils = require('modules/settings_utils');
|
||||
var FirefoxSyncPanel =
|
||||
require('panels/firefox_sync/firefox_sync');
|
||||
|
||||
return () => {
|
||||
return SettingsPanel({
|
||||
onInit: function fxsync_onInit(panel) {
|
||||
// We only query the high level screen elements here.
|
||||
// Each screen has specific elements associated and we lazy load
|
||||
// these elements depending on the screen that we need to show to
|
||||
// the user according to the state of Sync.
|
||||
var screens = {
|
||||
loggedIn: panel.querySelector('.fxsync-logged-in'),
|
||||
loggedOut: panel.querySelector('.fxsync-logged-out')
|
||||
};
|
||||
|
||||
this.syncPanel = FirefoxSyncPanel(panel, screens);
|
||||
var header = panel.querySelector('gaia-header');
|
||||
SettingsUtils.runHeaderFontFit(header);
|
||||
},
|
||||
|
||||
onShow: function fxsync_onShow() {
|
||||
this.syncPanel.refresh();
|
||||
},
|
||||
|
||||
onHide: function fxsync_onHide() {
|
||||
this.syncPanel.clean();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
72
gaia/apps/settings/js/panels/frame/panel.js
Executable file
72
gaia/apps/settings/js/panels/frame/panel.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
|
||||
return function ctor_frame_panel() {
|
||||
var framePanel;
|
||||
var iframe;
|
||||
var noReflow;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
framePanel = panel;
|
||||
},
|
||||
onBeforeShow: function(panel, options) {
|
||||
// noReflow is used to memorize whether users had pressed `home` button
|
||||
// before. This is used to differentiate these two scenarios which would
|
||||
// trigger onBeforeShow().
|
||||
//
|
||||
// 1. Embmed the app at the first time.
|
||||
// 2. Recovering settings app after pressing home button first.
|
||||
if (noReflow) {
|
||||
noReflow = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozapp', options.mozapp);
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = options.src;
|
||||
iframe.addEventListener('mozbrowserclose', this._onBrowserClose);
|
||||
iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
|
||||
var message = self._escapeHTML(e.detail.message);
|
||||
var initialValue = e.detail.initialValue;
|
||||
var type = e.detail.promptType;
|
||||
|
||||
switch (type) {
|
||||
case 'alert':
|
||||
window.alert(message);
|
||||
break;
|
||||
case 'prompt':
|
||||
e.detail.returnValue = window.prompt(message, initialValue);
|
||||
break;
|
||||
case 'confirm':
|
||||
e.detail.returnValue = window.confirm(message);
|
||||
break;
|
||||
}
|
||||
});
|
||||
panel.appendChild(iframe);
|
||||
},
|
||||
onHide: function() {
|
||||
if (document.hidden) {
|
||||
noReflow = true;
|
||||
return;
|
||||
}
|
||||
framePanel.removeChild(iframe);
|
||||
},
|
||||
_onBrowserClose: function() {
|
||||
SettingsService.back();
|
||||
},
|
||||
_escapeHTML: function(str) {
|
||||
var span = document.createElement('span');
|
||||
span.textContent = str;
|
||||
// Escape space for displaying multiple space in message.
|
||||
span.innerHTML = span.innerHTML.replace(/\n/g, '<br/>');
|
||||
return span.innerHTML;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
58
gaia/apps/settings/js/panels/full_developer_mode/panel.js
Executable file
58
gaia/apps/settings/js/panels/full_developer_mode/panel.js
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var Panel = require('modules/panel');
|
||||
|
||||
/**
|
||||
* The panel can be invoked by triggering an activity:
|
||||
* new MozActivity({
|
||||
* name: 'configure',
|
||||
* data: {
|
||||
* target: 'device',
|
||||
* section: 'full-developer-mode'
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
return function ctor_full_dev_panel() {
|
||||
return Panel({
|
||||
onInit: function(panel) {
|
||||
this._panel = panel;
|
||||
SettingsCache.getSettings((results) => {
|
||||
var devMenuEnabled = !!results['developer.menu.enabled'];
|
||||
if (devMenuEnabled) {
|
||||
this._showFullDevModeWarning();
|
||||
} else {
|
||||
this._showEnableDevMenuWarning();
|
||||
}
|
||||
});
|
||||
},
|
||||
_close: function() {
|
||||
SettingsService.back();
|
||||
},
|
||||
_showFullDevModeWarning: function() {
|
||||
var warningDialog = this._panel.querySelector('.full-dev-mode-warning');
|
||||
var okBtn = warningDialog.querySelector('button[type="submit"]');
|
||||
var cancelBtn = warningDialog.querySelector('button[type="reset"]');
|
||||
|
||||
warningDialog.hidden = false;
|
||||
cancelBtn.onclick = this._close;
|
||||
okBtn.onclick = () => {
|
||||
DialogService.show('full-developer-mode-final-warning').then(() => {
|
||||
this._close();
|
||||
});
|
||||
};
|
||||
},
|
||||
_showEnableDevMenuWarning: function() {
|
||||
var warningDialog =
|
||||
this._panel.querySelector('.dev-menu-disabled-warning');
|
||||
var closeBtn = warningDialog.querySelector('button[type="submit"]');
|
||||
|
||||
warningDialog.hidden = false;
|
||||
closeBtn.onclick = this._close;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
54
gaia/apps/settings/js/panels/full_developer_mode_final_warning/panel.js
Executable file
54
gaia/apps/settings/js/panels/full_developer_mode_final_warning/panel.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
|
||||
return function ctor_full_dev_warning() {
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
this._warningDialog = {
|
||||
root: panel.querySelector('.warning-dialog'),
|
||||
okBtn: panel.querySelector('.warning-dialog button[type="submit"]'),
|
||||
cancelBtn:
|
||||
panel.querySelector('.warning-dialog button[type="reset"]'),
|
||||
warningInfo: panel.querySelector('.warning-dialog .warning-info')
|
||||
};
|
||||
},
|
||||
onBeforeShow: function() {
|
||||
this._count = 10;
|
||||
this._updateWarningInfo(this._count);
|
||||
},
|
||||
onSubmit: function() {
|
||||
if (this._count === 1) {
|
||||
this._wipe().catch(() => {
|
||||
this.close();
|
||||
});
|
||||
return Promise.reject();
|
||||
} else {
|
||||
this._count--;
|
||||
this._updateWarningInfo(this._count);
|
||||
return Promise.reject();
|
||||
}
|
||||
},
|
||||
_updateWarningInfo: function(count) {
|
||||
document.l10n.setAttributes(this._warningDialog.warningInfo,
|
||||
'enable-full-dev-mode-final-warning-msg', {
|
||||
count: count
|
||||
});
|
||||
},
|
||||
_wipe: function() {
|
||||
var power = navigator.mozPower;
|
||||
if (!power) {
|
||||
console.error('Cannot get mozPower');
|
||||
return Promise.reject();
|
||||
}
|
||||
if (!power.factoryReset) {
|
||||
console.error('Cannot invoke mozPower.factoryReset()');
|
||||
return Promise.reject();
|
||||
}
|
||||
power.factoryReset('root');
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
61
gaia/apps/settings/js/panels/full_developer_mode_warning/panel.js
Executable file
61
gaia/apps/settings/js/panels/full_developer_mode_warning/panel.js
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
|
||||
return function ctor_full_dev_panel() {
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
this._panel = panel;
|
||||
|
||||
this._devMenuEnabled = false;
|
||||
this._fullDevModeWarning =
|
||||
panel.querySelector('.full-dev-mode-warning');
|
||||
this._enableDevMenuWarning =
|
||||
panel.querySelector('.dev-menu-disabled-warning');
|
||||
},
|
||||
onBeforeShow: function(panel) {
|
||||
return new Promise((resolve) => {
|
||||
SettingsCache.getSettings((results) => {
|
||||
this._devMenuEnabled = !!results['developer.menu.enabled'];
|
||||
if (this._devMenuEnabled) {
|
||||
this._showFullDevModeWarning();
|
||||
} else {
|
||||
this._showEnableDevMenuWarning();
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
onHide: function() {
|
||||
this._fullDevModeWarning.hidden = true;
|
||||
this._enableDevMenuWarning.hidden = true;
|
||||
[].slice.call(this._panel.querySelectorAll('button')).forEach((btn) => {
|
||||
btn.removeAttribute('type');
|
||||
});
|
||||
},
|
||||
onSubmit: function() {
|
||||
if (this._devMenuEnabled) {
|
||||
return Promise.resolve('final_warning');
|
||||
}
|
||||
},
|
||||
_showFullDevModeWarning: function() {
|
||||
var warningDialog = this._fullDevModeWarning;
|
||||
var enableBtn = warningDialog.querySelector('button.enable-btn');
|
||||
var cancelBtn = warningDialog.querySelector('button.cancel-btn');
|
||||
|
||||
enableBtn.setAttribute('type', 'submit');
|
||||
cancelBtn.setAttribute('type', 'reset');
|
||||
warningDialog.hidden = false;
|
||||
},
|
||||
_showEnableDevMenuWarning: function() {
|
||||
var warningDialog = this._enableDevMenuWarning;
|
||||
var closeBtn = warningDialog.querySelector('button.close-btn');
|
||||
|
||||
closeBtn.setAttribute('type', 'submit');
|
||||
warningDialog.hidden = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
25
gaia/apps/settings/js/panels/help/panel.js
Executable file
25
gaia/apps/settings/js/panels/help/panel.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Used to show Device/Help panel
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Support = require('panels/help/support');
|
||||
|
||||
return function ctor_support_panel() {
|
||||
var help = Support();
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var elements = {
|
||||
userGuide: panel.querySelector('#user-guide'),
|
||||
help: panel,
|
||||
supportText: panel.querySelector('#help-online-support-text'),
|
||||
supportNumber: panel.querySelector('#help-call-support-numbers')
|
||||
};
|
||||
help.init(elements);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
151
gaia/apps/settings/js/panels/help/support.js
Executable file
151
gaia/apps/settings/js/panels/help/support.js
Executable file
|
|
@ -0,0 +1,151 @@
|
|||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var Support = function() {};
|
||||
|
||||
Support.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*/
|
||||
init: function support_init(elements) {
|
||||
this._elements = elements;
|
||||
/**
|
||||
* We'll stash the support info in here when reading from the
|
||||
* Settings.
|
||||
* If no values are found in Settings for support info We'll
|
||||
* refer to the JSON File Data.
|
||||
*/
|
||||
this._callSupportInfo = null;
|
||||
var url = 'http://support.mozilla.org/products/firefox-os';
|
||||
this._elements.userGuide.addEventListener('click', () => {
|
||||
this._elements.userGuide.blur();
|
||||
var activity = new window.MozActivity({
|
||||
name: 'view',
|
||||
data: { type: 'url', url: url }
|
||||
});
|
||||
// For workaround jshint.
|
||||
activity.onsuccess = function() {};
|
||||
});
|
||||
|
||||
// parse support information from data
|
||||
this._getSupportInfo(this._displaySupportInfo.bind(this));
|
||||
},
|
||||
/**
|
||||
* Clean states for test
|
||||
*/
|
||||
uninit: function() {
|
||||
this._elements = null;
|
||||
this._supportInfo = null;
|
||||
this._callSupportInfo = null;
|
||||
},
|
||||
/**
|
||||
* get support data from cache or from resource file
|
||||
*/
|
||||
_getSupportInfo: function support_getSupportInfo(callback) {
|
||||
if (this._supportInfo) {
|
||||
callback(this._supportInfo);
|
||||
return;
|
||||
}
|
||||
LazyLoader.getJSON('/resources/support.json')
|
||||
.then(function loadSupportInfo(data) {
|
||||
this._supportInfo = data;
|
||||
callback(this._supportInfo);
|
||||
}.bind(this));
|
||||
},
|
||||
/**
|
||||
* create support url items
|
||||
*/
|
||||
_createLinkNode: function support_createLinkNode(data, type) {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute('href', data.href);
|
||||
link.classList.add('link-text');
|
||||
if (type == 'tel') {
|
||||
link.textContent = data.title + ' (' + data.href + ')';
|
||||
} else {
|
||||
link.textContent = data.title;
|
||||
}
|
||||
return link;
|
||||
},
|
||||
/**
|
||||
* Indicate to our panel that there is support info present.
|
||||
*/
|
||||
_enableSupportInfo: function support_enableSupportInfo() {
|
||||
this._elements.help.dataset.hasSupportInfo = true;
|
||||
},
|
||||
/**
|
||||
* Local helper function to set the online support information
|
||||
* once it's retrieved or we've determined that we'll use
|
||||
* what's in the build JSON file.
|
||||
*/
|
||||
_setOnlineSupportInfo:
|
||||
function support_setOnlineSupportInfo(onlineSupportInfo) {
|
||||
this._enableSupportInfo();
|
||||
var nodes = this._createLinkNode(onlineSupportInfo);
|
||||
this._elements.supportText.appendChild(nodes);
|
||||
},
|
||||
/**
|
||||
* Local helper function to set the information once we've
|
||||
* retrieved it.
|
||||
*/
|
||||
_setCallSupportInfo:
|
||||
function support_setCallSupportInfo(supportInfo) {
|
||||
this._enableSupportInfo();
|
||||
for (var id in supportInfo) {
|
||||
this._elements.supportNumber
|
||||
.appendChild(this._createLinkNode(supportInfo[id], 'tel'));
|
||||
}
|
||||
},
|
||||
_renderSupportInfo: function support_renderSupportInfo(result) {
|
||||
var onlineSupportInfo = null;
|
||||
var onlineSupportTitle = result['support.onlinesupport.title'];
|
||||
if (onlineSupportTitle !== '') {
|
||||
onlineSupportInfo = { title: onlineSupportTitle };
|
||||
onlineSupportInfo.href = result['support.onlinesupport.href'];
|
||||
this._setOnlineSupportInfo(onlineSupportInfo);
|
||||
} else if (this._supportInfo) {
|
||||
this._setOnlineSupportInfo(this._supportInfo.onlinesupport);
|
||||
}
|
||||
|
||||
// Check to see if we have a title for the first support number.
|
||||
var callSupport1Title = result['support.callsupport1.title'];
|
||||
// If we have a title we'll go ahead and load the href for it too.
|
||||
if (callSupport1Title !== '') {
|
||||
this._callSupportInfo = [
|
||||
{
|
||||
'title': callSupport1Title,
|
||||
'href': result['support.callsupport1.href']
|
||||
}
|
||||
];
|
||||
|
||||
// Now check to see if we have a title for the second
|
||||
// support number. If we do, we'll load it's href as well.
|
||||
var callSupport2Title = result['support.callsupport2.title'];
|
||||
if (callSupport2Title !== '') {
|
||||
this._callSupportInfo.push({
|
||||
'title': callSupport2Title,
|
||||
'href': result['support.callsupport2.href']
|
||||
});
|
||||
// Finally set the support info retreived from Settings.
|
||||
this._setCallSupportInfo(this._callSupportInfo);
|
||||
}
|
||||
} else if (this._supportInfo) {
|
||||
// No customized values, use what's in the JSON file.
|
||||
this._setCallSupportInfo(this._supportInfo.callsupport);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* display Support information
|
||||
*/
|
||||
_displaySupportInfo: function support_displaySupportInfo(supportInfo) {
|
||||
SettingsCache.getSettings(this._renderSupportInfo.bind(this));
|
||||
}
|
||||
};
|
||||
return function ctor_support() {
|
||||
return new Support();
|
||||
};
|
||||
});
|
||||
106
gaia/apps/settings/js/panels/homescreen_details/homescreen_details.js
Executable file
106
gaia/apps/settings/js/panels/homescreen_details/homescreen_details.js
Executable file
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Homescreen detail panel.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
const MANIFEST_URL_PREF = 'homescreen.manifestURL';
|
||||
const DEFAULT_MANIFEST = 'app://homescreen.gaiamobile.org/manifest.webapp';
|
||||
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var mozApps = require('modules/navigator/mozApps');
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
|
||||
var HomescreenDetails = function ctor_homescreen_details() {
|
||||
this._elements = {};
|
||||
this._app = null;
|
||||
};
|
||||
|
||||
HomescreenDetails.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @param {Object} elements A list of HTML elements.
|
||||
*/
|
||||
init: function hd_init(elements) {
|
||||
this._elements = elements;
|
||||
|
||||
this._elements.uninstallButton.addEventListener('click',
|
||||
this.uninstall.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Update detail contents.
|
||||
*
|
||||
* @param {Object} options Home screen data
|
||||
*/
|
||||
onBeforeShow: function hd_onBeforeShow(options) {
|
||||
options.author = options.author || { url: '', name: '' };
|
||||
options.version = options.version || '0.0.0';
|
||||
options.description = options.description || '';
|
||||
|
||||
this._app = options.app;
|
||||
|
||||
var iconSrc = '../style/images/default.png';
|
||||
var manifest = new ManifestHelper(this._app.manifest ||
|
||||
this._app.updateManifest);
|
||||
if (manifest.icons && Object.keys(manifest.icons).length) {
|
||||
var key = Object.keys(manifest.icons)[0];
|
||||
var iconUrl = manifest.icons[key];
|
||||
if (!(/^(http|https|data):/.test(iconUrl))) {
|
||||
iconUrl = this._app.origin + '/' + iconUrl;
|
||||
}
|
||||
iconSrc = iconUrl;
|
||||
}
|
||||
|
||||
this._elements.icon.src = iconSrc;
|
||||
this._elements.headerTitle.textContent = options.name;
|
||||
this._elements.detailTitle.textContent = options.name;
|
||||
this._elements.detailName.textContent = options.author.name;
|
||||
this._elements.detailVersion.textContent = `v.${options.version}`;
|
||||
this._elements.detailDescription.textContent = options.description;
|
||||
this._elements.detailURLLink.href = options.author.url;
|
||||
this._elements.detailURLLink.textContent = options.author.url;
|
||||
this._elements.uninstallButton.disabled = !options.removable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall the current app.
|
||||
*/
|
||||
uninstall: function hd_uninstall() {
|
||||
const settings = window.navigator.mozSettings;
|
||||
|
||||
// Function to uninstall app and return to the previous panel.
|
||||
var uninstallApp = () => {
|
||||
mozApps.mgmt.uninstall(this._app).onsuccess = () => {
|
||||
this.back();
|
||||
};
|
||||
};
|
||||
|
||||
// Change the home screen back to the default one if we're
|
||||
// uninstalling it.
|
||||
var manifestURL = this._app.manifestURL;
|
||||
var homescreenSetting = settings.createLock().get(MANIFEST_URL_PREF);
|
||||
homescreenSetting.onsuccess = () => {
|
||||
var homeManifestURL = homescreenSetting.result[MANIFEST_URL_PREF];
|
||||
if (homeManifestURL === manifestURL) {
|
||||
var settingObject = {};
|
||||
settingObject[MANIFEST_URL_PREF] = DEFAULT_MANIFEST;
|
||||
homescreenSetting = settings.createLock().set(settingObject);
|
||||
homescreenSetting.onsuccess = () => { uninstallApp(); };
|
||||
} else {
|
||||
uninstallApp();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Back to the previous panel.
|
||||
*/
|
||||
back: function hd_back() {
|
||||
SettingsService.navigate('homescreens-list');
|
||||
}
|
||||
};
|
||||
|
||||
return () => new HomescreenDetails();
|
||||
});
|
||||
42
gaia/apps/settings/js/panels/homescreen_details/panel.js
Executable file
42
gaia/apps/settings/js/panels/homescreen_details/panel.js
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Used to show homescreen details panel.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var HomescreensDetails =
|
||||
require('panels/homescreen_details/homescreen_details');
|
||||
|
||||
return function ctor_homescreen_details_panel() {
|
||||
var homescreensDetails = HomescreensDetails();
|
||||
|
||||
return SettingsPanel({
|
||||
/**
|
||||
* @param {HTMLElement} panel The panel HTML element.
|
||||
*/
|
||||
onInit: function hdp_onInit(panel) {
|
||||
homescreensDetails.init({
|
||||
icon: panel.querySelector('.developer-infos img'),
|
||||
headerTitle: panel.querySelector('.detail-title'),
|
||||
detailTitle: panel.querySelector('.developer-infos .title'),
|
||||
detailName: panel.querySelector('.developer-infos .name'),
|
||||
detailVersion: panel.querySelector('.version'),
|
||||
detailDescription: panel.querySelector('.description'),
|
||||
detailURL: panel.querySelector('.url'),
|
||||
detailURLLink: panel.querySelector('.url a'),
|
||||
uninstallButton: panel.querySelector('button.uninstall-homescreen')
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} panel The panel HTML element.
|
||||
* @param {Object} options
|
||||
*/
|
||||
onBeforeShow: function hdp_onBeforeShow(panel, options) {
|
||||
homescreensDetails.onBeforeShow(options);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
76
gaia/apps/settings/js/panels/homescreens/homescreen_cols.js
Executable file
76
gaia/apps/settings/js/panels/homescreens/homescreen_cols.js
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Manage the column layout of the home screen.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var Module = require('modules/base/module');
|
||||
var HomescreenSettings = require('shared/homescreens/homescreen_settings');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
|
||||
var HomescreenCols = Module.create(function HomescreenCols() {
|
||||
this.super(Observable).call(this);
|
||||
|
||||
this._isUpdating = false;
|
||||
this._cachedColsValue = null;
|
||||
|
||||
// We may update this value somewhere in other apps.
|
||||
HomescreenSettings.addEventListener('updated', e => {
|
||||
var prop = e.target;
|
||||
if (prop.name === 'grid.cols') {
|
||||
this._cols = prop.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Set the default value.
|
||||
HomescreenSettings.get('grid.cols').then(number => {
|
||||
this._cols = number;
|
||||
});
|
||||
}).extend(Observable);
|
||||
|
||||
/**
|
||||
* @memberOf HomescreenCols
|
||||
* @type {Number}
|
||||
* @public
|
||||
*/
|
||||
Observable.defineObservableProperty(HomescreenCols.prototype, 'cols', {
|
||||
readonly: true,
|
||||
value: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Change the value of `grid.cols` preference of the homescreen.
|
||||
*
|
||||
* @param {Number} value
|
||||
*/
|
||||
HomescreenCols.prototype.setCols = function hc_setCols(value) {
|
||||
if (!this._isUpdating) {
|
||||
this._isUpdating = true;
|
||||
HomescreenSettings.put('grid.cols', value).then(() => {
|
||||
this._cols = value;
|
||||
this._isUpdating = false;
|
||||
if (this._cachedColsValue) {
|
||||
var cachedValue = this._cachedColsValue;
|
||||
this._cachedColsValue = null;
|
||||
this.setCols(cachedValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._cachedColsValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(HomescreenCols.prototype, 'verticalhomeActive', {
|
||||
set: function(active) {
|
||||
HomescreenSettings.setStoreName(active ?
|
||||
'vertical_preferences_store' : 'homescreen_settings');
|
||||
HomescreenSettings.get('grid.cols').then(number => {
|
||||
this._cols = number;
|
||||
});
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
return HomescreenCols;
|
||||
});
|
||||
101
gaia/apps/settings/js/panels/homescreens/homescreen_name.js
Executable file
101
gaia/apps/settings/js/panels/homescreens/homescreen_name.js
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* HomescreenName module has a `name` property that contains the name of the
|
||||
* currently used home screen and can be watched.
|
||||
*
|
||||
* @module HomescreenName
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
const MANIFEST_URL_PREF = 'homescreen.manifestURL';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var AppsCache = require('modules/apps_cache');
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
var Module = require('modules/base/module');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
|
||||
var HomescreenName = Module.create(function HomescreenName() {
|
||||
this.super(Observable).call(this);
|
||||
this._init();
|
||||
}).extend(Observable);
|
||||
|
||||
/**
|
||||
* Name of the currently installed home screen.
|
||||
*
|
||||
* @memberOf HomescreenName
|
||||
* @type {String}
|
||||
* @public
|
||||
*/
|
||||
Observable.defineObservableProperty(HomescreenName.prototype, 'name', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Init HomescreenName module.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
HomescreenName.prototype._init = function hn_init() {
|
||||
this._watchNameChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the value of MANIFEST_URL_PREF from settings and update name
|
||||
* accordingly.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
HomescreenName.prototype._watchNameChange = function hn_watchNameChange() {
|
||||
SettingsListener.observe(MANIFEST_URL_PREF, '', manifestURL => {
|
||||
this._updateManifestName(manifestURL).then(name => {
|
||||
this._name = name;
|
||||
}).catch(e => {
|
||||
console.warn('Could not get manifest name.', e);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the value of name when the module is instantiated.
|
||||
*/
|
||||
HomescreenName.prototype.getName = function hn_getName() {
|
||||
const settings = window.navigator.mozSettings;
|
||||
|
||||
var homescreenSetting = settings.createLock().get(MANIFEST_URL_PREF);
|
||||
homescreenSetting.onsuccess = () => {
|
||||
var manifestURL = homescreenSetting.result[MANIFEST_URL_PREF];
|
||||
this._updateManifestName(manifestURL);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a manifest URL, returns a promise that resolves to the app name.
|
||||
*
|
||||
* @param {string} manifestURL
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
HomescreenName.prototype._updateManifestName = function hn_umn(manifestURL) {
|
||||
return new Promise((resolve, reject) => {
|
||||
AppsCache.apps().then(apps => {
|
||||
var manifest = null;
|
||||
apps.some(app => {
|
||||
if (app.manifestURL === manifestURL) {
|
||||
manifest = new ManifestHelper(app.manifest || app.updateManifest);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!manifest) {
|
||||
return reject(new Error('Manifest URL not found'));
|
||||
}
|
||||
|
||||
resolve(manifest.name);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return HomescreenName;
|
||||
});
|
||||
100
gaia/apps/settings/js/panels/homescreens/panel.js
Executable file
100
gaia/apps/settings/js/panels/homescreens/panel.js
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* Manage wallpaper and replaceable home screens.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
const VERTICALHOME_MANIFEST =
|
||||
'app://verticalhome.gaiamobile.org/manifest.webapp';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Wallpaper = require('panels/homescreens/wallpaper');
|
||||
var HomescreenCols = require('panels/homescreens/homescreen_cols');
|
||||
var HomescreenName = require('panels/homescreens/homescreen_name');
|
||||
|
||||
var elements = {};
|
||||
var gridSelect = null;
|
||||
|
||||
return function ctor_homescreen_panel() {
|
||||
var wallpaper = Wallpaper();
|
||||
var homescreenCols = HomescreenCols();
|
||||
var homescreenName = HomescreenName();
|
||||
|
||||
return SettingsPanel({
|
||||
/**
|
||||
* @param {HTMLElement} panel The panel HTML element.
|
||||
*/
|
||||
onInit: function hp_onInit(panel) {
|
||||
elements = {
|
||||
wallpaper: panel.querySelector('.wallpaper'),
|
||||
wallpaperPreview: panel.querySelector('.wallpaper-preview'),
|
||||
currentHomescreen: panel.querySelector('.current-homescreen')
|
||||
};
|
||||
|
||||
elements.wallpaper.addEventListener('click',
|
||||
wallpaper.selectWallpaper.bind(wallpaper));
|
||||
|
||||
SettingsListener.observe('homescreen.manifestURL', '', manifestURL => {
|
||||
homescreenCols.verticalhomeActive =
|
||||
manifestURL === VERTICALHOME_MANIFEST;
|
||||
});
|
||||
|
||||
gridSelect = panel.querySelector('[name="grid.layout.cols"]');
|
||||
gridSelect.addEventListener('change', function() {
|
||||
homescreenCols.setCols(this.value);
|
||||
});
|
||||
},
|
||||
|
||||
onBeforeShow: function hp_onBeforeShow(panel, options) {
|
||||
this._setWallpaperPreviewSrc(wallpaper.wallpaperSrc);
|
||||
this._setHomescreenName(homescreenName.name);
|
||||
this._updateCols(homescreenCols.cols);
|
||||
|
||||
homescreenCols.observe('cols', this._updateCols);
|
||||
|
||||
wallpaper.observe('wallpaperSrc', this._setWallpaperPreviewSrc);
|
||||
homescreenName.observe('name', this._setHomescreenName);
|
||||
},
|
||||
|
||||
onBeforeHide: function hp_onBeforeHide() {
|
||||
wallpaper.unobserve('wallpaperSrc');
|
||||
homescreenName.unobserve('name');
|
||||
homescreenCols.unobserve('cols');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} src
|
||||
* @private
|
||||
*/
|
||||
_setWallpaperPreviewSrc: function hp_setHomescreenName(src) {
|
||||
elements.wallpaperPreview.src = src;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} number The number of columns in the layout.
|
||||
* @private
|
||||
*/
|
||||
_updateCols: function hdp_updateCols(number) {
|
||||
if (!number) {
|
||||
return;
|
||||
}
|
||||
|
||||
var option =
|
||||
gridSelect.querySelector('[value="' + number + '"]');
|
||||
|
||||
if (option) {
|
||||
option.selected = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @private
|
||||
*/
|
||||
_setHomescreenName: function hp_setHomescreenName(name) {
|
||||
elements.currentHomescreen.textContent = name;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
133
gaia/apps/settings/js/panels/homescreens/wallpaper.js
Executable file
133
gaia/apps/settings/js/panels/homescreens/wallpaper.js
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* Wallpaper:
|
||||
* - Select wallpaper by calling wallpaper.selectWallpaper.
|
||||
* - Update wallpaperSrc if wallpaper.image is changed, which is watched
|
||||
* by Observable module.
|
||||
* Wallpaper handles only data and does not involve in any UI logic.
|
||||
*
|
||||
* @module Wallpaper
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
const WALLPAPER_KEY = 'wallpaper.image';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsURL = require('shared/settings_url');
|
||||
var ForwardLock = require('shared/omadrm/fl');
|
||||
var Module = require('modules/base/module');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
|
||||
var Wallpaper = Module.create(function Wallpaper() {
|
||||
this.super(Observable).call(this);
|
||||
this._init();
|
||||
}).extend(Observable);
|
||||
|
||||
/**
|
||||
* Source path of wallpaper.
|
||||
*
|
||||
* @memberOf Wallpaper
|
||||
* @type {String}
|
||||
* @public
|
||||
*/
|
||||
Observable.defineObservableProperty(Wallpaper.prototype, 'wallpaperSrc', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Init Wallpaper module.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._init = function w_init() {
|
||||
this.wallpaperURL = new SettingsURL();
|
||||
this._watchWallpaperChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the value of wallpaper.image from settings and change wallpaperSrc.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._watchWallpaperChange = function w_watchWallpaper() {
|
||||
SettingsListener.observe(WALLPAPER_KEY, '', value => {
|
||||
this._wallpaperSrc = this.wallpaperURL.set(value);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch to wallpaper or gallery app to pick wallpaper.
|
||||
*
|
||||
* @param {String} secret
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._triggerActivity = function w_triggerActivity(secret) {
|
||||
var mozActivity = new window.MozActivity({
|
||||
name: 'pick',
|
||||
data: {
|
||||
type: ['wallpaper', 'image/*'],
|
||||
includeLocked: (secret !== null),
|
||||
// XXX: This will not work with Desktop Fx / Simulator.
|
||||
width: Math.ceil(window.screen.width * window.devicePixelRatio),
|
||||
height: Math.ceil(window.screen.height * window.devicePixelRatio)
|
||||
}
|
||||
});
|
||||
mozActivity.onsuccess = () => {
|
||||
this._onPickSuccess(mozActivity.result.blob, secret);
|
||||
};
|
||||
|
||||
mozActivity.onerror = this._onPickError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call back when picking success.
|
||||
*
|
||||
* @param {String} blob
|
||||
* @param {String} secret
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._onPickSuccess = function w_onPickSuccess(blob, secret) {
|
||||
if (!blob) {
|
||||
return;
|
||||
}
|
||||
if (blob.type.split('/')[1] === ForwardLock.mimeSubtype) {
|
||||
// If this is a locked image from the locked content app, unlock it.
|
||||
ForwardLock.unlockBlob(secret, blob, unlocked => {
|
||||
this._setWallpaper(unlocked);
|
||||
});
|
||||
} else {
|
||||
this._setWallpaper(blob);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the value of wallpaper.image from settings.
|
||||
*
|
||||
* @param {String} value
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._setWallpaper = function w_setWallpaper(value) {
|
||||
var config = {};
|
||||
config[WALLPAPER_KEY] = value;
|
||||
SettingsListener.getSettingsLock().set(config);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call back when picking fail.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Wallpaper.prototype._onPickError = function w_onPickError() {
|
||||
console.warn('pick failed!');
|
||||
};
|
||||
|
||||
/**
|
||||
* Start to select wallpaper.
|
||||
*/
|
||||
Wallpaper.prototype.selectWallpaper = function w_selectWallpaper() {
|
||||
ForwardLock.getKey(this._triggerActivity.bind(this));
|
||||
};
|
||||
|
||||
return Wallpaper;
|
||||
});
|
||||
196
gaia/apps/settings/js/panels/homescreens_list/homescreens_list.js
Executable file
196
gaia/apps/settings/js/panels/homescreens_list/homescreens_list.js
Executable file
|
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* Handle home screens list panel functionality.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var AppsCache = require('modules/apps_cache');
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsService = require('modules/settings_service');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var ManifestHelper = require('shared/manifest_helper');
|
||||
|
||||
var HomescreensList = function ctor_homescreens_list() {
|
||||
this._apps = [];
|
||||
this._elements = {};
|
||||
this.manifestURL = '';
|
||||
};
|
||||
|
||||
HomescreensList.prototype = {
|
||||
/**
|
||||
* initialization.
|
||||
*
|
||||
* @param {Array.<Object>} elements An array of HTML elements.
|
||||
*/
|
||||
init: function hl_init(elements) {
|
||||
this._elements = elements;
|
||||
this._renderHomescreens();
|
||||
|
||||
window.addEventListener('applicationinstall',
|
||||
this._renderHomescreens.bind(this));
|
||||
window.addEventListener('applicationuninstall',
|
||||
this._renderHomescreens.bind(this));
|
||||
|
||||
this._elements.moreLink.addEventListener('click',
|
||||
this._showMoreHomescreens.bind(this));
|
||||
|
||||
// Refresh the list whenever apps are installed/uninstalled.
|
||||
AppsCache.addEventListener('oninstall',
|
||||
this._renderHomescreens.bind(this));
|
||||
AppsCache.addEventListener('onuninstall',
|
||||
this._renderHomescreens.bind(this));
|
||||
|
||||
SettingsListener.observe('homescreen.manifestURL', '', manifestURL => {
|
||||
this.manifestURL = manifestURL;
|
||||
// There is a race condition, so we need to refresh the list.
|
||||
this.listBuilder();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* handle label click event.
|
||||
*
|
||||
* @param {Number} index link index.
|
||||
* @private
|
||||
*/
|
||||
_labelClick: function hl_labelClick(index) {
|
||||
var app = this._apps[index];
|
||||
var manifest = new ManifestHelper(app.manifest || app.updateManifest);
|
||||
|
||||
SettingsService.navigate('homescreen-details', {
|
||||
name: manifest.name,
|
||||
author: manifest.developer,
|
||||
version: manifest.version,
|
||||
description: manifest.description,
|
||||
removable: app.removable,
|
||||
app: app
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* handle radio click event.
|
||||
*
|
||||
* @param {Number} index link index.
|
||||
* @private
|
||||
*/
|
||||
_radioClick: function hl_radioClick(index) {
|
||||
var app = this._apps[index];
|
||||
const settings = window.navigator.mozSettings;
|
||||
|
||||
// Change the current home screen.
|
||||
settings.createLock().set({
|
||||
'homescreen.manifestURL': app.manifestURL
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Build list item:
|
||||
* <li data-app-index="0">
|
||||
* <gaia-radio value="0" name="homescreen">
|
||||
* <label class="name">New Home Screen</label>
|
||||
* </gaia-radio>
|
||||
* </li>
|
||||
*
|
||||
* @param {Object} app app object.
|
||||
* @param {Number} index link index.
|
||||
* @private
|
||||
*/
|
||||
_listItemBuilder: function hl_listItemBuilder(app, index) {
|
||||
var li = document.createElement('li');
|
||||
li.dataset.appIndex = index;
|
||||
|
||||
var radio = document.createElement('gaia-radio');
|
||||
radio.className = 'split';
|
||||
radio.name = 'homescreen';
|
||||
radio.value = index;
|
||||
radio.checked = (app.manifestURL === this.manifestURL);
|
||||
|
||||
var manifest =
|
||||
new ManifestHelper(app.manifest || app.updateManifest);
|
||||
|
||||
var radioLabel = document.createElement('label');
|
||||
radioLabel.classList.add('name');
|
||||
radioLabel.textContent = manifest.name;
|
||||
radio.appendChild(radioLabel);
|
||||
|
||||
li.appendChild(radio);
|
||||
|
||||
// Keep track if we clicked the label or not.
|
||||
// This is needed because you can't stop the event from propagating to the
|
||||
// shadow DOM from the label currently.
|
||||
var labelClicked = false;
|
||||
|
||||
// Register the handler for the click event on the checkbox.
|
||||
radio.addEventListener('click', event => {
|
||||
if (!labelClicked) {
|
||||
this._radioClick(index);
|
||||
}
|
||||
labelClicked = false;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}, true);
|
||||
|
||||
// Register the handler for the mouseup event on the label.
|
||||
radioLabel.addEventListener('mouseup', () => {
|
||||
labelClicked = true;
|
||||
this._labelClick(index);
|
||||
});
|
||||
|
||||
return li;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the home screens list.
|
||||
*/
|
||||
listBuilder: function hl_listBuilder() {
|
||||
this._elements.homescreensList.innerHTML = '';
|
||||
var listFragment = document.createDocumentFragment();
|
||||
var item;
|
||||
this._apps.forEach((app, index) => {
|
||||
item = this._listItemBuilder(app, index);
|
||||
listFragment.appendChild(item);
|
||||
});
|
||||
this._elements.homescreensList.appendChild(listFragment);
|
||||
},
|
||||
|
||||
/**
|
||||
* Look for installed home screens and then display that list.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_renderHomescreens: function hl_renderHomescreens() {
|
||||
return AppsCache.apps().then(apps => {
|
||||
this._apps = apps.filter(app => {
|
||||
var manifest =
|
||||
new ManifestHelper(app.manifest || app.updateManifest);
|
||||
return manifest && manifest.role && manifest.role === 'homescreen';
|
||||
});
|
||||
|
||||
this.listBuilder();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a web activity to the Marketplace for the available home screens.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_showMoreHomescreens: function hl_showMoreHomescreens() {
|
||||
this._elements.moreLink.blur();
|
||||
SettingsCache.getSettings(result => {
|
||||
var version = result['deviceinfo.os'];
|
||||
/* jshint nonew: false */
|
||||
new window.MozActivity({
|
||||
name: 'marketplace-category',
|
||||
data: {
|
||||
slug: 'homescreens',
|
||||
// Marketplace expects major.minor
|
||||
fxos_version: version.split('.').slice(0, 2).join('.')
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return () => new HomescreensList();
|
||||
});
|
||||
29
gaia/apps/settings/js/panels/homescreens_list/panel.js
Executable file
29
gaia/apps/settings/js/panels/homescreens_list/panel.js
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* List installed replaceable home screens.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var HomescreensList = require('panels/homescreens_list/homescreens_list');
|
||||
|
||||
return function ctor_homescreens_panel() {
|
||||
var homescreensList = HomescreensList();
|
||||
|
||||
return SettingsPanel({
|
||||
/**
|
||||
* @param {HTMLElement} panel The panel HTML element.
|
||||
*/
|
||||
onInit: function hp_onInit(panel) {
|
||||
homescreensList.init({
|
||||
homescreensList: panel.querySelector('.my-home-screens'),
|
||||
moreLink: panel.querySelector('.get-more-home-screens')
|
||||
});
|
||||
},
|
||||
|
||||
onBeforeShow: function hp_onBeforeShow() {
|
||||
homescreensList.listBuilder();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
330
gaia/apps/settings/js/panels/hotspot/hotspot.js
Executable file
330
gaia/apps/settings/js/panels/hotspot/hotspot.js
Executable file
|
|
@ -0,0 +1,330 @@
|
|||
/**
|
||||
* Hotspot is a singleton that you can easily use it to fetch
|
||||
* some shared data across different panels
|
||||
*
|
||||
* @module Hotspot
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
// modules / helpers
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
|
||||
const RE_ENABLE_WIFI_TETHERING_TIME = 1000;
|
||||
|
||||
var Hotspot = function() {
|
||||
this._settings = navigator.mozSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* @alias module:hotspot/hotspot
|
||||
* @requires module:hotspot/hotspot_settings
|
||||
* @returns {Hotspot}
|
||||
*/
|
||||
Hotspot.prototype = {
|
||||
/**
|
||||
* Wifi hotspot setting
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Boolean}
|
||||
*/
|
||||
_hotspotSetting: null,
|
||||
/**
|
||||
* Usb hotspot setting
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Boolean}
|
||||
*/
|
||||
_usbHotspotSetting: null,
|
||||
/**
|
||||
* Usb storage setting
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Boolean}
|
||||
*/
|
||||
_usbStorageSetting: null,
|
||||
/**
|
||||
* These listeners would be called when hotspot setting is changed
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Array}
|
||||
*/
|
||||
_hotspotChangeListeners: [],
|
||||
|
||||
/**
|
||||
* These listeners would be called when usb hotspot setting is changed
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Array}
|
||||
*/
|
||||
_usbHotspotChangeListeners: [],
|
||||
|
||||
/**
|
||||
* These listeners would be called when usb storage setting is changed
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Array}
|
||||
*/
|
||||
_usbStorageChangeListeners: [],
|
||||
|
||||
/**
|
||||
* These listeners would be called when incompatibles settings are
|
||||
* enabled at the same time
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @type {Array}
|
||||
*/
|
||||
_incompatibleSettingsListeners: [],
|
||||
|
||||
/**
|
||||
* Wifi tethering setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Hotspot
|
||||
* @type {String}
|
||||
*/
|
||||
tetheringWifiKey: 'tethering.wifi.enabled',
|
||||
|
||||
/**
|
||||
* Usb tethering setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Hotspot
|
||||
* @type {String}
|
||||
*/
|
||||
tetheringUsbKey: 'tethering.usb.enabled',
|
||||
|
||||
/**
|
||||
* Usb storage setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf Hotspot
|
||||
* @type {String}
|
||||
*/
|
||||
usbStorageKey: 'ums.enabled',
|
||||
|
||||
/**
|
||||
* Init is used to initialize some basic stuffs
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
init: function h_init() {
|
||||
this._bindEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* We will bind some default listeners here
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
_bindEvents: function() {
|
||||
// Wifi tethering enabled
|
||||
SettingsListener.observe(this.tetheringWifiKey, false,
|
||||
this._hotspotSettingChange.bind(this));
|
||||
|
||||
// USB tethering enabled
|
||||
SettingsListener.observe(this.tetheringUsbKey, false,
|
||||
this._usbHotspotSettingChange.bind(this));
|
||||
|
||||
// USB storage enabled
|
||||
SettingsListener.observe(this.usbStorageKey, false,
|
||||
this._usbStorageSettingChange.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* When wifi hotspot is changed, we will call all registered listeners
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
_hotspotSettingChange: function(enabled) {
|
||||
this._hotspotSetting = enabled;
|
||||
this._hotspotChangeListeners.forEach(function(listener) {
|
||||
listener(enabled);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* When usb hotspot is changed, we will call all registered listeners
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
_usbHotspotSettingChange: function(enabled) {
|
||||
this._usbHotspotSetting = enabled;
|
||||
this._usbHotspotChangeListeners.forEach(function(listener) {
|
||||
listener(enabled);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* When usb storage is changed, we will call all registered listeners
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
_usbStorageSettingChange: function(enabled) {
|
||||
this._usbStorageSetting = enabled;
|
||||
this._usbStorageChangeListeners.forEach(function(listener) {
|
||||
listener(enabled);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* When two incompatible settings are enabled we will call all
|
||||
* registered listeners.
|
||||
*
|
||||
* @param bothConflicts Indicates that usb hotspot has the two
|
||||
* possible conflicts (wifi hotspot and usb storage)
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
_incompatibleSettings: function(newSetting, oldSetting, bothConflicts) {
|
||||
this._incompatibleSettingsListeners.forEach(function(listener) {
|
||||
listener(newSetting, oldSetting, bothConflicts);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if two incompatible settings are enabled
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
*/
|
||||
checkIncompatibleSettings: function(newSetting, value) {
|
||||
switch(newSetting) {
|
||||
case this.tetheringWifiKey:
|
||||
// Early return if the user has disabled the setting
|
||||
if (!value) {
|
||||
this._setWifiTetheringSetting(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value && this._usbHotspotSetting) {
|
||||
this._incompatibleSettings(this.tetheringWifiKey,
|
||||
this.tetheringUsbKey, false);
|
||||
} else {
|
||||
this._setWifiTetheringSetting(value);
|
||||
}
|
||||
break;
|
||||
case this.tetheringUsbKey:
|
||||
// Early return if the user has disabled the setting or the
|
||||
// incompatible settings are disabled
|
||||
if (!value || (!this._hotspotSetting && !this._usbStorageSetting)) {
|
||||
this._setUsbTetheringSetting(value);
|
||||
return;
|
||||
}
|
||||
if (this._usbStorageSetting && this._hotspotSetting) {
|
||||
this._incompatibleSettings(this.tetheringUsbKey, null, true);
|
||||
} else {
|
||||
var oldSetting = this._usbStorageSetting ? this.usbStorageKey :
|
||||
this.tetheringWifiKey;
|
||||
this._incompatibleSettings(this.tetheringUsbKey, oldSetting, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
reEnableWifiTetheringSetting: function() {
|
||||
if (!this.wifiHotspotSetting) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The time is used to avoid the quick changes on the toggle that may
|
||||
// cause bad user experience.
|
||||
this._setWifiTetheringSetting(false);
|
||||
setTimeout(() => {
|
||||
this._setWifiTetheringSetting(true);
|
||||
}, RE_ENABLE_WIFI_TETHERING_TIME);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is an internal function that can help us find out the matched
|
||||
* callback from catched listeners and remove it
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @param {Array} listeners
|
||||
* @param {Function} callback
|
||||
*/
|
||||
_removeEventListener: function(listeners, callback) {
|
||||
var index = listeners.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This is an internal function that set a value to the
|
||||
* Wifi tethering setting
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @param {Boolean} Setting value
|
||||
*/
|
||||
_setWifiTetheringSetting: function(value) {
|
||||
var cset = {};
|
||||
cset[this.tetheringWifiKey] = value;
|
||||
this._settings.createLock().set(cset);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is an internal function that set a value to the
|
||||
* Usb tethering setting
|
||||
*
|
||||
* @memberOf Hotspot
|
||||
* @param {Boolean} Setting value
|
||||
*/
|
||||
_setUsbTetheringSetting: function(value) {
|
||||
var cset = {};
|
||||
cset[this.tetheringUsbKey] = value;
|
||||
this._settings.createLock().set(cset);
|
||||
},
|
||||
|
||||
addEventListener: function(eventName, callback) {
|
||||
if (eventName === 'incompatibleSettings') {
|
||||
this._incompatibleSettingsListeners.push(callback);
|
||||
} else if (eventName === 'wifiHotspotChange') {
|
||||
this._hotspotChangeListeners.push(callback);
|
||||
} else if (eventName === 'usbHotspotChange') {
|
||||
this._usbHotspotChangeListeners.push(callback);
|
||||
} else if (eventName === 'usbStorageChange') {
|
||||
this._usbStorageChangeListeners.push(callback);
|
||||
}
|
||||
},
|
||||
|
||||
removeEventListener: function(eventName, callback) {
|
||||
if (eventName === 'incompatibleSettings') {
|
||||
this._removeEventListener(
|
||||
this._incompatibleSettingsListeners, callback);
|
||||
} else if (eventName === 'wifiHotspotChange') {
|
||||
this._removeEventListener(
|
||||
this._hotspotChangeListeners, callback);
|
||||
} else if (eventName === 'usbHotspotChange') {
|
||||
this._removeEventListener(
|
||||
this._usbHotspotChangeListeners, callback);
|
||||
} else if (eventName === 'usbStorageChange') {
|
||||
this._removeEventListener(
|
||||
this._usbStorageChangeListeners, callback);
|
||||
}
|
||||
},
|
||||
|
||||
get wifiHotspotSetting() {
|
||||
return this._hotspotSetting;
|
||||
},
|
||||
|
||||
get usbHotspotSetting() {
|
||||
return this._usbHotspotSetting;
|
||||
},
|
||||
|
||||
get usbStorageSetting() {
|
||||
return this._usbStorageSetting;
|
||||
},
|
||||
|
||||
set hotspotSetting(value) {
|
||||
this._setWifiTetheringSetting(value);
|
||||
},
|
||||
|
||||
set usbHotspotSetting(value) {
|
||||
this._setUsbTetheringSetting(value);
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_hotspot() {
|
||||
return new Hotspot();
|
||||
};
|
||||
});
|
||||
244
gaia/apps/settings/js/panels/hotspot/hotspot_settings.js
Executable file
244
gaia/apps/settings/js/panels/hotspot/hotspot_settings.js
Executable file
|
|
@ -0,0 +1,244 @@
|
|||
/**
|
||||
* Hotspot Settings:
|
||||
* - Update Hotspot Settings
|
||||
* @module HotspotSettings
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = require('shared/settings_listener');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var Module = require('modules/base/module');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
|
||||
/**
|
||||
* @requires module:modules/mvvm/observable
|
||||
* @requires module:modules/settings_cache
|
||||
* @returns {HotspotSettings}
|
||||
*/
|
||||
var HotspotSettings = Module.create(function HotspotSettings() {
|
||||
this.super(Observable).call(this);
|
||||
this._init();
|
||||
}).extend(Observable);
|
||||
|
||||
/**
|
||||
* Hotspot SSID setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Object.defineProperty(HotspotSettings.prototype, 'tetheringSSIDKey', {
|
||||
get: function() {
|
||||
return 'tethering.wifi.ssid';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hotspot security type setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Object.defineProperty(HotspotSettings.prototype, 'tetheringSecurityKey', {
|
||||
get: function() {
|
||||
return 'tethering.wifi.security.type';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hotspot password setting key
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Object.defineProperty(HotspotSettings.prototype, 'tetheringPasswordKey', {
|
||||
get: function() {
|
||||
return 'tethering.wifi.security.password';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hotspot SSID.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(HotspotSettings.prototype,
|
||||
'hotspotSSID', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Hotspot security type
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(HotspotSettings.prototype,
|
||||
'hotspotSecurity', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Hotspot Password
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(HotspotSettings.prototype,
|
||||
'hotspotPassword', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Init module.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
*/
|
||||
HotspotSettings.prototype._init = function() {
|
||||
this._settings = navigator.mozSettings;
|
||||
this._bindEvents();
|
||||
this._updatePasswordIfNeeded();
|
||||
};
|
||||
|
||||
/**
|
||||
* We will generate a random password for the hotspot
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
*/
|
||||
HotspotSettings.prototype._generateHotspotPassword = function() {
|
||||
var words = ['amsterdam', 'ankara', 'auckland',
|
||||
'belfast', 'berlin', 'boston',
|
||||
'calgary', 'caracas', 'chicago',
|
||||
'dakar', 'delhi', 'dubai',
|
||||
'dublin', 'houston', 'jakarta',
|
||||
'lagos', 'lima', 'madrid',
|
||||
'newyork', 'osaka', 'oslo',
|
||||
'porto', 'santiago', 'saopaulo',
|
||||
'seattle', 'stockholm', 'sydney',
|
||||
'taipei', 'tokyo', 'toronto'];
|
||||
var password = words[Math.floor(Math.random() * words.length)];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
password += Math.floor(Math.random() * 10);
|
||||
}
|
||||
return password;
|
||||
};
|
||||
|
||||
/**
|
||||
* We will update hotspot password if needed
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
*/
|
||||
HotspotSettings.prototype._updatePasswordIfNeeded = function() {
|
||||
var self = this;
|
||||
SettingsCache.getSettings(function(results) {
|
||||
if (!results[self.tetheringPasswordKey]) {
|
||||
var pwd = self._generateHotspotPassword();
|
||||
self.setHotspotPassword(pwd);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the value to the tethering SSID setting
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype.setHotspotSSID = function(value) {
|
||||
var cset = {};
|
||||
cset[this.tetheringSSIDKey] = value;
|
||||
this._settings.createLock().set(cset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the value to the tethering security type setting
|
||||
*
|
||||
* @access public
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype.setHotspotSecurity = function(value) {
|
||||
var cset = {};
|
||||
cset[this.tetheringSecurityKey] = value;
|
||||
this._settings.createLock().set(cset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the value to the tethering password setting
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype.setHotspotPassword = function(value) {
|
||||
var cset = {};
|
||||
cset[this.tetheringPasswordKey] = value;
|
||||
this._settings.createLock().set(cset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current value of hotspot SSID
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype._onSSIDChange = function(value) {
|
||||
this._hotspotSSID = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current value of hotspot security type
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype._onSecurityChange = function(value) {
|
||||
this._hotspotSecurity = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current value of hotspot password
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
* @param {String} value
|
||||
*/
|
||||
HotspotSettings.prototype._onPasswordChange = function(value) {
|
||||
this._hotspotPassword = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen to hotspot settings changes
|
||||
*
|
||||
* @access private
|
||||
* @memberOf HotspotSettings
|
||||
*/
|
||||
HotspotSettings.prototype._bindEvents = function() {
|
||||
SettingsListener.observe(this.tetheringSSIDKey,
|
||||
'', this._onSSIDChange.bind(this));
|
||||
|
||||
SettingsListener.observe(this.tetheringSecurityKey,
|
||||
'wpa-psk', this._onSecurityChange.bind(this));
|
||||
|
||||
SettingsListener.observe(this.tetheringPasswordKey,
|
||||
'', this._onPasswordChange.bind(this));
|
||||
};
|
||||
|
||||
return HotspotSettings;
|
||||
});
|
||||
202
gaia/apps/settings/js/panels/hotspot/panel.js
Executable file
202
gaia/apps/settings/js/panels/hotspot/panel.js
Executable file
|
|
@ -0,0 +1,202 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogService = require('modules/dialog_service');
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var Hotspot = require('panels/hotspot/hotspot');
|
||||
var HotspotSettings =
|
||||
require('panels/hotspot/hotspot_settings');
|
||||
|
||||
return function ctor_hotspot() {
|
||||
var elements;
|
||||
var hotspot = Hotspot();
|
||||
var hotspotSettings = HotspotSettings();
|
||||
var hotspotSSID;
|
||||
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
this._incompatibleSettingsDialog = 'incompatible-settings-dialog';
|
||||
|
||||
elements = {
|
||||
panel: panel,
|
||||
hotspotSettingBtn:
|
||||
panel.querySelector('#hotspot-settings-section a'),
|
||||
hotspotElement:
|
||||
panel.querySelector('#tethering-wifi-enabled'),
|
||||
hotspotMsg:
|
||||
panel.querySelector('#wifi-hotspot-msg'),
|
||||
usbTetheringElement:
|
||||
panel.querySelector('#tethering-usb-enabled')
|
||||
};
|
||||
|
||||
this.incompatibleSettingsHandler =
|
||||
this._openIncompatibleSettingsDialog.bind(this);
|
||||
|
||||
hotspot.init();
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
// Wifi tethering enabled
|
||||
hotspot.addEventListener('wifiHotspotChange',
|
||||
this._setHotspotSettingsEnabled);
|
||||
|
||||
// USB tethering enabled
|
||||
hotspot.addEventListener('usbHotspotChange',
|
||||
this._setUSBTetheringCheckbox);
|
||||
|
||||
// Incompatible settings
|
||||
hotspot.addEventListener('incompatibleSettings',
|
||||
this.incompatibleSettingsHandler);
|
||||
|
||||
// Wi-fi hotspot event listener
|
||||
elements.hotspotElement.addEventListener('change',
|
||||
this._onWifiHotspotChange);
|
||||
|
||||
// USB tethering event listener
|
||||
elements.usbTetheringElement.addEventListener('change',
|
||||
this._onUsbHotspotChange);
|
||||
|
||||
elements.hotspotSettingBtn.addEventListener('click',
|
||||
this._onHotspotSettingsClick);
|
||||
|
||||
hotspotSettings.observe('hotspotSSID', this._updateHotspotSSID);
|
||||
|
||||
this._updateUI();
|
||||
},
|
||||
|
||||
onBeforeHide: function(panel, options) {
|
||||
// Wifi tethering
|
||||
hotspot.removeEventListener('wifiHotspotChange',
|
||||
this._setHotspotSettingsEnabled);
|
||||
|
||||
// USB tethering
|
||||
hotspot.removeEventListener('usbHotspotChange',
|
||||
this._setUSBTetheringCheckbox);
|
||||
|
||||
// Incompatible settings
|
||||
hotspot.removeEventListener('incompatibleSettings',
|
||||
this.incompatibleSettingsHandler);
|
||||
|
||||
// Wi-fi hotspot event listener
|
||||
elements.hotspotElement.removeEventListener('change',
|
||||
this._onWifiHotspotChange);
|
||||
|
||||
// USB tethering event listener
|
||||
elements.usbTetheringElement.removeEventListener('change',
|
||||
this._onUsbHotspotChange);
|
||||
|
||||
elements.hotspotSettingBtn.removeEventListener('click',
|
||||
this._onHotspotSettingsClick);
|
||||
|
||||
hotspotSettings.unobserve('hotspotSSID');
|
||||
},
|
||||
|
||||
_updateHotspotSSID: function(newValue) {
|
||||
hotspotSSID = newValue;
|
||||
},
|
||||
|
||||
_setHotspotSettingsEnabled: function(enabled) {
|
||||
elements.hotspotElement.checked = enabled;
|
||||
if (enabled) {
|
||||
document.l10n.setAttributes(
|
||||
elements.hotspotMsg,
|
||||
'wifi-hotspot-enabled-msg',
|
||||
{ hotspotSSID: hotspotSSID }
|
||||
);
|
||||
} else {
|
||||
elements.hotspotMsg.setAttribute('data-l10n-id', 'disabled');
|
||||
}
|
||||
},
|
||||
|
||||
_setUSBTetheringCheckbox: function(enabled) {
|
||||
elements.usbTetheringElement.checked = enabled;
|
||||
},
|
||||
|
||||
_onWifiHotspotChange: function(event) {
|
||||
var checkbox = event.target;
|
||||
hotspot.checkIncompatibleSettings(
|
||||
hotspot.tetheringWifiKey, checkbox.checked);
|
||||
},
|
||||
|
||||
_onUsbHotspotChange: function(event) {
|
||||
var checkbox = event.target;
|
||||
hotspot.checkIncompatibleSettings(
|
||||
hotspot.tetheringUsbKey, checkbox.checked);
|
||||
},
|
||||
|
||||
_onHotspotSettingsClick: function() {
|
||||
DialogService.show('hotspot-wifiSettings', {
|
||||
settings: hotspotSettings
|
||||
}).then((result) => {
|
||||
if (result.type === 'submit') {
|
||||
// reconnect hotspot settings to make new settings take effect.
|
||||
hotspot.reEnableWifiTetheringSetting();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_openIncompatibleSettingsDialog:
|
||||
function(newSetting, oldSetting, bothConflicts) {
|
||||
// if both conflict, ensure that two incompatible settings
|
||||
// are disabled before turning on.
|
||||
if (bothConflicts) {
|
||||
return this._openIncompatibleSettingsDialog(
|
||||
hotspot.tetheringUsbKey, hotspot.tetheringWifiKey, null)
|
||||
.then((updated) => {
|
||||
updated && this._openIncompatibleSettingsDialog(
|
||||
hotspot.tetheringUsbKey, hotspot.usbStorageKey, null);
|
||||
});
|
||||
}
|
||||
|
||||
var headerL10nMap = {
|
||||
'ums.enabled': 'is-warning-storage-header',
|
||||
'tethering.usb.enabled': 'is-warning-tethering-header',
|
||||
'tethering.wifi.enabled': 'is-warning-wifi-header'
|
||||
};
|
||||
var messageL10nMap = {
|
||||
'ums.enabled': {
|
||||
'tethering.usb.enabled': 'is-warning-storage-tethering-message'
|
||||
},
|
||||
'tethering.usb.enabled': {
|
||||
'ums.enabled': 'is-warning-tethering-storage-message',
|
||||
'tethering.wifi.enabled': 'is-warning-tethering-wifi-message'
|
||||
},
|
||||
'tethering.wifi.enabled': {
|
||||
'tethering.usb.enabled': 'is-warning-wifi-tethering-message'
|
||||
}
|
||||
};
|
||||
|
||||
var headerL10n = headerL10nMap[newSetting];
|
||||
var messageL10n =
|
||||
messageL10nMap[newSetting] &&
|
||||
messageL10nMap[newSetting][oldSetting];
|
||||
|
||||
return DialogService.confirm(messageL10n, {
|
||||
title: headerL10n,
|
||||
submitButton: { id: 'enable', style: 'recommend' },
|
||||
cancelButton: { id: 'cancel' },
|
||||
}).then((result) => {
|
||||
var enabled = result.type === 'submit';
|
||||
var lock = navigator.mozSettings.createLock();
|
||||
var cset = {};
|
||||
|
||||
cset[newSetting] = enabled;
|
||||
cset[oldSetting] = !enabled;
|
||||
lock.set(cset);
|
||||
|
||||
return enabled;
|
||||
});
|
||||
},
|
||||
|
||||
_updateUI: function() {
|
||||
this._setHotspotSettingsEnabled(
|
||||
hotspot.wifiHotspotSetting
|
||||
);
|
||||
this._setUSBTetheringCheckbox(
|
||||
hotspot.usbHotspotSetting
|
||||
);
|
||||
this._updateHotspotSSID(hotspotSettings.hotspotSSID);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
166
gaia/apps/settings/js/panels/hotspot_wifi_settings/panel.js
Executable file
166
gaia/apps/settings/js/panels/hotspot_wifi_settings/panel.js
Executable file
|
|
@ -0,0 +1,166 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
|
||||
return function ctor_hotspot_wifi_settings() {
|
||||
var elements;
|
||||
var hotspotSettings;
|
||||
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {
|
||||
panel: panel,
|
||||
securityTypeSelector: panel.querySelector('.security-selector'),
|
||||
passwordItem: panel.querySelector('.password'),
|
||||
passwordInput: panel.querySelector('input[name="password"]'),
|
||||
submitBtn: panel.querySelector('button.save-hotspotSettings'),
|
||||
showPassword: panel.querySelector('[name="show_password"]'),
|
||||
passwordDesc: panel.querySelector('.password-description'),
|
||||
tethering_ssid: panel.querySelector(
|
||||
'[data-setting="tethering.wifi.ssid"]'),
|
||||
allFields: panel.querySelectorAll(
|
||||
'[data-setting]:not([data-ignore])')
|
||||
};
|
||||
|
||||
this._initSecurityOptions();
|
||||
|
||||
elements.showPassword.addEventListener('change', function() {
|
||||
elements.passwordInput.type = this.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
elements.securityTypeSelector.addEventListener('change', (evt) => {
|
||||
this._settingsTouched = true;
|
||||
this._updatePasswordItemVisibility(evt.target.value);
|
||||
this._updateSubmitButtonState();
|
||||
});
|
||||
|
||||
elements.tethering_ssid.addEventListener('input', () => {
|
||||
this._settingsTouched = true;
|
||||
this._updateSubmitButtonState();
|
||||
});
|
||||
|
||||
elements.passwordInput.addEventListener('input', () => {
|
||||
this._settingsTouched = true;
|
||||
this._updateSubmitButtonState();
|
||||
});
|
||||
},
|
||||
|
||||
onBeforeShow: function(panel, options) {
|
||||
hotspotSettings = options.settings;
|
||||
this._initWifiSettingsDialog();
|
||||
},
|
||||
|
||||
onSubmit: function() {
|
||||
if (this._isNotSubmitable()) {
|
||||
return Promise.reject();
|
||||
} else {
|
||||
this._submit();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
_initSecurityOptions: function() {
|
||||
var types = ['open', 'wpa-psk', 'wpa2-psk'];
|
||||
types.forEach((type) => {
|
||||
var option = document.createElement('option');
|
||||
option.value = type;
|
||||
option.setAttribute('data-l10n-id', 'hotspot-security-' + type);
|
||||
elements.securityTypeSelector.appendChild(option);
|
||||
});
|
||||
},
|
||||
|
||||
_initWifiSettingsDialog: function() {
|
||||
elements.showPassword.checked = false;
|
||||
elements.passwordInput.type = 'password';
|
||||
this._reset();
|
||||
this._settingsTouched = false;
|
||||
this._updateSubmitButtonState();
|
||||
},
|
||||
|
||||
_updateSubmitButtonState: function() {
|
||||
elements.submitBtn.disabled = this._isNotSubmitable();
|
||||
},
|
||||
|
||||
_updatePasswordItemVisibility: function(securityType) {
|
||||
elements.passwordItem.hidden =
|
||||
elements.passwordDesc.hidden = (securityType === 'open');
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
var fields = elements.allFields;
|
||||
this._updatePasswordItemVisibility(hotspotSettings.hotspotSecurity);
|
||||
for (var i = 0, len = fields.length; i < len; i++) {
|
||||
this._configInput(fields[i]);
|
||||
}
|
||||
},
|
||||
|
||||
_isNotSubmitable: function() {
|
||||
var securityType = elements.securityTypeSelector.value;
|
||||
var pwdLength = elements.passwordInput.value.length;
|
||||
var ssidLength = elements.tethering_ssid.value.trim().length;
|
||||
if (ssidLength === 0) {
|
||||
return true;
|
||||
}
|
||||
return (pwdLength < 8 || pwdLength > 63) && (securityType !== 'open') ||
|
||||
!this._settingsTouched;
|
||||
},
|
||||
|
||||
_configInput: function(input) {
|
||||
var key = input.dataset.setting;
|
||||
var setting;
|
||||
|
||||
switch(key) {
|
||||
case hotspotSettings.tetheringSSIDKey:
|
||||
setting = hotspotSettings.hotspotSSID;
|
||||
break;
|
||||
case hotspotSettings.tetheringPasswordKey:
|
||||
setting = hotspotSettings.hotspotPassword;
|
||||
break;
|
||||
case hotspotSettings.tetheringSecurityKey:
|
||||
setting = hotspotSettings.hotspotSecurity;
|
||||
break;
|
||||
}
|
||||
|
||||
input.value = setting || '';
|
||||
|
||||
// dispatch the event manually for select element
|
||||
if (input.nodeName === 'SELECT') {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('change', true, true);
|
||||
input.dispatchEvent(evt);
|
||||
}
|
||||
},
|
||||
|
||||
// validate all settings in the dialog box
|
||||
_submit: function() {
|
||||
var fields = elements.allFields;
|
||||
|
||||
var ignorePassword = (elements.securityTypeSelector.value == 'open');
|
||||
|
||||
// mozSettings does not support multiple keys in the cset object
|
||||
// with one set() call,
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=779381
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var input = fields[i];
|
||||
var key = input.dataset.setting;
|
||||
|
||||
switch(key) {
|
||||
case hotspotSettings.tetheringSSIDKey:
|
||||
hotspotSettings.setHotspotSSID(input.value);
|
||||
break;
|
||||
case hotspotSettings.tetheringPasswordKey:
|
||||
if (!(ignorePassword &&
|
||||
key == hotspotSettings.tetheringPasswordKey)) {
|
||||
hotspotSettings.setHotspotPassword(input.value);
|
||||
}
|
||||
break;
|
||||
case hotspotSettings.tetheringSecurityKey:
|
||||
hotspotSettings.setHotspotSecurity(input.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
544
gaia/apps/settings/js/panels/icc/icc.js
Executable file
544
gaia/apps/settings/js/panels/icc/icc.js
Executable file
|
|
@ -0,0 +1,544 @@
|
|||
require([
|
||||
'shared/stk_helper'
|
||||
], function(STKHelper) {
|
||||
'use strict';
|
||||
(function() {
|
||||
var iccManager = window.navigator.mozIccManager;
|
||||
|
||||
function getIcc(iccId) {
|
||||
window.DUMP('ICC Getting ICC for ' + iccId);
|
||||
return iccManager.getIccById(iccId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move settings to foreground
|
||||
*/
|
||||
function reopenSettings() {
|
||||
navigator.mozApps.getSelf().onsuccess = function getSelfCB(evt) {
|
||||
var app = evt.target.result;
|
||||
app.launch('settings');
|
||||
};
|
||||
}
|
||||
|
||||
// Consts
|
||||
// 3GPP spec: TS 11.14
|
||||
// 13.4 Type of Command and Next Action Indicator
|
||||
const STK_NEXT_ACTION_INDICATOR = {
|
||||
16: 'stkItemsNaiSetUpCall',
|
||||
17: 'stkItemsNaiSendSs',
|
||||
18: 'stkItemsNaiSendUssd',
|
||||
19: 'stkItemsNaiSendSms',
|
||||
32: 'stkItemsNaiPlayTone',
|
||||
33: 'stkItemsNaiDisplayText',
|
||||
34: 'stkItemsNaiGetInkey',
|
||||
35: 'stkItemsNaiGetInput',
|
||||
36: 'stkItemsNaiSelectItem',
|
||||
37: 'stkItemsNaiSetUpMenu',
|
||||
40: 'stkItemsNaiSetIdleModeText',
|
||||
48: 'stkItemsNaiPerformCardApdu', // class "a"
|
||||
49: 'stkItemsNaiPowerOnCard', // class "a"
|
||||
50: 'stkItemsNaiPowerOffCard', // class "a"
|
||||
51: 'stkItemsNaiGetReaderStatus', // class "a"
|
||||
64: 'stkItemsNaiOpenChannel', // class "e"
|
||||
65: 'stkItemsNaiCloseChannel', // class "e"
|
||||
66: 'stkItemsNaiReceiveData', // class "e"
|
||||
67: 'stkItemsNaiSendData', // class "e"
|
||||
68: 'stkItemsNaiGetChannelStatus', // class "e"
|
||||
96: 'Reserved', // for TIA/EIA-136
|
||||
129: 'stkItemsNaiEndOfTheProactiveSession'
|
||||
};
|
||||
|
||||
/**
|
||||
* Init
|
||||
*/
|
||||
var iccStkList = document.getElementById('icc-stk-list');
|
||||
var iccStkMainHeader = document.getElementById('icc-stk-main-header');
|
||||
var iccStkHeader = document.getElementById('icc-stk-header');
|
||||
var iccStkSubheader = document.getElementById('icc-stk-subheader');
|
||||
var stkOpenAppName = null;
|
||||
var stkLastSelectedText = null;
|
||||
var goBackTimer = {
|
||||
timer: null,
|
||||
timeout: 0
|
||||
};
|
||||
var selectTimer = {
|
||||
timer: null,
|
||||
timeout: 0
|
||||
};
|
||||
var _visibilityChangeHandler = null;
|
||||
var _backHandler = null;
|
||||
init();
|
||||
|
||||
|
||||
/**
|
||||
* This is a helper for localizing DOM Elements using L10nID.
|
||||
* Second argument may take one of the following forms:
|
||||
* a string -> l10nId
|
||||
* an object -> {id: l10nId, args: l10nArgs}
|
||||
* an object -> {raw: string}
|
||||
*
|
||||
* Read more at MDN: http://mzl.la/1A76Fby
|
||||
*/
|
||||
function localizeElement(element, l10n) {
|
||||
if (typeof(l10n) === 'string') {
|
||||
element.setAttribute('data-l10n-id', l10n);
|
||||
} else if (l10n.raw) {
|
||||
element.removeAttribute('data-l10n-id');
|
||||
element.textContent = l10n.raw;
|
||||
} else {
|
||||
document.l10n.setAttributes(element, l10n.id, l10n.attrs);
|
||||
}
|
||||
}
|
||||
|
||||
function sendVisibilityChangeEvent() {
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
app.connect('settingsstk').then(function onConnAccepted(ports) {
|
||||
window.DUMP('STK_Settings IAC: ' + ports);
|
||||
ports.forEach(function(port) {
|
||||
window.DUMP('STK_Settings IAC: ' + port);
|
||||
port.postMessage('StkMenuHidden');
|
||||
});
|
||||
}, function onConnRejected(reason) {
|
||||
window.DUMP('STK_Settings IAC is rejected');
|
||||
window.DUMP(reason);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getNextActionString(nextActionList, index) {
|
||||
window.DUMP('STK NAL: ' + nextActionList);
|
||||
|
||||
var nextActionString;
|
||||
if (nextActionList &&
|
||||
nextActionList[index] !== null &&
|
||||
nextActionList[index] !== 96 &&
|
||||
STK_NEXT_ACTION_INDICATOR[nextActionList[index]]) {
|
||||
nextActionString = STK_NEXT_ACTION_INDICATOR[nextActionList[index]];
|
||||
} else {
|
||||
nextActionString = null;
|
||||
}
|
||||
return nextActionString;
|
||||
}
|
||||
|
||||
function visibilityChangeHandler() {
|
||||
if (document.hidden && Settings && Settings.currentPanel == '#icc') {
|
||||
window.DUMP('STK_Settings visibilityChangeHandler');
|
||||
sendVisibilityChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init STK UI
|
||||
*/
|
||||
function init() {
|
||||
window.addEventListener('stkasynccommand',
|
||||
function do_handleAsyncSTKCmd(event) {
|
||||
handleSTKMessage(event.detail.message);
|
||||
});
|
||||
|
||||
window.addEventListener('stkmenuselection',
|
||||
function do_handleAsyncSTKCmd(event) {
|
||||
updateMenu(event.detail.menu);
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange',
|
||||
visibilityChangeHandler, false);
|
||||
|
||||
iccStkMainHeader.addEventListener('action', function onBack() {
|
||||
if (typeof _backHandler === 'function') {
|
||||
_backHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addCloseNotificationsEvents(message) {
|
||||
function onVisibilityChange() {
|
||||
if (document.hidden && Settings && Settings.currentPanel == '#icc') {
|
||||
document.removeEventListener('visibilitychange',
|
||||
_visibilityChangeHandler, false);
|
||||
stkResTerminate(message);
|
||||
}
|
||||
}
|
||||
document.removeEventListener('visibilitychange',
|
||||
_visibilityChangeHandler, false);
|
||||
_visibilityChangeHandler = onVisibilityChange;
|
||||
document.addEventListener('visibilitychange',
|
||||
_visibilityChangeHandler, false);
|
||||
window.onbeforeunload = function() {
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_NO_RESPONSE_FROM_USER
|
||||
}, true);
|
||||
};
|
||||
}
|
||||
|
||||
function returnToSettingsMainMenu() {
|
||||
if (Settings) {
|
||||
Settings.currentPanel = '#root';
|
||||
}
|
||||
}
|
||||
|
||||
function stkResTerminate(message) {
|
||||
returnToSettingsMainMenu();
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_UICC_SESSION_TERM_BY_USER
|
||||
}, true);
|
||||
}
|
||||
|
||||
function stkResGoBack(message) {
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_BACKWARD_MOVE_BY_USER
|
||||
});
|
||||
// We'll return to settings if no STK response received in a grace period
|
||||
var reqTimerGoBack =
|
||||
window.navigator.mozSettings.createLock().get('icc.goBackTimeout');
|
||||
reqTimerGoBack.onsuccess = function icc_getTimerGoBackSuccess() {
|
||||
goBackTimer.timeout = reqTimerGoBack.result['icc.goBackTimeout'];
|
||||
goBackTimer.timer = setTimeout(function() {
|
||||
returnToSettingsMainMenu();
|
||||
}, goBackTimer.timeout);
|
||||
};
|
||||
}
|
||||
|
||||
function stkResNoResponse(message) {
|
||||
var reqTimerSelect =
|
||||
window.navigator.mozSettings.createLock().get('icc.selectTimeout');
|
||||
reqTimerSelect.onsuccess = function icc_getTimerSelectSuccess() {
|
||||
selectTimer.timeout = reqTimerSelect.result['icc.selectTimeout'];
|
||||
selectTimer.timer = setTimeout(function() {
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_NO_RESPONSE_FROM_USER
|
||||
}, true);
|
||||
returnToSettingsMainMenu();
|
||||
}, selectTimer.timeout);
|
||||
};
|
||||
}
|
||||
|
||||
function stkCancelGoBack() {
|
||||
if (goBackTimer.timer) {
|
||||
window.clearTimeout(goBackTimer.timer);
|
||||
goBackTimer.timer = null;
|
||||
}
|
||||
if (selectTimer.timer) {
|
||||
window.clearTimeout(selectTimer.timer);
|
||||
selectTimer.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response ICC Command
|
||||
*/
|
||||
function responseSTKCommand(message, response) {
|
||||
window.DUMP('sendStkResponse to message: ', message);
|
||||
window.DUMP('sendStkResponse -- # response = ', response);
|
||||
|
||||
getIcc(message.iccId).sendStkResponse(message.command, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ICC Messages
|
||||
*/
|
||||
function handleSTKMessage(message) {
|
||||
window.DUMP('STK Proactive Message:', message);
|
||||
|
||||
stkCancelGoBack();
|
||||
reopenSettings();
|
||||
|
||||
switch (message.command.typeOfCommand) {
|
||||
case iccManager.STK_CMD_SELECT_ITEM:
|
||||
addCloseNotificationsEvents(message);
|
||||
updateSelection(message);
|
||||
Settings.currentPanel = '#icc';
|
||||
_backHandler = stkResGoBack.bind(null, message);
|
||||
break;
|
||||
|
||||
default:
|
||||
window.DUMP('STK Message not managed... response OK');
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_OK
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate through all available STK applications
|
||||
*/
|
||||
function updateMenu(menu) {
|
||||
window.DUMP('Showing STK main menu: ', menu);
|
||||
stkOpenAppName = null;
|
||||
|
||||
stkCancelGoBack();
|
||||
|
||||
clearList();
|
||||
|
||||
if (!menu || !menu.entries || !menu.entries.items ||
|
||||
(menu.entries.items.length == 1 && menu.entries.items[0] === null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.DUMP('STK Main App Menu title: ' + menu.entries.title);
|
||||
window.DUMP('STK Main App Menu default item: ' +
|
||||
menu.entries.defaultItem);
|
||||
|
||||
showTitle({raw: menu.entries.title});
|
||||
menu.entries.items.forEach(function(menuItem, index) {
|
||||
window.DUMP('STK Main App Menu item: ' + menuItem.text + ' # ' +
|
||||
menuItem.identifier);
|
||||
var nextActionString = getNextActionString(menu.entries.nextActionList,
|
||||
index);
|
||||
// Get the first icon if exists
|
||||
var icon = STKHelper.getFirstIconRawData(menuItem);
|
||||
if (icon) {
|
||||
iccStkList.dataset.customIcon = true;
|
||||
}
|
||||
|
||||
window.DUMP('STK NEXTACTION: ' + nextActionString);
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-menuitem-' + menuItem.identifier,
|
||||
icon: icon,
|
||||
text: {raw: menuItem.text},
|
||||
nai: nextActionString,
|
||||
onclick: onMainMenuItemClick,
|
||||
attributes: [
|
||||
['stk-menu-item-identifier', menuItem.identifier],
|
||||
['stk-menu-item-iccId', menu.iccId]
|
||||
]
|
||||
}));
|
||||
});
|
||||
|
||||
// Optional Help menu
|
||||
if (menu.entries.isHelpAvailable) {
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-helpmenuitem',
|
||||
text: 'operatorServices-helpmenu',
|
||||
onclick: function __onHelpClick__(event) {
|
||||
showHelpMenu(menu, event);
|
||||
},
|
||||
attributes: []
|
||||
}));
|
||||
}
|
||||
|
||||
getIcc(menu.iccId).onstksessionend = function handleSTKSessionEnd(event) {
|
||||
updateMenu(menu);
|
||||
Settings.currentPanel = '#icc';
|
||||
};
|
||||
|
||||
_backHandler = function backToRootPanel() {
|
||||
Settings.currentPanel = '#root';
|
||||
};
|
||||
}
|
||||
|
||||
function onMainMenuItemClick(event) {
|
||||
var iccId = event.target.getAttribute('stk-menu-item-iccId');
|
||||
var identifier = event.target.getAttribute('stk-menu-item-identifier');
|
||||
window.DUMP('sendStkMenuSelection: ', identifier);
|
||||
|
||||
getIcc(iccId).sendStkMenuSelection(identifier, false);
|
||||
stkLastSelectedText = event.target.textContent;
|
||||
stkOpenAppName = stkLastSelectedText;
|
||||
}
|
||||
|
||||
function showHelpMenu(menu, event) {
|
||||
window.DUMP('Showing STK help menu');
|
||||
stkOpenAppName = null;
|
||||
|
||||
clearList();
|
||||
|
||||
showTitle('operatorServices-helpmenu');
|
||||
menu.entries.items.forEach(function(menuItem) {
|
||||
window.DUMP('STK Main App Help item: ' + menuItem.text + ' # ' +
|
||||
menuItem.identifier);
|
||||
var icon = STKHelper.getFirstIconRawData(menuItem);
|
||||
if (icon) {
|
||||
iccStkList.dataset.customIcon = true;
|
||||
}
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-helpitem-' + menuItem.identifier,
|
||||
text: {raw: menuItem.text},
|
||||
icon: icon,
|
||||
onclick: onMainMenuHelpItemClick,
|
||||
attributes: [
|
||||
['stk-help-item-identifier', menuItem.identifier],
|
||||
['stk-menu-item-iccId', menu.iccId]
|
||||
]
|
||||
}));
|
||||
});
|
||||
|
||||
_backHandler = updateMenu.bind(null, menu);
|
||||
}
|
||||
|
||||
function onMainMenuHelpItemClick(event) {
|
||||
var iccId = event.target.getAttribute('stk-menu-item-iccId');
|
||||
var identifier = event.target.getAttribute('stk-help-item-identifier');
|
||||
window.DUMP('sendStkHelpMenuSelection: ', identifier);
|
||||
|
||||
getIcc(iccId).sendStkMenuSelection(identifier, true);
|
||||
stkLastSelectedText = event.target.textContent;
|
||||
stkOpenAppName = stkLastSelectedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate through the STK application options
|
||||
*/
|
||||
function updateSelection(message) {
|
||||
var menu = message.command.options;
|
||||
|
||||
window.DUMP('Showing STK menu');
|
||||
clearList();
|
||||
|
||||
window.DUMP('STK App Menu title: ' + menu.title);
|
||||
window.DUMP('STK App Menu default item: ' + menu.defaultItem);
|
||||
|
||||
showTitle({raw: menu.title});
|
||||
menu.items.forEach(function(menuItem, index) {
|
||||
window.DUMP('STK App Menu item: ' + menuItem.text + ' # ' +
|
||||
menuItem.identifier);
|
||||
var nextActionString = getNextActionString(menu.nextActionList, index);
|
||||
window.DUMP('STK NEXTACTION: ' + nextActionString);
|
||||
var icon = STKHelper.getFirstIconRawData(menuItem);
|
||||
if (icon) {
|
||||
iccStkList.dataset.customIcon = true;
|
||||
}
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-menuitem-' + menuItem.identifier,
|
||||
text: {raw: menuItem.text},
|
||||
icon: icon,
|
||||
nai: nextActionString,
|
||||
onclick: function onSelectOptionClick(event) {
|
||||
document.removeEventListener('visibilitychange',
|
||||
_visibilityChangeHandler, false);
|
||||
onSelectOption(message, event);
|
||||
},
|
||||
attributes: [['stk-select-option-identifier', menuItem.identifier]]
|
||||
}));
|
||||
});
|
||||
|
||||
// Optional Help menu
|
||||
if (menu.isHelpAvailable) {
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-helpmenuitem',
|
||||
text: 'operatorServices-helpmenu',
|
||||
onclick: function __onHelpClick__(event) {
|
||||
showHelpSelection(message, event);
|
||||
},
|
||||
attributes: []
|
||||
}));
|
||||
}
|
||||
|
||||
stkResNoResponse(message);
|
||||
}
|
||||
|
||||
function onSelectOption(message, event) {
|
||||
var identifier =
|
||||
event.target.getAttribute('stk-select-option-identifier');
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_OK,
|
||||
itemIdentifier: identifier
|
||||
});
|
||||
stkLastSelectedText = event.target.textContent;
|
||||
}
|
||||
|
||||
function showHelpSelection(message, event) {
|
||||
var menu = message.command.options;
|
||||
|
||||
window.DUMP('Showing STK help menu');
|
||||
stkOpenAppName = null;
|
||||
|
||||
clearList();
|
||||
|
||||
showTitle('operatorServices-helpmenu');
|
||||
menu.items.forEach(function(menuItem) {
|
||||
window.DUMP('STK Main App Help item: ' + menuItem.text + ' # ' +
|
||||
menuItem.identifier);
|
||||
var icon = STKHelper.getFirstIconRawData(menuItem);
|
||||
if (icon) {
|
||||
iccStkList.dataset.customIcon = true;
|
||||
}
|
||||
iccStkList.appendChild(buildMenuEntry({
|
||||
id: 'stk-helpitem-' + menuItem.identifier,
|
||||
text: {raw: menuItem.text},
|
||||
icon: icon,
|
||||
onclick: function onSelectOptionClick(event) {
|
||||
onSelectionHelpItemClick(message, event);
|
||||
},
|
||||
attributes: [
|
||||
['stk-help-item-identifier', menuItem.identifier],
|
||||
['stk-menu-item-iccId', menu.iccId]
|
||||
]
|
||||
}));
|
||||
});
|
||||
|
||||
_backHandler = updateSelection.bind(null, message);
|
||||
}
|
||||
|
||||
function onSelectionHelpItemClick(message, event) {
|
||||
var identifier = event.target.getAttribute('stk-help-item-identifier');
|
||||
window.DUMP('sendStkHelpMenuSelection: ', identifier);
|
||||
|
||||
responseSTKCommand(message, {
|
||||
resultCode: iccManager.STK_RESULT_HELP_INFO_REQUIRED,
|
||||
itemIdentifier: identifier
|
||||
});
|
||||
stkLastSelectedText = event.target.textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auxiliar methods
|
||||
*/
|
||||
function showTitle(titleL10n) {
|
||||
// If the application is automatically opened (no come from main menu)
|
||||
if (!stkOpenAppName) {
|
||||
stkOpenAppName = titleL10n;
|
||||
}
|
||||
localizeElement(iccStkHeader, stkOpenAppName);
|
||||
|
||||
// Show section
|
||||
if (stkOpenAppName != titleL10n) {
|
||||
localizeElement(iccStkSubheader, titleL10n);
|
||||
iccStkSubheader.parentNode.classList.remove('hiddenheader');
|
||||
iccStkSubheader.classList.remove('hidden');
|
||||
} else {
|
||||
localizeElement(iccStkSubheader, {raw: ''});
|
||||
iccStkSubheader.parentNode.classList.add('hiddenheader');
|
||||
iccStkSubheader.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function clearList() {
|
||||
while (iccStkList.hasChildNodes()) {
|
||||
iccStkList.removeChild(iccStkList.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function buildMenuEntry(entry) {
|
||||
var li = document.createElement('li');
|
||||
|
||||
if (entry.nai) {
|
||||
var small = document.createElement('small');
|
||||
localizeElement(small, entry.nai);
|
||||
li.appendChild(small);
|
||||
}
|
||||
|
||||
if (entry.icon) {
|
||||
var iconContainer = document.createElement('span');
|
||||
iconContainer.appendChild(STKHelper.getIconCanvas(entry.icon));
|
||||
li.appendChild(iconContainer);
|
||||
}
|
||||
|
||||
var a = document.createElement('a');
|
||||
a.id = entry.id;
|
||||
entry.attributes.forEach(function attrIterator(attr) {
|
||||
a.setAttribute(attr[0], attr[1]);
|
||||
});
|
||||
localizeElement(a, entry.text);
|
||||
a.onclick = entry.onclick;
|
||||
a.href = '#icc';
|
||||
li.appendChild(a);
|
||||
return li;
|
||||
}
|
||||
})();
|
||||
|
||||
window.dispatchEvent(new CustomEvent('iccPageLoaded', {
|
||||
detail: {}
|
||||
}));
|
||||
});
|
||||
125
gaia/apps/settings/js/panels/improve_browser_os/panel.js
Executable file
125
gaia/apps/settings/js/panels/improve_browser_os/panel.js
Executable file
|
|
@ -0,0 +1,125 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsPanel = require('modules/settings_panel');
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
|
||||
const METRICLEVELSETTING = 'metrics.selectedMetrics.level';
|
||||
const DOGFOODSETTING = 'debug.performance_data.dogfooding';
|
||||
const SHAREDATASETTING = 'debug.performance_data.shared';
|
||||
const DEVTOOLSOVERLAY = 'devtools.overlay';
|
||||
|
||||
const LEVELSMAP = {
|
||||
'Basic': 'Basic',
|
||||
'Enhanced': 'Enhanced',
|
||||
'None': 'None'
|
||||
};
|
||||
|
||||
return function ctor_improveBrowserOS() {
|
||||
return SettingsPanel({
|
||||
onInit: function(panel) {
|
||||
var appUsage = panel.querySelector('#appUsageByRegion');
|
||||
var metricsBasic = panel.querySelector('#metrics-basic');
|
||||
var metricsEnhanced = panel.querySelector('#metrics-enhanced');
|
||||
var metricsNone = panel.querySelector('#metrics-none');
|
||||
|
||||
appUsage.addEventListener('click', this);
|
||||
metricsBasic.addEventListener('click', this);
|
||||
metricsEnhanced.addEventListener('click', this);
|
||||
metricsNone.addEventListener('click', this);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
SettingsCache.getSettings((results) => {
|
||||
var metricsLevel = results[METRICLEVELSETTING];
|
||||
|
||||
switch(metricsLevel) {
|
||||
case 'Basic':
|
||||
metricsBasic.checked = true;
|
||||
break;
|
||||
case 'Enhanced':
|
||||
metricsEnhanced.checked = true;
|
||||
break;
|
||||
case 'None':
|
||||
metricsNone.checked = true;
|
||||
break;
|
||||
default:
|
||||
// When upgrading from a release that has only the
|
||||
// 'share data' checkbox, use the 'sharing' status
|
||||
// to determine the default metric level.
|
||||
if (results[SHAREDATASETTING]) {
|
||||
metricsBasic.checked = true;
|
||||
this._setMetricLevel('Basic');
|
||||
} else {
|
||||
metricsNone.checked = true;
|
||||
this._setMetricLevel('None');
|
||||
}
|
||||
}
|
||||
|
||||
// Dogfooders should not be able to disable the metrics.
|
||||
if (results[DOGFOODSETTING]) {
|
||||
metricsBasic.setAttribute('disabled', 'true');
|
||||
metricsEnhanced.setAttribute('disabled', 'true');
|
||||
metricsNone.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
metricsBasic.removeAttribute('disabled');
|
||||
metricsEnhanced.removeAttribute('disabled');
|
||||
metricsNone.removeAttribute('disabled');
|
||||
}
|
||||
resolve(this._setMetricLevel); // Unit testing support.
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
switch (event.target.id) {
|
||||
case 'appUsageByRegion':
|
||||
var activity = new window.MozActivity({
|
||||
name: 'marketplace-search',
|
||||
data: {
|
||||
type: 'firefox-os-app-stats'
|
||||
}
|
||||
});
|
||||
activity.onerror = function() {
|
||||
console.log('Error opening app stats page in Marketplace');
|
||||
};
|
||||
break;
|
||||
|
||||
case 'metrics-basic':
|
||||
case 'metrics-enhanced':
|
||||
case 'metrics-none':
|
||||
this._setMetricLevel(event.target.value);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setMetricLevel: function(level) {
|
||||
level = LEVELSMAP[level];
|
||||
if (level) {
|
||||
updateSetting(METRICLEVELSETTING, level);
|
||||
|
||||
// If the metrics level is being set to 'enhanced', DevTools
|
||||
// must enabled. This is because the 'enhanced' metrics
|
||||
// are collected using the Developer Tools components.
|
||||
if (level === LEVELSMAP.Enhanced) {
|
||||
updateSetting(DEVTOOLSOVERLAY, true);
|
||||
} else {
|
||||
// The metrics level is being set to one that does not require
|
||||
// DevTools to be enabled; disable DevTools.
|
||||
//
|
||||
// The use case where a developer manually enables the HUD and then
|
||||
// sets the metric level to 'basic' or 'none' is not being
|
||||
// addressed; DevTools will be still disabled by this page. The
|
||||
// developer will need to manually enable the HUD.
|
||||
updateSetting(DEVTOOLSOVERLAY, false);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSetting(name, value) {
|
||||
var setting = {};
|
||||
setting[name] = value;
|
||||
navigator.mozSettings.createLock().set(setting);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
});
|
||||
85
gaia/apps/settings/js/panels/keyboard/enabled_layouts.js
Executable file
85
gaia/apps/settings/js/panels/keyboard/enabled_layouts.js
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* The module initializes a ListView displaying all enabled layouts.
|
||||
* Implementation details please refer to {@link KeyboardEnabledLayoutsCore}.
|
||||
*
|
||||
* @module keyboard/enabled_layouts
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ListView = require('modules/mvvm/list_view');
|
||||
|
||||
/**
|
||||
* @alias module:keyboard/enabled_layouts
|
||||
* @class KeyboardEnabledLayoutsCore
|
||||
* @requires module:modules/mvvm/list_view
|
||||
* @param {KeyboardContext} context
|
||||
The kyboard context providing enabled layouts.
|
||||
* @param {Function} template
|
||||
The template function used to render a layout.
|
||||
* @returns {KeyboardEnabledLayoutsCore}
|
||||
*/
|
||||
function KeyboardEnabledLayoutsCore(context, template) {
|
||||
this._enabled = false;
|
||||
this._listView = null;
|
||||
this._keyboardContext = context;
|
||||
this._layoutTemplate = template;
|
||||
}
|
||||
|
||||
KeyboardEnabledLayoutsCore.prototype = {
|
||||
/**
|
||||
* The value indicates whether the module is responding. If it is false, the
|
||||
* UI stops reflecting the updates from the keyboard context.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf KeyboardEnabledLayoutsCore.prototype
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
set enabled(value) {
|
||||
this._enabled = value;
|
||||
if (this._listView) {
|
||||
this._listView.enabled = this._enabled;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @access private
|
||||
* @memberOf KeyboardAddLayoutsCore.prototype
|
||||
* @param {HTMLElement} listViewRoot
|
||||
* @param {ObservableArray} layouts
|
||||
* @param {Function} layoutTemplate
|
||||
*/
|
||||
_initEnabledLayoutListView:
|
||||
function kepl_initListView(listViewRoot, layouts, layoutTemplate) {
|
||||
this._listView = ListView(listViewRoot, layouts, layoutTemplate);
|
||||
},
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @memberOf KeyboardEnabledLayoutsCore.prototype
|
||||
* @param {Array} elements
|
||||
* Elements needed by this module.
|
||||
* @param {HTMLElement} elements.listViewRoot
|
||||
* The root element for the list view displaying the
|
||||
* installed keyboards.
|
||||
*/
|
||||
init: function kepl_onInit(elements) {
|
||||
var that = this;
|
||||
this._keyboardContext.init(function() {
|
||||
that._keyboardContext.enabledLayouts(function(layouts) {
|
||||
that._initEnabledLayoutListView(
|
||||
elements.listViewRoot, layouts, that._layoutTemplate);
|
||||
that.enabled = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_keplCore(context, template) {
|
||||
return new KeyboardEnabledLayoutsCore(context, template);
|
||||
};
|
||||
});
|
||||
87
gaia/apps/settings/js/panels/keyboard/installed_keyboards.js
Executable file
87
gaia/apps/settings/js/panels/keyboard/installed_keyboards.js
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* The module initializes a ListView displaying the installed keyboards.
|
||||
* Implementation details please refer to {@link KeyboardCore}.
|
||||
*
|
||||
* @module keyboard/installed_keyboards
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var ListView = require('modules/mvvm/list_view');
|
||||
|
||||
/**
|
||||
* @alias module:keyboard/installed_keyboards
|
||||
* @class KeyboardCore
|
||||
* @requires module:modules/mvvm/list_view
|
||||
* @param {KeyboardContext} context
|
||||
The kyboard context providing installed keyboards.
|
||||
* @param {Function} template
|
||||
The template function used to render an installed
|
||||
keyboard.
|
||||
* @returns {KeyboardCore}
|
||||
*/
|
||||
function KeyboardCore(context, template) {
|
||||
this._enabled = false;
|
||||
this._listView = null;
|
||||
this._keyboardContext = context;
|
||||
this._keyboardTemplate = template;
|
||||
}
|
||||
|
||||
KeyboardCore.prototype = {
|
||||
/**
|
||||
* The value indicates whether the module is responding. If it is false, the
|
||||
* UI stops reflecting the updates from the keyboard context.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf KeyboardCore.prototype
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
set enabled(value) {
|
||||
this._enabled = value;
|
||||
if (this._listView) {
|
||||
this._listView.enabled = this._enabled;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @access private
|
||||
* @memberOf KeyboardCore.prototype
|
||||
* @param {HTMLElement} listViewRoot
|
||||
* @param {ObservableArray} keyboards
|
||||
* @param {Function} keyboardTemplate
|
||||
*/
|
||||
_initAllKeyboardListView:
|
||||
function k_initListView(listViewRoot, keyboards, keyboardTemplate) {
|
||||
listViewRoot.hidden = (keyboards.length === 0);
|
||||
this._listView = ListView(listViewRoot, keyboards, keyboardTemplate);
|
||||
},
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @memberOf KeyboardCore.prototype
|
||||
* @param {Array} elements
|
||||
* Elements needed by this module.
|
||||
* @param {HTMLElement} elements.listViewRoot
|
||||
* The root element for the list view displaying the
|
||||
* installed keyboards.
|
||||
*/
|
||||
init: function k_init(elements) {
|
||||
var that = this;
|
||||
this._keyboardContext.init(function() {
|
||||
that._keyboardContext.keyboards(function(keyboards) {
|
||||
that._initAllKeyboardListView(
|
||||
elements.listViewRoot, keyboards, that._keyboardTemplate);
|
||||
that.enabled = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function ctor_keyboardCore(context, template) {
|
||||
return new KeyboardCore(context, template);
|
||||
};
|
||||
});
|
||||
34
gaia/apps/settings/js/panels/keyboard/keyboard_template.js
Executable file
34
gaia/apps/settings/js/panels/keyboard/keyboard_template.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* The template function for generating an UI element for a keyboard object.
|
||||
*
|
||||
* @module keyboard/keyboard_template
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
return function kp_keyboardTemplate(keyboard, recycled) {
|
||||
var container = null;
|
||||
var a;
|
||||
var span;
|
||||
if (recycled) {
|
||||
container = recycled;
|
||||
a = container.querySelector('a');
|
||||
span = container.querySelector('span');
|
||||
} else {
|
||||
container = document.createElement('li');
|
||||
a = document.createElement('a');
|
||||
span = document.createElement('span');
|
||||
|
||||
a.href = '#';
|
||||
a.className = 'menu-item';
|
||||
a.appendChild(span);
|
||||
container.appendChild(a);
|
||||
}
|
||||
|
||||
container.onclick = function() {
|
||||
keyboard.app.launch();
|
||||
};
|
||||
span.textContent = keyboard.name;
|
||||
return container;
|
||||
};
|
||||
});
|
||||
37
gaia/apps/settings/js/panels/keyboard/layout_template.js
Executable file
37
gaia/apps/settings/js/panels/keyboard/layout_template.js
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* A template function for generating an UI element for a layout object.
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var layoutTemplate = function layoutTemplate(layout, recycled) {
|
||||
var container = null;
|
||||
var nameBdi;
|
||||
var small;
|
||||
if (recycled) {
|
||||
container = recycled;
|
||||
nameBdi = container.querySelector('bdi');
|
||||
small = container.querySelector('small');
|
||||
} else {
|
||||
container = document.createElement('li');
|
||||
nameBdi = document.createElement('bdi');
|
||||
small = document.createElement('small');
|
||||
container.appendChild(document.createElement('span'));
|
||||
container.lastChild.appendChild(nameBdi);
|
||||
container.appendChild(small);
|
||||
}
|
||||
var refreshName = function() {
|
||||
nameBdi.textContent = layout.name;
|
||||
if (layout.nameL10nId) {
|
||||
nameBdi.dataset.l10nId = layout.nameL10nId;
|
||||
}
|
||||
small.textContent = layout.appName;
|
||||
};
|
||||
refreshName();
|
||||
layout.observe('appName', refreshName);
|
||||
layout.observe('name', refreshName);
|
||||
return container;
|
||||
};
|
||||
|
||||
return layoutTemplate;
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue