Mostrando postagens com marcador location. Mostrar todas as postagens
Mostrando postagens com marcador location. Mostrar todas as postagens

terça-feira, 13 de janeiro de 2015

Dominando o Android - Atualização 1 - Capítulo 14

Olá pessoal,

Acho que com o livro as atualizações aqui do blog ficarão mais frequentes. Pelo menos é o que eu espero. Vou fazer o possível para colocar as correções/atualizações aqui através de mini-posts falando rapidamente sobre a mudança e o por quê.

Para quem não sabe, o código-fonte dos exemplos do livro estão disponíveis no meu gitHub (https://github.com/nglauber/dominando_android). E lá vocês vão ver dois branchs master e master_updates. O código que estiver com algum bug, eu vou corrigir e colocar no branch master e o que for atualização ficará no branch master_updates.
Também criei um grupo de discussão (http://groups.google.com.br/d/forum/livro-dominando-o-android) que todos podem se inscrever.

Recebi hoje uma avaliação do livro que informava que o mesmo "tinha uma falha grave" e "estava desatualizado" porque eu não estou utilizando API do Google Play Services 6.5. E que uma classe (a LocationClient) que eu usava no exemplo não existia.

Não sei se todos sabem, mas o processo de finalização de um livro requer algumas etapas que demandam tempo: revisão ortográfica, diagramação, checar figuras, referências, tempo de impressão na gráfica, etc.  A versão 6.5 do Google Play Services foi anunciada no dia 17/11/2014, mas só foi disponibilizada para os desenvolvedores dia 08/12/2014. Época que o livro já estava com a editora em processo de finalização e impressão. Sendo assim, não pude atualizar o capítulo 14 (Mapas e Localização) a tempo. Sendo assim, peço desculpas pelo meu livro ter ficado desatualizado, por conta de alguns dias... :(
Mas quero reiterar que o código apresentado no livro funciona perfeitamente do jeito que ele se encontra.

--

Pedido de desculpas feito, vamos ao que mudou! As alterações feitas em relação ao exemplo do livro estão nesse commit aqui no branch master_updates, uma vez que é uma atualização da nova API e não uma correção de um erro do livro.

Conforme o comentário do leitor, a classe com.google.android.gms.location.LocationClient não existe mais. Mas seus métodos foram distribuídos em outras classes.
  • Na classe GeofenceReceiver, os eventos de Geofence podem ser tratados com a classe GeofencingEvent.
  • Na classe MainActivity, muito do que era feito com a LocationClient passou a ser responsabilidade da GoogleApiClient, inclusive os eventos de conexão (ConnectionCallbacks e OnConnectionFailedListener).
  • Ainda na MainActivity para obter a localização atual passamos a utilizar a classe LocationServices.FusedLocationApi. Bem como para requisitar atualizações sobre mudança na localização.
  • E finalmente, para adicionar um Geofence, usamos a classe LocationServices.GeofencingApi.
Isso é um resumo pessoal. Quem quiser mais informações sobre essa nova API, recomendo esses links:



Qualquer dúvida, sugestão ou crítica serão muito bem-vindas.

4br4ç05,
nglauber

segunda-feira, 21 de novembro de 2011

Google Maps e GPS

Olá povo,

Nesse post de 2009 mostrei como fazer um "Hello World" com a API do Google Maps. Agora vamos incrementar um pouco esse exemplo colocando uma imagem para indicar o seu local baseado na posição GPS.

Pré-requisitos
A configuração inicial para obter a chave e exibir o mapa na tela é idêntica a apresentada no post de dois anos atrás, mas caso não consiga gerar a chave, dê uma olhada nesse post aqui (Dica 5). Outra ressalva é que, caso você esteja usando o emulador para testar sua aplicação, certifique-se de que ele esteja usando a "Google APIs" no "Android AVD Manager" conforme a figura abaixo:



O código do Overlay
Uma coordenada geográfica é representada pela classe GeoPoint. Ela tem a latitude e longitude de um ponto no mapa. Entretanto essa classe não é visual, ou seja, ela não serve para enxergarmos o ponto no mapa. Quando queremos adicionar no mapa alguma indicação para uma coordenada geográfica, devemos utilizar a classe Overlay, que é uma subclasse de View. O código abaixo cria um Overlay que desenhará uma imagem em um dado ponto no mapa (representado por um GeoPoint).
public class MeuOverlay extends Overlay {

private Bitmap imagem;
private GeoPoint geopoint;

public MeuOverlay(Bitmap img ) {
imagem = img;
}

public void setGeopoint(GeoPoint geopoint) {
this.geopoint = geopoint;
}

@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
super.draw(canvas, mapView, shadow);

if (geopoint != null){
Point pontoNaTela = mapView.getProjection()
.toPixels(geopoint, null);

canvas.drawBitmap(imagem,
pontoNaTela.x - (imagem.getWidth() / 2),
pontoNaTela.y - (imagem.getHeight()), null);
}
}
}

Essa classe tem dois atributos: um Bitmap e um GeoPoint. O Bitmap será a imagem que desenharemos para representar a o ponto no mapa; já o GeoPoint, como o próprio nome diz, é um ponto geográfico, que obteremos a partir da posição do GPS.
No método draw() é desenhado o ponto no mapa. Inicialmente checamos se o GeoPoint foi setado, em caso positivo, convertemos essa coordenada geográfica em um ponto na tela, que é representado pela classe Point. Quem faz essa conversão é a classe Projection obtida através do método getProjection() do MapView recebido como parâmetro do método. Depois, é só desenhar a imagem na tela. O cálculo feito na chamada do método é para que a imagem fique imediatamente acima do ponto geográfico.

O arquivo de layout
O arquivo de layout não tem nada de mais, apenas a declaração da MapView com a API key (veja no post de 2009 como obter a sua API key).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Exemplo GPS e MapActivity" />

<com.google.android.maps.MapView
android:id="@+id/mapa"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="SUA_API_KEY" />
</LinearLayout>

A Activity
O código abaixo representa a Activity da aplicação e está todo comentado e só vou fazer alguns comentários no final. A nossa classe herda de MapActivity e implementa a interface LocationListener, que é utilizada para receber as notificações do GPS.
public class Aula09Activity extends MapActivity
// Interface que tem os métodos para tratar
// os eventos do GPS
implements LocationListener {

private MapView mapa;
private MapController controller;
private MeuOverlay overlay;
private LocationManager locationManager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// O LocationManager vai registrar e desregistrar
// a classe para ouvir eventos do GPS
locationManager = (LocationManager)
getSystemService(LOCATION_SERVICE);

// Configurando a MapView
mapa = (MapView)findViewById(R.id.mapa);
// Habilita os botões de Zoom
mapa.setBuiltInZoomControls(true);
// Mostra em modo satélite
mapa.setSatellite(true);

// O MapController 'controla' o mapa :)
controller = mapa.getController();
controller.setZoom(17);

// Cria a imagem que vai representar o Overlay
Bitmap marcador = BitmapFactory.decodeResource(
getResources(), R.drawable.ponto);

// Cria o Overlay e adiciona ao mapView
overlay = new MeuOverlay(marcador);
mapa.getOverlays().add(overlay);

// Determinando um ponto inicial
int latitude = (int)(-8.058698 * 1E6);
int longitude = (int)(-34.872129 * 1E6);

GeoPoint geopoint = new GeoPoint(latitude, longitude);
controller.setCenter(geopoint);
overlay.setGeopoint(geopoint);
}

@Override
protected void onResume() {
super.onResume();
// Registrando a Activity para receber notificações
// de mudança na posição GPS
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);

locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, this);
}

