Neste post pretendo falar sobre uma das funcionalidades mais interessantes disponíveis no Android, o Google Maps. Vamos integrar esta tecnologia junto com o GPS para concluirmos o artigo com um projeto funcional muito útil. Apesar desta funcionalidade não ser difícil de ser compreendida, é requisito deste artigo ter conhecimentos básicos sobre desenvolvimento para a plataforma Android.

Configuração

A API do Google Maps disponibilizada pelo Google é muito simples de ser utilizada no ponto de vista de codificação. No entanto, o desenvolvedor pode ter um pouco de trabalho para configurar um projeto com esta tecnologia.

Nota: Os passos que serão descritos a seguir já levam em conta que o leitor tenha o ambiente Android configurado em sua máquina. Caso este não seja o seu caso, acesse este link (http://developer.android.com/tools/index.html) para executar esta tarefa.

Vejamos os passos para se criar um projeto com o Google Maps.

  • Passo 1: Crie um novo projeto Android (no eclipse ou Android Developer Tool).
    • No meu caso meu projeto se chama GooglePlaces e tem o seguinte pacote base: br.com.lvc.googleplaces
  • Passo 2: Configurar libs (adicionar as bibliotecas Google-play-service e android-support-v4)
    • Google-play-service é um projeto biblioteca que fica localizado:

Pasta do SDK Android à Extras à google à google_play_services à libproject. Adicione-o no eclipse como um projeto biblioteca e o vincule ao seu projeto recém criado. Caso não encontre este projeto siga o link abaixo:

http://developer.android.com/google/play-services/setup.html

  • Para acrescentar o arquivo android-support-v4, basta adicionar o arquivo que se encontra na pasta:
    • Pasta do SDK Android à Extras à android à support à v4 à android-support-v4.jar.
    • No diretório libs do seu projeto Android.
  • Passo 3: Configurar Projeto no Google Console (habilitar serviço e obter chave)

Entre no seguinte endereço Web:

https://code.google.com/apis/console/

Logo na entrada irá aparecer uma lista de projetos já configurados caso você tenha realizado este procedimento antes. Caso contrário aparecerá uma opção para criar um novo projeto. Ao acionar esta opção, será solicitado o nome e o pacote base da aplicação, após preencher essas informações. Clique na opção Services que esta presente na aba superior a esquerda da tela.

clip_image002

Após clicar na Aba services, uma lista de API’s disponibilizada pelo Google será listada para você. Devemos agora selecionar as API’s que iremos utilizar em nosso projeto, são elas: Google Maps Android API V2 e Places API. Habilite estas duas opções, como nas imagens abaixo:

clip_image004

clip_image005

Agora precisamos criar uma chave de acesso que vincule o nosso projeto com as API’s que gostaríamos de utilizar. No menu superior a esquerda selecione o item API Access. Procure pelo botão Create New Android Key.

Ao clicar nesta opção aparecerá a seguinte tela:

clip_image007

Para criar uma chave de acesso, precisamos informar o SHA1 (informação presente na keystore) junto com o nome do pacote base da aplicação. Para recuperar o SHA1, utilize o comando:

keytool -list -v -keystore debug.keystore

Direto no console. Como resultado deste comando receberemos as seguintes informações:

clip_image009

Identifique o código SHA1, presente neste emaranhado de informações. E com este dado, junto como o pacote base (definido no momento da criação de um projeto Android no meu caso br.com.lvc.googleplaces) conseguiremos gerar uma chave de acesso.

No meu caso informei:

36:91:28:DE:8F:ED:96:7C:81:D0:DD:D8:C3:49:2D:11:53:2E:FF:63;br.com.lvc.googleplaces

E obtive a minha chave de acesso:

AIzaSyBk7On0KxUVi9oa81XX_hUNOI3XE4MNPkU

Nota: Cada chave esta vinculada a uma Keystore.Logo se utilizar a minha chave sem a minha keystore seu aplicativo não funcionará corretamente.

Moral da historia: Gere a sua chave a partir do seu keystore.

Passo 4: Configuração do AndroidManifest.xml

O AndroidManifest é um arquivo de configuração da plataforma Android. Todo aplicativo Android é obrigatório ter um, é nele que colocamos as permissões, declaramos as Activitys, Services e BroadcastReceivers que serão utilizados pelo aplicativo. O uso do Maps requer uma configuração especial no AndroidManifest, vamos a esta implementação, para que em seguida possamos iniciar o desenvolvimento do aplicativo. Para facilitar no entendimento desta parte, logo abaixo consta um exemplo de um AndroidManifest já configurado.

Fique atento aos comentários que explicam as particularidades.

Nota: Se você se sentiu perdido nesta configuração, talvez este artigo aqui poderá lhe ajudar a configurar o ambiente. http://www.frameworksystem.com/blog/tutorial-google-maps-android-api-v2/

Com a configuração feita agora podemos enfim codificar o nosso sistema.

Google Maps

A primeira coisa que nos propomos a entender neste artigo é o Google Maps. O Google nos disponibiliza este componente como um Fragment, mais especificamente com.google.android.gms.maps.SupportMapFragment.

Logo se criarmos um layout XML, e incluirmos este Fragment nele. Automaticamente já poderemos fazer uso do Google Maps no nosso aplicativo.

Faça um teste, crie um layout XML e coloque o seguinte código:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>

Em seguida vincule este layout com uma Activity.

Repare que estou herdando FragmentActivity e não diretamente de Activity. Você conseguiria compilar herdando uma Activity, mas teria um problema na execução do projeto caso a Activity não seja a FragmentActivity.

public class MapPuro extends FragmentActivity {
	@Override
	protected void onCreate(Bundle arg0) {
		super.onCreate(arg0);
		setContentView(R.layout.activity_main);
	}
}

clip_image011

Ao executar o projeto teremos algo similar ao apresentado na imagem acima. Nota: Caso você não consiga o resultado acima, reveja os passos de configuração para ver se não esta esquecendo nada. Perceba que você pode dar zoom, e ter acesso a todas as funcionalidades essenciais do Google maps com um mínimo de código.

Mas ainda há mais coisas para ver. Agora vamos pegar uma referencia do objeto GoogleMap, para podermos explorarmos mais as possibilidades que esta tecnologia oferece. Logo abaixo segue uma explanação sobre algumas classes que devemos saber, antes de prosseguir:

GoogleMap: É a classe da API do Android que gerencia o Maps. Através dele conseguimos dar zoom em alguma localização, atribuir marcadores e definir configurações sobre a forma no qual o Map irá se apresentar ao usuário.

LatLng: Representa uma coordenada geográfica, ou seja uma localização Latitude e Longitude.

Logo abaixo segue um exemplo de uso para estas duas estruturas:

private static final LatLng PRACA_TIRADENTES = new LatLng(-19.929762, -43.931773);

SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
GoogleMap map = mapFragment.getMap();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(PRACA_TIRADENTES, 15));

