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.
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.
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.
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.