@Override
protected void onPause() {
super.onPause();
// Desregistrando a Activity para receber
// notificações de mudança na posição GPS
locationManager.removeUpdates(this);
}

@Override
protected boolean isRouteDisplayed() {
return false;
}

@Override
public void onLocationChanged(Location location) {
// Método chamado quando a posição GPS muda
int latitude = (int)(location.getLatitude() * 1E6);
int longitude = (int)(location.getLongitude() * 1E6);

GeoPoint geopoint = new GeoPoint(latitude, longitude);
overlay.setGeopoint(geopoint);
controller.animateTo(geopoint);
}

@Override
public void onProviderDisabled(String provider) {
// Método chamado quando o GPS é desabilitado
}

@Override
public void onProviderEnabled(String provider) {
// Método chamado quando o GPS é habilitado
}

@Override
public void onStatusChanged(String provider,
int status, Bundle extras) {
// Método chamado quando o status do GPS muda.
// Pode ser: OUT_OF_SERVICE,
// TEMPORARILY_UNAVAILABLE e AVAILABLE
}
}

No código acima fazemos algumas inicializações no método onCreate. A classe LocationManager é responsável por registrar uma classe para "ouvir" os eventos de GPS (definidos na interface LocationListener que nossa Activity está implementando). Para criar um GeoPoint devemos passar a latitude e longitude em microdegrees, para tal devemos multiplicar os valores por 1.000.000 ou em notação científica 1E6 (10 elevado a 6).
No método onResume registramos nossa classe para ouvir os eventos do GPS. O método requestLocationUpdates recebe como parâmetros:
- o provedor de informações de posição GPS: aqui podemos utilizar o GPS do telefone, que demora mais a obter os dados, porém eles são mais precisos; e o da rede de dados, que obtém a posição baseado no esquema de triangulação de antenas da operadora de telefonia;
- a distância mínima em metros na posição GPS;
- o intervalo de tempo em milisegundos para receber a atualização;
- o objeto de uma classe que implemente a interface LocationListener (no nosso caso, a própria Activity).
No método onPause, desregistramos nossa classe para não ouvir mais os eventos de GPS. Isso evitará que ao sair da aplicação o Android tente ficar enviando as coordenadas mesmo com a aplicação inativa.
O método onLocationChanged é o mais importante, pois a cada vez que a posição GPS muda, esse método é chamado pelo Android. A partir do objeto Location que vem como parâmetro, obtemos a latitude e longitude para atualizarmos a posição do Overlay.
AndroidManifest.xml
No manifest, adicionamos as permissões INTERNET e ACCESS_FINE_LOCATION para podermos utilizar o Google Maps e obter a posição GPS respectivamente. Outra informação relevante é a tag <uses-library> que adicionamos para usar a API do Google Maps, uma vez que ela não é "padrão" da plataforma Android.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="br.edu.cesar.aula09"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />

