Esse artigo faz parte da revista WebMobile edição 09. Clique aqui para ler todos os artigos desta edição

Capa_WM09_M.gif

Clique aqui para ler este artigo em PDFimagem_pdf.jpg

Fazendo backup da agenda de contatos na web

Aprenda a utilizar a API de PIM para gravar e recuperar os contatos de um aparelho celular em um servidor Web

Após a satisfação pela aquisição de um novo aparelho celular vem sempre uma tarefa chata: a migração das informações do aparelho antigo para o novo. A tecnologia GSM, através dos chips SIM, resolveu parcialmente esse problema, pois as informações ficam gravadas nesse cartão. Contudo, caso o SIM Card seja danificado ou perdido, o usuário terá perdido estas informações de vez, tendo que informá-las novamente.

Uma possível solução para esse problema seria realizar o backup dessas informações em um local diferente do aparelho e possibilitar a importação desse backup quando necessário. Mas como por esta solução em prática? A API de PIM (Personal Information Management), que faz parte da JSR 75, existe para nos auxiliar nesta tarefa.

Este artigo tem como objetivo demonstrar a um desenvolvedor como funciona esta API. Através do desenvolvimento de uma aplicação J2ME vamos mostrar o que deve ser feito para realizar o backup da agenda de contatos de um aparelho celular em um servidor web usando um Servlet. Ao final, o desenvolvedor não só terá os conhecimentos necessários para trabalhar com a PIM API como também saberá como fazer uma MIDlet se comunicar com um Servlet.

Para a melhor compreensão deste artigo, o desenvolvedor deve ter conhecimentos básicos de J2ME e de desenvolvimento de servlets.

Introdução à JSR75 e à PIM API

A JSR 75 também conhecida por PDA Optional Packages for the J2ME Platform, ou apenas PDAProfile, foi uma das primeiras JSRs para a plataforma J2ME a ter uma implementação de referência (Nota 1) disponibilizada pela IBM em maio de 2004.

Definida como um perfil J2ME composto por duas APIs opcionais – FileConnection e PIM – ela oferece ao desenvolvedor meios para manipular o sistema de arquivos do celular e de gerenciar suas informações pessoais. Sendo estas últimas compostas por Contatos, Eventos e Tarefas. Como já foi dito, estas duas APIs são opcionais, ou seja, um dispositivo que diz implementar a JSR75 pode ter apenas uma delas, sendo necessário informar qual está disponível.

 

Nota 1. O que é uma implementação de referência?

Quando se obtém um documento de especificação final para uma JSR, normalmente algum dos participantes do Expert Group – grupo de usuários/entidades que participaram da especificação da JSR – toma a frente para implementar pela primeira vez a API. Esta implementação é conhecida como implementação de referência, pois deverá seguir estritamente os requisitos definidos na documentação e servirá de modelo para futuras implementações além de já servir aos desenvolvedores como API para desenvolvimento.

Juntamente com o lançamento da implementação de referência, ou logo após, o mesmo grupo que a criou lança um TCK (Technology Compatibility Kit), que, como sugere o próprio nome, é uma suite de testes focados na JSR em questão, baseando-se tanto na especificação quanto na implementação de referência.

 

A PIM API pode ser vista como uma das APIs de melhor visibilidade junto aos usuários comuns de telefones celulares. Afinal, para estes o aparelho serve apenas para falar e armazenar os números de telefones mais utilizados, não importando se ele é capaz de rodar jogos com gráficos 3D, ou de executar aplicativos com funções avançadas – controle de estoque, planilha de custos, etc. Dessa forma, desenvolver aplicações que façam uso desta API significa desenvolver aplicações com grandes chances de serem utilizadas por usuários comuns.

Apesar de o maior atrativo ser a manipulação dos Contatos, não devemos esquecer que também nos é dada a chance de manipular os Eventos e as Tarefas do aparelho. Entenda que a diferença entre as duas categorias é mínima, pois Eventos são Tarefas que possuem algum tipo de repetição, ou não. Além disso, os primeiros passam a idéia de algo que vai acontecer, e pode se repetir, enquanto que os últimos representam objetivos a serem cumpridos.

