Source: js/call_forwarding.js

/* global BaseModule, asyncStorage, SIMSlotManager, SettingsHelper,
          CallForwardingsIcon, LazyLoader */
'use strict';

(function() {
  // Must be in sync with nsIDOMMozMobileCFInfo interface.
  var _cfReason = {
    CALL_FORWARD_REASON_UNCONDITIONAL: 0,
    CALL_FORWARD_REASON_MOBILE_BUSY: 1,
    CALL_FORWARD_REASON_NO_REPLY: 2,
    CALL_FORWARD_REASON_NOT_REACHABLE: 3,
    CALL_FORWARD_REASON_ALL_CALL_FORWARDING: 4,
    CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING: 5
  };
  var _cfAction = {
    CALL_FORWARD_ACTION_DISABLE: 0,
    CALL_FORWARD_ACTION_ENABLE: 1,
    CALL_FORWARD_ACTION_QUERY_STATUS: 2,
    CALL_FORWARD_ACTION_REGISTRATION: 3,
    CALL_FORWARD_ACTION_ERASURE: 4
  };

  /**
   * The module initializes the call forwarding icon states and update the icons
   * when call forwarding states changed.
   * As we are not able to query the current call forwarding states from the
   * carrier on startup, we initialized the icon state based on the cached
   * information. When call forwarding states are changed, the module caches
   * the state along with the corresponding iccId.
   * @class CallForwarding
   * @requires SIMSlotManager
   * @requires SettingsHelper
   */
  function CallForwarding() {
    this._slots = null;
    this._callForwardingHelper = null;
    this._defaultCallForwardingIconStates = null;
    this._callForwardingIconInitializedStates = null;
  }
  CallForwarding.STATES = [
    'enabled'
  ];
  CallForwarding.SETTINGS = [
    'ril.cf.enabled'
  ];

  BaseModule.create(CallForwarding, {
    name: 'CallForwarding',

    /**
     * Add related event handlers. The sim cards may not be ready when starting.
     * We register to "simslot-cardstatechange" and "simslot-iccinfochange" for
     * updating the icons when ready. When users query the current call
     * forwarding states in settings app, 'ril.cf.carrier.enabled' is used to
     * notify the state changes. When users change the call forwarding states in
     * settings app, we will receive the 'cfstatechange' event. All the events
     * must be watched to have correct icon states.
     * @memberof CallForwarding.prototype
     */
    _addEventHandlers: function() {
      window.addEventListener('simslot-cardstatechange', (function(event) {
        this._initCallForwardingState(event.detail);
      }).bind(this));
      window.addEventListener('simslot-iccinfochange', (function(event) {
        this._initCallForwardingState(event.detail);
      }).bind(this));

      // Get notified when users query the latest call forwarding states in
      // settings app
      navigator.mozSettings.addObserver('ril.cf.carrier.enabled',
        (function(event) {
          var detail = event.settingValue;
          if (detail) {
            this._onCallForwardingStateChanged(detail.index, detail.enabled);
          }
      }).bind(this));

      this._slots.forEach(function(slot) {
        var conn = slot.conn;
        conn.addEventListener('cfstatechange',
          this._updateCallForwardingIconState.bind(this, slot));
      }, this);
    },

    /**
     * Initialize the icon states based on iccId and cached information.
     * @param {SIMSlot} slot The target sim slot.
     * @memberof CallForwarding.prototype
     */
    _initCallForwardingState: function(slot) {
      var index = slot.index;
      var simCard = slot.simCard;

      if (this._callForwardingIconInitializedStates[index]) {
        return;
      }

      var that = this;
      var cardState = simCard && simCard.cardState;
      var iccid = simCard && simCard.iccInfo && simCard.iccInfo.iccid;
      // Disable call forwarding icon and early return when:
      // 1. sim card not present, or
      // 2. sim card state is not ready, or
      // 3. sim card iccId is not available.
      if (!simCard || cardState !== 'ready' || !iccid) {
        that._callForwardingHelper.get(function(states) {
          states[index] = false;
          that._callForwardingHelper.set(states);
        });
        return;
      }

      asyncStorage.getItem('ril.cf.enabled.' + iccid, function(value) {
        if (value === null) {
          value = false;
        }
        that._callForwardingHelper.get(function(states) {
          states[index] = value;
          that._callForwardingHelper.set(states, function() {
            that._callForwardingIconInitializedStates[index] = true;
          });
        });
      });
    },

    /**
     * Gets called when receiving "cfstatechange". It updates the cached
     * information and icon states.
     * @param {SIMSlot} slot The target sim slot.
     * @param {Event} event The event.
     * @memberof CallForwarding.prototype
     */
    _updateCallForwardingIconState: function(slot, event) {
      if (!event ||
          (event.reason != _cfReason.CALL_FORWARD_REASON_UNCONDITIONAL &&
           event.reason != _cfReason.CALL_FORWARD_REASON_ALL_CALL_FORWARDING)) {
        return;
      }

      var index = slot.index;
      var simCard = slot.simCard;

      var enabled = false;
      switch (event.action) {
        case _cfAction.CALL_FORWARD_ACTION_REGISTRATION:
        case _cfAction.CALL_FORWARD_ACTION_ENABLE:
          enabled = true;
          break;
        case _cfAction.CALL_FORWARD_ACTION_ERASURE:
          enabled = false;
          break;
        default:
          enabled = false;
          break;
      }

      this._callForwardingHelper.get((function(states) {
        states[index] = enabled;
        this._callForwardingHelper.set(states);
      }).bind(this));

      if (!simCard) {
        return;
      }
      var iccid = simCard.iccInfo && simCard.iccInfo.iccid;
      asyncStorage.setItem('ril.cf.enabled.' + iccid, enabled);
    },

    /**
     * Gets called when 'ril.cf.carrier.enabled' changes. It updates the cached
     * information and icon states.
     * @param {Number} index The index of the sim card being changed.
     * @param {Boolean} enabled The call forwarding state.
     * @memberof CallForwarding.prototype
     */
    _onCallForwardingStateChanged: function(index, enabled) {
      this._callForwardingHelper.get((function(states) {
        states[index] = enabled;
        this._callForwardingHelper.set(states);
      }).bind(this));

      var simCard = this._slots[index].simCard;
      if (!simCard) {
        return;
      }
      var iccid = simCard.iccInfo && simCard.iccInfo.iccid;
      asyncStorage.setItem('ril.cf.enabled.' + iccid, enabled);
    },

    enabled: function(index) {
      return this._enabled[index];
    },

    /**
     * Start the module.
     * @memberof CallForwarding.prototype
     */
    _start: function() {
      this._slots = SIMSlotManager.getSlots();
      this._enabled = [];
      this._defaultCallForwardingIconStates =
        Array.prototype.map.call(this._slots, function() { return false; });
      this._callForwardingIconInitializedStates =
        Array.prototype.map.call(this._slots, function() { return false; });

      // XXX:
      // We should use observe/read/write functionality in baseModule
      // and remove the usage here.
      LazyLoader.load(['shared/js/settings_helper.js']).then(() => {
        this._callForwardingHelper =
          SettingsHelper('ril.cf.enabled',
            this._defaultCallForwardingIconStates);

        this._addEventHandlers();

        // Initialize the icon states
        this._slots.forEach(function(slot) {
          this._initCallForwardingState(slot);
        }, this);
      }).catch((err) => {
        console.error(err);
      });
    },

    '_observe_ril.cf.enabled': function(value) {
      this._enabled = value || [];
      var isAnyEnabled =
        this._enabled.some(function(enabled) { return enabled; });
      if (!this.icon && isAnyEnabled) {
        LazyLoader.load(['js/call_forwarding_icon.js']).then(function() {
          this.icon = new CallForwardingsIcon(this);
          this.icon.start();
        }.bind(this)).catch(function(err) {
          console.error(err);
        });
      }
      this.icon && this.icon.update();
    }
  });
})();