<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".Aula09Activity" >
<intent-filter >
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<uses-library
android:name="com.google.android.maps"/>
</application>
</manifest>

Para testar o código da aplicação, você deve habilitar as opções de GPS no dispositivo através da opção Settings > Location & Security > My Location. Notem que temos duas opções nessa seção: GPS satélites e redes móveis. A primeira é mais precisa, porém demora mais; já a segunda obtém a posição baseada triangulação entre as distâncias das redes de telefonia ou wireless.

Mas se você estiver usando o emulador, use a aba "Emulator control" da perspectiva DDMS do Eclipse (para exibir, acesse Window > Open perspective > DDMS).

Digite uma coordena válida e clique em "Send". Para ober uma posição, vá no Google Maps, clique com o botão direito e selecione "O que há aqui? | What's here?". Na barra de busca, ficará a latitude e a longitude, aí é só copiar e testar.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

quinta-feira, 20 de outubro de 2011

Android: Dicas 5

Olá povo,

Esse é mais um post da série de dicas de Android. Aproveitem e deixem seus comentários :)

Dica 1 - Alterando a fonte dos componentes
O Android, por padrão, tem apenas 3 fontes que podem ser utilizadas nos componentes visuais: sans, serif e monospace. Para utilizar cada uma delas, basta atribuir o valor desejado à propriedade android:typeface. Mas se quiser utilizar uma nova fonte, basta adicionar o arquivo *.ttf na pasta assets e carregá-la utilizando o código abaixo.
Typeface typeface =
Typeface.createFromAsset(getAssets(), "Aliens.ttf");

TextView txt = (TextView)findViewById(R.id.textView1);
txt.setTypeface(typeface);

Infelizmente, não achei uma forma de setar essa fonte no XML. Se alguém souber, deixe um comentário. Ah! A fonte que usei nesse exemplo foi baixada do site http://www.webpagepublicity.com/free-fonts.html.