Na documentação da API você descobrirá que para representar cada item de uma dessas categorias – Contatos, Eventos e Tarefas – foram utilizados padrões já existentes e bem conhecidos: vCard para Contatos e vCalendar para Eventos e Tarefas. Estes padrões são utilizados pela maioria dos gerenciadores de informações pessoais, tais como Microsoft Outlook, KDEPim & KDEOrganizer e Mozilla Firebird. Você poderá encontrar maiores informações sobre estes padrões no site do IMC – Internet Mail Consortium, na seção Personal Data Interchange (ver Links). Para uma visão geral da API, observe a Figura 1.

 

image002.jpg

Figura 1. PIM API.

Objetivos da aplicação

Para demonstrar as principais funcionalidades fornecidas por essa API, vamos descrever como implementar uma aplicação de exemplo que deverá fazer o backup da agenda de contatos de um aparelho celular em um servidor web.

A plataforma a ser utilizada no aparelho celular é bastante óbvia no nosso caso, que é J2ME, mas qual tecnologia usaremos no servidor web? Para começar, vamos pensar em como será esse acesso ao servidor. Ele deve ser simples, rápido e não precisa ter uma interface gráfica, afinal a comunicação será feita apenas entre a aplicação J2ME e o servidor, não necessitando a intervenção de usuário.

Dessa forma, podemos escolher o modelo de Servlets, pois ele supre todas essas nossas necessidades, como poderemos ver ao longo do artigo.

Mantendo uma visão de alto nível podemos pensar na seguinte forma de interação entre usuário e sistema: o usuário iniciará a aplicação, que poderá mostrar uma splash screen para só depois chegar à tela principal, a qual exibirá a listagem dos contatos além de disponibilizar duas opções de comandos, exportar e importar.

Após o usuário selecionar uma das opções, a MIDlet entrará em contato com o Servlet informando qual operação deverá ser executada e em seguida receberá uma resposta – sucesso ou falha – que será exibida para o usuário.

Pensando em um pouco mais de detalhes, podemos fazer a seguinte pergunta: será que só devemos ter essas duas opções para os contatos, exportar e importar? E quanto a detalhar e editar um contato? Nesse caso, devemos nos lembrar que estas funções já são providas pelo aparelho celular, e mesmo que nós as implementássemos de uma forma melhor que as nativas, dificilmente o usuário as utilizaria, dado o costume de uso. Pensando assim, podemos deixar essas funcionalidades de fora – ao menos desta versão – já que não seriam de suma importância para a aplicação e sairia um pouco do escopo deste artigo, que é de apresentar a API.

Após a definição acima, temos também que definir como se dará a comunicação MIDlet/Servlet. E tomando como base o artigo “Usando conexões HTTP em um jogo J2ME/MIDP” – edição número três da revista WebMobile – podemos chegar à conclusão de que a comunicação será mais eficiente se utilizarmos um protocolo com base em valores hexadecimais.

Nesse artigo foi apresentado um modelo de comunicação MIDlet/Servlet que utiliza o Generic Connection Framework do MIDP, mais especificamente a interface HttpConnection, para montar uma requisição HTTP contendo dados hexadecimais para minimizar o volume de informações trocadas. A conexão feita com o servidor é feita em threads separadas para não bloquear a aplicação para o usuário. No servidor web, o Servlet utiliza os seus métodos doPost e doGet para ler as informações enviadas pela MIDlet e executar as operações necessárias.

O nosso Servlet funcionará tal qual o Servlet do artigo citado acima, tendo este comportamento descrito a seguir. A MIDlet utilizará uma requisição do tipo POST para enviar os dados, e uma do tipo GET para recuperá-los. Tudo isso usando o mesmo protocolo hexadecimal. Ao receber uma requisição POST (exportação), o Servlet recuperará os dados e os persistirá em um arquivo texto – esta abordagem é a mais adequada, uma vez que arquivos XML ou banco de dados adicionariam uma complexidade desnecessária, haja vista o baixo volume de dados. Já com uma requisição GET ele fará o caminho inverso, lendo do arquivo texto e enviando os dados conforme tenha sido definido pelo protocolo.

