Estaremos zarpando do extremo sul ao norte contemplando e experimentado as belezas naturais desse pai phodaum muntado em um monster solar bike monitorada em tempo pelo google earth maps e outros da mesma forma que monitoramos os indices de emissoes com pombas (gosto duvidoso)
http://pigeonblog.mapyourcity.net/map/i
Ate la teremos uma cobertura ampla da Nextel e segue um exemplo de coleta de dados para um celular iden a ser integrado a um back end servlet java ou php.
Aceitamos sugestoes, pois queremos usar api location pura para manter a independencia de plataforma pois outros amigos de estrada podem ter outro dispositivo movel.
ps. upgrade da bike
fonte: http://www.orkut.com.br/AlbumZoom.aspx?
Um Exemplo de MIDlet para demonstração do uso de aGPS
Uma das funcionalidades especiáis dos equipamentos Nextel é a inclusão de um chip GPS que permite obter a latitude e longitude de onde está localizado o equipamento. Neste artigo, vamos analisar em detalhe um aplicativo Java ME (J2ME) que usa não somente as APIs de localização disponíveis nos equipamentos Motorola iDEN, mais também usa um mecanismo de multithreading para dar uma ótima experiência ao usuário.
Um exemplo têm tanto valor como um bom guia de desenvolvimento: então, este artigo é quase totalmente baseado numa análise de código. Não supõe que você conheça o Java ME com profundidade, mas sempre ajuda saber mais -- então, se precisa de um tutorial, os nossos amigos do portaljava.com.br têm desenvolvido uma série de guias que explicam vários aspectos do Java ME com mais detalhe.
Localização - um processo muito simples
Olhemos o código.
A primeira parte (e quase poderíamos dizer que é a mais importante), apresenta as inclusões dos pacotes Java ME que nós precisamos:
import java.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.lang.System;
import java.util.*;
import com.motorola.iden.position.*;
As seis inclusões iniciais são essenciais para todos os aplicativos Java ME que querem fazer algo mais que simplesmente dizer "Hello World". A última inclusão (com.motorola.iden.position.*) é a que nós precisamos para poder usar as funções de localização em todos os equipamentos iDEN com aGPS. (Também poderia usar javax.microedition.location nos equipamentos iDEN que têm suporte do API de localização padrão de Java ME, JSR-179; nesse caso, o código fonte seria bastante diferente e será parte de outro tutorial).
Depois das inclusões, vêm as declarações do MIDlet, que implementa a interface CommandListener diretamente para administrar ao nível do MIDlet os comandos que possa receber o aplicativo:
Declaramos um objeto Calendar() para ter funções de datas:
e uma lista que usaremos depois para determinar o tipo de localização aGPS que precisamos e a tolerância de espera que estamos dispostos a ter:
String[] testList = {
"(1)Conexão: delay=no",
"(2)Conexão: delay=low",
"(3)Conexão: delay=high",
"(4)Sair",
};
Esta lista têm informação importante. Cada um dos valores da conexão (delay=no, delay=low, delay=high) representa um parâmetro que indica à API de localização se nós queremos uma localização que requer a oferecida pelo chip GPS, ou si podemos trabalhar somente com as coordenadas de torre/celID à que esta ligado o equipamento. O parâmetro delay=high indica à API que o aplicativo está disposto a esperar um tempo para obter coordenadas de mais precisão, o que implica o uso do chip aGPS. Usando delay=no determina que, ao contrário, o aplicativo precisa informação imediatamente; porém, somente receberá as coordenadas da torre de serviço. Falaremos más do parâmetro delay=low depois.
Seguem as declarações do Canvas, do thread que usaremos para pedir as coordenadas, as variáveis para manter informação de estado, os comandos que precisamos, e o construtor do MIDLet.
Display myDisplay;
// Menu principal
List myList;
// Tela de progresso que usamos para comunicar ao usuário uma operação em processo
ProgressCanvas myPC;
// thread que usamos para obter as coordenadas GPS
Thread gpsThread;
// OK Command para o menu principal.
Command okCommand;
// Nos permite saber se o aplicativo já foi inicializado
boolean inicializado = false;
/** Construtor para PosDemo. Geralmente é aqui onde se instância o
* objeto Display para o aplicativo
*/
public PosDemo() {
myDisplay = Display.getDisplay(this);
}
O construtor em si não deve ser o mecanismo pela inicialização das variáveis do aplicativo; fazé-lo pode causar problemas na execução do aplicativo. Recomenda-se que a inicialização seja feita em startApp().
No método startApp(), que é requerido pelo modelo Java ME, é chamado pelo equipamento cada vez que o aplicativo volta de uma pausa ou é iniciado. O método pode ser chamado muitas vezes numa execução de um MIDlet; porém, é importante que a inicialização seja feita somente quando seja requerida.
* então precisa ter em conta não somente a inicialização, mais também
* a continuidade. O método deve voltar rapidamente porque o Displayable
* de cuada aplicativo não é visualizado até que o método tenha terminado.
*
* @throws MIDletStateChangeException É lançado se o aplicativo não pôde
* iniciar imediatamente mais se possivelmente possa iniciar depois.
*/
protected void startApp() throws MIDletStateChangeException {
if( !inicializado ) {
myList = new List("Eleger prova:", List.IMPLICIT, testList, null);
okCommand = new Command("OK", Command.OK, 1);
myList.addCommand(okCommand);
myList.setCommandListener(this);
myPC = new ProgressCanvas();
myDisplay.setCurrent(myList);
}
}
A inicialização adiciona o comando à tela, une a lista de opções ao comando, e estabelece o conteúdo da tela.
O seguinte método é pauseApp(), o qual é chamado cada vez que o aplicativo é suspendido. Este método é essencial, porque lhe da a oportunidade ao aplicativo de soltar recursos e se assegurar que possa resumir a operação no estado apropriado se for necessário.
* no estado de pausa, o MIDlet deve soltar recursos que não precisa manter no
* fundo, e de ali precisa ficar quieto. No estado de pausa, o aplicativo
* pôde continuar fazendo operações que não precisem da tela.
*/
protected void pauseApp() {
System.out.println("pauseApp chamado");
initialized = true;
}
O método final do ciclo de vida de um MIDlet, destroyApp(), vem em seguida. destroyApp() é o método que invoca o ambiente Java ME no equipamento quando ele pede ao aplicativo que termine completamente a operação e que entre no estado Destroyed. O destroyApp() é chamado por exemplo quando o usuário desliga o aplicativo empurrando o botão END duas vezes, quando o aplicativo chama notifyDestroyed(), ou quando o equipamento é desligado por completo com o botão de ON/OFF (é claro que não é chamado no caso em que o aplicativo está em processo de execução e a bateria do equipamento é puxada enquanto o aparelho está ligado).
O método destroyApp() permite que o aplicativo termine a operação limpamente. No estado Destroyed, o aplicativo precisa soltar todos os recursos (conexões de rede, conexões ao GPS, ao sistema de memória, threads, etc.) e guardar qualquer informação que precise persistir na memória (bancos de dados, etc.). Este método pôde ser chamado desde os estados Paused ou Active.
* @param unconditional Si o parâmetro e verdadeiro ao ser chamado o
* método, o aplicativo precisa completar as operações e soltar
* todos os recursos que esteja usando. Si fosse falso, o aplicativo pôde
* lançar uma exceção tipo MIDletStateChangeException para indicar que
* não deseja ser destruída neste momento.
*
* @throws MIDletStateChangeException Lançado si o aplicativo não deseja
* terminar neste momento
*/
protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException {
}
A continuação, o código que administra os comandos e as atividades que eles iniciaram:
* Nota para o desenvolvedor: o método precisa retornar imediatamente.
*
* @param c Um objeto tipo Command que identifica o comando. Ele debe ser o um
* dos comandos que foram adicionados ao Displayable con addCommand(Command) ou
* com o comando implícito SELECT_COMMAND asociado com os objetos tipo List.
* @param d O objeto tipo Displayable em que ocorreu o evento
*/
public void commandAction(Command c, Displayable d) {
if (d == myList) {
switch (((List)d).getSelectedIndex()) {
case 0:
case 1:
case 2:
//Visualizar uma barra de progresso ao usuário
myDisplay.setCurrent(myPC);
// Iniciar um novo thread (Thread) para que a tela
// não fique inativa
gpsThread = new Thread(new SRunnable(((List)d).getSelectedIndex()));
gpsThread.start();
break;
case 3:
// Sair do aplicativo
notifyDestroyed();
break;
default:
break;
}
} else {
// Mostrar menu principal
myDisplay.setCurrent(myList);
}
}
En termos de serviços de localização, o comando mais importante na parte anterior é aquele que começa um novo thread (gpsThread = new Thread(...)). As funções de localização podem demorar muitos segundos para gerar um resultado, porém é importante que o aplicativo fique acessível enquanto as funções de localização retornam com um resultado.
O thread recebe como parâmetro o índice escolhido da lista que já definimos na parte inicial do código. Como indicamos antes, este índice inclui um parâmetro que indica a tolerância de demoras que têm o aplicativo em termos de quanto tempo pôde esperar até receber as coordenadas.
Seguimos com o coração do aplicativo; ele usa o parâmetro indicado e as funções das classes AggregatePosition e PositionConnection para obter as coordenadas do equipamento:
* precisamos. Os modos de localização incluem "no delay" (demora nenhuma), "low delay" (pouca
* demora), e "high delay" (alta demora). Isso indica qual tolerância têm o aplicativo
* e quanto tempo esta disposto a esperar pelas coordenadas.
*
* @param delay A demora aceitável ao obter uma localização.
*/
private void getFix(int delay) {
try{
String connectionString = null;
switch(delay){
case 0:
connectionString = "mposition:delay=no";
break;
case 1:
connectionString = "mposition:delay=low";
break;
case 2:
connectionString = "mposition:delay=high";
break;
default:
connectionString = "mposition:delay=no";
break;
}
// Notificar ao usuário do progresso
myPC.updateMessage("Obteniendo ubicación...");
// Obter conexão PositionConnection e a posição AggregatePosition
AggregatePosition pos = null;
//Iniciar uma conexão de localização especificando a demora aceitável
PositionConnection posCon = (PositionConnection)Connector.open(connectionString);
pos = posCon.getPosition();
...
O objeto posCon (tipo PositionConnection) é aquele que nos permite obter os dados de localização do equipamento. A cadeia de conexão que recebe o método open() do PositionConnection indica duas coisas: primeiro, que a conexão é do tipo mposition, o que indica ao equipamento que tem que estabelecer uma conexão com o chip GPS do equipamento, e segundo, o parâmetro 'delay', que indica quais tipos de respostas serão aceitáveis e quanto tempo pôde esperar o aplicativo.
Em geral, o equipamento sempre tentará de dar os dados de maior precisão que estejam disponíveis e que possam ser obtidos num tempo menor ao indicado por 'delay'. O parâmetro "delay=no" indica que os dados tem que já estar no equipamento. Isso implica que o aplicativo sempre recebera as coordenadas da torre/celID de serviço, e em momento nenhum dará acesso ao chip GPS. Quando o equipamento estiver fora de cobertura, a latitude e longitude disponíveis terão um valor de zero ao ser especificado "delay=no".
Quando o parâmetro é "delay=low", o equipamento usará os dados de assistência que ele tenha recebido se os dados existem, e pede dados novos à rede se estão ultrapassados ou não existem. Este tipo de localização opera dentro ou fora de rede de dados, mais haverá maior possibilidade de obter uma localização dentro da cobertura da rede. Existe um tempo de operação máximo de 32 segundos.
O parâmetro "delay=high" permite que o equipamento tente obter uma localização com GPS por até 180 segundos, seja que o equipamento esteja na rede ou fora dela. O equipamento, neste caso, usa os dados de assistência somente se eles já existem no equipamento e ainda têm vigência; no caso que não existam ou estejam ultrapassados, o equipamento não tentará os obter da rede. Quando é preciso obter uma localização autônoma -- por exemplo, quando definitivamente não existe cobertura da rede ou onde poderia tomar más de 32 segundos obter uma localização (caso de ter visibilidade limitada ao céu), "delay=high" é o parâmetro a usar.
Uma vez que temos o resultado, precisamos o extrair do objeto tipo AggregatePosition. A classe AggregatePosition têm muitas funções, então para os nossos propósitos (demostrar a funcionalidade da API), definimos uma função printPosition() que vai nos permitir obter esses valores. Então, completemos a função getFix() e a declaração de printPosition():
// Imprimir os dados de localização
printPosition(posCon, pos);
}catch(Exception e) {
// Notificação em caso de erro
myPC.updateMessage("Excepción "+e);
}
}
/**
* Imprimir posição y outra informação
*/
private void printPosition(PositionConnection posCon, AggregatePosition pos) {
// Estabelecer o formulário onde poremos os valores para visualizá-los
Form myOutput = new Form("Informação de posição");
myOutput.addCommand(okCommand);
myOutput.setCommandListener(this);
myPC.updateMessage("Abrindo conexão...");
try {
// Revisa para assegurar que AggregatePosition não esteja nulo, o que
if(pos == null) {
myDisplay.setCurrent(myOutput);
myOutput.append("Objecto de Posición nulo");
return;
}
// Verifica os permissões de GPS que o usuário estabeleceu no equipamento
if (posCon.getStatus() == PositionConnection.POSITION_RESPONSE_RESTRICTED){
myDisplay.setCurrent(myOutput);
myOutput.append("Permissões restringidos na configuração do GPS");
return;
}
// Verifica que o usuário tenha dado permissão de atualizar os dados do almanaque GPS
if (posCon.getStatus() == PositionConnection.POSITION_RESPONSE_NO_ALMANAC_OVERRIDE){
myDisplay.setCurrent(myOutput);
myOutput.append("Permissões restringidos para atualização do almanaque");
return;
}
O tema dos permissões é importante. O usuário dum equipamento iDEN tem a possibilidade de restringir qual tipo de acesso tem um aplicativo às coordenadas GPS, usando a opção de privacidade de "Menu -> GPS" no equipamento. (Em certos modelos, existem funções administrativas que permitem estabelecer senhas para restringir quem pôde modificar essa o outras configurações.
A seguir, verificamos os códigos de resposta que gera o sistema en caso de erros:
// Atualizar indicador de progresso
myPC.updateMessage("Revisando erros");
Thread.currentThread().sleep(500);
// Verificar que a resposta de AggregatePosition seja adequada.
// Revisar o estado de PositionConnection
if(posCon.getStatus() != PositionConnection.POSITION_RESPONSE_OK &&
pos.getResponseCode() != PositionDevice.POSITION_OK) {
myDisplay.setCurrent(myOutput);
switch(pos.getResponseCode()){
case PositionDevice.FIX_NOT_ATTAINABLE:
myOutput.append("Não foi possível obter localização: FIX_NOT_ATTAINABLE");
break;
case PositionDevice.ACCURACY_NOT_ATTAINABLE:
myOutput.append("Não foi possível obter grado de exatidão: ACCURACY_NOT_ATTAINABLE");
break;
case PositionDevice.FIX_NOT_ATTAIN_ASSIST_DATA_UNAV:
myOutput.append("Não foi possível obter localização; dados de assistência não disponíveis: FIX_NOT_ATTAINABLE_ASSIST_DATA_UNAVAILBLE");
break;
case PositionDevice.ACC_NOT_ATTAIN_ASSIST_DATA_UNAV:
myOutput.append("Não foi possível obter grado de exatidão; dados de assistência não disponíveis: ACC_NOT_ATTAINABLE_ASSIST_DATA_UNAVAILBLE");
break;
case PositionDevice.BATTERY_TOO_LOW:
myOutput.append("Nível de bateria baixo: BATTERY_TOO_LOW");
break;
case PositionDevice.GPS_CHIPSET_MALFUNCTION:
myOutput.append("Falha no chip GPS: GPS_CHIPSET_MALFUNCTION");
break;
case PositionDevice.ALMANAC_OUT_OF_DATE:
myOutput.append("Almanaque GPS ultrapassado: ALMANAC_OUT_OF_DATE");
break;
case PositionDevice.UNAVAILABLE:
myOutput.append("Erro desconhecido. Em casso de ver esta mensagem, por favor informar à Nextel e a Motorola");
break;
default:
myOutput.append("Erro improvável...");
break;
}
return;
}
Se não se observaram erros, o que falta é determinar qual tipo de localização temos obtido. Se temos uma localização completa (ou seja, uma com GPS), então podemos tentar extrair todos os valores que oferece a classe AggregatePosition. Se temos uma localização de torre/celID, podemos extrair as coordenadas da torre para, pelo menos, poder usar essa informação.
// Se a posição tem latitude y longitude significa que iniciamos a
// conexão com delay=low ou delay=high. Imprimir todos os
// dados que temos sobre a posição
if(pos.hasLatLon() ) {
// Variáveis de instancia usadas para gerar informação para imprimir
int ano, mes, dia, hora, minuto, segundo, longD, latD, longM, latM;
String LAT = pos.getLatitude(Position2D.DEGREES);
String LONG = pos.getLongitude(Position2D.DEGREES);
String mihora;
String horaSys;
Date mifecha= new Date(pos.getTimeStamp());
Date fechaSys = new Date(System.currentTimeMillis());
// Obter marca de data e horário de localização
myCalendar.setTime(mifecha);
ano = myCalendar.get(Calendar.YEAR);
mes = myCalendar.get(Calendar.MONTH);
dia = myCalendar.get(Calendar.DAY_OF_MONTH);
hora = myCalendar.get(Calendar.HOUR_OF_DAY);
minuto = myCalendar.get(Calendar.MINUTE);
segundo = myCalendar.get(Calendar.SECOND);
mihora = ""+dia+"/"+"/"+mes+"/"+ano+" "+hora+":"+minuto+":"+segundo;
// Obter marca de data y horário atual
myCalendar.setTime(fechaSys);
ano = myCalendar.get(Calendar.YEAR);
mes = myCalendar.get(Calendar.MONTH);
dia = myCalendar.get(Calendar.DAY_OF_MONTH);
hora = myCalendar.get(Calendar.HOUR_OF_DAY);
minuto = myCalendar.get(Calendar.MINUTE);
segundo = myCalendar.get(Calendar.SECOND);
horaSys = ""+dia+"/"+"/"+mes+"/"+ano+" "+hora+":"+minuto+":"+segundo;
// Notificação de progresso
myPC.updateMessage("Verificação completa");
Thread.currentThread().sleep(500);
// Añadir datos al formulario para mostrarlos
myOutput.append("(1) Latitude " + LAT);
myOutput.append("(2) Longitude " + LONG);
myOutput.append("(3) Altitude = " + pos.getAltitude() + " metros ");
myOutput.append("(4) Velocidade = " + pos.getSpeed() + " km/h ");
myOutput.append("(5) # de satélites = " + pos.getNumberOfSatsUsed());
myOutput.append("(6) Exatidão lat/lon = " + pos.getLatLonAccuracy() + " milímetros ");
myOutput.append("(7a) TimeMillis = " + pos.getTimeStamp());
myOutput.append("(7b) Horário localização= "+mytime);
myOutput.append("(7c) Horário Systema = "+ horaSys);
myOutput.append("(8) Rumo = " + pos.getTravelDirection());
myOutput.append("(9) Nível de incerteza da velocidade = " + pos.getSpeedUncertainty() + " km/h ");
myOutput.append("(10) Nível de incerteza da altitude = " + pos.getAltitudeUncertainty() + " milímetros \n");
myOutput.append("(11) Assistência usada = "+pos.getAssistanceUsed());
myOutput.append("(12) Latitude torre = " +pos.getServingCellLatitude(Position2D.DEGREES));
myOutput.append("(13) Longitude torre = " + pos.getServingCellLongitude(Position2D.DEGREES));
}
//si somente obtivemos localização da torre de servicio
else {
// Notificação de progresso
myPC.updateMessage("Verificação completa");
Thread.currentThread().sleep(500);
myOutput.append("Latitude torre = " +pos.getServingCellLatitude(Position2D.DEGREES));
myOutput.append("Longitude torre = " + pos.getServingCellLongitude(Position2D.DEGREES));
}
}catch(Exception e) {
// Imprimir erro
myOutput.append("Exceção " + e);
}finally{
// Mostrar progresso
myDisplay.setCurrent(myOutput);
}
}
O último que precisamos em termos dos serviços de localização é a criação de uma interface pelo thread que vai executar a conexão com o sistema de localização do equipamento -- SRunnable.
* thread que usamos para obter coordenadas GPS.
* Usando um thread, a interface fica ativa,
* evitando criar a impressão que colgou o aplicativo
*/
class SRunnable implements Runnable {
// Modo usado pela operação do API de localização.
int mode = 0;
/** O modo que passamos corresponde à tolerância de demora que usa o
* API de localização:
* 0 = no delay
* 1 = low delay
* 2 = high delay
* @param mode A tolerância de demora do API de localização
*/
public SRunnable(int mode) {
this.mode = mode;
}
/** Chama ao método certo para obter coordenadas GPS. O modo em que
* se faz o chamando ao método getFix() depende da variável "mode".
*/
public void run() {
switch(mode){
case 0:
case 1:
case 2:
getFix(mode);
break;
default:
break;
}
}
}
Por último, para completar o MIDlet, declaramos uma classe interna, ProgressCanvas, que nos permite mostrar uma barra de progresso ao usuário quando estejamos fazendo alguma operação que possa demorar.
* Tela de progresso
*/
class ProgressCanvas extends Canvas implements Runnable{
boolean active;
private String message = null;
Thread t = null;
int x, y, limit, counter;
boolean expand;
ProgressCanvas(){
x = getWidth()/2;
y = getHeight()/3;
limit = 14;
expand = true;
}
/** Visualizar o Canvas na tela
* @param g O objeto tipo Graphics usado para desenhar o Canvas na tela
*/
protected void paint(Graphics g){
// Limpar a tela
g.setColor(0x000000);
g.fillRect(0, 0, getWidth(), getHeight());
// Atualizar o indicador de progresso
g.setColor(0xffffff);
g.drawArc(x - counter, y - counter, counter *2, counter *2, 0, 360);
if(expand){
if(counter >= limit){
expand = false;
}
counter += 2;
}else{
if(counter < 1) {
expand = true;
}
counter -= 2;
}
// Mostrar a mensagem
g.setColor(0xffffff);
g.setFont(Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL));
g.drawString(message, getWidth()/2, (getHeight()* 2)/3, Graphics.HCENTER|Graphics.BASELINE);
}
/** Usado para atualizar a mensagem no ProgressCanvas. Este método
* não oferece nenhuma funcionalidade de word wrap automático.
*
* @param message A mensagem a desenhar
*/
public void updateMessage(String message){
this.message = new String(message);
repaint();
serviceRepaints();
}
/** A implementação chama showNotify() imediatamente antes que o
* Canvas seja visível na tela. As subclasses de Canvas podem
* sobrecarregar o método para executar operações antes de
* serem mostradas, tales como estabelecer animações, iniciar
* contadores, etc. A implementação padrão do método na classe
* Canvas não contêm operação nenhuma
*/
protected void showNotify(){
active = true;
t = new Thread(this);
t.start();
}
/** A implementação chama hideNotify() imediatamente antes que o
* Canvas seja apagado da tela. As subclasses de Canvas podem
* sobrecarregar o método para executar operações para dar pausa
* às animações, fechar contadores, etc. A implementação padrão
* do método na classe Canvas não contêm operação nenhuma
*/
protected void hideNotify(){
active = false;
t = null;
}
/** Cuando um objeto que implementa a interface Runnable é usado para
* criar um thread, iniciar o thread faz que o método run() do objeto
* seja chamado no novo thread que foi iniciado.
*/
public void run() {
while(active){
try{
/**
* Desenhar na tela enquanto o Canvas seja visível
*/
t.sleep(100);
if(isShown())
repaint();
}catch(Exception e){
}
}
}
}
}
O código fonte completo em inglês, espanhol e português vai anexo, junto com um projeto pronto para compilar com o SDK Java ME iDEN da Motorola.
ps: para o codigo na integra entre em
http://programa.nii.com/pt/tecnologias/javame/gps_exemplo e, por favor, nao use os codigos em portugues e espanhol pois nao compilam. Da mesma forma retire o ~en~ no final do nome da classe para serem iguais nome da classe e arquivo java.
Este e' um condigo funcional e nao os 1/2 boca encontrados por ai :)))))