Source: js/dialer_agent.js

'use strict';

/* global SettingsListener, SettingsURL, CallscreenWindow, applications */
/* global Service, LazyLoader, toneUpgrader */
/* r=? dialer+system peers for changes in this file. */

(function(exports) {
  /**
   *
   * The delay between an incoming phone call and the first ring or vibration
   * needs to be as short as possible.
   *
   * This simple module keeps the ringtone (blob) around and starts alerting the
   * user as soon as a new incoming call is detected via the mozTelephony API.
   * And it opens an AttentionWindow with the preloaded callscreen app inside.
   *
   * We also listen for the sleep and volumedown hardware buttons to provide
   * the user with an easy way to stop the ringing.
   *
   * @example
   * var dialerAgent = new DialerAgent();
   * dialerAgent.start(); // Attach the event listeners.
   * dialerAgent.stop();  // Deattach the event listeners.
   *
   * @class    DialerAgent
   * @requires SettingsListener
   * @requires SettingsURL
   * @requires Service
   * @requires LazyLoader
   *
   **/

  var DialerAgent = function DialerAgent() {
    var telephony = navigator.mozTelephony;
    if (!telephony) {
      return;
    }

    this._telephony = telephony;

    this._started = false;
    this._shouldRing = null;
    this._shouldVibrate = true;
    this._alerting = false;
    this._vibrateInterval = null;

    var player = new Audio();
    this._player = player;
    // XXX: This will need to be updated for bug 961967
    // (audio competing in system app)
    player.mozAudioChannelType = 'ringer';
    player.preload = 'metadata';
    player.loop = true;
    this.freeCallscreenWindow = this.freeCallscreenWindow.bind(this);
    this.makeFakeNotification = this.makeFakeNotification.bind(this);
  };

  DialerAgent.prototype.freeCallscreenWindow = function() {
    var numOpenLines = this._telephony.calls.length +
      (this._telephony.conferenceGroup.calls.length ? 1 : 0);

    /* Free the callscreen window unless there are active calls or the
     * callscreen is visible. */
    if (this._callscreenWindow && (numOpenLines === 0) &&
        !this._callscreenWindow.isVisible()) {
      this._callscreenWindow.free();
    }
  };

  DialerAgent.prototype.start = function da_start() {
    if (!this._telephony) {
      return;
    }

    if (this._started) {
      throw 'Instance should not be start()\'ed twice.';
    }
    this._started = true;

    SettingsListener.observe('audio.volume.notification', 7, function(value) {
      this._shouldRing = !!value;
      if (this._shouldRing && this._alerting) {
        this._player.play();
      }
    }.bind(this));

    SettingsListener.observe('dialer.ringtone', '', function(value) {
      LazyLoader.load(['shared/js/settings_url.js']).then(function() {
        var phoneSoundURL = new SettingsURL();

        this._player.pause();
        this._player.src = phoneSoundURL.set(value);

        if (this._shouldRing && this._alerting) {
          this._player.play();
        }
      }.bind(this)).catch((err) => {
        console.error(err);
      });
    }.bind(this));

    // We have new default ringtones in 2.0, so check if the version is upgraded
    // then execute the necessary migration.
    if (Service.query('justUpgraded')) {
      LazyLoader.load('js/tone_upgrader.js').then(() => {
        toneUpgrader.perform('ringtone');
      });
    }

    SettingsListener.observe('vibration.enabled', true, function(value) {
      this._shouldVibrate = !!value;
    }.bind(this));

    this._telephony.addEventListener('callschanged', this);

    window.addEventListener('sleep', this);
    window.addEventListener('volumedown', this);
    window.addEventListener('mozmemorypressure', this.freeCallscreenWindow);

    this._callscreenWindow = new CallscreenWindow();
    this._callscreenWindow.hide();

    if (applications && applications.ready) {
      this.makeFakeNotification();
    } else {
      window.addEventListener('applicationready', this.makeFakeNotification);
    }

    return this;
  };

  DialerAgent.prototype.makeFakeNotification = function() {
    window.removeEventListener('applicationready', this.makeFakeNotification);
    this._callscreenWindow && this._callscreenWindow.makeNotification();
  };

  DialerAgent.prototype.stop = function da_stop() {
    if (!this._started) {
      return;
    }
    this._started = false;

    this._telephony.removeEventListener('callschanged', this);

    window.removeEventListener('sleep', this);
    window.removeEventListener('volumedown', this);
    window.removeEventListener('mozmemorypressure', this.freeCallscreenWindow);

    // TODO: should remove the settings listener once the helper
    // allows it.
    // See bug 981373.
  };

  DialerAgent.prototype.handleEvent = function da_handleEvent(evt) {
    if (evt.type === 'sleep' || evt.type === 'volumedown') {
      this._stopAlerting();
      return;
    }

    if (evt.type !== 'callschanged') {
      return;
    }

    var calls = this._telephony.calls;
    if (calls.length === 0) {
      return;
    }

    var calling = calls.some(function(call) {
      if (call.state === 'incoming' || call.state === 'dialing') {
        return true;
      }
    });

    if (calling) {
      this.openCallscreen();
    }

    if (this._alerting || calls[0].state !== 'incoming') {
      return;
    }

    var incomingCall = calls[0];
    var self = this;

    self._startAlerting();

    incomingCall.addEventListener('statechange', function callStateChange() {
      incomingCall.removeEventListener('statechange', callStateChange);

      self._stopAlerting();
    });
  };

  DialerAgent.prototype._startAlerting = function da_startAlerting() {
    this._alerting = true;

    if ('vibrate' in navigator && this._shouldVibrate) {
      this._vibrateInterval = window.setInterval(function vibrate() {
        navigator.vibrate([200]);
      }, 600);
      navigator.vibrate([200]);
    }

    if (this._shouldRing) {
      this._player.play();
    }
  };

  DialerAgent.prototype._stopAlerting = function da_stopAlerting() {
    var player = this._player;

    this._alerting = false;
    if (player && player.readyState > player.HAVE_NOTHING) {
      player.pause();
      player.currentTime = 0;
    }

    window.clearInterval(this._vibrateInterval);
  };

  DialerAgent.prototype.openCallscreen = function() {
    if (this._callscreenWindow) {
      this._callscreenWindow.ensure();
      this._callscreenWindow.requestOpen();
    }
  };

  exports.DialerAgent = DialerAgent;
}(window));