Source: panels/simcard_manager/simcard_manager.js

  1. /**
  2. * SimCardManager is responsible for
  3. * 1. handling simcard UI
  4. * 2. handling simcard virtual status (please refer SimUIModel class)
  5. * 3. handling related mozSettings (please refer SimSettingsHelper class)
  6. *
  7. * @module SimCardManager
  8. */
  9. define(function(require) {
  10. 'use strict';
  11. var _ = window.navigator.mozL10n.get;
  12. var l10n = window.navigator.mozL10n;
  13. var Tagged = require('shared/tagged');
  14. var SimSettingsHelper = require('shared/sim_settings_helper');
  15. var AirplaneModeHelper = require('shared/airplane_mode_helper');
  16. var MobileOperator = require('shared/mobile_operator');
  17. var SimUIModel = require('panels/simcard_manager/sim_ui_model');
  18. var SimCardManager = function(elements) {
  19. // we store all SimUIModel instances into this array
  20. this._elements = elements;
  21. this._simcards = [];
  22. this._isAirplaneMode = false;
  23. };
  24. SimCardManager.prototype = {
  25. /**
  26. * Initiazlization
  27. *
  28. * @memberOf SimCardManager
  29. * @access public
  30. */
  31. init: function scm_init() {
  32. // `handleEvent` is used to handle these sim related changes
  33. this._elements.outgoingCallSelect.addEventListener('change', this);
  34. this._elements.outgoingMessagesSelect.addEventListener('change', this);
  35. // XXX because we handle `onchange` event differently in value selector,
  36. // in order to show confirm dialog after users changing value, the better
  37. // way right now is to check values when `onblur` event triggered.
  38. this._addOutgoingDataSelectEvent();
  39. this._addVoiceChangeEventOnConns();
  40. this._addCardStateChangeEventOnIccs();
  41. this._addLocalizedChangeEventOnIccs();
  42. // SMS app will directly change this value if users are going to
  43. // donwload specific sms from differnt simcard, so we have to
  44. // make sure our UI will reflect the right value at the moment.
  45. SimSettingsHelper.observe('outgoingData',
  46. this._outgoingDataChangeEvent.bind(this));
  47. // because in fugu, airplaneMode will not change cardState
  48. // but we still have to make UI consistent. In this way,
  49. // when airplaneMode is on in fugu, we have to mimic the nosim
  50. // situation in single sim.
  51. this._addAirplaneModeChangeEvent();
  52. this._isAirplaneMode =
  53. AirplaneModeHelper.getStatus() === 'enabled' ? true : false;
  54. // init UI
  55. this._initSimCardsInfo();
  56. this._initSimCardManagerUI();
  57. },
  58. simItemView: function({simIndex}) {
  59. return Tagged.escapeHTML `<li class="sim-card sim-card-${simIndex}">
  60. <div class="sim-card-icon"></div>
  61. <div class="information-container">
  62. <p class="sim-card-name"></p>
  63. <p class="sim-card-operator"></p>
  64. <bdi class="sim-card-number"></bdi>
  65. </div>
  66. </li>`;
  67. },
  68. /**
  69. * We will initialize SimUIModel and store them into our internal
  70. * variables.
  71. *
  72. * @memberOf SimCardManager
  73. * @access public
  74. */
  75. _initSimCardsInfo: function scm__initSimCardsInfo() {
  76. var conns = window.navigator.mozMobileConnections;
  77. for (var cardIndex = 0; cardIndex < conns.length; cardIndex++) {
  78. var conn = conns[cardIndex];
  79. var iccId = conn.iccId;
  80. var simcard = SimUIModel(cardIndex);
  81. this._simcards.push(simcard);
  82. this._updateCardState(cardIndex, iccId);
  83. }
  84. },
  85. /**
  86. * Handle incoming events
  87. *
  88. * @memberOf SimCardManager
  89. * @access private
  90. * @param {Event} evt
  91. */
  92. handleEvent: function scm_handlEvent(evt) {
  93. var cardIndex = evt.target.value;
  94. // it means users is seleting '--' options
  95. // when _simcards are all disabled
  96. if (cardIndex === SimSettingsHelper.EMPTY_OPTION_VALUE) {
  97. return;
  98. }
  99. switch (evt.target) {
  100. case this._elements.outgoingCallSelect:
  101. SimSettingsHelper.setServiceOnCard('outgoingCall', cardIndex);
  102. break;
  103. case this._elements.outgoingMessagesSelect:
  104. SimSettingsHelper.setServiceOnCard('outgoingMessages', cardIndex);
  105. break;
  106. }
  107. },
  108. /**
  109. * Handle mozSettings change event for `outgoing data` key
  110. *
  111. * @memberOf SimCardManager
  112. * @access private
  113. * @param {Number} cardIndex
  114. */
  115. _outgoingDataChangeEvent: function scm__outgoingDataChangeEvent(cardIndex) {
  116. this._elements.outgoingDataSelect.value = cardIndex;
  117. },
  118. /**
  119. * Handle change event for `outgoing data` select
  120. *
  121. * @memberOf SimCardManager
  122. * @access private
  123. */
  124. _addOutgoingDataSelectEvent: function scm__addOutgoingDataSelectEvent() {
  125. var prevCardIndex;
  126. var newCardIndex;
  127. // initialize these two variables when focus
  128. this._elements.outgoingDataSelect.addEventListener('focus', function() {
  129. prevCardIndex = this.selectedIndex;
  130. newCardIndex = this.selectedIndex;
  131. });
  132. this._elements.outgoingDataSelect.addEventListener('blur', function() {
  133. newCardIndex = this.selectedIndex;
  134. if (prevCardIndex !== newCardIndex) {
  135. // UX needs additional hint for users to make sure
  136. // they really want to change data connection
  137. var wantToChange =
  138. window.confirm(_('change-outgoing-data-confirm'));
  139. if (wantToChange) {
  140. SimSettingsHelper.setServiceOnCard('outgoingData',
  141. newCardIndex);
  142. } else {
  143. this.selectedIndex = prevCardIndex;
  144. }
  145. }
  146. });
  147. },
  148. /**
  149. * Get count of current simcards
  150. *
  151. * @memberOf SimCardManager
  152. * @access private
  153. * @return {Number} count of simcards
  154. */
  155. _getSimCardsCount: function scm__getSimCardsCount() {
  156. return this._simcards.length;
  157. },
  158. /**
  159. * Get information of simcard
  160. *
  161. * @memberOf SimCardManager
  162. * @access private
  163. * @param {Number} cardIndex
  164. * @return {Object} information stored in SimUIModel
  165. */
  166. _getSimCardInfo: function scm__getSimCardInfo(cardIndex) {
  167. return this._simcards[cardIndex].getInfo();
  168. },
  169. /**
  170. * Get simcard
  171. *
  172. * @memberOf SimCardManager
  173. * @access private
  174. * @param {Number} cardIndex
  175. * @return {SimUIModel}
  176. */
  177. _getSimCard: function scm__getSimCard(cardIndex) {
  178. return this._simcards[cardIndex];
  179. },
  180. /**
  181. * Iterate stored instances of SimUIModel and update each Sim UI
  182. *
  183. * @memberOf SimCardManager
  184. * @access private
  185. */
  186. _updateSimCardsUI: function scm__updateSimCardsUI() {
  187. this._simcards.forEach(function(simcard, cardIndex) {
  188. this._updateSimCardUI(cardIndex);
  189. }.bind(this));
  190. },
  191. /**
  192. * We would use specified instance of SimUIModel based on passing cardIndex
  193. * to render related UI on SimCardManager.
  194. *
  195. * @memberOf SimCardManager
  196. * @access private
  197. * @param {Number} cardIndex
  198. */
  199. _updateSimCardUI: function scm__updateSimCardUI(cardIndex) {
  200. var simcardInfo = this._getSimCardInfo(cardIndex);
  201. var selectors = ['name', 'number', 'operator'];
  202. var cardSelector = '.sim-card-' + cardIndex;
  203. var cardDom =
  204. this._elements.simCardContainer.querySelector(cardSelector);
  205. // reflect cardState on UI
  206. cardDom.classList.toggle('absent', simcardInfo.absent);
  207. cardDom.classList.toggle('locked', simcardInfo.locked);
  208. cardDom.classList.toggle('enabled', simcardInfo.enabled);
  209. // we are in three rows now, we have to fix styles
  210. cardDom.classList.toggle('with-number', !!simcardInfo.number);
  211. // relflect wordings on UI
  212. selectors.forEach(function(selector) {
  213. // will generate ".sim-card-0 .sim-card-name" for example
  214. var targetSelector = cardSelector + ' .sim-card-' + selector;
  215. var element =
  216. this._elements.simCardContainer.querySelector(targetSelector);
  217. if (selector === 'number') {
  218. element.textContent = simcardInfo[selector];
  219. } else {
  220. var l10nObj = simcardInfo[selector];
  221. if (l10nObj.id) {
  222. l10n.setAttributes(element, l10nObj.id, l10nObj.args);
  223. } else {
  224. element.removeAttribute('data-l10n-id');
  225. element.textContent = l10nObj.text;
  226. }
  227. }
  228. }.bind(this));
  229. },
  230. /**
  231. * Initialize SimCardManager UIs which includes
  232. * SimCardsUI, selectOptionsUI, simSecurityUI
  233. *
  234. * @memberOf SimCardManager
  235. * @access private
  236. */
  237. _initSimCardManagerUI: function scm__initSimCardManagerUI() {
  238. this._initSimCardsUI();
  239. this._updateSelectOptionsUI();
  240. // we only inject basic DOM from templates before
  241. // , so we have to map UI to its info
  242. this._updateSimCardsUI();
  243. this._updateSimSettingsUI();
  244. },
  245. /**
  246. * Initialize SimCardsUI
  247. *
  248. * @memberOf SimCardManager
  249. * @access private
  250. */
  251. _initSimCardsUI: function scm__initSimCardsUI() {
  252. var simItemHTMLs = [];
  253. // inject new childs
  254. this._simcards.forEach(function(simcard, index) {
  255. simItemHTMLs.push(
  256. this.simItemView({
  257. simIndex: index.toString()
  258. })
  259. );
  260. }.bind(this));
  261. this._elements.simCardContainer.innerHTML = simItemHTMLs.join('');
  262. },
  263. /**
  264. * Update the UI of the sim settings section
  265. *
  266. * @memberOf SimCardManager
  267. * @access private
  268. */
  269. _updateSimSettingsUI: function scm__updateSimSettingsUI() {
  270. var firstCardInfo = this._simcards[0].getInfo();
  271. var secondCardInfo = this._simcards[1].getInfo();
  272. var hidden = firstCardInfo.absent && secondCardInfo.absent ||
  273. this._isAirplaneMode;
  274. // if we don't have any card available right now
  275. // or if we are in airplane mode
  276. this._elements.simSettingsHeader.hidden = hidden;
  277. this._elements.simSettingsList.hidden = hidden;
  278. },
  279. /**
  280. * Update SelectOptions UI
  281. *
  282. * @memberOf SimCardManager
  283. * @access private
  284. */
  285. _updateSelectOptionsUI: function scm__updateSelectOptionsUI() {
  286. var firstCardInfo = this._simcards[0].getInfo();
  287. var secondCardInfo = this._simcards[1].getInfo();
  288. // two cards all are not absent, we have to update separately
  289. if (!firstCardInfo.absent && !secondCardInfo.absent) {
  290. SimSettingsHelper.getCardIndexFrom('outgoingCall',
  291. function(cardIndex) {
  292. this._updateSelectOptionUI('outgoingCall', cardIndex,
  293. this._elements.outgoingCallSelect);
  294. }.bind(this));
  295. SimSettingsHelper.getCardIndexFrom('outgoingMessages',
  296. function(cardIndex) {
  297. this._updateSelectOptionUI('outgoingMessages', cardIndex,
  298. this._elements.outgoingMessagesSelect);
  299. }.bind(this));
  300. SimSettingsHelper.getCardIndexFrom('outgoingData',
  301. function(cardIndex) {
  302. this._updateSelectOptionUI('outgoingData', cardIndex,
  303. this._elements.outgoingDataSelect);
  304. }.bind(this));
  305. } else {
  306. // there is one card absent while the other one is not
  307. var selectedCardIndex;
  308. // if two cards all are absent
  309. if (firstCardInfo.absent && secondCardInfo.absent) {
  310. // we will just set on the first card even
  311. // they are all with '--'
  312. selectedCardIndex = 0;
  313. } else {
  314. // if there is one card absent, the other one is not absent
  315. // we have to set defaultId to available card automatically
  316. // and disable select/option
  317. selectedCardIndex = firstCardInfo.absent ? 1 : 0;
  318. }
  319. // for these two situations, they all have to be disabled
  320. // and can not be selected by users
  321. this._elements.outgoingCallSelect.disabled = true;
  322. this._elements.outgoingMessagesSelect.disabled = true;
  323. this._elements.outgoingDataSelect.disabled = true;
  324. // then change related UI
  325. this._updateSelectOptionUI('outgoingCall', selectedCardIndex,
  326. this._elements.outgoingCallSelect);
  327. this._updateSelectOptionUI('outgoingMessages', selectedCardIndex,
  328. this._elements.outgoingMessagesSelect);
  329. this._updateSelectOptionUI('outgoingData', selectedCardIndex,
  330. this._elements.outgoingDataSelect);
  331. }
  332. },
  333. /**
  334. * Update SelectOption UI
  335. *
  336. * @memberOf SimCardManager
  337. * @access private
  338. * @param {String} storageKey
  339. * @param {Number} selectedCardIndex
  340. * @param {HTMLElement} selectedDOM
  341. */
  342. _updateSelectOptionUI: function scm__updateSelectOptionUI(
  343. storageKey, selectedCardIndex, selectDOM) {
  344. // We have to remove old options first
  345. while (selectDOM.firstChild) {
  346. selectDOM.removeChild(selectDOM.firstChild);
  347. }
  348. // then insert the new ones
  349. this._simcards.forEach(function(simcard, index) {
  350. var simcardInfo = simcard.getInfo();
  351. var option = document.createElement('option');
  352. option.value = index;
  353. if (simcardInfo.absent) {
  354. option.value = SimSettingsHelper.EMPTY_OPTION_VALUE;
  355. option.text = SimSettingsHelper.EMPTY_OPTION_TEXT;
  356. } else {
  357. if (simcardInfo.name.id) {
  358. l10n.setAttributes(option,
  359. simcardInfo.name.id, simcardInfo.name.args);
  360. } else {
  361. option.text = simcardInfo.name.text;
  362. }
  363. }
  364. if (index == selectedCardIndex) {
  365. option.selected = true;
  366. }
  367. selectDOM.add(option);
  368. });
  369. // we will add `always ask` option these two select
  370. if (storageKey === 'outgoingCall' ||
  371. storageKey === 'outgoingMessages') {
  372. var option = document.createElement('option');
  373. option.value = SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;
  374. option.setAttribute('data-l10n-id', 'sim-manager-always-ask');
  375. if (SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE ===
  376. selectedCardIndex) {
  377. option.selected = true;
  378. }
  379. selectDOM.add(option);
  380. }
  381. },
  382. /**
  383. * Check whether current cardState is locked or not.
  384. *
  385. * @memberOf SimCardManager
  386. * @access private
  387. * @param {String} cardState
  388. * @return {Boolean}
  389. */
  390. _isSimCardLocked: function scm__isSimCardLocked(cardState) {
  391. var lockedState = [
  392. 'pinRequired',
  393. 'pukRequired',
  394. 'networkLocked',
  395. 'serviceProviderLocked',
  396. 'corporateLocked',
  397. 'network1Locked',
  398. 'network2Locked',
  399. 'hrpdNetworkLocked',
  400. 'ruimCorporateLocked',
  401. 'ruimServiceProviderLocked'
  402. ];
  403. // make sure the card is in locked mode or not
  404. return lockedState.indexOf(cardState) !== -1;
  405. },
  406. /**
  407. * Check whether current cardState is blocked or not.
  408. *
  409. * @memberOf SimCardManager
  410. * @access private
  411. * @param {String} cardState
  412. * @return {Boolean}
  413. */
  414. _isSimCardBlocked: function scm__isSimCardBlocked(cardState) {
  415. var uselessState = [
  416. 'permanentBlocked'
  417. ];
  418. return uselessState.indexOf(cardState) !== -1;
  419. },
  420. /**
  421. * If voidechange happened on any conn, we would upate its cardState and
  422. * reflect the change on UI.
  423. *
  424. * @memberOf SimCardManager
  425. * @access private
  426. */
  427. _addVoiceChangeEventOnConns: function scm__addVoiceChangeEventOnConns() {
  428. var conns = window.navigator.mozMobileConnections;
  429. for (var i = 0; i < conns.length; i++) {
  430. var iccId = conns[i].iccId;
  431. conns[i].addEventListener('voicechange',
  432. this._updateCardStateWithUI.bind(this, i, iccId));
  433. }
  434. },
  435. /**
  436. * Iterate conns to add changeEvent
  437. *
  438. * @memberOf SimCardManager
  439. * @access private
  440. */
  441. _addCardStateChangeEventOnIccs:
  442. function scm__addCardStateChangeEventOnIccs() {
  443. var conns = window.navigator.mozMobileConnections;
  444. var iccManager = window.navigator.mozIccManager;
  445. for (var i = 0; i < conns.length; i++) {
  446. var iccId = conns[i].iccId;
  447. var icc = iccManager.getIccById(iccId);
  448. if (icc) {
  449. this._addChangeEventOnIccByIccId(iccId);
  450. }
  451. }
  452. },
  453. /**
  454. * When localized event happened, we would update each cardState and its
  455. * UI.
  456. *
  457. * @memberOf SimCardManager
  458. * @access private
  459. */
  460. _addLocalizedChangeEventOnIccs:
  461. function scm__addLocalizedChangeEventOnIccs() {
  462. var conns = window.navigator.mozMobileConnections;
  463. window.addEventListener('localized', function() {
  464. for (var i = 0; i < conns.length; i++) {
  465. var iccId = conns[i].iccId;
  466. this._updateCardStateWithUI(i, iccId);
  467. }
  468. }.bind(this));
  469. },
  470. /**
  471. * Add change event on each icc and would update UI if possible.
  472. *
  473. * @memberOf SimCardManager
  474. * @access private
  475. * @param {String} iccId
  476. */
  477. _addChangeEventOnIccByIccId:
  478. function scm__addChangeEventOnIccByIccId(iccId) {
  479. var self = this;
  480. var icc = window.navigator.mozIccManager.getIccById(iccId);
  481. if (icc) {
  482. icc.addEventListener('cardstatechange', function() {
  483. var cardIndex = self._getCardIndexByIccId(iccId);
  484. self._updateCardStateWithUI(cardIndex, iccId);
  485. // If we make PUK locked for more than 10 times,
  486. // we sould get `permanentBlocked` state, in this way
  487. // we have to update select/options
  488. if (self._isSimCardBlocked(icc.cardState)) {
  489. self._updateSelectOptionsUI();
  490. }
  491. });
  492. }
  493. },
  494. /**
  495. * If the state of APM is changed, we will update states and update all
  496. * related UIs.
  497. *
  498. * @memberOf SimCardManager
  499. * @access private
  500. */
  501. _addAirplaneModeChangeEvent: function scm__addAirplaneModeChangeEvent() {
  502. var self = this;
  503. AirplaneModeHelper.addEventListener('statechange', function(state) {
  504. // we only want to handle these two states
  505. if (state === 'enabled' || state === 'disabled') {
  506. var enabled = (state === 'enabled') ? true : false;
  507. self._isAirplaneMode = enabled;
  508. self._updateCardsState();
  509. self._updateSimCardsUI();
  510. self._updateSimSettingsUI();
  511. }
  512. });
  513. },
  514. /**
  515. * Iterate conns to call updateCardState on each conn.
  516. *
  517. * @memberOf SimCardManager
  518. * @access private
  519. */
  520. _updateCardsState: function scm__updateCardsState() {
  521. var conns = window.navigator.mozMobileConnections;
  522. for (var cardIndex = 0; cardIndex < conns.length; cardIndex++) {
  523. var iccId = conns[cardIndex].iccId;
  524. this._updateCardState(cardIndex, iccId);
  525. }
  526. },
  527. /**
  528. * we will use specified conn to update its state on our internal simcards
  529. *
  530. * @memberOf SimCardManager
  531. * @access private
  532. * @param {Number} cardIndex
  533. * @param {String} iccId
  534. */
  535. _updateCardState: function scm__updateCardState(cardIndex, iccId) {
  536. var iccManager = window.navigator.mozIccManager;
  537. var conn = window.navigator.mozMobileConnections[cardIndex];
  538. var simcard = this._simcards[cardIndex];
  539. if (!iccId || this._isAirplaneMode) {
  540. simcard.setState('nosim');
  541. } else {
  542. // else if we can get mobileConnection,
  543. // we have to check locked / enabled state
  544. var icc = iccManager.getIccById(iccId);
  545. var iccInfo = icc.iccInfo;
  546. var cardState = icc.cardState;
  547. var operatorInfo = MobileOperator.userFacingInfo(conn);
  548. if (this._isSimCardLocked(cardState)) {
  549. simcard.setState('locked');
  550. } else if (this._isSimCardBlocked(cardState)) {
  551. simcard.setState('blocked');
  552. } else {
  553. // TODO:
  554. // we have to call Gecko API here to make sure the
  555. // simcard is enabled / disabled
  556. simcard.setState('normal', {
  557. number: iccInfo.msisdn || iccInfo.mdn || '',
  558. operator: operatorInfo.operator
  559. });
  560. }
  561. }
  562. },
  563. /**
  564. * Sometimes, we have to update state and UI at the same time, so this is
  565. * a handy function to use.
  566. *
  567. * @memberOf SimCardManager
  568. * @access private
  569. * @param {Number} cardIndex
  570. * @return {String} iccId
  571. */
  572. _updateCardStateWithUI:
  573. function scm__updateCardStateWithUI(cardIndex, iccId) {
  574. this._updateCardState(cardIndex, iccId);
  575. this._updateSimCardUI(cardIndex);
  576. this._updateSimSettingsUI();
  577. },
  578. /**
  579. * This method would help us find out the index of passed in iccId.
  580. *
  581. * @memberOf SimCardManager
  582. * @access private
  583. * @param {String} iccId
  584. * @return {Number} cardIndex
  585. */
  586. _getCardIndexByIccId: function scm__getCardIndexByIccId(iccId) {
  587. var conns = window.navigator.mozMobileConnections;
  588. var cardIndex;
  589. for (var i = 0; i < conns.length; i++) {
  590. if (conns[i].iccId == iccId) {
  591. cardIndex = i;
  592. }
  593. }
  594. return cardIndex;
  595. }
  596. };
  597. return SimCardManager;
  598. });