Um detalhe que ainda não pensamos é sobre a importação de contatos. Como devemos tratar os contatos existentes no celular no momento em que recebermos os itens do Servlet? Esses contatos deverão ser sobrescritos? Deveremos dar opções para resolução de conflitos? Ou eles deverão ser adicionados ao final da lista atual? A solução que vamos adotar é a mais simples, a sobrescrita dos contatos. Mais uma vez, as outras soluções poderão ser adotadas em uma outra versão da aplicação.

Modelando a aplicação

Tendo em mente o esquema de funcionamento da aplicação, podemos começar a sua modelagem.

 

Aplicação J2ME

A classe principal da nossa aplicação J2ME se chamará AgendaBackupMIDlet, e como sabemos herda da classe MIDlet. Devemos ter em mente também que as aplicações J2ME tendem a ser menos orientadas a objetos em virtude dos recursos limitados da plataforma. Evitar criar classes desnecessárias, utilizar acesso direto a atributos da classe – não usar métodos bean – são algumas das recomendações já conhecidas. Porém, como a nossa aplicação terá um tamanho bastante pequeno, nós vamos adotar outra postura na modelagem, como pode ser visto na Figura 2.

 

image004.jpg

Figura 2. Diagrama de Classes da MIDlet Suíte.

As classes descritas na Figura 2 foram modeladas pensando nas telas a serem exibidas, além da necessidade de uma representação de um contato a ser utilizada para exibição. Entenda que esta necessidade vem do fato de que a manipulação de um Contact da PIM API é bastante trabalhosa, pois implica sempre na verificação da existência dos campos desejados. Portanto, a melhor solução deve ser criar uma classe que vá representar um contato, de forma que a leitura dos dados originais só necessite ser feita uma vez, para fornecer parâmetros para o construtor da nossa classe. Feito isso, o acesso aos dados será bem mais prático.

Começando pela MIDlet, que tem como função gerenciar todo o fluxo e exibição de dados, e tirando os métodos já conhecidos – herdados da super classe e implementados pela interface – podemos começar pelos métodos geraTelaListSelection e geraTelaContactsList(boolean).

Esses métodos são responsáveis por criar e exibir as telas de seleção de lista de contatos e de exibição de contatos. A tela de seleção de contatos foi implementada de forma a poupar recursos uma vez que é gerada apenas quando ainda não tiver sido gerada. Já a tela de lista de contatos tem a sua criação forçada após a ocorrência de uma importação.

Além da exibição de tela, nós encontramos duas inner classes e métodos responsáveis pelas operações de importação e exportação. Observe que eles são implementados através de threads para executar conexões HTTP.

Tanto a classe ListSelection quanto a classe ContactsList herdam de javax.microedition.lcdui.List e ambas possuem comportamento e função similar. Enquanto que a primeira servirá para a escolha da lista de contatos (Nota 2), a segunda exibirá os contatos e os comandos de exportar e importar.

 

Nota 2. Seleção da listas disponíveis

A API de PIM especifica que um dispositivo pode possuir mais de uma lista para cada tipo de informação pessoal (Contatos, Eventos e Tarefas) e, com isso, pode não existir apenas uma lista de contatos no dispositivo (no caso dos telefones celulares pode haver uma na memória do aparelho e outra no SIMCard). Se o desenvolvedor assim desejar e não especificar qual lista ele quer acessar – utilizando o método PIM.openPIMList(int,int) – a implementação se encarregará de lhe passar a lista default. Porém, é conveniente oferecer ao usuário a chance de escolher qual das listas de determinado tipo ele deseja carregar, através do método PIM.listPIMLists(int) que retorna um array de Strings com os nomes de cada lista. E uma vez escolhido nome de uma lista, deve-se utilizar o método PIM.openPIMList(int, int, String) para abri-la.

 

Observe que estas classes possuem métodos que retornam objetos do tipo Command, que servirão para fornecer acesso público aos seus atributos privados, permitindo assim que a MIDlet utilize-os no controle de fluxo da aplicação – dentro do método AgendaBackupMIDlet.commandAction(Displayable, Command). Esta abordagem foi tomada para diminuir a complexidade de código da MIDlet, apesar de ir de encontro com a recomendação já citada de poupar o máximo de recursos.

