Post atualizado para Android 6+ e ampliado em 21/05/17!
Olá, este será mais um post sobre a melhor plataforma de desenvolvimento mobile da atualidade, o Android. Os números não me deixam mentir, com o SO do Google dominando mais de 50% do mercado de smartphones no mundo e com centenas de milhares de aplicativos disponíveis na Google Play.
Neste post, irei tratar do uso do hardware de GPS presente na maioria dos dispositivos com Android. A ideia é utilizar tal hardware para descobrir onde o usuário da app está e com essa informação em mãos, você fazer o que quiser, como “pinnar” esta localização em um mapa do Google Maps, por exemplo.
Mas já não existe aplicações que fazem isso?
Sim, e aos montes.
Então por que diabos eu iria querer aprender a fazer isso?
Simplesmente porque a ideia aqui é estudar o funcionamento de aplicações que consumam os dados do GPS nativo do aparelho, para então, desenvolver suas próprias aplicações que necessitem de tais dados. Como exemplo podemos citar aplicações como o Whatsapp, que permite que você envie a sua localização para um contato ou o Google Latitude, que permite ver onde seus amigos estão neste exato momento, só para citar algumas.
No meu caso o estudo é para ajudar um amigo que está fazendo um TCC de faculdade e está tendo dificuldades com isso.
Veremos neste artigo:
- Usando a Android Location-Based API
- Ajustando o nível de permissão
- Criando o layout de sua aplicação
- Escrevendo o código da sua Activity
- Testando rotas GPS
Usando a Android Location-Based API
O Android SDK contém uma API que foi criada especialmente para utilizar de forma abstrata sua Activity com qualquer hardware de GPS que possa existir em seu dispositivo. Uma vez que normalmente trabalhamos com um emulador baseado em software (O AVD – Android Virtual Device), e não com um dispositivo de verdade, a presença do hardware de GPS terá que ser simulada.
Para saber se o GPS do seu emulador está corretamente configurado, execute ele pelo AVD Manager (fica na toolbar do Android Studio e demais IDEs) acesse Configurações/Settings > Location/Localização. Deixe o GPS ligado (on) e se quiser maior precisão, mude o Mode/Modo de Device Only para High Accuracy, que usará além do hardware de GPS do dispositivo, a Internet para ter uma localização mais precisa.
Uma vez que seu AVD tenha suporte à GPS, você pode usar o console do ADB (Android Debugger Bridge) para enviar comandos de localização, simulando que o usuário do dispositivo se locomoveu. Também pode usar linha de comando via telnet e até mesmo o simulador para enviar as coordenadas que você deseja testar.
O jeito mais comum é usando os controles estendidos do simulador, como mostra a imagem abaixo (note que aquela barra da esquerda fica à direita do AVD quando ele está rodando).
Nesta tela você tem campos para definir latitude, longitude e muito mais, brinque à vontade (no final do artigo ensino como configurar rotas completas aqui)!
A outra forma, é usando o Console do ADB, que você habilita indo na aba Gerenciador de Dispositivos e clicando no ícone Console. Uma vez com o console aberto, você pode enviar comandos ao emulador que está rodando em seu AVD. O comando que você utiliza para enviar posições GPS é o geo. O geo na verdade é um grupo de comandos para manipulação de localização, e o que iremos usar é o geo fix. Digitando “geo fix help” no console você consegue dicas de como usar este comando. Resumindo, basta digitar “geo fix valorLongitude valorLatitude” visto que dificilmente você irá usar os demais parâmetros, como altitude, por exemplo. A figura abaixo mostra o funcionamento do console, inserindo as coordenadas de Porto Alegre-RS:
Se preferir, você pode executar esse mesmo comando via cmd/shell usando telnet, da seguinte maneira:
1 2 3 4 5 |
telnet localhost 5554 auth /OYaznXpPMc6HJz5 geo fix <longitude> <latitude> |
Aquele comando auth ali pede um token de acesso ao AVD que você está se conectando. Você consegue esse token indo na sua pasta de usuário com um arquivo oculto que o próprio telnet vai te dizer o nome (no meu caso era ‘/Users/luizfduartejr/.emulator_console_auth_token‘). É só abrir o arquivo com um bloco de notas e copiar o token pra usar no comando auth. Depois, basta usar o geo fix passando longitude e latitude (nessa ordem) para alterar a localização do GPS do simulador em tempo real.
Entendeu?
O restante deste post irá tratar exclusivamente do desenvolvimento de uma aplicação chamada “OndEstou” que identifica a localização atual do usuário, em tempo real, sendo esta primeira parte bem simples, pois apenas exibirá a informação de latitude e longitude de onde o usuário se encontra, lhe dando uma base para prosseguir com o restante dos estudos da API. Os seguintes passos serão executados para que nossa demo tenha sucesso: devemos ajustar os níveis de permissão, criar o layout de nossa Activity, escrever o código da Activity e testá-la.
Ajustando o Nível de Permissão
O primeiro passo para trabalhar com a Android Location-Based API é ajustar o nível de permissão. Usar a API por si só não requer nenhuma permissão específica, mas usar a mesma para acessar informações de localização no GPS sim. A maneira mais fácil de setar permissões para um aplicativo é através da edição do manifesto do seu projeto, que é um arquivo XML muito simples.
Primeiramente, vamos criar um novo projeto Android com o nome de OndEstou, a versão do Android que vou usar aqui é a Marshmallow (6.0) mas poderia ser qualquer uma após a 2.2, com a Activity MainActivity.java. Em seguida, vamos abrir o arquivo AndroidManifest.xml e incluir o seguinte código XML logo antes da tag </manifest>, que adiciona a permissão de GPS:
1 2 3 4 |
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" /> |
Note que adicionou duas vezes a mesma permissão, mas a segunda linha é especificamente para a versão 6+ do Android, pois a partir dessa versão a granularidade das permissões mudaram. Antes da v6, o Android somente pedia as permissões necessárias para rodar um app na hora da instalação e, uma vez concedida, o usuário não tinha mais controle sobre o que o app iria fazer com essa permissão. Agora, os usuários de Android 6+ podem desativar permissões específicas que não concordem em fornecer e com isso todo um modelo novo de programação Android deve ser pensado para tratar as possibilidades que isso trouxe.
Mas não se preocupe, vou mostrar como você deve fazer direitinho! 😉
Criando o layout de sua aplicação
Para iniciar o desenvolvimento do seu layout, abra o arquivo activity_main.xml que fica na sua pasta res/layout. Você terá de adicionar um Button e quatro TextView no seu layout, como a tela abaixo mostra (dois TextViews estão sem texto, logo ao lado de latitude e longitude).
Este layout pode ser obtido com o código de exemplo abaixo, mas encorajo que você tente criar o seu sem utilizar este código (afinal, é um layout bem simples):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="br.com.luiztools.ondestou.MainActivity"> <Button android:id="@+id/btnGps" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Onde estou?"/> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large" android:text="Latitude: "/> <TextView android:id="@+id/txtLatitude" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large" android:text="Longitude: "/> <TextView android:id="@+id/txtLongitude" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout> |
Escrevendo o código da sua Activity
Agora que você criou o layout, você pode começar a escrever o código que irá executar sua Activity. Seu Button precisa iniciar o serviço de monitoramento do GPS (não obrigatoriamente, mas aqui resolvi iniciar no botão), para então enviar a latitude e longitude ao TextView correspondente.
Primeiro, vou colocar aqui as diretivas import que usaremos. Você pode adicioná-las desde já no topo da sua MainActivity.java ou ir adicionando aos poucos, usando o recurso automático que as IDEs possuem, como o ALT+Enter do Android Studio e do NetBeans (aguarde ele grifar sua classe não encontrada e depois execute o comando ALT+Enter para adicionar o import necessário):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; |
A seguir, devemos criar o código para o evento OnCreate de sua Activity, que irá carregar os widgets de sua aplicação e definir o algoritmo para o evento Click do Button. O objetivo é que quando se clique no botão, seja registrado um Listener para o serviço de GPS que passará a enviar informações de localização ao app.
No entanto, vamos ter de antes verificar (e talvez pedir) permissões do usuário, por isso no clique do botão chamaremos um método pedirPermissoes() que programaremos mais pra frente. O código abaixo exemplifica o código necessário no momento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Button btnGps; TextView txtLatitude, txtLongitude; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtLatitude = (TextView) findViewById(R.id.txtLatitude); txtLongitude = (TextView) findViewById(R.id.txtLongitude); btnGps = (Button) findViewById(R.id.btnGps); btnGps.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { pedirPermissoes(); } }); } |
Note que ainda não criamos o método pedirPermissoes(), então é natural que sua IDE acuse um erro neste código. Além de carregarmos o botão, carregamos os TextViews que serão editados (txtLongitude e txtLatitude, neste exemplo). Os demais TextViews não serão instanciados pois eles são apenas rótulos de texto, e não serão editados posteriormente.
Agora vamos ao método pedirPermissoes, que deve ser colocado dentro da sua Activity, antes ou depois do onCreate:
1 2 3 4 5 6 7 8 9 10 11 12 |
private void pedirPermissoes() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); } else configurarServico(); } |
Aqui temos um if onde verificamos se o app já possui permissão para acessar a localização do usuário (precisa/FINE ou imprecisa/COARSE), se ele já tiver essa permissão, chamaremos um método configurarServico() que programaremos depois.
Caso ele não tenha a permissão necessária (o que somente pode acontecer na versão 6+ do Android), chamamos um método padrão do Android para requisitar a permissão que queremos, o ActivityCompat.requestPermissions, que estou reproduzindo novamente abaixo apenas para discutirmos ele melhor.
1 2 3 |
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); |
Neste método o primeiro parâmetro é o Context (this indica a Activity atual), o segundo parâmetro é um array de String pedindo as permissões que queremos (neste caso a de GPS) e o terceiro parâmetro é um código dessa requisição de permissão, que será usado mais tarde e que eu defini arbitrariamente aqui como 1.
Essa método é nativo do Android e vai disparar para o usuário uma solicitação padrão de permissão, informando à ele qual app está pedindo qual permissão. Para que possamos capturar a resposta do usuário, devemos sobrescrever o método onRequestPermissionsResult, como abaixo, em qualquer parte da sua Activity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case 1: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { configurarServico(); } else { Toast.makeText(this, "Não vai funcionar!!!", Toast.LENGTH_LONG).show(); } return; } } } |
Neste método, que será disparado automaticamente após o usuário decidir se vai dar permissão ou não ao seu app, avaliamos o requestCode (aquele que definimos como 1, lembra?) para saber de qual requisição de permissão estamos falando e testamos se o usuário deu ou não a permissão. Se ele concordou, executaremos um método configurarServico(), que ainda não programamos. Caso contrário, aqui apenas joguei um Toast reclamando pro usuário…
Agora sim vamos fazer o método que de fato lê o serviço de GPS do Android! Este método é o configurarServico() e ele invocará um objeto LocationManager, que carrega o serviço de localização do dispositivo, como segue (coloque dentro da Activity):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public void configurarServico(){ try { LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) { atualizar(location); } public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } }; locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); }catch(SecurityException ex){ Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show(); } } |
Neste método defini um listener que ficará atento às mudanças de localização do GPS e, a cada mudança, chama um método atualizar que fará o refresh na tela com as novas informações. Os demais métodos do listener eu não implementei, mas dizem respeito ao desligamento do recurso de GPS, por exemplo, que eu não me importo no momento. Já o try/catch foi obrigação do Android.
E por fim, devemos codificar o método atualizar, que nada mais faz do que pegar o objeto Location enviado pelo serviço de GPS e jogar suas informações nos TextViews correspondentes à latitude e longitude:
1 2 3 4 5 6 7 8 9 10 |
public void atualizar(Location location) { Double latPoint = location.getLatitude(); Double lngPoint = location.getLongitude(); txtLatitude.setText(latPoint.toString()); txtLongitude.setText(lngPoint.toString()); } |
Para testar sua aplicação, basta rodá-la no AVD que configuramos no início desse tutorial e clicar no botão “Onde estou?” para inicializar o monitoramento do serviço de GPS. Se estiver usando Android 6+, na primeira execução ele vai lhe pedir a permissão de GPS para funcionar. Uma vez funcionando, sempre que seu AVD perceber que mudou a localização GPS dele (via telnet, console, DDMS, etc) ele vai atualizar as coordenadas sozinho. O resultado esperado você vê abaixo:
Testando rotas GPS
Mas e se eu quiser testar uma rota GPS no simulador, como faço?
Neste caso você deve criar um arquivo XML contendo todos os pontos GPS que o seu dispositivo vai percorrer nessa rota e subir pela janela de controles estendidos do AVD, como mostrei na imagem no início do post. Como aqui ninguém gosta de criar essas coisas na mão, vou te dar uma barbada.
#1 – Abre o Google Maps e traça a rota que deseja testar
Neste exemplo, tracei uma rota de Gravataí/RS até Porto Alegre/RS. Agora copie a URL que está na barra de endereço do seu navegador, pois ela contém todas as informações dessa rota.
#2 – Gere o arquivo XML a partir do Google Maps usando o Maps to GPX
Abra o site https://mapstogpx.com/ e cole a sua URL do Google Maps na barra de pesquisa, clicando em Lets Go, ele vai baixar um arquivo .gpx com as coordenadas da sua rota na sua pasta Downloads.
#3 – Suba o arquivo GPX no AVD
Abra a janela de controles estendidos do AVD, com seu app OndEstou rodando, e na aba Location, use a opção Load GPX/XML que fica abaixo na direita, procure o seu arquivo GPX e dê um play para ver sua rota funcionando no app.
Quando precisar alterar o seu GPX, basta carregar um novo e dar novamente o play. Note que algumas rotas podem demorar até terminar, isso principalmente quando as distâncias são grandes, etc. Você pode inclusive aumentar a velocidade da simulação para ter certeza que está funcionando toda a rota.
Outra opção é usando essa ferramenta bem bacana que permite a você alterar a localização do GPS do seu AVD visualmente, usando um mapa do Google Maps. Para fazer funcionar, baixe a versão release mais recente e execute com o seguinte comando no console:
1 2 3 |
java -jar android-gps-emulator-0.2.0.war |
Uma vez com o emulador de GPS funcionando (note que você tem de colocar o caminho completo até o local onde baixou o arquivo .war), abra seu navegador (do desktop mesmo) e acesse o seguinte endereço: http://localhost:8080/gpsemulator/ para iniciar o emulador e você começar a brincar de viajar com seu GPS pelo mapa.
Se quiser evoluir essa aplicação, dê uma olhada neste tutorial de como criar um app com mapa.
Espero que tenham gostado do post e até a próxima.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Post muito bacana! Obrigado por compartilhar com a gente seu conhecimento.
Uma dúvida dentro do assunto: pra criar um app tipo o uber, segue esse mesmo esquema? Digo, um app de intermediar algo e visualizar o percurso do motorista e a localização do cliente no mapa.
Um abraço
Valeu pelo feedback. Sim, mas daí você tem de pinar no mapa a localização de cada um, pintar trajetória, etc. É mais complicado visualmente de fazer, mas a coleta de coordenadas é igual à desse post. Tem um post aqui no blog sobre como usar mapas no Android que pode te ajudar, dá uma pesquisada ali na barra de busca da esquerda.
Muito bom suas publicações e dicas.
Venho pesquisando a possibilidade de produzir um APP de controle de passagens, utilizando cartões contact less. Primeiro preciso saber se há possibilidade de fazer um aparelho Android “conversar” com um equipamento de gravação/leitura destes cartões, e a segunda, mas não menos importante, saber se há possibilidade da aplicação definir o valor da passagem com base no embarque, gravando no cartão a coordenada do local onde embarcou, e posteriormente onde ele desceu.
Exemplo usando seu mapa da região, entre São Leopoldo e Porto Alegre temos as cidades de Sapucaia, Esteio e Canoas, ou seja, cinco cidades ao todo. Digamos que temos as seguintes tarifas para o trecho:
São Leopoldo – Porto Alegre – R$5,00
São Leopoldo – Canoas – R$4,00
São Leopoldo – Esteio – R$3,00
São Leopoldo – Sapucaia – R$2,00
Ao passageiro embarcar em São Leopoldo e solicitar a “passagem” até Canoas, será gravado no cartão uma tarifa de R$4,00, Origem São Leopoldo e Destino Canoas, mas ele decide descer em Porto Alegre. Ao apresentar o cartão a leitora, o APP teria como informar que há diferença de R$1,00? Há possibilidade de administrar quadrantes dentro do mapa?
Abraço, obrigado.
Obrigado pelo feedback André.
O dispositivo Android, facilmente, consegue conversar com qualquer aparelho que tenha interfaces web, USB, bluetooth e NFC (dependendo do aparelho). Se o aparelho que grava/lê possuir alguma dessas interfaces, tranquilo. Caso contrário, não faço ideia.
O lance das coordenadas é possível também. Toda a parte de administração de regras de negócio em cima das coordenadas, preços, etc fica sob seu controle e desenvolvimento. Pro Android não há diferença alguma.
Parabéns pela postagem!
Fico feliz que tenha gostado. É um tutorial antigo, ainda funcionou?
Esse post vai me ajudar muito para o trabalho da faculdade, muito obrigado! Luiz, uma dúvida, em localização em tempo real onde caso o celular fique sem sinal, o GPS não funcionaria, correto? Na sua percepção, como você acha que daria para fazer um sistema que registra a sua última localização de quando ainda tinha sinal? A ponto de deixar guardada. Não sei se minha dúvida ficou bem esclarecida, tentei explicar da melhor maneira possível. Aguardo retorno, abraços!
Sem sinal o GPS não funciona pois o smartphone não tem um GPS real, mas um GPS assistido pelo sinal telefônico. Nunca fiz o que você deseja fazer, mas acho que eu guardaria de tempos em tempos a posição localmente em um SQLite ou remotamente via API, dependendo de quem precise dessa informação da última localização (você ou um terceiro).
Ótimo conteúdo.
Teria como guardar as coordenadas que o GPS traçou? Tipo no Google mãos? Com data e horas?
Parabéns?
Para isso você terá de salvar em um banco de dados. Tem tutoriais de banco de dados aqui no blog e também no canal.
Outra pergunta? Se uma pessoa utilizar o aplicativo (onde estou).
Tem como acompanhar isso no Google maps? Ou em outro aplicativo de mapa qualquer?
Mais uma vez parabéns.
Uma dica! Seria bom você publicar isso em 1 canal no YouTube. Você terá até mais retorno financeiramente falando. Só acho.
Se procurar por LuizTools no Youtube achará meu canal, mas não trabalho mais com Android SDK tem vários anos, então não encontrará vídeos disso por lá. Esse post mesmo é bem antigo, mas fico feliz se ele ainda lhe ajudar de alguma forma.