Faça sua primeira aplicação J2ME – Parte 02

Descrição da Aplicação

A aplicação CompanyExtensions (Ramais da empresa) a ser desenvolvida neste artigo mostra uma lista contendo os nomes de todos funcionários de uma empresa. Através da lista é possível selecionar um funcionário e escolher a opção “View” para visualizar seu ramal, ou “Exit” para sair da aplicação. Dentro da tela de visualização de um ramal é possível iniciar uma chamada para o ramal selecionado ou retornar para a lista. A Figura 3 ilustra as telas da aplicação.

 

wm-20-05-2008pic01.JPG 

Figura 3. Telas do CompanyExtensions.

 

É claro que você não deseja digitar todos os nomes e ramais no celular. A aplicação deverá ser capaz de ler essa informação. É comum encontrar na intranet das empresas uma homepage ou planilha eletrônica contendo a lista de funcionários e seus respectivos ramais.  Para esse artigo, vamos considerar que é possível extrair manualmente essas informações para um arquivo texto a ser importado pela aplicação. Tal arquivo deve conter duas colunas (ramal e nome) separadas por espaço em branco. Um exemplo pode ser observado na Listagem 1.

 

092444555 Hallyson Melo

092333222 Lidia Vasti

Listagem 1. Arquivo ramais.txt.

 

A aplicação possui apenas quatro classes:

 

1.        Extension: classe entidade que encapsula o nome e ramal de um funcionário;

2.        ExtensionView: classe de interface gráfica para visualizar um Extension;

3.        FileHandler: classe utilitária para ler o arquivo ramais.txt e converter seu conteúdo em um array de objetos Extension;

4.        CompanyExtensions: classe MIDlet responsável pelo controle do display e pela manipulação dos botões (comandos) adicionados as telas.

 

Outras classes poderiam ter sido criadas, por exemplo, ExtensionList para encapsular o array de objetos Extension. Nessa classe poderia ser adicionado o método search() para localizar um funcionário pelo nome. Outra classe poderia ser criada para encapsular o objeto gráfico que lista os nomes dos funcionários.

Veremos a partir de agora como implementamos cada uma dessas classes.

Classe Extension

A classe Extension é uma classe de entidade representando um ramal e pode ser vista na Listagem 2. Como a aplicação não permite a criação ou modificação de um ramal, a classe possui apenas métodos get.

 

// Arquivo Extension.java

 

public class Extension {

   private String name;

   private String phone;

 

   public Extension(String name, String phone) {

      this.name = name;

      this.phone = phone;

   }

 

   public String getName() {

      return name;

   }

 

   public String getPhone() {

      return phone;

   }

}

Listagem 2. Classe Extension.

Leitura dos ramais

A classe FileHandler (apresentada na Listagem 3) é responsável pela leitura do arquivo txt e conversão do texto em objetos Extension. O método estático loadExtensions() retorna um array contendo os objetos Extension encontrados no arquivo.

Há três formas de ler um arquivo usando J2ME. A primeira é usando APIs proprietárias dos fabricantes, a segunda é utilizando a PIM API (JSR75) através da interface FileConnection, e a última é utilizando o método Class.getResourceAsStream() que existe desde a especificação MIDP 1.0.

Para deixar a aplicação mais portável, usaremos a última opção, com a desvantagem que deveremos gerar o arquivo “JAR” sempre que a lista de ramais for modificada. Claro, é possível conectar o celular na Internet e fazer o download da lista de ramais, apenas modificando o método loadExtensions(). Caso você deseje adicionar essa funcionalidade, dê uma olhada na documentação do pacote javax.microedition.io.

 

import java.util.Vector;

import java.io.*;

 

class FileHandler {

 

   private static final String FILENAME = "/ramais.txt";

 

   private FileHandler() { }

 

   static Extension[] loadExtensions() throws IOException {

      String data = openFileAsString(FILENAME);

 

      return parseText(data);

   }

 

   private static String openFileAsString(String fileName) throws IOException {

 

      Class classObject = fileName.getClass();

      InputStream is = classObject.getResourceAsStream(fileName);

 

      byte[] data = new byte[is.available()];

      is.read(data, 0, data.length);

      is.close();

 

      return new String(data);

   }

 

   private static Extension[] parseText(byte[] data) {

 

      Vector vtExtensions = new Vector();

 

      int position = 0;

 

      String name = null;

      String extension = null;

 

      while (position < text.length()) {

         extension = text.substring(position, text.indexOf(" ", position));

         position += extension.length() + 1;

 

         name = text.substring(position, text.indexOf("\n", position) - 1);

         position += name.length() + 2;

 

         vtExtensions.addElement(new Extension(name, extension));

      }

 

      Extension[] extensions = new Extension[vtExtensions.size()];

      vtExtensions.copyInto(extensions);

 

      return extensions;

   }

}

Listagem 3. Classe FileHandler.

 

O método privado openFileAsString() é responsável por abrir o arquivo e retornar o conteúdo como um único String. Essa tarefa é feita através do método Class.getResourceAsStream(), que retorna um objeto java.io.InputStream. Esse objeto encapsula os bytes obtidos do arquivo. Então usamos o método read() para obter o array de bytes que é convertido em texto através do construtor String(byte[]).

Finalmente, o método parseText() faz uso de alguns métodos da classe String pra extrair os ramais e nomes de cada funcionário separadamente, para a criação de um array de objetos Extension.

