Source: js/visibility_manager.js

/* global Service */
'use strict';

(function(exports) {
  /**
   * VisibilityManager manages visibility events and broadcast
   * to AppWindowManager.
   * When an UI layer is overlapping the current app,
   * WindowManager should set the visibility of app iframe to false
   * And reset to true when the layer is gone.
   * We may need to handle windowclosing, windowopened in the future.
   *
   * @class VisibilityManager
   * @requires Service
   */
  var VisibilityManager = function VisibilityManager() {
    this._normalAudioChannelActive = false;
    this._deviceLockedTimer = 0;
    this.overlayEvents = [
      'lockscreen-appopened',
      'lockscreen-request-unlock',
      'attentionwindowmanager-deactivated',
      'attentionopened',
      'mozChromeEvent',
      'appclosing',
      'homescreenopened',
      'searchrequestforeground',
      'apprequestforeground',
      'lockscreen-apprequestforeground',
      'secure-apprequestforeground',
      'homescreenrequestforeground'
    ];
  };

  /**
   * Debug flag.
   * @type {Boolean}
   */
  VisibilityManager.prototype.DEBUG = false;
  VisibilityManager.prototype.CLASS_NAME = 'VisibilityManager';

  /**
   * Startup. Start listening all related events that changes visibility.
   *
   * @memberof VisibilityManager.prototype
   */
  VisibilityManager.prototype.start = function start() {
    this.overlayEvents.forEach(function overlayEventIterator(event) {
      window.addEventListener(event, this);
    }, this);
  };

  VisibilityManager.prototype.handleEvent = function vm_handleEvent(evt) {
    this.debug('handling ' + evt.type + ' event..');
    switch (evt.type) {
      case 'searchrequestforeground':
      case 'homescreenrequestforeground':
      case 'lockscreen-apprequestforeground':
      case 'secure-apprequestforeground':
        // XXX: Use hierachy manager to know who is top most.
        if (!Service.query('AttentionWindowManager.hasActiveWindow')) {
          evt.detail.setVisible(true);
        }
        break;
      case 'apprequestforeground':
        // XXX: Use hierachy manager to know who is top most.
        if (!Service.query('locked') &&
            !Service.query('AttentionWindowManager.hasActiveWindow')) {
          evt.detail.setVisible(true);
        }
        break;
      // XXX: See Bug 999318.
      // Audio channel is always normal without going back to none.
      // We are actively discard audio channel state when homescreen
      // is opened.
      case 'appclosing':
      case 'homescreenopened':
        this._normalAudioChannelActive = false;
        break;
      case 'attentionwindowmanager-deactivated':
        if (window.Service.query('locked')) {
          this.publish('showlockscreenwindow');
          return;
        }
        this.publish('showwindow', { type: evt.type });
        this._resetDeviceLockedTimer();
        break;
      case 'lockscreen-request-unlock':
        var detail = evt.detail,
            activity = null,
            notificationId = null;

        if (detail) {
          activity = detail.activity;
          notificationId = detail.notificationId;
        }

        if (!Service.query('AttentionWindowManager.hasActiveWindow')) {
          this.publish('showwindow', {
            activity: activity,  // Trigger activity opening in AWM
            notificationId: notificationId
          });
        }
        this._resetDeviceLockedTimer();
        break;
      case 'lockscreen-appopened':
        // If the audio is active, the app should not set non-visible
        // otherwise it will be muted.
        // TODO: Remove this hack.
        this.debug('locking, hide the whole windows',
          this._normalAudioChannelActive);
        if (!this._normalAudioChannelActive) {
          this.publish('hidewindow',
            { screenshoting: false, type: evt.type });
        }
        this._resetDeviceLockedTimer();
        break;

      case 'attentionopened':
        if (!Service.query('locked')) {
          this.publish('hidewindow', { type: evt.type });
        }
        break;
      case 'mozChromeEvent':
        if (evt.detail.type == 'visible-audio-channel-changed') {
          this._resetDeviceLockedTimer();

          if (this._normalAudioChannelActive &&
              evt.detail.channel !== 'normal' && Service.query('locked')) {
            this._deviceLockedTimer = setTimeout(function setVisibility() {
              if (window.Service.query('locked')) {
                this.publish('hidewindow',
                  { screenshoting: false, type: evt.type });
              }
            }.bind(this), 3000);
          }

          this._normalAudioChannelActive = (evt.detail.channel === 'normal');
          this.debug('Normal AudioChannel changes to ',
            evt.detail.channel, this._normalAudioChannelActive);
        }
        break;
    }
  };

  VisibilityManager.prototype._resetDeviceLockedTimer =
    function vm_resetDeviceLockedTimer() {
      if (this._deviceLockedTimer) {
        clearTimeout(this._deviceLockedTimer);
        this._deviceLockedTimer = 0;
      }
    };

  VisibilityManager.prototype.publish = function vm_publish(eventName, detail) {
    this.debug('publishing: ', eventName);
    var evt = new CustomEvent(eventName, { detail: detail });
    window.dispatchEvent(evt);
  };

  VisibilityManager.prototype.debug = function vm_debug() {
    if (this.DEBUG) {
      console.log('[' + this.CLASS_NAME + ']' +
        '[' + Service.currentTime() + ']' +
        Array.slice(arguments).concat());
    }
  };
  exports.VisibilityManager = VisibilityManager;
}(window));