terça-feira, 22 de outubro de 2013

Curso de Criação de Apps Móveis com PhoneGap e jQuery Mobile - Licão 4


Nesta lição, estudaremos a Conectividade.

Baixe os arquivos correspondentes a esta aula. A pasta completa está em: 






O trabalho "Criação de aplicações Móveis com PhoneGap e jQuery Mobile" de Cleuton Sampaio de Melo Jr está licenciado com uma Licença Creative Commons - Atribuição-CompartilhaIgual 4.0 Internacional. Isso inclui: Textos, páginas, gráficos e código-fonte.


Conectividade

O grande lance das aplicações móveis é a conectividade. Aplicações “standalone”, que não se comunicam com nenhum Serviço, são raras e, geralmente, de pouca utilidade em plataforma móvel. Temos que lembrar que o usuário é movel e está com uma plataforma computacional de grande poder em seu bolso.

No início da Internet móvel (WAP), a grande novidade era acessar seu e-mail ou então navegar em websites WAP. Hoje em dia, podemos fazer muito mais do que isso.


Arquitetura típica de conectividade


Basicamente, temos dois tipos de comunicação: Consumo de Serviços e “Push”. O Consumo de serviços é quando o dispositivo envia um Request a um Servidor na Web, o que, geralmente, é feito utilizando-se HTTP, e recebe uma resposta. O Web Service pode ser SOAP ou HTTP puro, e o formato de dados pode ser JSON ou XML.



Consumo usando HTTP




Podemos consumir serviços utilizando HTTP puro, e o jQuery nos fornece vários métodos para isto:



Get de dados a partir de um servidor:

$.get(“<url>”, <callback>)

