Source: directives/voip/voip.js

    /**
     * @ngdoc directives
     * @name Voip
     * @module s4c.directives.voip.Voip
     *
     * @description
     * `voipCtrl` Responsável por exibir as funções do voip na tela, bem como o seu funcionamento
     * também possui acesso a api do backe
     *
     *@example
     *   <s4c-voip>
     *   </s4c-voip>
     */

(function () {
    'use strict';

    voipCtrl.$inject = [
        '$scope',
        '$http',
        '$mdDialog',
        '$state',
        'API_ENDPOINT',
        '$q',
        'TarefaService',
        'MainState',
        'CommService',
        'AuthService',
        'TarefaManager',
        'localize',
        'CamadasService',
        'Base',
        'Usuario',
        'ParametrosS4C',
        'DetalhamentoManager',
        'MapaService',
        'VoipManager',
        'VoipService'
    ];


	/** This is a description of the voip function. */
    function voipCtrl($scope, $http, $mdDialog, $state,
        API_ENDPOINT, $q, TarefaService, MainState, CommService, AuthService, TarefaManager, localize, CamadasService, Base, Usuario, ParametrosS4C, DetalhamentoManager, MapaService, VoipManager, VoipService) {

        $scope.res = $scope.$root.res;

        var ringtone_ = document.getElementById("ringtone");
        var ringbacktone_ = document.getElementById("ringbacktone");
        var audioRemote = document.getElementById("audio_remote");
        var audioLocal = document.getElementById("audio_remote");

        $("#voip_button").css('opacity', '0.5');

        /**
        * keybord numeric voip multivis Fusion
        *
        *@autor - Rafael Araujo
        *@param div classPai, textoBtn, input
        */
        $scope.numClick = function (classPai, textoBtn, input) {
            $('.' + classPai).on('click', function () {
                var num = $(this);
                var text = $.trim(num.find('.' + textoBtn).clone().children().remove().end().text());
                var telNumber = $(input);
                $(telNumber).val(telNumber.val() + text);
                // $('.campoTel').addClass('md-input-focused')
                $('#ramal').focus()
            });
        };
        /**
         * Limpar campo
         *
         *@autor - Rafael Araujo
         *@param Input, btnAcao
         */
        $scope.limparCampo = function (inputNumero, btnAcao) {
            $('.' + btnAcao).on('click', function () {
                var telNumber = $(inputNumero);
                $(telNumber).val('');
            });
        };

        $scope.numClick('num', 'txt', '[data-set="numero"]');
        $scope.limparCampo('[data-set="numero"]', 'limpar');

        /**
         * @method hangup
         * @param {*} ramal_
         */
        CommService.on('hangup', function (ramal_) {
            if (ramal_ != null && ramal_ == VoipService.usuarioRamal) {
                desligar();
            }
        });

        /**
         * @method mute
         * @param {*} ramal_
         */
        CommService.on('mute', function (ramal_) {
            if (ramal_ != null && ramal_ == VoipService.usuarioRamal) {
                mute();
            }
        });

        /**
         * @method readyCallback
         * @param {*} e 
         */
        var readyCallback = function (e) {
            if (MainState.isToShow('voip')) {
                createSipStack(); // see next section
            }
        };

        /**
         * @method errorCallback
         * @param {*} e 
         */
        var errorCallback = function (e) {
            console.error('Failed to initialize the engine: ' + e.message);
        }

        /**
         * @method eventsListener
         * @param {*} e 
         */
        var eventsListener = function (e) {
            console.info('session event = ' + e.type);
            if (e.type == 'started') {

                login();
            }
            else if (e.type == 'i_new_message') { // incoming new SIP MESSAGE (SMS-like)
                acceptMessage(e);
            }
            else if (e.type == 'i_new_call') { // incoming audio/video call

                startRingTone();

                VoipService.sipSessionCall = e.newSession;

                VoipManager.abrir();

                $("#making_call").hide();
                $("#discador").hide();
                $("#dicador").hide();
                $("#receiving_call").show();
                $("#in_call").hide();

                $("#making_call_field").hide();
                $("#receiving_call_field").show();
                $('#ramal').focus()
                setTimeout(function () { $('#ramal').focus(); }, 500);

                VoipService.s_user_name = e.o_event.o_message.o_hdr_From.o_uri.s_user_name;

                VoipService.displayName = e.o_event.o_message.o_hdr_From.s_display_name;

                if (VoipService.displayName == null) {
                    VoipService.displayName = VoipService.s_user_name;
                } else if (VoipService.displayName != null && VoipService.s_user_name != null) {
                    VoipService.displayName += ", ramal " + VoipService.s_user_name;
                }

                $("#mensagem").text("Você está recebendo uma ligação do " + VoipService.displayName);


            } else if (e.type == 'connected' && e.session == VoipService.registerSession) {
                publishPresence();
                subscribePresence(VoipService.usuarioRamal); // watch johndoe's presence status change
            }
            else if (e.type == 'connected' && e.description == 'In Call') {
                if ($('#in_call').is(':visible')) {
                    $("#mensagem").text("A chamada está ativa com o ramal " + VoipService.displayName);
                    stopRingBackTone();
                }
            }
            else if (e.type == 'i_request_cancel' && e.description == 'Media Removed') {
                $("#mensagem").text("A ligação foi encerrada");
                stopRingTone();
                hangupCall(e);
            }
            else if (e.type == 'i_notify') {
                console.info('NOTIFY content = ' + e.getContentString());
                console.info('NOTIFY content-type = ' + e.getContentType());

                if (e.getContentType() == 'application/pidf+xml') {
                    if (window.DOMParser) {
                        var parser = new DOMParser();
                        var xmlDoc = parser ? parser.parseFromString(e.getContentString(), "text/xml") : null;
                        var presenceNode = xmlDoc ? xmlDoc.getElementsByTagName("presence")[0] : null;
                        if (presenceNode) {
                            var entityUri = presenceNode.getAttribute("entity");
                            var tupleNode = presenceNode.getElementsByTagName("tuple")[0];
                            if (entityUri && tupleNode) {
                                var statusNode = tupleNode.getElementsByTagName("status")[0];
                                if (statusNode) {
                                    var basicNode = statusNode.getElementsByTagName("basic")[0];
                                    if (basicNode) {
                                        console.info('Presence notification: Uri = ' + entityUri + ' status = ' + basicNode.textContent);
                                    }
                                }
                            }
                        }
                    }
                }
            } else if (e.type == 'terminated' && e.description == 'Call terminated') {
                hangupCall(e);
            } else if (e.type == 'i_ao_request' && e.description == 'Busy Here') {
                stopRingBackTone();
            } else if (e.type == 'terminated' && e.description == 'Busy Here') {
                hangupCall(e);
            } else if (e.type == 'failed_to_start') {
                VoipService.isConnecting = false;

                $mdDialog.show($mdDialog.alert()
                    .title($scope.res('COMUM_AVISO'))
                    .content($scope.res('VOIP_CANNOT_CONECT'))
                    .ok($scope.res('COMUM_OK')));
                $('#voip_button_div').attr('ng-click', '');
                $("#voip_button_div").click(function () {
                    if (!VoipService.isConnecting && !VoipService.isConnected) {
                        $mdDialog.show($mdDialog.alert()
                            .title($scope.res('COMUM_AVISO'))
                            .content($scope.res('VOIP_RETRY_CONECT'))
                            .ok($scope.res('COMUM_OK')));

                        createSipStack();
                    }
                });
            }
        }

        /**
         * @method createSipStack
         */
        function createSipStack() {
            Usuario.obterPorId(AuthService.user.info.id).then(function (usuario) {

                if (ParametrosS4C.parametros.domainVoip == null || usuario.voipRamal == null || ParametrosS4C.parametros.urlServerVoip == null || usuario.senhaVoipRamal == null) {
                    VoipService.isSetCorrectly = false;
                    return;
                }
                try {
                    if (ParametrosS4C.parametros.domainVoip != null && ParametrosS4C.parametros.urlServerVoip != null && !VoipService.isConnected && !VoipService.isConnecting) {
                        VoipService.isConnecting = true;
                        VoipService.isSetCorrectly = true;
                        VoipService.usuarioRamal = usuario.voipRamal;
                        VoipService.sipStack = new SIPml.Stack({
                            realm: ParametrosS4C.parametros.domainVoip, // mandatory: domain name
                            impi: usuario.voipRamal, // mandatory: authorization name (IMS Private Identity)
                            impu: 'sip:' + usuario.voipRamal + '@' + ParametrosS4C.parametros.domainVoip, // mandatory: valid SIP Uri (IMS Public Identity)
                            password: usuario.senhaVoipRamal, // optional
                            ice_servers: [], // Array vazio para desabilitar o Stun / turn, quando a rede não tiver um nat.
                            //display_name: 'Bob legend', // optional
                            websocket_proxy_url: ParametrosS4C.parametros.urlServerVoip, // optional
                            // outbound_proxy_url: 'udp://example.org:5060', // optional
                            //enable_rtcweb_breaker: false, // optional
                            events_listener: { events: '*', listener: eventsListener }, // optional: '*' means all events
                            sip_headers: [ // optional
                                { name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.0.0' },
                                { name: 'Organization', value: 'Doubango Telecom' }
                            ]
                        });

                        VoipService.sipStack.start();

                    }
                } catch (Err) {
                    $mdDialog.show($mdDialog.alert()
                        .title($scope.res('COMUM_AVISO'))
                        .content($scope.res('VOIP_CANNOT_CONECT'))
                        .ok($scope.res('COMUM_OK')));
                }
            });

        }

        /**
         * @method login
         */
        var login = function () {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            VoipService.registerSession = VoipService.sipStack.newSession('register', {
                events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
            });
            //VoipService.registerSession.unregister();
            VoipService.registerSession.register();
        }

        /**
         * @method makeCall
         * @param {*} callid 
         */
        var makeCall = function (callid) {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            VoipService.sipSessionCall = VoipService.sipStack.newSession('call-audio', VoipService.oConfigMakeCall);
            VoipService.sipSessionCall.call(callid);
            startRingBackTone();
        }

        /**
         * @method acceptCall
         */
        var acceptCall = function () {
            VoipService.sipSessionCall.accept(VoipService.oConfigMakeCall);
        }

        /**
         * @method rejectCall
         */
        var rejectCall = function () {
            VoipService.displayName = '';
            VoipService.sipSessionCall.reject(VoipService.oConfigMakeCall); // e.newSession.reject() to reject the call
        }

        /**
         * @method hangupCall
         * @param {*} e 
         */
        var hangupCall = function (e) {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            if (e.session != null) {
                e.session.hangup({
                    events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
                }); // e.newSession.reject() to reject the call
            }
            //publishPresence();
            //subscribePresence(VoipService.usuarioRamal);

            $("#making_call").show();
            $("#discador").show();
            $("#receiving_call").hide();
            $("#in_call").hide();

            $("#making_call_field").show();
            $("#receiving_call_field").hide();

            stopRingBackTone();

            VoipService.displayName = '';

            VoipService.b_mute = false;
            $("#mute").show();
            $("#sound").hide();
        }

        /**
         * @method sendMessage
         */
        var sendMessage = function () {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            VoipService.messageSession = VoipService.sipStack.newSession('message', {
                events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
            });
            // VoipService.messageSession.send('johndoe', 'Pêche à la moule', 'text/plain;charset=utf-8');
        }

        /**
         * @method acceptMessage
         * @param {*} e 
         */
        var acceptMessage = function (e) {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            e.newSession.accept(); // e.newSession.reject(); to reject the message
            $mdDialog.show($mdDialog.alert()
                .title('SMS received')
                .content(e.getContentString())
                .ok($scope.res('COMUM_OK')));
            console.info('SMS-content = ' + e.getContentString() + ' and SMS-content-type = ' + e.getContentType());
        }

        /**
         * @method publishPresence
         */
        var publishPresence = function () {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            VoipService.publishSession = VoipService.sipStack.newSession('publish', {
                events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
            });
            var contentType = 'application/pidf+xml';
            var content = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n' +
                '<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n' +
                ' xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"' +
                ' entity=\"sip:bob@example.com\">\n' +
                '<tuple id=\"s8794\">\n' +
                '<status>\n' +
                '   <basic>open</basic>\n' +
                '   <im:im>away</im:im>\n' +
                '</status>\n' +
                '<contact priority=\"0.8\">tel:+33600000000</contact>\n' +
                '<note  xml:lang=\"fr\">Bonjour de Paris :)</note>\n' +
                '</tuple>\n' +
                '</presence>';

            // send the PUBLISH request
            VoipService.publishSession.publish(content, contentType, {
                expires: 200,
                sip_caps: [
                    { name: '+g.oma.sip-im' },
                    { name: '+sip.ice' },
                    { name: 'language', value: '\"en,fr\"' }
                ],
                sip_headers: [
                    { name: 'Event', value: 'presence' },
                    { name: 'Organization', value: 'Doubango Telecom' }
                ]
            });
        }

        /**
         * @method subscribePresence
         * @param {*} to 
         */
        var subscribePresence = function (to) {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }
            VoipService.subscribeSession = VoipService.sipStack.newSession('subscribe', {
                expires: 200,
                events_listener: { events: '*', listener: eventsListener },
                sip_headers: [
                    { name: 'Event', value: 'presence' }, // only notify for 'presence' events
                    { name: 'Accept', value: 'application/pidf+xml' } // supported content types (COMMA-sparated)
                ],
                sip_caps: [
                    { name: '+g.oma.sip-im', value: null },
                    { name: '+audio', value: null },
                    { name: 'language', value: '\"en,fr\"' }
                ]
            });
            // start watching for entity's presence status (You may track event type 'connected' to be sure that the request has been accepted by the server)
            //subscribeSession.unsubscribe();
            VoipService.subscribeSession.subscribe(to);
            VoipService.isConnected = true;
            VoipService.isConnecting = false;
            $("#voip_button").css('opacity', '1');
        }

        /**
         * @method startTalking
         */
        function startTalking() {
            try { audioRemote.play(); }
            catch (e) { }
        }

        /**
         * @method stopTalking
         */
        function stopTalking() {
            try { audioRemote.stop(); }
            catch (e) { }
        }

        /**
         * @method startRingTone
         */
        function startRingTone() {
            try { ringtone_.play(); }
            catch (e) { }
        }

        /**
         * @method stopRingTone
         */
        function stopRingTone() {
            try { ringtone_.pause(); }
            catch (e) { }
        }

        /**
         * @method startRingBackTone
         */
        function startRingBackTone() {
            try { ringbacktone_.play(); }
            catch (e) { }
        }

        /**
         * @method stopRingBackTone
         * 
         */
        function stopRingBackTone() {
            try { ringbacktone_.pause(); }
            catch (e) { }
        }

        /**
         * Definição do Objeto oConfigMakeCall
         * @instance oConfigMakeCall
         */
        VoipService.oConfigMakeCall = {
            from: VoipService.usuarioRamal,
            audio_remote: audioRemote,
            //audio_local: audioLocal,
            events_listener: { events: '*', listener: eventsListener },
            sip_caps: [
                { name: '+g.oma.sip-im' },
                { name: 'language', value: '\"en,fr\"' }
            ]
        };

        /**
         * @method _verfiyConfig
         */
        function _verfiyConfig() {
            $mdDialog.show($mdDialog.alert()
                .title($scope.res('COMUM_AVISO'))
                .content($scope.res('CHECK_VOIP_CONFIG'))
                .ok($scope.res('COMUM_OK')));
        }

        /**
         * @method ligar
         * @param {*} ramal_
         */
        function ligar(ramal_) {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }
            var ramal = null;

            if (ramal_ != null && ramal_ != "") {
                ramal = ramal_;
            } else if ($("#ramal").val() != null && $("#ramal").val() != "") {
                ramal = $("#ramal").val();
            }

            if (ramal != null) {
                makeCall(ramal);

                $("#making_call").hide();
                $("#discador").hide();
                $("#receiving_call").hide();
                $("#in_call").show();

                $("#making_call_field").hide();
                $("#receiving_call_field").show();

                $("#mensagem").text("Você está ligando para o ramal " + ramal);

                $("#ramal").val("");

                VoipService.displayName = ramal;

                Base.obter('pois/ramal/' + ramal).then(function (pois) {
                    if (pois != null && pois.length > 0) {
                        var latlng = {
                            lat: pois[0].latitude,
                            lng: pois[0].longitude
                        };
                        if (!DetalhamentoManager.samePoi(pois[0].id)) {
                            DetalhamentoManager.abrirPoi(pois[0].id, latlng);
                            CamadasService.ativarMenuCategoria(pois[0].CategoriumId);
                            MapaService.voarPara([
                                pois[0].longitude,
                                pois[0].latitude
                            ]);
                            MapaService.piscarAzul(latlng);
                        }
                    }
                });
            } else {
                $mdDialog.show($mdDialog.alert()
                    .title($scope.res('COMUM_AVISO'))
                    .content($scope.res('INFORMAR_RAMAL'))
                    .ok($scope.res('COMUM_OK')));
            }
            //document.getElementById("ligar").innerHTML = '<md-button class="md-primary md-raised" ng-click="desligar()"  aria-label="Ligar" ><span class="ng-scope">Encerrar</span></md-button>';
        }

        /**
         * @method desligar
         */
        function desligar() {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            VoipService.b_mute = false;
            $("#mute").show();
            $("#sound").hide();

            $("#making_call").show();
            $("#discador").show();
            $("#receiving_call").hide();
            $("#in_call").hide();

            $("#making_call_field").show();
            $("#receiving_call_field").hide();

            VoipService.displayName = '';

            //document.getElementById("ligar").innerHTML = '<md-button class="md-primary md-raised" ng-click="ligar()"  aria-label="Ligar" ><span class="ng-scope">Ligar</span></md-button>';
            //document.getElementById("desligar").innerHTML = '<md-button class="md-primary md-raised" ng-click="desligar()"  aria-label="Ligar" ></md-button>';

            VoipService.sipSessionCall.o_session.hangup({
                events_listener: { events: '*', listener: eventsListener }
            });

            stopRingBackTone();

        }

        /**
         * @method atender
         */
        function atender() {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            stopRingTone();
            stopRingBackTone();
            acceptCall();

            $("#discador").hide();
            $("#making_call").hide();
            $("#receiving_call").hide();
            $("#in_call").show();

            $("#making_call_field").hide();
            $("#receiving_call_field").show();

            $("#mensagem").text("Ligação em andamento com o ramal " + VoipService.displayName);

            if (VoipService.s_user_name != null && VoipService.s_user_name != "") {
                Base.obter('pois/ramal/' + VoipService.s_user_name).then(function (pois) {
                    if (pois != null && pois.length > 0) {
                        var latlng = {
                            lat: pois[0].latitude,
                            lng: pois[0].longitude
                        };


                        Base.obter('date_server/time_millis').then(function (time_millis) {

                            function openWindow(url) {
                                var token = JSON.parse(window.sessionStorage.getItem('s4cToken')).access_token;
                                url = url.replace("[token]", token);
                                url += "&atendimento_voip=true&chave_incidente=" + time_millis;
                                var win = window.open(url, "Abrir incidente", "toolbar=no,location=no,directories=no,status=no,menubar=no,location=no,scrollbars=yes,resizable=yes,maximize=yes,width=" + screen.width + ",height=" + screen.height + ",top=0,left=0");
                            }

                            MapaService.voarPara([
                                pois[0].longitude,
                                pois[0].latitude
                            ]);
                            MapaService.piscarAzul(latlng);

                            if (pois[0].extras && pois[0].extras.informacoes) {
                                for (var index in pois[0].extras.informacoes) {
                                    if (pois[0].extras.informacoes[index].label == 'Comando_URL') {
                                        var Comando_URL = pois[0].extras.informacoes[index].valor;
                                        openWindow(Comando_URL);
                                    }
                                }
                            }
                        });

                    }
                });
            }

        }

        /**
         * @method rejeitar
         */
        function rejeitar() {
            if (VoipService.sipStack == null) {
                _verfiyConfig();
                return;
            }

            stopRingTone();
            rejectCall();

            $("#discador").show();
            $("#making_call").show();
            $("#receiving_call").hide();
            $("#in_call").hide();

            $("#making_call_field").show();
            $("#receiving_call_field").hide();
            $("#mensagem").text("");

        }

        /**
         * @method deslogar
         */
        function deslogar() {
            if (VoipService.sipStack != null) {
                VoipService.isConnected = false;
                try{
                	VoipService.sipStack.stop();
                }
                catch(err){
                	console.log("Não foi possível deslogar do voip:");
                }
            }
        }

        /**
         * @method mute
         * 
         */
        function mute() {
            VoipService.b_mute = !VoipService.b_mute;
            if (VoipService.b_mute) {
                $("#mute").hide();
                $("#sound").show();
            } else {
                $("#mute").show();
                $("#sound").hide();
            }
            VoipService.sipSessionCall.o_session.set_mute('audio', VoipService.b_mute);
        }

        angular.extend($scope, {
            data: {},
            ligar: ligar,
            desligar: desligar,
            atender: atender,
            rejeitar: rejeitar,
            mute: mute,
            verificaMenuCarregado: verificaMenuCarregado,
            $apivoip: {
                ligar: ligar,
                desligar: desligar,
                atender: atender,
                rejeitar: rejeitar,
                deslogar: deslogar,
                mute: mute,
                createSipStack: createSipStack
            }
        });

        MainState.registerDirective('voip', $scope.$apivoip);

        $scope.$on('$destroy', function () {
            MainState.unregisterDirective('voip');
        });

        /**
         * @method verificaMenuCarregado
         */
        function verificaMenuCarregado() {
            if (!CamadasService.isLoaded && $state.current.name == "main") {
                setTimeout(function () {
                    verificaMenuCarregado();
                }, 1000);
            } else {
                setTimeout(function () {
                    var inicializado = SIPml.isInitialized();
                    if (!inicializado) {
                        SIPml.init(readyCallback, errorCallback);
                    } else if (SIPml.isInitialized && VoipService.sipStack == null) {
                        createSipStack();
                    } else if (VoipService.isConnected && $state.current.name == "main") {
                        $("#voip_button").css('opacity', '1');
                    }
                }, 2000);
            }
        }

        if (MainState.isToShow('voip')) {
            verificaMenuCarregado();
        }

    }



    function s4cVoip() {
        return {
            restrict: 'EA',
            templateUrl: 'app/directives/voip/voip.html',
            replace: true,
            scope: {},
            controller: voipCtrl
        };
    }

    /**
     * @ngdoc overview
     * @name s4c.components.voip
     */
    angular.module('s4c.components.voip', [])
        .directive('s4cVoip', s4cVoip);

}());