No código acima, estamos recuperando uma referencia de um objeto GoogleMap. E estamos falando que ele aponte a câmera para o LatLng representando pela constante PRACA_TIRADENTES (praça famosa em Belo Horizonte).

Veja que especificamos um grau de zoom para focar a câmera do mapa neste ponto, no caso foi 15, este grau pode ser de 2 até 21 onde 2 é o mais longe e o 21 é o mais próximo possível.Outra funcionalidade legal que vale a pena ser destacada é o método setMapType também presente no GoogleMap.

Este método aguarda uma constante como parâmetro, cada constante representa uma perspectiva diferente de visualização do mapa. As constantes possíveis são:

· MAP_TYPE_NONE
· MAP_TYPE_NORMAL
· MAP_TYPE_SATELLITE
· MAP_TYPE_HYBRID
· MAP_TYPE_TERRAIN

Nota: Veja um post legal somente sobre este método: (http://android-er.blogspot.com.br/2012/12/set-map-type-of-googlemap.html). Em caráter de teste coloquei o seguinte parâmetro:

map.setMapType(GoogleMap.MAP_TYPE_SATELITTE);

podemos ver o resultado na imagem abaixo.

clip_image013

Agora que sabemos um pouco mais sobre a API do Google Maps, vamos implementar uma funcionalidade mais interessante. Como por exemplo, colocar um marcador nossa própria localização atual.

Integração com GPS

Pra mostrar nossa localização no mapa primeiro devemos captura-la.

Para fazer isso a estrutura do Android nos disponibiliza duas classes LocationManager e LocationListener.

Respectivamente o LocationManager possui métodos que possibilitam o inicio e o fim das capturas de localização, e o LocationListener sabe como tratar as localizações que foram capturadas.

Nota: Não vou falar dos detalhes de como utilizar GPS no Android, para isso você pode acompanhar este artigo: http://androiddevbr.wordpress.com/2012/10/21/gps-no-android/.

O LocationListener na verdade é uma interface, e a nossa própria Activity que exibe o Map vai implementa-la. Ao implementar esta interface nos vemos forçados a sobrescrever 4 métodos:

onLocationChanged: Método mais importante, chamado todas as vezes que o sistema capturar uma localização.
onProviderDisabled: Toda localização é capturada por um provedor. Esse método é chamado quando um provedor for bloqueado por um cliente.
onProviderEnabled: Esse método é chamado quando um provedor for liberado por um cliente.
onStatusChanged: Este método é chamado quando o provedor muda de estado. Os estados disponíveis são:

Fora de serviço, Disponível ou Temporariamente Inacessível.

Iremos sobrescrever apenas o onLocationChanged.

Basicamente desejamos que toda vez que uma localização nova for capturada, um marcador será colocado no mapa para indicar a mesma. Com o objetivo estabelecido, devemos agora dar início a captura das localizações. Fazemos isso com método abaixo:

private void startCaptureLocation() {
	LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
	locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, INTERVAL_CAPTURE_IN_MILIS, INTERVAL_PHYSICAL_SPACE_IN_METERS, this);
	locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, INTERVAL_CAPTURE_IN_MILIS, INTERVAL_PHYSICAL_SPACE_IN_METERS, this);
}