Finalmente, temos a classe Contato que auxiliará na manipulação dos dados de cada contato. Observe os seus métodos geter e seter bem como seu construtor, que recebe todos os dados necessários para criar um contato. Neste ponto temos que decidir quais dados serão importantes para nós, uma vez que a API nos dá 18 campos possíveis para um contato – sendo dois deles, NAME e ADDR, do tipo Array, contendo alguns sub campos. Mais uma vez pensando na simplicidade, vamos optar por Nome, Sobrenome e Telefone – respectivamente Contact.NAME_GIVEN, Contact.NAME_FAMILY, Contact.TEL.

 

Aplicação web

Continuando com a idéia de simplicidade, chegamos à nossa aplicação web. Agora é possível termos apenas uma classe, o Servlet, que tratará as requisições da MIDlet e persistirá e lerá os dados do arquivo texto. Observando a Figura 3, teremos além dos métodos doGet e doPost, os métodos responsáveis pela carga e persistência dos contatos, carregarContatos e salvarContatos.

 

image006.jpg

Figura 3. Diagrama de Classes da aplicação Web.

Implementação

Antes de detalharmos a implementação, vamos apresentar o ambiente utilizado, lembrando sempre que o foco deste artigo não é o detalhamento da instalação e configuração das ferramentas.

A escolha para IDE foi o Eclipse 3.1.2, juntamente com os plugins EclipseME (para trabalhar com aplicações J2ME) e Sysdeo (para controlar o servlet container Tomcat). Como ambiente para execução de Servlets utilizaremos o Tomcat, e finalmente o ambiente J2ME foi provido pelo Sun Wireless Toolkit 2.3 Beta. Vale ressaltar que este último possui várias JSRs disponíveis, inclusive a 75. Porém, verifique se ao criar o novo projeto J2ME, utilizando o toolkit já citado, a biblioteca jsr75.jar está presente no buildpath do projeto.

 

MIDlet Suite

Obedecendo a nossa modelagem, vamos começar criando a classe AgendaBackupMIDlet.

A primeira tela funcional da nossa aplicação, a listagem de Listas de Contatos, deverá ser exibida fazendo uso do método geraTelaListSelection() (Listagem 1). Este, por sua vez, repassa a criação desta tela para o construtor da classe ListSelection(CommandListener) (Listagem 2), onde é criada uma lista implícita que tem seus items populados a partir de um array de String contendo os nomes de todas as listas de contatos existentes, nomes estes recuperados através do método PIM.getInstance().listPIMLists(PIM.CONTACT_LIST), que retorna os nomes de todas as listas disponíveis para o tipo informado (PIM.CONTACT_LIST).

 

Listagem 1.  AgendaBackupMIDlet.java.

334              private Displayable geraTelaListSelection() {

335

336                       if (listaSelecao == null) {

337                                 listaSelecao = new ListSelection(this);

338                       }

339

340                       return listaSelecao;

341              }

 

Listagem 2.  ListSelection.java.

17                public ListSelection(CommandListener midlet){

18                         super("Escolha uma das listas", List.IMPLICIT);

19                         this.addCommand(cmdOk);

20                         this.addCommand(cmdCancel);

21                         this.setCommandListener(midlet);

22

23                         String[] pimLists = PIM.getInstance().listPIMLists(PIM.CONTACT_LIST);

24

25                         for (int i = 0; i < pimLists.length; i++) {

26                                  this.append(pimLists[i],null);

27                         }

28                }

 

Agora com uma tela para exibição, nós devemos cuidar de seus comandos. Teremos duas opções, Cancelar – que fechará a aplicação – e Ok. Este último deverá nos levar à segunda tela, onde poderemos ver a listagem dos contatos pertencentes à lista selecionada. Analogamente ao outro método de criação de tela, este – geraTelaContactsList(boolean) (Listagem 3) – também verifica se a tela é nula além de verificar se uma recarga dos contatos é necessária.

 

Listagem 3.  AgendaBackupMIDlet.java.

343              private Displayable geraTelaContactsList(boolean forceReload) {

344

345                       if (listaContatos == null || forceReload) {

346                                 listaContatos = new ContactsList(this, listaSelecao

347                                                    .getString(listaSelecao.getSelectedIndex()));

348                       }

349

...

Quer ler esse conteúdo completo? Tenha acesso completo