Aplicação completa JME com LWUIT e Floggy
Hoje estou disponibilizando para todos o acesso ao fonte de uma aplicação que a gente fez em apenas em alguns dias… para ser sincero código mesmo foi feito em apenas 4 dias (ou melhor 4 noites), tivemos mais três dias para escolha do nome, um dia para definição da equipe, definição de logotipo e definições do caso de uso e 2 dias eu descansei e trabalhei em outras coisas… Então foram 10 dias (somente tempo livre) contando todas as etapas para a aplicação completa. A idéia genial do projeto surgiu do Egberto, alguns casos de uso e o vídeo também foi feito por ele, testes, casos de uso, colaboração com idéias e algumas imagens como o logotipo foram feitos pelo Éver. E eu fiz o código.
Queríamos participar do concurso da Nokia, mas não deu tempo de assinar a aplicação e ela não ficou como queríamos, então não ganhamos, mas valeu como esperiência. Como estávamos prevendo o desenvolvimento com mais gente e acabou não dando certo, eu adotei uma estrutura de pacotes semelhante ao desenvolvimento web, já que a maioria que entrou no projeto vinha desse contexto e não de desenvolvimento mobile. O código ainda tem alguns bugs, não está limpando alguns campos, mas a aplicação está funcional, embora não esteja otimizada. Tem também alguns erros de inglês no vídeo… vamos corrigir depois
Utilizamos LWUIT e Floggy de framework para a aplicação. Abaixo está a descrição do projeto.
GREENHOUSE
Sua consciência ecológica!
“O efeito estufa é gerado pela derrubada de florestas, lançamento de gases poluentes na atmosfera, principalmente os que resultam da queima de combustíveis fósseis. Esse efeito ocasiona o super aquecimento do nosso planeta. O aplicativo greenhouse será sua consciência ecológica, informando a você a quantidade de árvores necessárias para consumir o CO2 produzido, de acordo com o meio de transporte automotivo.”
O projeto foi feito com Netbeans 6.9. O código fonte encontra-se disponível para download pelo subversion: http://gvlabs.org/svn/mobile/Greenhouse/
Se você deseja somente instalar no seu celular, segue o download da aplicação: http://www.thiagovespa.com.br/apps/Greenhouse.zip
Se você não sabe utilizar o subversion no Netbeans, aqui tem um breve tutorial: http://www.thiagovespa.com.br/blog/2010/09/20/subversion-no-netbeans/
Instalação do ambiente:
- Fazer download do Netbeans Completo (Já vem com o JME) e instalar. Quem tiver interesse pode instalar o JME SDK 3.0 também: http://java.sun.com/javame/downloads/sdk30.jsp
- Abrir o Netbeans, ir em Tools, Plugins e atualiza-lo.
- Fazer o download do Perl: http://strawberry-perl.googlecode.com/files/strawberry-perl-5.12.0.1.msi e instalar
- Reiniciar o computador
- Baixar o SDK da Nokia Symbian^3 0.8: http://www.forum.nokia.com/info/sw.nokia.com/id/ec866fab-4b76-49f6-b5a5-af0631419e9c/S60_All_in_One_SDKs.html
- Instalar o SDK da Nokia. A partir de agora você já pode abrir o emulador da Nokia
- Abrir o Emulador e configurar data e hora. Pode usar o teclado do computador, não precisa usar somente o teclado do celular.
Configuraçõe Adicionais – Netbeans
Configurar o emulador da Nokia
- Tools, Java Platform, Add Platform, Java ME MIDP Platform, Next
- Verificar se encontrou o emulador da Nokia, Next
- Finish
Configurar a lib do LWUIT
- Faça o Download do LWUIT em: http://java.sun.com/javame/technology/lwuit/ no botão Download LWUIT
- Descompacte ele em uma pasta
- Vá no netbeans, Tools, Libraries
- New Library…, Library Name: LWUIT, Library Type: Class Libraries
- Add Jar/Folder, Escolher o LWUIT.jar que está na pasta lib do LWUIT, Apertar Ok
Configurar a lib do Floggy
- Faça o Download do Floggy em: http://floggy.sourceforge.net/download.html na seção Main Bundle
- Descompacte ele em uma pasta
- Vá no netbeans, Tools, Libraries
- New Library…, Library Name: Floggy, Library Type: Class Libraries
- Add Jar/Folder, Escolher o floggy-persistence-framework.jar que está na pasta lib do Floggy, Apertar Ok
- Abrir o arquivo build.xml (Visualizar por Window, Files) e mudar a propriedade floggy.path para o caminho da instalação do floggy (Favor não commitar o build.xml com suas alterações)
Configurar o Checkstyle (somente para commiters)
- Ir em Tools, Plugin
- Settings, Add
- Name: Checkstyle, URL: http://www.sickboy.cz/checkstyle/autoupdate/autoupdate-2.xml – OK
- Avaliable Plugins, Reload Catalog
- Selecionar Checkstyle beans plugin e library, Install
- Confimar as opções até o final
Espero que gostem. Quem quiser contribuir para melhorar esse projeto é só me avisar que eu libero acesso como commiter no repositório.
FileChooser com LWUIT
O LWUIT é um framework desenvolvido pela Sun Microsystems para facilitar o desenvolvimento de interface gráficas em celulares. Ele é bem parecido com o Swing para desktop.
Fiquei conhecendo esse framework graças ao Antônio Marin Neto em seu blog, que aborda características do LWUIT como Recursos, Estabilidade, Desempenho e outros. Eu achei o LWUIT muito fácil de usar e com vários recursos, embora o tempo para iniciar um programa feito nele é um pouco grande. Nada que um loading screen não resolva.
A criação do FileChooser veio da necessidade de trabalhar com arquivos em um projeto que estou desenvolvendo para um Wiki móvel. Em breve disponibilizarei os fontes do projeto como open source para o pessoal poder contribuir.
O primeiro passo foi utilizar a JSR 75 para manipular os arquivos. Para verificar se o dispositivo tem suporte a essa JSR, você pode utilizar o seguinte código:
// Verifica se a JSR 75 está disponível
return System.getProperty("microedition.io.file.FileConnection.version") != null;
}
Os próximos código terão referências às classes Language e ExceptionHandler. A primeira foi utilizada para localização utilizando o editor de resources do LWUIT e a segunda é responsável por processar exceções. Posteriormente eu explico o funcionamento dessas duas classes. Para funcionar o código sem elas, basta você realizar o tratamento de erros da sua maneira e substituir a chamada de Language.getLocalizedString(“…”) por alguma outra String.
Agora vamos criar uma classe utilitária (FileUtil) para trabalhar com arquivos. É necessário pegar o separador de arquivos, qual é a string de diretório que representa o nível superior e o prefixo para acessar arquivos.
Para verificar se o caminho passado é uma raiz, utilizamos um workaround.
// Operação sobre o path ao invés de verificar na lista de roots
// Verifica se o primeiro separador de arquivo é o último caractere
if (path != null && path.indexOf(FileUtil.FILE_SEPARATOR) != path.length() - 1) {
return false;
}
return true;
}
Depois é só utilizarmos o Connector.open para obter um FileConnection e executar as operações em arquivos.
Com isso temos o seguinte arquivo:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
/**
* Classe para manipulação de arquivos
*
* @author Thiago Galbiatti Vespa
*
*/
public final class FileUtil {
public final static String FILE_SEPARATOR = (System
.getProperty("file.separator") != null) ? System
.getProperty("file.separator") : "/";
public final static String TOP_DIR = "..";
public final static String FILE_PREFIX = "file:///";
/**
* Verifica se o caminho é uma raiz baseado na string passada, pode resultar
* em resultado falso caso seja passada um caminho inválido
*
* @param path
* caminho a ser verificado
* @return verdadeiro se o formato da string for raiz
*/
public static boolean isRoot(String path) {
// Operação sobre o path ao invés de verificar na lista de roots
// Verifica se o primeiro separador de arquivo é o último caractere
if (path != null
&& path.indexOf(FileUtil.FILE_SEPARATOR) != path.length() - 1) {
return false;
}
return true;
}
/**
* Cria um diretório
*
* @param path
* diretório a ser criado
* @return true se o diretorio foi criado, false caso contrário
*/
public static boolean createDir(String path) {
FileConnection newDir = null;
try {
newDir = (FileConnection) Connector.open(FileUtil.FILE_PREFIX
+ path + FileUtil.FILE_SEPARATOR, Connector.READ_WRITE);
if (!newDir.exists()) {
newDir.mkdir();
return true;
} else {
ExceptionHandler.handleException(new IOException(Language
.getLocalizedString("dirAlreadyExist")));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
if (newDir != null) {
try {
newDir.close();
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
}
return false;
}
/**
* Cria um novo arquivo
*
* @param path
* arquivo a ser criado
* @return true se o arquivo foi criado, false caso contrário
*/
public static boolean createNewFile(String path, String content) {
FileConnection newFile = null;
DataOutputStream dos = null;
try {
newFile = (FileConnection) Connector.open(FileUtil.FILE_PREFIX
+ path, Connector.READ_WRITE);
if (!newFile.exists()) {
newFile.create();
dos = newFile.openDataOutputStream();
dos.writeUTF(content);
dos.flush();
return true;
} else {
ExceptionHandler
.handleException(new IOException(Language
.getLocalizedString("file")
+ path
+ Language
.getLocalizedString("already exists!")));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (newFile != null) {
newFile.close();
}
if (dos != null) {
dos.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return false;
}
/**
* Atualiza arquivo
*
* @param path
* arquivo a ser atualizado
* @return true se o arquivo foi atualizado, false caso contrário
*/
public static boolean updateFile(String path, String content) {
FileConnection file = null;
DataOutputStream dos = null;
try {
file = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ_WRITE);
if (!file.exists()) {
// Cria se não existe
file.create();
} else {
// Trunca se já existe
file.truncate(0);
}
dos = file.openDataOutputStream();
dos.writeUTF(content);
dos.flush();
return true;
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (file != null) {
file.close();
}
if (dos != null) {
dos.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return false;
}
public static boolean existsFile(String path) {
FileConnection file = null;
try {
file = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ);
if (file.exists()) {
return true;
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (file != null) {
file.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return false;
}
/**
* Lê um arquivo
*
* @param path
* arquivo a ser lido
* @return conteúdo do arquivo
*/
public static String loadFile(String path) {
FileConnection file = null;
DataInputStream dis = null;
String retVal = null;
try {
file = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ);
if (file.exists()) {
dis = file.openDataInputStream();
retVal = dis.readUTF();
} else {
ExceptionHandler.handleException(new IOException("File " + path
+ " do not exists!"));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (file != null) {
file.close();
}
if (dis != null) {
dis.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return retVal;
}
/**
* Lê um diretório
*
* @param path
* diretório a ser lido
* @return lista
*/
public static String[] loadDir(String path, String pattern) {
FileConnection dir = null;
String[] retVal = null;
Vector files = new Vector();
try {
dir = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ);
if (dir.exists()) {
Enumeration en = dir.list(pattern, true);
while (en.hasMoreElements()) {
String object = (String) en.nextElement();
files.addElement(object);
}
if (files != null) {
retVal = new String[files.size()];
files.copyInto(retVal);
}
} else {
ExceptionHandler.handleException(new IOException("File " + path
+ " do not exists!"));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (dir != null) {
dir.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return retVal;
}
/**
* Remove um diretório
*
* @param path
* diretório a ser removido
* @return true se removido e false caso contrário
*/
public static boolean deleteDir(String path) {
FileConnection dir = null;
try {
dir = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ_WRITE);
if (dir.exists()) {
Enumeration en = dir.list("*", true);
while (en.hasMoreElements()) {
String object = (String) en.nextElement();
if (object.endsWith(FileUtil.FILE_SEPARATOR)) {
deleteDir(path + object);
} else {
deleteFile(path + object);
}
}
dir.delete();
dir.close();
return true;
} else {
ExceptionHandler.handleException(new IOException(Language
.getLocalizedString("file")
+ path + Language.getLocalizedString("doNotExist")));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (dir != null) {
dir.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return false;
}
/**
* Remove um arquivo
*
* @param path
* arquivo a ser removido
* @return true se removido e false caso contrário
*/
public static boolean deleteFile(String path) {
FileConnection file = null;
try {
file = (FileConnection) Connector.open(FileUtil.FILE_PREFIX + path,
Connector.READ_WRITE);
if (file.exists()) {
file.delete();
file.close();
return true;
} else {
ExceptionHandler.handleException(new IOException(Language
.getLocalizedString("file")
+ path + Language.getLocalizedString("doNotExist")));
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
} catch (SecurityException e) {
ExceptionHandler.handleException(e, Language
.getLocalizedString("securityExceptionDesc"));
} finally {
try {
if (file != null) {
file.close();
}
} catch (IOException e) {
ExceptionHandler.handleException(e);
}
}
return false;
}
}
No próximo post a gente parte para a parte gráfica do FileChooser.
Até a próxima!


