Um dos recursos exclusivos dos aplicativos para dispositivos móveis é o reconhecimento de local. Os usuários levam os aparelhos para todo lugar, e o reconhecimento de local oferece uma experiência mais contextual.
Exemplos de código
O repositório ApiDemos (link em inglês) no GitHub inclui exemplos que demonstram o uso do local em um mapa:
Kotlin
- MyLocationDemoActivity: usar a camada "My Location", incluindo permissões de execução
- LocationSourceDemoActivity: usar uma
LocationSource
personalizada - CurrentPlaceDetailsOnMap: encontrar a localização atual de um dispositivo Android e mostrar detalhes do lugar (ponto de interesse comercial ou de outro tipo) nesse local. Veja o tutorial sobre como exibir detalhes do lugar atual em um mapa.
Java
- MyLocationDemoActivity: usar a camada "My Location", incluindo permissões de execução
- LocationSourceDemoActivity: usar uma
LocationSource
personalizada - CurrentPlaceDetailsOnMap: encontrar a localização atual de um dispositivo Android e mostrar detalhes do lugar (ponto de interesse comercial ou de outro tipo) nesse local. Veja o tutorial sobre como exibir detalhes do lugar atual em um mapa.
Trabalhar com dados de local
Os dados disponíveis para um dispositivo Android incluem a localização atual do aparelho (identificado usando uma combinação de tecnologias), a direção e o método de movimento, e se o dispositivo foi movido em uma fronteira geográfica predefinida ou virtual. Há diversas formas de trabalhar com os dados de local, dependendo das necessidades do aplicativo:
- A camada My Location oferece uma maneira simples de mostrar a localização de um dispositivo no mapa. Ela não fornece dados.
- A API Location do Google Play Services é recomendada em todas as solicitações programáticas para os dados de local.
- A interface
LocationSource
permite que você indique um provedor de local personalizado.
Permissões de localização
Se o seu app precisar acessar o local do usuário, peça permissão adicionando as permissões de localização relevantes do Android.
O Android oferece duas permissões desse tipo: ACCESS_COARSE_LOCATION
e ACCESS_FINE_LOCATION
. A permissão escolhida determina a precisão do local retornado pela API.
android.permission.ACCESS_COARSE_LOCATION
: permite que a API retorne o local aproximado do dispositivo. A permissão dá uma estimativa de local do dispositivo com base nos serviços de localização, conforme descrito na documentação sobre precisão do local aproximado.android.permission.ACCESS_FINE_LOCATION
: permite que a API determine a localização mais exata possível com base nos provedores de local disponíveis, incluindo o Sistema de Posicionamento Global (GPS), além de dados móveis e de Wi-Fi.
Adicionar as permissões ao manifesto do app
Se o local aproximado for necessário apenas para que o aplicativo funcione, adicione a permissão ACCESS_COARSE_LOCATION
ao arquivo de manifesto do app:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp" >
...
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
...
</manifest>
No entanto, se o local exato for necessário, adicione as permissões ACCESS_COARSE_LOCATION
e ACCESS_FINE_LOCATION
ao arquivo de manifesto do app:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp" >
...
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
...
</manifest>
Pedir permissões de execução
O Android 6.0 (Marshmallow) introduziu um novo modelo de gerenciamento de permissões, que otimiza o processo na instalação e atualização de apps. Se seu aplicativo segmenta o nível de API 23 ou superior, você pode usar o novo modelo de permissões.
Se o app for compatível com esse novo modelo e o dispositivo executar o Android 6.0 (Marshmallow) ou versões mais recentes, o usuário não terá que dar permissões quando instalar ou atualizar o aplicativo. Enquanto aberto, o app precisa verificar se a permissão necessária existe e pedi-la, se for o caso. O sistema mostra uma caixa de diálogo pedindo a permissão ao usuário.
Para oferecer a melhor experiência, é importante solicitar a permissão no contexto. Se o local for essencial para o funcionamento do seu app, solicite a permissão de localização na inicialização do aplicativo. Uma boa maneira de fazer isso é com uma tela de boas-vindas ou um assistente que informa aos usuários por que a permissão é necessária.
Se ela só for necessária para alguns recursos, solicite apenas quando o aplicativo executar a ação relacionada.
Caso o usuário não conceda permissão, o app precisará tratar a situação com normalidade. Por exemplo, se ela for necessária para um recurso, basta desativar a função relacionada. Se a permissão for essencial para o funcionamento do aplicativo, o app poderá desativar todos os recursos e informar que ela precisa ser usada.
O exemplo de código a seguir verifica se há permissão usando a biblioteca do AndroidX antes de ativar a camada "My Location". Depois, processa o resultado do pedido de permissão implementando o objeto ActivityCompat.OnRequestPermissionsResultCallback
da Biblioteca de Suporte:
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.example.kotlindemos
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback
import androidx.core.content.ContextCompat
import com.example.kotlindemos.PermissionUtils.PermissionDeniedDialog.Companion.newInstance
import com.example.kotlindemos.PermissionUtils.isPermissionGranted
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener
import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
/**
* This demo shows how GMS Location can be used to check for changes to the users location. The
* "My Location" button uses GMS Location to set the blue dot representing the users location.
* Permission for [Manifest.permission.ACCESS_FINE_LOCATION] and [Manifest.permission.ACCESS_COARSE_LOCATION]
* are requested at run time. If either permission is not granted, the Activity is finished with an error message.
*/
class MyLocationDemoActivity : AppCompatActivity(),
OnMyLocationButtonClickListener,
OnMyLocationClickListener, OnMapReadyCallback,
OnRequestPermissionsResultCallback {
/**
* Flag indicating whether a requested permission has been denied after returning in
* [.onRequestPermissionsResult].
*/
private var permissionDenied = false
private lateinit var map: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_location_demo)
val mapFragment =
supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
googleMap.setOnMyLocationButtonClickListener(this)
googleMap.setOnMyLocationClickListener(this)
enableMyLocation()
}
/**
* Enables the My Location layer if the fine location permission has been granted.
*/
@SuppressLint("MissingPermission")
private fun enableMyLocation() {
// 1. Check if permissions are granted, if so, enable the my location layer
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
map.isMyLocationEnabled = true
return
}
// 2. If if a permission rationale dialog should be shown
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) || ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
)
) {
PermissionUtils.RationaleDialog.newInstance(
LOCATION_PERMISSION_REQUEST_CODE, true
).show(supportFragmentManager, "dialog")
return
}
// 3. Otherwise, request permission
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
LOCATION_PERMISSION_REQUEST_CODE
)
}
override fun onMyLocationButtonClick(): Boolean {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT)
.show()
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false
}
override fun onMyLocationClick(location: Location) {
Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG)
.show()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
super.onRequestPermissionsResult(
requestCode,
permissions,
grantResults
)
return
}
if (isPermissionGranted(
permissions,
grantResults,
Manifest.permission.ACCESS_FINE_LOCATION
) || isPermissionGranted(
permissions,
grantResults,
Manifest.permission.ACCESS_COARSE_LOCATION
)
) {
// Enable the my location layer if the permission has been granted.
enableMyLocation()
} else {
// Permission was denied. Display an error message
// Display the missing permission error dialog when the fragments resume.
permissionDenied = true
}
}
override fun onResumeFragments() {
super.onResumeFragments()
if (permissionDenied) {
// Permission was not granted, display error dialog.
showMissingPermissionError()
permissionDenied = false
}
}
/**
* Displays a dialog with error message explaining that the location permission is missing.
*/
private fun showMissingPermissionError() {
newInstance(true).show(supportFragmentManager, "dialog")
}
companion object {
/**
* Request code for location permission request.
*
* @see .onRequestPermissionsResult
*/
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
}
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.example.mapdemo;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.widget.Toast;
/**
* This demo shows how GMS Location can be used to check for changes to the users location. The "My
* Location" button uses GMS Location to set the blue dot representing the users location.
* Permission for {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and {@link
* android.Manifest.permission#ACCESS_COARSE_LOCATION} are requested at run time. If either
* permission is not granted, the Activity is finished with an error message.
*/
public class MyLocationDemoActivity extends AppCompatActivity
implements
OnMyLocationButtonClickListener,
OnMyLocationClickListener,
OnMapReadyCallback,
ActivityCompat.OnRequestPermissionsResultCallback {
/**
* Request code for location permission request.
*
* @see #onRequestPermissionsResult(int, String[], int[])
*/
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
/**
* Flag indicating whether a requested permission has been denied after returning in {@link
* #onRequestPermissionsResult(int, String[], int[])}.
*/
private boolean permissionDenied = false;
private GoogleMap map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_location_demo);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
map = googleMap;
map.setOnMyLocationButtonClickListener(this);
map.setOnMyLocationClickListener(this);
enableMyLocation();
}
/**
* Enables the My Location layer if the fine location permission has been granted.
*/
@SuppressLint("MissingPermission")
private void enableMyLocation() {
// 1. Check if permissions are granted, if so, enable the my location layer
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
map.setMyLocationEnabled(true);
return;
}
// 2. Otherwise, request location permissions from the user.
PermissionUtils.requestLocationPermissions(this, LOCATION_PERMISSION_REQUEST_CODE, true);
}
@Override
public boolean onMyLocationButtonClick() {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show();
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false;
}
@Override
public void onMyLocationClick(@NonNull Location location) {
Toast.makeText(this, "Current location:\n" + location, Toast.LENGTH_LONG).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (PermissionUtils.isPermissionGranted(permissions, grantResults,
Manifest.permission.ACCESS_FINE_LOCATION) || PermissionUtils
.isPermissionGranted(permissions, grantResults,
Manifest.permission.ACCESS_COARSE_LOCATION)) {
// Enable the my location layer if the permission has been granted.
enableMyLocation();
} else {
// Permission was denied. Display an error message
// Display the missing permission error dialog when the fragments resume.
permissionDenied = true;
}
}
@Override
protected void onResumeFragments() {
super.onResumeFragments();
if (permissionDenied) {
// Permission was not granted, display error dialog.
showMissingPermissionError();
permissionDenied = false;
}
}
/**
* Displays a dialog with error message explaining that the location permission is missing.
*/
private void showMissingPermissionError() {
PermissionUtils.PermissionDeniedDialog
.newInstance(true).show(getSupportFragmentManager(), "dialog");
}
}
Camada "My Location"
Você pode usar a camada e o botão "Meu local" se quiser mostrar ao usuário a posição atual dele no mapa. Chame mMap.setMyLocationEnabled()
para ativar a camada "My Location".
O exemplo a seguir mostra um uso simples da camada "My Location":
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.maps.example.kotlin
import android.annotation.SuppressLint
import android.location.Location
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener
import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.maps.example.R
internal class MyLocationLayerActivity : AppCompatActivity(),
OnMyLocationButtonClickListener,
OnMyLocationClickListener,
OnMapReadyCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_location)
val mapFragment =
supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
@SuppressLint("MissingPermission")
override fun onMapReady(map: GoogleMap) {
// TODO: Before enabling the My Location layer, you must request
// location permission from the user. This sample does not include
// a request for location permission.
map.isMyLocationEnabled = true
map.setOnMyLocationButtonClickListener(this)
map.setOnMyLocationClickListener(this)
}
override fun onMyLocationClick(location: Location) {
Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG)
.show()
}
override fun onMyLocationButtonClick(): Boolean {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT)
.show()
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false
}
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.maps.example;
import android.annotation.SuppressLint;
import android.location.Location;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
class MyLocationLayerActivity extends AppCompatActivity
implements GoogleMap.OnMyLocationButtonClickListener,
GoogleMap.OnMyLocationClickListener,
OnMapReadyCallback {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_location);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@SuppressLint("MissingPermission")
@Override
public void onMapReady(GoogleMap map) {
// TODO: Before enabling the My Location layer, you must request
// location permission from the user. This sample does not include
// a request for location permission.
map.setMyLocationEnabled(true);
map.setOnMyLocationButtonClickListener(this);
map.setOnMyLocationClickListener(this);
}
@Override
public void onMyLocationClick(@NonNull Location location) {
Toast.makeText(this, "Current location:\n" + location, Toast.LENGTH_LONG)
.show();
}
@Override
public boolean onMyLocationButtonClick() {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT)
.show();
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false;
}
}
Quando essa camada está ativada, o botão "Meu local" aparece no canto superior direito do mapa. Quando um usuário clica no botão, a câmera centraliza a visualização no local atual do dispositivo, se for conhecido. A localização será indicada no mapa como um pequeno ponto azul se o dispositivo estiver parado ou como uma seta se estiver em movimento.
A captura de tela a seguir mostra o botão "Meu local" no canto superior direito e o ponto azul relacionado no centro do mapa:
Para impedir que o botão "Meu local" seja exibido, chame UiSettings.setMyLocationButtonEnabled(false)
.
Seu app pode responder aos seguintes eventos:
- Se o usuário clicar no botão "Meu local", o app receberá um callback
onMyLocationButtonClick()
doGoogleMap.OnMyLocationButtonClickListener
. - Se ele clicar no ponto azul, o app vai receber um callback
onMyLocationClick()
doGoogleMap.OnMyLocationClickListener
.
API Location do Google Play Services
A API Location do Google Play Services é o método preferido para adicionar o reconhecimento de local ao seu app Android. Ela inclui recursos que permitem:
- determinar a localização do dispositivo;
- ouvir as mudanças de local;
- determinar o meio de transporte, se o dispositivo está em movimento;
- criar e monitorar regiões geográficas predefinidas, conhecidas como fronteiras geográficas virtuais.
Com as APIs Location, é mais fácil criar aplicativos com reconhecimento de local e baixo consumo de bateria. Assim como o SDK do Maps para Android, a API Location é distribuída como parte do SDK do Google Play Services. Para mais informações sobre essa API, consulte a classe de treinamento Como permitir o reconhecimento de local do seu app do Android ou a Referência da API Location. Exemplos de código são incluídos como parte do Google Play Services SDK.