Construção da Interface Gráfica

Como vimos anteriormente, a aplicação terá apenas duas telas. Uma lista mostrando os nomes dos funcionários, e um formulário para visualizar o ramal de um usuário selecionado.

Lista de Funcionários

O componente gráfico utilizado para mostrar a lista de ramais é a classe javax.microedition.lcdui.List. Os métodos importantes aqui são descritos na Tabela 3.


wm-20-05-2008pic02.JPG

Tabela 3. Métodos da classe List.

 

Cada elemento da lista tem um String associado e pode ter um ícone (um Image). A lista que usaremos é do tipo IMPLICITY. Os outros tipos de lista, EXCLUSIVE e MULTIPLE, usam botões de rádio e checkbox ao lado de cada elemento. A lista é instanciada no construtor da classe CompanyExtension e os elementos são adicionados usando o método append(). Veremos na Listagem 5 como manipular a essa lista.

Visualizando os ramais

A tela para visualizar o nome e ramal é implementada estendendo a classe Form. Um objeto Form é uma espécie de container de objetos do tipo Item. Os itens disponíveis na MIDP 2.0 são descritos na Tabela 4. Como mostra a Listagem 4 o nome e telefone são adicionados ao Form como StringItems.

wm-20-05-2008pic03.JPG
Tabela 4.
Itens definidos na MIDP 2.0.

 

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.StringItem;

 

public class ExtensionView extends Form {

 

   private Extension ext;

   private StringItem siName, siPhone;

 

   public ExtensionView(Extension ext) {

      super("Extension Viewer");

 

      this.ext = ext;

 

      siName  = new StringItem("Name:", ext.getName());

      siPhone = new StringItem("Extension:", ext.getPhone());

 

      append(siName);

      append(siPhone);

   }

 

   public String getPhone() {

      return ext.getPhone();

   }

}

Listagem 4. classe ExtensionView.

Classe MIDlet

Finalmente iremos construir a classe MIDlet, que é responsável pela máquina de estados da aplicação. O MIDlet adiciona comandos as telas, e executa as operações associadas. A classe CompanyExtensions, apresentada na Listagem 5, apenas faz uso das classes anteriores controlando assim o fluxo de mensagens da aplicação.

Após criar a lista responsável por exibir os nomes dos funcionários, o construtor usa o objeto Display (um singleton) para colocar o List na tela com o método setCurrent(). O objeto Display é obtido apenas com a instância do MIDlet, que é passada no método getDisplay(MIDLet).

Para receber notificações de comandos, a classe MIDlet implementa a interface CommandListener. Classes filhas de Displayble herdam o método setCommandListener() usado para registrar um listener em uma tela.

Caso ocorra um erro, o método showErrMsg(String text) é chamado com a mensagem de erro. Esse método usa a classe Alert para exibir o erro para o usuário.

Para sair da aplicação o MIDlet invoca o método herdado notifyDestroyed(). A ligação telefônica iniciada com o comando “Call” é implementada com o método platformRequest(URL).

 

import javax.microedition.io.ConnectionNotFoundException;

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

import java.io.IOException;

 

 

   public class CompanyExtensions extends MIDlet implements CommandListener {

   private Display display;

 

   private Extension[] extensions;

   private ExtensionView view;

   private List listUI;

 

   private Command cmView = new Command("View", Command.SCREEN, 1);

   private Command cmCall = new Command("Call", Command.SCREEN, 1);

   private Command cmBack = new Command("Back", Command.SCREEN, 1);

   private Command cmExit = new Command("Exit", Command.SCREEN, 1);

 

   public CompanyExtensions() {

 

      display = Display.getDisplay(this);

 

      try {

         extensions = FileHandler.loadExtensions();

      } catch (IOException ioe) {

            showErrMsg("Problemas na leitura do arquivo!");

         return;

      }

 

      listUI = new List("Company Extensions", List.IMPLICIT);

 

      // Adiciona comandos a lista

      listUI.addCommand(cmView);

      listUI.addCommand(cmExit);

 

      // Registra o listener na lista

      listUI.setCommandListener(this);

 

      // Adiciona nomes a lista

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

         listUI.append(extensions[i].getName(), null);

      }

 

      display.setCurrent(listUI);

 

   }

 

   public void startApp() { }

 

   public void pauseApp() { }

 

   public void destroyApp(boolean unconditional) { }

 

   public void commandAction(Command c, Displayable d) {

      if ((c == cmView) || (c == List.SELECT_COMMAND)) {

         int index = listUI.getSelectedIndex();

 

         view = new ExtensionView(extensions[index]);

         view.addCommand(cmBack);

         view.addCommand(cmCall);

         view.setCommandListener(this);

         display.setCurrent(view);

 

      } else if (c == cmBack) {

         display.setCurrent(listUI);

         view = null;

      } else if (c == cmCall) {

         try {

            platformRequest("tel:" + view.getPhone());

         } catch (ConnectionNotFoundException e) {

            showErrMsg("Problemas na ligacao!");

         }

      } else if (c == cmExit) {

         notifyDestroyed();

      }

   }

 

   private void showErrMsg(String text) {

      Alert alert = new Alert(null, text, null, AlertType.ERROR);

      alert.addCommand(cmExit);

      alert.setCommandListener(this);

 

      display.setCurrent(alert);

   }

}

Listagem 5. classe CompanyExtensions.