Analisando o código acima, podemos perceber que apenas recuperamos uma referencia do LocationManager, e utilizamos o método requestLocationUpdates do próprio LocationManager para dar início ao processo. Este método exige três parametros:

Tipo de provedor, como por exemplo GPS.

O intervalo de captura em mili-segundos, ou seja, passou deste período de tempo ele tentara capturar novamente. E o intervalo de captura em metros, ou seja, passou deste limite em metro ele tentara capturar novamente. Acione este método no onCreate da Activity, deste modo iremos assegurar que assim que o sistema for acionado iremos dar início a captura das localizações. Faça a sua Activity implementar a interface LocationListener que é passada como parâmetro para o início da captura de localizações.

Sobrescreva o método onLocationChanged pelo código abaixo:

@Override

public void onLocationChanged(Location location) {
	MarkerOptions markerOptions = new MarkerOptions();
	LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
	markerOptions.position(latLng);
	markerOptions.title("Eu");
	markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.boneco));
	map.addMarker(markerOptions);
	map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
}

Veja que criamos uma estrutura chamada MarkerOptions. É com esta classe que iremos indicar de modo gráfico um determinado ponto no mapa.

Para criar um MarkerOptions, informamos uma posição(LatLng), no caso uma criada a partir da localização recém capturada pelo dispositivo.

Um título(String), ou seja, um texto que será exibido quando o usuário tocar no ponto demarcado.

E uma imagem que irá aparecer no mapa no local indicado (neste exemplo meu boneco de policial).

Nota: Imagem que escolhi pra mostrar minha localização no mapa http://icons.iconarchive.com/icons/leo-bridle/people/32/Police-man-icon.png

Após acrescentar o marker no map, comandamos a câmera para focar na localização recém capturada. No final teremos o seguinte resultado:

clip_image015

Agora estamos quase no fim deste artigo, la vai as últimas dicas…

Com o intuito de economizar recursos do dispositivo, devemos parar de tratar novas capturas pela Activity quando a mesma for finalizada.

Fazemos isso sobrescrevendo o método onDestroy pelo código abaixo:

@Override

protected void onDestroy() {
	super.onDestroy();
	LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
	locationManager.removeUpdates(this);
}

Este método apenas libera a nossa Activity de tratar novas capturas de localizações.

Pronto! Agora temos um aplicativo Android que exibe as minhas localizações mais recentes no mapa através de uma imagem da minha escolha. Você também aprendeu a configurar este mapa, e a focar a câmera no local que quiser. No final do artigo segue o código completo da nossa Activity.

Até breve.

Nota: Outra referência legal para esse assunto: http://www.androidhive.info/2013/08/android-working-with-google-maps-v2/

Código completo da Activity.

import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapPuro extends FragmentActivity implements LocationListener {

	public static final long MINUTE = 60000;
	public static final long INTERVAL_CAPTURE_IN_MILIS = 2 * MINUTE;
	public static final long INTERVAL_PHYSICAL_SPACE_IN_METERS = 100;
	private static final LatLng PRACA_TIRADENTES = new LatLng(-19.929762, -43.931773);

	private GoogleMap map;

	@Override
	protected void onCreate(Bundle arg0) {

		super.onCreate(arg0);
		setContentView(R.layout.activity_main);
		SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
		map = mapFragment.getMap();
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(PRACA_TIRADENTES, 15));
		map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
		startCaptureLocation();

	}

	private void startCaptureLocation() {

		LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, INTERVAL_CAPTURE_IN_MILIS, INTERVAL_PHYSICAL_SPACE_IN_METERS, this);
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, INTERVAL_CAPTURE_IN_MILIS, INTERVAL_PHYSICAL_SPACE_IN_METERS, this);

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		locationManager.removeUpdates(this);
	}

	@Override
	public void onLocationChanged(Location location) {
		MarkerOptions markerOptions = new MarkerOptions();
		LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
		markerOptions.position(latLng);
		markerOptions.title("Eu");
		markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.boneco));
		map.addMarker(markerOptions);
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
	}

	@Override
	public void onProviderDisabled(String provider) {

	}

	@Override
	public void onProviderEnabled(String provider) {

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {

	}

}