$("button").click(function(){
$.get("demo_test.asp",function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});

Post de dados para um servidor:

$.post(URL,data,callback);

$("button").click(function(){
$.post("demo_test_post.asp",
{
name:"Donald Duck",
city:"Duckburg"
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});

Get de um objeto JSON a partir de um servidor:

$.getJSON( url [, data ] [, success ] )

$.getJSON( "ajax/test.json", function( data ) {
var items = [];
$.each( data, function( key, val ) {
items.push( "<li id='" + key + "'>" + val + "</li>" );
});
$( "<ul/>", {
"class": "my-new-list",
html: items.join( "" )
}).appendTo( "body" );
});

E temos também o método “Ajax”, que é mais completo:

$.ajax({
type: "POST",
url: wsUrl,
contentType: "text/xml",
dataType: "xml",
data: soapRequest,
success: processSuccess,
error: processError
});

Sempre temos que passar “callbacks” para os eventos, além de testarmos o status da resposta:


200 OK

O Request foi bem sucedido. O conteúdo da resposta estará de acordo com o campo “Content-type”.

301 Moved Permanently
302 Found
303 See Other

É necessário fazer um “redirect” para outra URL, informada no HEADER da resposta.

304 Not Modified

O Recurso que você está tentando obter não sofreu modificações, logo, o que está em seu “cache” é válido. Isso depende do conteúdo do ETag e da data de modificação, e você pode controlar isso com os tags no Request: “if-none-match” e “if-modified-since”.

400 Bad Request

Seu request não foi entendido pelo Servidor. Verifique o formato e os HEADERS.

401 Unauthorized

O acesso ao Recurso exige autenticação. A resposta deve incluir um HEADER “WWW-Authenticate” que contém um “challenge” a ser devolvido no próximo request. O Cliente pode repetir o request incluindo o HEADER: “Authorization”. Se o Request original já incluiu o HEADER “Authorization”, então é uma indicação que o usuário não tem direito de acesso ao Recurso.

403 Forbidden

O Servidor entendeu o request, porém não vai enviar o recurso. Neste caso, não adianta incluir o HEADER “Authorization” e o Request não deve ser repetido.

404 Not Found

O Recurso não foi encontrado. Pode ser um problema de URI, indicando que o nome ou a identificação do Recurso estejam inválidos.

405 Method Not Allowed

O Método HTTP que você está enviando em seu Request não é aceito pelo Servidor para este Recurso. Por exemplo, você pode estar tentando obter um recurso com GET, mas ele só está disponível via POST.

406 Not Acceptable

Quando criamos um Request, podemos especificar quais tipos de resposta aceitaremos, por exemplo: XML, JSON, HTML ou TEXT. Se o Servidor concluir que não pode enviar o Recurso naquele formato, ele retorna um erro 406.

5XX Erro de servidor

Este tipo de erro é retornado devido a um problema no Servidor. Pode ser que tenha dado uma exception, ou mesmo que o Serviço esteja fora do ar.

Exercício 1

Vamos fazer um exercício breve (guiado) para consumir um serviço via HTTP. Vamos verificar de onde é nosso Endereço IP.

Para começar:
  • cordova create geoip com.exemplo.geoip geoip
  • cordova platform add android
Depois, crie uma página HTML “index.html” de acordo com as orientações da Sessão 3, página 5. Lembre-se de copiar os arquivos do “jQuery” e do “jQuery Mobile” para dentro da pasta “js” (estão dentro da sessão 3 em “jquery-mobile”). E a CSS do jQuery Mobile para a pasta “css”.

Importe os projetos “geoip” e “geoip-CordovaLib” para sua Workspace. (“Inport / Android / Existing Android Code...”). Se der erro no projeto “geoip”, abra o “Build Path” e adicione o projeto “geoip-CordovaLib”.

Lembre-se de excluir os “Resource filters” para que os arquivos www apareçam.

O que você vai fazer? Vai acessar uma API REST que retorna um JSON. Pode usar o método $.getJSON, do jQuery:

console.log("************ Fazendo o request:");
$.getJSON( "http://freegeoip.net/json", function( data ) {
console.log("************ Dados recebidos: " ]
+ JSON.stringify(data));
$("#saida").text(JSON.stringify(data));
});


Eis o resultado esperado:


O serviço “freegeoip.net” retorna as informações geográficas de um endereço IP, e pode fazê-lo em formato JSON ou XML. Eis a url:

http://freegeoip/<formato>[/<endereço ip>]

O código de acesso ao Webservice deve ser incluído no evento “ondeviceready”, do Cordova.

Trabalhando o JSON

As respostas JSON são fáceis de trabalhar em Javascript. Elas já são Objetos e podemos usar suas propriedades e métodos. O objeto recebido foi mais ou menos esse:

{
"ip":"189.9.136.5",
"country_code":"BR",
"country_name":"Brazil",
"region_code":"",
"region_name":"",
"city":"",
"zipcode":"",
"latitude":-10,
"longitude":-55,
"metro_code":"",
"area_code":""
}

Logo, poderíamos ter trabalhado os valores individualmente:

console.log("@@@ País: " + data.country_name);

E podemos usar o método JSON.stringify() para transformar um Objeto em String.

Trabalhando com XML

Trabalhar com XML em plataforma móvel não é fácil. Nem todos os recursos estão disponíveis. Se você tiver oportunidade, evite trabalhar com XML a qualquer custo!

Isto posto, vamos ver com trabalhar com XML no ambiente móvel com jQuery. O serviço “freegeoip” também permite obter os dados em XML. Por exemplo:


Podemos trabalhar isso de maneira simples:

$.get("http://freegeoip.net/xml", function( data ) {
console.log("$$$$ " + $('CountryName',data).text());
}
);

Usamos o próprio seletor do jQuery para pegar o elemento “CountryName” de dentro da resposta XML, que estava na variável “data”. Usamos o método “text()” para obter o conteúdo do Tag.

Poderíamos ter navegado em todos os tags que estão dentro do tag inicial (<Response>) dessa forma:

$.get("http://freegeoip.net/xml", function( data ) {
$("Response", data).each(function() {
console.log("@@@@@@@@@@@@@@@@@@");
console.log("Ip: " + $(this).find("Ip").text());
console.log("CountryCode: " + $(this).find("CountryCode").text());
console.log("CountryName: " + $(this).find("CountryName").text());
});


Usamos o método “each()” dentro do tag “Response”, para navegar em todos os tags encontrados. Depois, para cada tag encontrado, mostramos o seu conteúdo textual.

O “find()” tem que ser executado no tag superior.

SOAP



SOAP é um protocolo de troca de informações, baseado na invocação remota de operações (métodos). Ele necessita de um formato específico de mensagem, descrito em um arquivo WSDL.


Antes de mais nada, instale o programa “soapui”:


Por que você vai precisar do SOAPUI? Porque você terá que montar o SOAP Envelope na mão!

Vamos criar um Projeto SOAP no SOAP UI e usar o serviço “Calculator”. Eis o endereço do WSDL:


Vamos invocar a soma (add):


Eis a Resposta:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <n:addResponse xmlns:n="http://www.parasoft.com/wsdl/calculator/">
         <n:Result xsi:type="xsd:float">700.0</n:Result>
      </n:addResponse>
   </soap:Body>
</soap:Envelope>

Como fazer isso em uma app?

Temos que montar o SOAP Envelope, enviar usando POST e depois analisar a resposta.

Montando o SOAP Envelope

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cal="http://www.parasoft.com/wsdl/calculator/">
   <soapenv:Header/>
   <soapenv:Body>
      <cal:add>
         <cal:x>500</cal:x>
         <cal:y>200</cal:y>
      </cal:add>
   </soapenv:Body>
</soapenv:Envelope>


Temos que colocar isso dentro de um String:

  var soapEnv = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'" 
            + " xmlns:cal='http://www.parasoft.com/wsdl/calculator/'>"
            + "<soapenv:Header/>"
            + "<soapenv:Body>"
            + "<cal:add>"
            + "<cal:x>" 
            + 500
            + "</cal:x>"
            + "<cal:y>"
            + 200
            + "</cal:y>"
            + "</cal:add>"
            + "</soapenv:Body>"
            + "</soapenv:Envelope>";


O Header SOAPAction


Note que o POST request tem um header SOAPAction, indicando qual é a operação desejada.

Algumas implementações de Web Services, especialmente usando SOAP 1.1, exigem a presença deste HEADER no POST.

Juntando tudo

Eis a implementação:

    receivedEvent: function(id) {
    var soapEnv = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'" 
            + " xmlns:cal='http://www.parasoft.com/wsdl/calculator/'>"
            + "<soapenv:Header/>"
            + "<soapenv:Body>"
            + "<cal:add>"
            + "<cal:x>" 
            + 500
            + "</cal:x>"
            + "<cal:y>"
            + 200
            + "</cal:y>"
            + "</cal:add>"
            + "</soapenv:Body>"
            + "</soapenv:Envelope>";

$.ajax({
             type: "POST",
             url: "http://ws1.parasoft.com/glue/calculator",
             headers: { 'SOAPAction': 'add' },
             contentType: "text/xml;charset=UTF-8",
             dataType: "xml",
             data: soapEnv,
             success: function(data, status, req) {
            console.log("****** OK: " + status);
            $("#saida").text($("Result",data).text());
             },
             error: function(request, status, error) {
            console.log("***** ERRO: " + status + " : " + error);
             }
         });
    }

E o resultado:


Notificações

Notificações “Push”, para Android e iOS, são providas por seus respectivos serviços de notificações:
Google Cloud Messaging


Apple Push Notification Service


Windows Phone Push Notification Service

A conectividade “Push” funciona sem necessidade do usuário acionar uma aplicação. Em seu dispositivo, um Serviço abre uma conexão com o Serviço de Notificações na Nuvem, e fica aguardando. Quando chega uma notificação, ela vem com o “id” da aplicação, logo, o Serviço de background consegue enviar a mensagem para a aplicação, mesmo que ela não esteja sendo executada.

Ao receber notificações, o dispositivo exibe uma lista:

Ao clicar em uma notificação, a aplicação correspondente será aberta, no “callback” de tratamento de notificações. Ela poderá fazer várias coisas, por exemplo abrir uma janela com a mensagem nova.

Badge number

No iOS é possível adicionar um “Badge number” ao ícone da aplicação, geralmente mostrando quantos novos itens estão disponíveis:

Nos dispositivos Android isso não é possível, pois a API não permite alterar o ícone de um aplicativo.

Porém, se for absolutamente necessário exibir esse tipo de informação, você pode criar um Widget para o usuário Android.

Em aparelhos Samsung, a interface TouchWiz permite criar Badge Numbers. A desvantagem é que só funciona com aparelhos Samsung.

O processo de notificação

Existe todo um processo para poder enviar notificações de “push” para os aparelhos. Para começar, é de bom tom perguntar ao usuário se o aplicativo pode lhe enviar notificações de “push”. Depois, é necessário registrar seu aplicativo nos serviços de “push” que deseja utilizar: GCM, APNs ou WPPNS .

Em segundo lugar, você terá que criar uma aplicação que enviará as mensagens ao Servidor de Notificações desejado. Essa aplicação se registrará no Servidor de Notificação (usando as credenciais que você registrou), e enviará as mensagens para os usuários que se registraram com a aplicação. Note que ele não enviará as mensagens diretamente aos aparelhos, mas para o Servidor do serviço de notificações apropriado.

Finalmente, sua aplicação deverá estar preparada para receber notificações de “push”, fornecendo um “callback” a ser executado quando as mesmas chegarem.

As mensagens de notificações não possuem garantia de entrega.


Sequência de passos para notificações

Toda aplicação que usa notificações de “push” deve ter pelo menos 4 partes:
  • Aplicação móvel;
  • Aplicação servidora (Servidor de Aplicação);
  • Banco de dados com o registro dos usuários;
  • Servidor de Notificação (Feito pela Google, Apple ou Microsoft).
A sequência seria mais ou menos esta:
  1. Usuário instala a aplicação;
  2. Aplicação se registra com o Servidor de Notificações (do provedor escolhido: GCM, APNs ou WPPNS);
  3. Aplicação envia ao Servidor de Aplicação a sua identificação, que é armazenada no banco de dados;
  4. Em um dado momento, o Servidor de Aplicação decide enviar notificação para um dos Clientes. Ele obtém a identificação do Banco de dados de registro;
  5. O Servidor de Aplicação contacta o Servidor de Notificações e solicita o envio de notificação para um ou mais registros de usuário;
  6. O Servidor de Notificação contata o dispositivo do usuário e envia a notificação;
  7. Um “callback” no dispositivo móvel registra a notificação e exibe na lista de notificações;
  8. Quando o usuário toca em uma notificação, a aplicação apropriada é acionada, e um “callback” é executado.
Dependendo do requisito, a aplicação pode fazer diversas coisas ao receber uma notificação:
  • Apagar o “Badge number”, se for o caso;
  • Enviar uma confirmação ao Servidor de Aplicação, para que este evite notificá-la novamente;

Notificações para aparelhos Android

Para enviar notificações para aparelhos Android, é necessário criar um projeto e se registrar no serviço Google Cloud Messaging.

Primeiro passo: Criar um projeto de uso de API Google

Acesse a Google Developers Console e crie um novo projeto:

Anote os campos “project ID” e “project Number”.

Na barra à esquerda, selecione “APIs & auth”.
Na lista de APIs, ligue a opção correspondente a “Google Cloud Messaging for Android”:


Segundo passo: Criar uma chave de API (API Key)
  1. No painel esquerdo, selecione: “APIs & auth” e “Credentials”;
  2. Dentro de “Public API access”, selecione “Create new key”;
  3. No diálogo “Create a new key”, selecione: “Server key”;
  4. No diálogo seguinte, informe o Endereço IP onde seu Servidor de Aplicação vai rodar. Para testar, deixe como: 0.0.0.0/0. ATENÇÃO: NUNCA COLOQUE EM PRODUÇÃO ASSIM!
  5. Selecione: “Create”;
  6. Na página a seguir, copie a “API key”. Você necessitará dela para autenticar seu Servidor de Aplicação com o Google Cloud Messaging.
Terceiro passo: Criar uma aplicação preparada para GCM


Este exemplo é baseado neste TUTORIAL.
  1. Crie uma aplicação PhoneGap (pode ser com os utilitários do Cordova);
  2. Adicione o “PushPlugin”:
    1. phonegap local plugin add https://github.com/phonegap-build/PushPlugin;
    2. Copie o arquivo “PushNotification.js”, que fica na pasta do projeto, dentro de “/plugins”, para a pasta raiz do projeto (que vai virar “assets/www”);
    3. Insira uma referência para o “PushNotification.js” dentro da sua página “index.html”:
      • <script type="text/javascript" src="PushNotification.js"></script>
  1. Registre a aplicação com o Google Cloud Messaging;
  2. Codifique o “callback” de sucesso no regisro da app;
  3. Codifique o “callback” de erro no registro da app;
  4. Codifique o “callback” a ser invocado quando chegarem mensagens para a app;

Importe o projeto “PushNotificationApp” para o eclipse.

Referência para o plugin (arquivo “index.html”):

<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript" src="PushNotification.js"></script>
<script type="text/javascript">
app.initialize();
</script>

Código que registra a app no GCM (arquivo “index.js”):

receivedEvent: function(id) {
    var pushNotification = window.plugins.pushNotification;
    pushNotification.register(
app.successHandler, 
app.errorHandler,
{"senderID":"954182423629",

"ecb":"app.onNotificationGCM"});

Você não precisa registrar sua app a todo momento no GCM. Pode fazer isso e armazenar em um Banco local, testando sempre se a tabela está vazia.

Os argumentos são:
  • app.successHandler : “callback” de sucesso no registro com o GCM;
  • app.errorHandler : “callback” de erro no registro com o GCM;
  • {"senderID":"954182423629","ecb":"app.onNotificationGCM"}: O “SenderID” é o seu “ProjectID”, recebido quando você criou o projeto na Google Developers Console, e o “ecb” é o “callback” que será invocado quando chegar uma mensagem;

Processamento de mensagens

O código que processa mensagens é bem simples:

onNotificationGCM: function(e) {
    switch( e.event )
    {
        case 'registered':
            if ( e.regid.length > 0 )
            {
                console.log("Regid " + e.regid);
                alert('registration id = '+e.regid);
            }
        break;

        case 'message':
          // this is the actual push notification. its format depends on the
   //data model from the push server
          alert('message = '+e.message+' msgcnt = '+e.msgcnt);
        break;

        case 'error':
          alert('GCM error = '+e.msg);
        break;

        default:
          alert('An unknown GCM event has occurred');
          break;
    }
}

Vários eventos podem ocorrer quando chega uma mensagem:
  • “registered”: Quando a aplicação se registrou com o GCM;
  • “message”: Quando uma mensagem foi recebida;
  • “error”: Quando ocorreu um erro.
O que vem dentro da mensagem e o que a sua aplicação vai fazer com ela, depende muito da sua funcionalidade. O normal é abrir uma tela com os dados da mensagem nova.

O Servidor de Aplicação

Para este tutorial, usamos um servidor Node.js (arquivo “notify.js”):

// notify.js

var gcm = require('node-gcm');
var message = new gcm.Message();

//API Server Key
var sender = new gcm.Sender('**** API KEY GCM ****');
var registrationIds = [];

// Value the payload data to send...
message.addData('message',"\u270C Peace, Love \u2764 and PhoneGap \u2706!");
message.addData('title','Push Notification Sample' );
message.addData('msgcnt','3'); // Shows up in the notification in the status bar
message.addData('soundname','beep.wav'); //Sound to play upon notification receipt - put in the www folder in app
message.collapseKey = 'demo';
message.delayWhileIdle = true; //Default is false
message.timeToLive = 3000;// Duration in seconds to hold in GCM and retry before timing out. Default 4 weeks (2,419,200 seconds) if not specified.

// At least one reg id required
registrationIds.push("APA91bG_WLY0gUm872ez4tOD3Bc7tIiIJr6DoQHh0YT2LKOZIgCDxqOA3hnrDrGBN4mI53PWCfuCuWfolAvZ2AfZP5hTBbRGI5X6dD9X8mVatP8C0d40L_WnW_y-N_W2sorNfN7XlVAVn-AOco4W4DztoVplNbYuI7vj5z6DKVxurtn409VEZZc");

/**
 * Parameters: message-literal, registrationIds-array, No. of retries, callback-function
 */
sender.send(message, registrationIds, 4, function (err, result) {
    if(err) {
      console.log('Erro: ' + err);
    }
    if (result) {
      console.log('Result: ' + JSON.stringify(result));
    }
    else {
      console.log('Result nulo');
    }
});

O ideal é termos um Webservice que a aplicação invoque para informar seu “Registration Id” (obtido quando ela se registra no GCM). Então, armazenamos esse Registration Id em um banco de dados.


Vamos criar outra app

Vamos criar uma app que permita ao usuário selecionar uma cidade e mostre o clima atual. Para isto, vamos usar o Webservice SOAP/XML “Global Weather”, para listar o clima da cidade desejada. (resposta em “exercicio2.zip”).


É importante que você tente seguir exatamente todos os passos e teste a aplicação!


Eis as duas telas da aplicação:



Abra o site do Webservice e veja como ele funciona.
WSDL: http://www.webservicex.net/stockquote.asmx?WSDL
Site: http://www.webservicex.com/ws/WSDetails.aspx?CATID=12&WSID=56




Usando o “soapui”, fiz um request para a operação “GetWeather”. Eis o request e o response:

Request:


POST http://www.webservicex.com/globalweather.asmx HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://www.webserviceX.NET/GetWeather"
Content-Length: 406
Host: www.webservicex.com
Connection: Keep-Alive

User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

SOAP Envelope:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET">
   <soapenv:Header/>
   <soapenv:Body>
      <web:GetWeather>
         <!--Optional:-->
         <web:CityName>Rio de Janeiro</web:CityName>
         <!--Optional:-->
         <web:CountryName>Brazil</web:CountryName>
      </web:GetWeather>
   </soapenv:Body>
</soapenv:Envelope>

Response (SOAP Envelope): 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <GetWeatherResponse xmlns="http://www.webserviceX.NET">
         <GetWeatherResult><![CDATA[<?xml version="1.0" encoding="utf-16"?>
<CurrentWeather>
  <Location>Rio De Janeiro Aeroporto , Brazil (SBRJ) 22-54S 043-10W</Location>
  <Time>Sep 23, 2014 - 06:00 AM EDT / 2014.09.23 1000 UTC</Time>
  <Wind> from the N (350 degrees) at 5 MPH (4 KT):0</Wind>
  <Visibility> greater than 7 mile(s):0</Visibility>
  <Temperature> 66 F (19 C)</Temperature>
  <DewPoint> 62 F (17 C)</DewPoint>
  <RelativeHumidity> 88%</RelativeHumidity>
  <Pressure> 30.15 in. Hg (1021 hPa)</Pressure>
  <Status>Success</Status>
</CurrentWeather>]]></GetWeatherResult>
      </GetWeatherResponse>
   </soap:Body>
</soap:Envelope>

O elemento <GetWeatherResult> é um CDATA, logo, é apenas um “string” e não um documento XML, apesar de estar formatado como tal. Ele será recebido com “&lt;” e “&gt;” ao invés dos caracteres: “<” e “>”. Veja o exemplo “raw”:


HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 23 Sep 2014 10:52:15 GMT
Content-Length: 1023
Connection: keep-alive

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NET"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;CurrentWeather&gt;
&lt;Location&gt;Rio De Janeiro Aeroporto , Brazil (SBRJ) 22-54S 043-10W&lt;/Location&gt;
&lt;Time&gt;Sep 23, 2014 - 06:00 AM EDT / 2014.09.23 1000 UTC&lt;/Time&gt;
&lt;Wind&gt; from the N (350 degrees) at 5 MPH (4 KT):0&lt;/Wind&gt;
&lt;Visibility&gt; greater than 7 mile(s):0&lt;/Visibility&gt;
&lt;Temperature&gt; 66 F (19 C)&lt;/Temperature&gt;
&lt;DewPoint&gt; 62 F (17 C)&lt;/DewPoint&gt;
&lt;RelativeHumidity&gt; 88%&lt;/RelativeHumidity&gt;
&lt;Pressure&gt; 30.15 in. Hg (1021 hPa)&lt;/Pressure&gt;
&lt;Status&gt;Success&lt;/Status&gt;
&lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope>



HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 23 Sep 2014 10:52:15 GMT
Content-Length: 1023
Connection: keep-alive

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NET"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;CurrentWeather&gt;
&lt;Location&gt;Rio De Janeiro Aeroporto , Brazil (SBRJ) 22-54S 043-10W&lt;/Location&gt;
&lt;Time&gt;Sep 23, 2014 - 06:00 AM EDT / 2014.09.23 1000 UTC&lt;/Time&gt;
&lt;Wind&gt; from the N (350 degrees) at 5 MPH (4 KT):0&lt;/Wind&gt;
&lt;Visibility&gt; greater than 7 mile(s):0&lt;/Visibility&gt;
&lt;Temperature&gt; 66 F (19 C)&lt;/Temperature&gt;
&lt;DewPoint&gt; 62 F (17 C)&lt;/DewPoint&gt;
&lt;RelativeHumidity&gt; 88%&lt;/RelativeHumidity&gt;
&lt;Pressure&gt; 30.15 in. Hg (1021 hPa)&lt;/Pressure&gt;
&lt;Status&gt;Success&lt;/Status&gt;
&lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope>



Então, podemos pegar o conteúdo textual do tag “GetWeatherResult” e transformar em XML:
  • Substituímos os “&lt;” e “&gt;” por “<” e “>”, respectivamente;
  • Convertemos o string resultante em XML;
  • Pegamos os elementos dentro de “<Location>”.



Passo 1: Crie uma aplicação e importe para o eclipse;

Passo 2: Adapte o “index.js” e o “index.html”:
Devem ficar como o exemplo da página 5 da sessão 3. Só que copie os scripts de “jquery-mobile” para dentro de “assets/www/js” e modifique a referência dentro de “index.html” para usar as cópias locais.

Passo 3: Crie as “pages” do exercício:

Página “inicial”, com um form:

     <div data-role="page" id="inicial">
<div data-role="header">Clima global</div>
<div role="main" class="ui-content">
           <h1>Selecione a cidade</h1>
           <form id="formcidade" action="#">
           <label for="cidade" class="select">Cidade:</label>
<select name="cidade" id="cidade" required 
title="Selecione uma cidade!">
   <option value="" selected>[selecione]</option>
   <option value="Rio de Janeiro" >Rio de
Janeiro</option>
   <option value="Sao Paulo">São Paulo</option>
   <option value="Belo Horizonte">Belo
Horizonte</option>
   <option value="Brasilia">Brasília</option>
   <option value="Porto Alegre">Porto Alegre</option>
   <option value="Fortaleza">Fortaleza</option>
</select>
<input type="submit" value="Obter" />
           </form>
</div>
<div data-role="footer"></div>
</div>

Teremos validação nesse form, logo, o script “jquery.validate.min.js” deve ser incluído no arquivo “index.html”.

Agora, uma página “resultado” para exibir uma listview:


         <div data-role="page" id="resultado">

<div data-role="header"><h1>Resultado</h1></div>

<div role="main" class="ui-content">
<div>
<ul id="saida" data-role="listview">
</ul>
</div>
</div>
<div data-role="footer"><hr/></div>
<a href="#inicial" data-transition="slidefade" 
class="ui-btn">Fechar</a>
</div>

Passo 4: Crie, no arquivo “index.js”, os códigos de validação e submit:
Intercepte o evento “ready” do documento:
  • $(document).ready(function() {…});
A primeira coisa a fazer no “callback” é validar o form:
  • $("#formcidade").validate();
Agora, temos que interceptar o evento “submit” do form:
  • $( "#formcidade" ).submit(function( event ) {
A primeira coisa que faremos dentro do “submit” é verificar se o form está válido:
  • event.preventDefault();
  • if($("#formcidade").valid()) {
O “preventDevault()” é para impedir o comportamento normal de subissão do formulário, já que vamos tratar tudo nós mesmos.

Até agora, temos isso:

         $(document).ready(function() {
$("#formcidade").validate();
$( "#formcidade" ).submit(function( event ) {
event.preventDefault();
if($("#formcidade").valid()) {
}
);
         });

Passo 5: Monte o “SOAPEnvelope” para invocar o serviço:


var soapEnv =
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
+ " xmlns:xsd='http://www.w3.org/2001/XMLSchema' "
+ " xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ "<soap:Body>"
+ "<GetWeather xmlns='http://www.webserviceX.NET'>"
+ "<CityName>"
+ $("#cidade").val()
+ "</CityName>"
+ "<CountryName>Brazil</CountryName>"
+ "</GetWeather>"
+ "</soap:Body>"
+ "</soap:Envelope>"; 

Passso 6: Monte a estrutura do comando $.ajax, para poder obter os dados do clima:


$.ajax({
type: "POST",
url: "http://www.webservicex.net/globalweather.asmx",
headers: { 'SOAPAction':
'http://www.webserviceX.NET/GetWeather' },
contentType: "text/xml;charset=UTF-8",
dataType: "xml",
data: soapEnv,
beforeSend: function() {
},
complete: function() {^
},
success: function(data, status, req) {
},
error: function(request, status, error) {
});

  • A URL do Webservice é obtida do “soapui”. Temos que acrescentar o HEADER HTTP: “SOAPAction”, conforme indicado no “soapui”;
  • Depois, temos que enviar o “soapEnv” que formatamos, dentro da propriedade “data”;
  • A propriedade “beforeSend” é um “callback” que será invocado imediatamente ANTES do request ser enviado. Podemos usar para exibir uma mensagem do tipo: “aguarde...”;
  • A propriedade “complete” é um “callback” que será invocado quando o request terminar. Podemos usá-la para fechar a mensagem “aguarde..”;
  • A propriedade “success” é um “callback” que será invocado quando o request terminar e os dados chegarem. Aqui, temos que trabalhar o XML e inserí-lo na <UL> da página “resultado”;
  • A propriedade “error” é um “callback” que será invocado em caso de erro;


Passo 7: Mensagem de “aguarde...”


Podemos usar o Widget “loading”, do jQuery Mobile, para exibir uma mensagem:  

beforeSend: function() {
$.mobile.loading( 'show', {
text: 'carregando...',
textVisible: true,
textonly: true,
theme: 'b',
html: ""
});
} ,
complete: function() {
$.mobile.loading('hide');
},

Passo 8: Callback de sucesso:

Aqui temos que fazer o seguinte:
  1. Obter o tag “<GetWeatherResponse>”;
  2. Navegar nos seus tags internos (só tem um);
  3. Pegar o conteúdo textual;
  4. Transformar o string em XML;
  5. Navegar no resultado e montar a listview;
Obter o tag “<GetWeatherResponse>”
var response = $(data).find("GetWeatherResponse");

Navegar nos seus tags internos
$(response).each(function() {
});

Pegar o conteúdo textual:
var respostaString = “”;
$(response).each(function() {
respostaString += $(this).text();
});

Transformar o String em XML:
respostaString = respostaString.replace("&lt;", "<");
respostaString = respostaString.replace("&gt;", ">");
var respostaXml = $.parseXML(respostaString);


Navegar no resultado e montar a listview:


        var resultHtml = “”;

        $('CurrentWeather', respostaXml).each(function() {

            resultHtml += "<li>Localização: " 
    + $(this).find("Location").text() + "</li>";
// … (outros campos)
       });
       $("#saida").html(resultHtml);
       $("#saida").listview().listview("refresh");
       $("#formcidade").get(0).reset();
       $.mobile.navigate( "#resultado", {transition: 'pop', role: "dialog" } );

Algumas notas... Para começar, eu não coloquei todos os campos, logo, você terá que fazer isso. Em segundo lugar, estamos jogando o resultado (um conjunto de tags “<li>”) dentro de uma <ul> “listview”. O comando de “refresh” é estranho, mas é assim mesmo:
$("#saida").listview().listview("refresh");

Como a página onde está esta <ul> não está carregada, temos que inicializar a “listview”. Daí a primeira referência ao método “listview()”. Então, temos que invocar o método “refresh” da listview, pois acabamos de inserir novos dados, daí a segunda chamada do método “listview()”.

Limpamos o form com o comando:
$("#formcidade").get(0).reset();

E navegamos (por Javascript) para o diálogo “resultado”, cuja “listview” nós preenchemos.

Não se esqueça de criar o “callback” de erro!