Dica 2 - Converter DIP (Density Independent Pixel) para Pixel
Após ministrar duas aulas seguidas (em turmas diferentes) sobre Views personalizadas no Android, uma dúvida frequente foi como converter DIP para PX (pixel). Isso é especialmente útil para que a View rode corretamente em diferentes densidades de tela (LDPI, MDPI, HDPI e agora XHDPI). Para tal, pode-se utilizar o método applyDimension da classe TypeValue.
Resources r = getResources();
float valorEmDp = 14;
float valorEmPixels = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, valorEmDp,
r.getDisplayMetrics());


Dica 3 - Evitando acumular Toasts
Um "bug" clássico que os engenheiros de teste do projeto em que eu trabalho levantam é sobre a utilização do Toast. Se você chamar o método show() dessa classe várias vezes seguidas, essas mensagens são acumuladas e ficam sendo exibidas sequencialmente. Um recurso que utilizo é criar um método separado, que verificará se já existe um Toast aberto, em caso positivo, ele o cancela para exibir um novo. Para tal, você deve criar um atributo da classe Toast (que abaixo chamo de 'toast') e utilizar o seguinte código:
// Declare o atributo
private static Toast toast;

// Método
public static void showToast(Context ctx, int res){
if (toast != null){
toast.cancel();
toast.setText(res);
} else {
toast = Toast.makeText(ctx, res, Toast.LENGTH_LONG);
}
toast.show();
}


Dica 4 - Definindo onde instalar sua aplicação
A partir da versão 2.2 (Froyo) é possível especificar onde sua aplicação pode ser instalada: cartão de memória ou memória interna. Por padrão, a aplicação será instalada na memória interna, mas você pode utilizar a propriedade android:installLocation da tag <manifest> do AndroidManifest.xml para um dos valores abaixo:
internalOnly - A aplicação só poderá ser instalada na memória interna (valor padrão).
auto - A aplicação é instalada na memória interna, mas poderá ser movida para o cartão de memória posteriormente ou quando a memória interna estiver cheia.
preferExternal - A aplicação deve ser instalada no cartão de memória preferencialmente, mas isso não é garantido.

Exemplo:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
... >


Dica 5: Ativar/Acender a tela do aparelho
Neste post eu mostrei como criar notificações no Android. Um recurso que pode ser utilizado juntamente com as notificações é acender/ligar a tela do aparelho. Para fazer isso, deve-se utilizar a classe PowerManager.

// Obtém a instância do PowerManager
PowerManager pm = (PowerManager)c.getSystemService(
Context.POWER_SERVICE);

// Liga o display do aparelho
WakeLock wakeLock = pm.newWakeLock(
// Liga a tela
PowerManager.SCREEN_DIM_WAKE_LOCK |
// Após liberar a tela para apagar,
// mantém a tela ligada por um pouco
// mais de tempo
PowerManager.ON_AFTER_RELEASE,
// Tag para debug
"tag_para_debug");

// Liga a tela por 10 segundos
wakeLock.acquire(10000);


Para usar o código acima, deve-se adicionar a permissão WAKE_LOCK no AndroidManifest.xml.
<uses-permission name="android.permission.WAKE_LOCK"/>


Dica 6: debug.keystore expirou

Essa dica foi enviada pelo meu colega Ricardo Gilson.
Toda aplicação Android é assinada para ser instalada no dispositivo, por padrão, ela é assinada com uma assinatura de debug através do arquivo debug.keystore que fica na subpasta ".android" no diretório do usuário (C:\Users\usuario\.android ou /Users/usuario/.android). Depois de um certo tempo sem utilizar a máquina, a mensagem abaixo pode ser exibida ao tentar rodar uma aplicação:

Error generating final archive: Debug Certificate expired on 24/09/2011.

Para resolver o problema é só apagar o arquivo debug.keystore, dar um clean no projeto do Eclipse (menu Project > Clean) e tentar rodar novamente.

Dica 7: Habilite a aceleração de Hardware (por Fernado Fragoso)
Habilitar a aceleração de hardware quando necessitar de manipulação de imagens, adicionar na tag application do AndroidManifest.xml.

android:hardwareAccelerated="true"

Faz uma diferença gigantesca! Valeu Fernando!

Por hoje é só, qualquer dúvida ou sugestão, deixem seus comentários.

4br4ç05,
nglauber