Este tutorial faz parte do curso Android avançado em Kotlin. Você obterá o máximo valor deste curso se trabalhar com os tutoriais em sequência, mas isso não é obrigatório. Todos os tutoriais do curso estão listados na página de destino Android avançado em Kotlin.
Bem-vindo à lição Android avançado em Kotlin sobre cercas geográficas!
A API de geofencing permite definir perímetros, também conhecidos como geofences, que circundam áreas de interesse. Seu aplicativo recebe uma notificação quando o dispositivo cruza uma geocerca, o que permite que você forneça uma experiência relevante quando os usuários estão dentro da área "cercada".
Por exemplo, um aplicativo de companhia aérea pode definir uma geocerca em torno de um aeroporto quando uma reserva de voo está perto do horário de embarque. Quando o dispositivo cruza a geocerca, o aplicativo pode enviar uma notificação que leva os usuários a uma atividade que lhes permite obter o cartão de embarque.
A API Geofencing usa sensores de dispositivo para detectar com precisão a localização do dispositivo de uma forma que economize bateria. O dispositivo pode estar em um de três estados, ou tipos de transição, relacionados à geocerca.
Enter: Indica que o dispositivo entrou na (s) geocerca (s).
Dwell: Indica que o dispositivo entrou e está residindo dentro da (s) geocerca (s) por um determinado período de tempo.
Exit: Indica que o dispositivo saiu da (s) geocerca (s).
A imagem abaixo mostra os locais da geocerca denotados por marcadores e os raios ao redor deles.
O aplicativo que você criará neste tutorial é um jogo de caça ao tesouro. Este aplicativo é uma caça ao tesouro que dá ao usuário uma pista, e quando o usuário inserir o local correto, o aplicativo irá avisá-lo com a próxima pista, ou uma tela de vitória se ele tiver terminado a caça.
As imagens abaixo mostram uma pista e a tela de vitória.
Observe que o código do jogo atual tem locais de São Francisco codificados, mas você aprenderá como personalizar o jogo criando suas próprias cercas geográficas para levar as pessoas a lugares em sua área.
Para começar, baixe o código:
Como alternativa, você pode clonar o repositório GitHub para o código e alternar para o branch starter-code
:
$ git clone https://github.com/googlecodelabs/android-kotlin-geo-fences
O aplicativo inicial contém código para ajudá-lo a começar e economizar algum trabalho. Ele contém recurso, layouts, uma atividade, um modelo de vista e um receptor de transmissão que você completará durante esta lição.
Abra as seguintes classes importantes fornecidas para você e familiarize-se com o código:
HuntMainActivity.kt
é a classe principal na qual você trabalhará. Esta classe contém o código básico para funções que tratam de permissões e para adicionar e remover cercas geográficas. GeofenceViewModel.kt
é o ViewModel
associado a HuntMainActivity.kt
. Esta classe trata com o GeofenceIndex
LiveData e determina qual dica deve ser mostrada na tela.NotificationUtils.kt
: Quando você entra em uma geocerca, uma notificação é exibida. Esta classe cria e estiliza essa notificação.activity_main.xml
atualmente exibe uma imagem de um Android, mas você irá implementá-la para exibir uma dica para levar seus jogadores ao próximo local.GeofenceBroadcastReceiver.kt
contém o código de esqueleto para o método onReceive()
do BroadcastReceiver.
Você irá atualizar o onReceive()
método neste tutorial.A primeira coisa que seu aplicativo precisa fazer é obter permissões de localização do usuário. Isso envolve as seguintes etapas de alto nível e será o mesmo para qualquer aplicativo que você criar e que precise de permissões.
A API Geofencing exige que o local seja compartilhado o tempo todo. Se você estiver no Android versão Q ou posterior, precisará solicitar especificamente ao usuário essa permissão.
AndroidManifest.xml
.ACCESS_FINE_LOCATION
e ACCESS_BACKGROUND_LOCATION
acima da etiquetaapplication
.<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Verifique se o dispositivo está executando o Android Q ou posterior. Para dispositivos que executam o Android Q (API 29) ou posterior, você terá que solicitar uma permissão adicional de localização em segundo plano.
HuntMainActivity.kt
.onCreate()
, adicione uma variável de membro chamada runningQOrLater
. Isso verificará qual API o dispositivo está executando.private val runningQOrLater = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q
Em seu aplicativo, você precisa verificar se as permissões foram concedidas e, em caso negativo, solicite-as.
HuntMainActivity
, substitua o código no método foregroundAndBackgroundLocationPermissionApproved()
pelo código abaixo, que é explicado posteriormente.@TargetApi(29)
private fun foregroundAndBackgroundLocationPermissionApproved(): Boolean {
val foregroundLocationApproved = (
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION))
val backgroundPermissionApproved =
if (runningQOrLater) {
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
} else {
true
}
return foregroundLocationApproved && backgroundPermissionApproved
}
ACCESS_FINE_LOCATION
foi concedida.val foregroundLocationApproved = (
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION))
ACCESS_BACKGROUND_LOCATION
foi concedida. Retorne true
se o dispositivo estiver executando uma versão inferior a Q, onde você não precisa de permissão para acessar o local em segundo plano.val backgroundPermissionApproved =
if (runningQOrLater) {
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
} else {
true
}
true
se as permissões foram concedidas e false
se não.return foregroundLocationApproved && backgroundPermissionApproved
requestForegroundAndBackgroundLocationPermissions()
. É aqui que você pede ao usuário para conceder permissões de localização. Cada etapa é explicada nos pontos abaixo.@TargetApi(29 )
private fun requestForegroundAndBackgroundLocationPermissions() {
if (foregroundAndBackgroundLocationPermissionApproved())
return
var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
val resultCode = when {
runningQOrLater -> {
permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
}
else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
Log.d(TAG, "Request foreground only location permission")
ActivityCompat.requestPermissions(
this@HuntMainActivity,
permissionsArray,
resultCode
)
}
return
fora do método.if (foregroundAndBackgroundLocationPermissionApproved())
return
permissionsArray
contém as permissões a serem solicitadas. Inicialmente, adicione ACCESS_FINE_LOCATION
uma vez que é necessário para todos os níveis de API. var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
resultCode
. Este código é diferente se o dispositivo está executando Q (API 29) ou posterior e determina se você precisa verificar se há uma permissão (localização precisa) ou várias permissões (localização fina e em segundo plano) quando o usuário retorna da tela de solicitação de permissão.
when
para verificar a versão em execução e atribua resultCode
a REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
se o dispositivo estiver executando Q (API 29) ou posterior, e REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
, se não.val resultCode = when {
runningQOrLater -> {
permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
}
else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
ActivityCompat.requestPermissions(
this@HuntMainActivity,
permissionsArray,
resultCode
)
Depois que o usuário responde à solicitação de permissão, você precisa tratar com sua resposta em onRequestPermissionsResult()
.
onRequestPermissionsResult()
. override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
Log.d(TAG, "onRequestPermissionResult")
if (
grantResults.isEmpty() ||
grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
(requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
PackageManager.PERMISSION_DENIED))
{
Snackbar.make(
binding.activityMapsMain,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE
)
.setAction(R.string.settings) {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.show()
} else {
checkDeviceLocationSettingsAndStartGeofence()
}
}
grantResults
estiver vazia, a interação foi interrompida e a solicitação de permissão cancelada. grantResults
em LOCATION_PERMISSION_INDEX
tiver PERMISSION_DENIED
, isso significa que o usuário negou as permissões de primeiro plano.REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
e BACKGROUND_LOCATION_PERMISSION_INDEX
for negado, isso significa que o dispositivo está executando Q (API 29) ou superior e que as permissões de segundo plano foram negadas.if (grantResults.isEmpty() ||
grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
(requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
PackageManager.PERMISSION_DENIED))
Snackbar.make(
binding.activityMapsMain,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE
)
.setAction(R.string.settings) {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.show()
checkDeviceLocationSettingsAndStartGeofence()
.else {
checkDeviceLocationSettingsAndStartGeofence()
}
Seu código agora pede ao usuário para dar permissões.
No entanto, se a localização do dispositivo do usuário estiver desativada, essa permissão não significará nada.
A próxima coisa a verificar é se a localização do dispositivo está ativada. Nesta etapa, você adicionará um código para verificar se o local do dispositivo de um usuário está habilitado e, se não estiver, exibirá uma atividade onde ele pode ativá-lo usando uma solicitação de localização.
checkDeviceLocationSettingsAndStartGeofence()
em HuntMainActivity.kt
. As etapas são explicadas nos pontos abaixo.private fun checkDeviceLocationSettingsAndStartGeofence(resolve:Boolean = true) {
val locationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_LOW_POWER
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
val settingsClient = LocationServices.getSettingsClient(this)
val locationSettingsResponseTask =
settingsClient.checkLocationSettings(builder.build())
locationSettingsResponseTask.addOnFailureListener { exception ->
if (exception is ResolvableApiException && resolve){
try {
exception.startResolutionForResult(this@HuntMainActivity,
REQUEST_TURN_DEVICE_LOCATION_ON)
} catch (sendEx: IntentSender.SendIntentException) {
Log.d(TAG, "Error getting location settings resolution: " + sendEx.message)
}
} else {
Snackbar.make(
binding.activityMapsMain,
R.string.location_required_error, Snackbar.LENGTH_INDEFINITE
).setAction(android.R.string.ok) {
checkDeviceLocationSettingsAndStartGeofence()
}.show()
}
}
locationSettingsResponseTask.addOnCompleteListener {
if ( it.isSuccessful ) {
addGeofenceForClue()
}
}
}
LocationRequest
e use-o com um LocationSettingsRequest
Builder
. val locationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_LOW_POWER
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
LocationServices
para obter o SettingsClient.
Crie um val
chamado locationSettingsResponseTask
e use-o para verificar as configurações de localização. val settingsClient = LocationServices.getSettingsClient(this)
val locationSettingsResponseTask =
settingsClient.checkLocationSettings(builder.build())
onFailureListener()
ao locationSettingsResponseTask
.
locationSettingsResponseTask.addOnFailureListener { exception ->
}
ResolvableApiException
e, em caso afirmativo, tente chamar o método startResolutionForResult()
para solicitar ao usuário que ligue localização do dispositivo.if (exception is ResolvableApiException && resolve){
try {
exception.startResolutionForResult(this@HuntMainActivity,
REQUEST_TURN_DEVICE_LOCATION_ON)
}
startResolutionForResult
entrar no bloco catch, imprima um log.catch (sendEx: IntentSender.SendIntentException) {
Log.d(TAG, "Error getting location settings resolution: " + sendEx.message)
}
ResolvableApiException
, apresente uma snackbar que alerte o usuário de que o local precisa ser habilitado para jogar a caça ao tesouro. else {
Snackbar.make(
binding.activityMapsMain,
R.string.location_required_error, Snackbar.LENGTH_INDEFINITE
).setAction(android.R.string.ok) {
checkDeviceLocationSettingsAndStartGeofence()
}.show()
}
locationSettingsResponseTask
for concluída, verifique se ela foi bem-sucedida e adicione uma geocerca como pista. locationSettingsResponseTask.addOnCompleteListener {
if ( it.isSuccessful ) {
addGeofenceForClue()
}
}
onActivityResult()
, substitua o código existente pelo código abaixo. Depois que o usuário escolhe se aceita ou negar as permissões de localização do dispositivo, verifica se o usuário optou por aceitar as permissões. Se não, pergunte novamente.override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_TURN_DEVICE_LOCATION_ON) {
checkDeviceLocationSettingsAndStartGeofence(false)
}
}
Agora que você concluiu a verificação de que as permissões apropriadas foram concedidas, adicione algumas geocerca!
Você precisa de uma maneira de tratar com as transições da geocerca, o que é feito com um PendingIntent
. Um PendingIntent
é uma descrição de um Intent e uma ação alvo a ser executada com ele. Você criará uma intenção pendente para um BroadcastReceiver
para tratar com as transições da geocerca.
HuntMainActivity.kt
, acima de onCreate()
, adicione uma variável privada chamada geofencePendingIntent
do tipo PendingIntent
para tratar com o transições de geocerca. Conecte geofencePendingIntent
ao GeofenceTransitionsBroadcastReceiver
. private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
intent.action = ACTION_GEOFENCE_EVENT
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
Um GeofencingClient
é o principal ponto de entrada para interagir com as APIs de geofencing.
onCreate()
, instancie o geofencingClient
que já está declarado no código inicial. geofencingClient = LocationServices.getGeofencingClient(this)
addGeofenceForClue()
. Cada etapa é explicada nos pontos abaixo.private fun addGeofenceForClue() {
if (viewModel.geofenceIsActive()) return
val currentGeofenceIndex = viewModel.nextGeofenceIndex()
if(currentGeofenceIndex >= GeofencingConstants.NUM_LANDMARKS) {
removeGeofences()
viewModel.geofenceActivated()
return
}
val currentGeofenceData = GeofencingConstants.LANDMARK_DATA[currentGeofenceIndex]
val geofence = Geofence.Builder()
.setRequestId(currentGeofenceData.id)
.setCircularRegion(currentGeofenceData.latLong.latitude,
currentGeofenceData.latLong.longitude,
GeofencingConstants.GEOFENCE_RADIUS_IN_METERS
)
.setExpirationDuration(GeofencingConstants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.build()
val geofencingRequest = GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
geofencingClient.removeGeofences(geofencePendingIntent)?.run {
addOnCompleteListener {
geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent)?.run {
addOnSuccessListener {
Toast.makeText(this@HuntMainActivity, R.string.geofences_added,
Toast.LENGTH_SHORT)
.show()
Log.e("Add Geofence", geofence.requestId)
viewModel.geofenceActivated()
}
addOnFailureListener {
Toast.makeText(this@HuntMainActivity, R.string.geofences_not_added,
Toast.LENGTH_SHORT).show()
if ((it.message != null)) {
Log.w(TAG, it.message)
}
}
}
}
}
}
if (viewModel.geofenceIsActive()) return
currentGeofenceIndex
em viewModel
. Remova quaisquer geofences existentes, chame geofenceActivated
no viewModel
e retorne. val currentGeofenceIndex = viewModel.nextGeofenceIndex()
if(currentGeofenceIndex >= GeofencingConstants.NUM_LANDMARKS){
removeGeofences()
viewModel.geofenceActivated()
return
}
val currentGeofenceData = GeofencingConstants.LANDMARK_DATA [currentGeofenceIndex]
currentGeofenceData
. Defina a duração da expiração usando a constante definida em GeofencingConstants
. Defina o tipo de transição para GEOFENCE_TRANSITION_ENTER
. Finalmente, construa a geocerca.val geofence = Geofence.Builder()
.setRequestId(currentGeofenceData.id)
.setCircularRegion(currentGeofenceData.latLong.latitude,
currentGeofenceData.latLong.longitude,
GeofencingConstants.GEOFENCE_RADIUS_IN_METERS
)
.setExpirationDuration(GeofencingConstants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.build()
INITIAL_TRIGGER_ENTER
, adicione a geocerca que acabou de construir e, em seguida, construa.val geofencingRequest = GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
removeGeofences()
no geofencingClient
para remover quaisquer geofences já associadas ao PendingIntent
.geofencingClient.removeGeofences(geofencePendingIntent)?.run {
}
removeGeofences()
for concluído, independentemente de seu sucesso ou falha, adicione as novas geofences. Você pode desconsiderar o sucesso ou o fracasso da remoção das geocerca porque, mesmo que a remoção falhe, não afetará a adição de outra geocerca.addOnCompleteListener {
geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent)?.run {
}
}
addOnSuccessListener {
Toast.makeText(this@HuntMainActivity, R.string.geofences_added,
Toast.LENGTH_SHORT)
.show()
Log.e("Add Geofence", geofence.requestId)
viewModel.geofenceActivated()
}
addOnFailureListener {
Toast.makeText(this@HuntMainActivity, R.string.geofences_not_added,
Toast.LENGTH_SHORT).show()
if ((it.message != null)) {
Log.w(TAG, it.message)
}
}
Seu aplicativo agora adiciona geofences. No entanto, tente navegar até a Golden Gate Bridge (o local correto para a primeira pista padrão). Nada acontece. Por que é que?
Quando o usuário entra em uma geocerca estabelecida por uma pista, no caso a Ponte Golden Gate, você quer ser avisado, para que possa apresentar a próxima pista. Você pode fazer isso usando um receptor de transmissão que pode receber detalhes sobre os eventos de transição da geocerca.
Os aplicativos Android podem enviar ou receber mensagens de broadcast do sistema Android e outros aplicativos usando Broadcast Receivers. Eles usam o padrão de design publicar-assinar, onde as transmissões são enviadas e os aplicativos podem se registrar para receber transmissões específicas. Quando uma transmissão assinada é enviada, o aplicativo é notificado.
GeofenceBroadcastReceiver.kt
, encontre a função onReceive()
e copie este código para a classe. Cada etapa é explicada nos pontos abaixo.override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ACTION_GEOFENCE_EVENT) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
val errorMessage = errorMessage(context, geofencingEvent.errorCode)
Log.e(TAG, errorMessage)
return
}
if (geofencingEvent.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
Log.v(TAG, context.getString(R.string.geofence_entered))
val fenceId = when {
geofencingEvent.triggeringGeofences.isNotEmpty() ->
geofencingEvent.triggeringGeofences[0].requestId
else -> {
Log.e(TAG, "No Geofence Trigger Found! Abort mission!")
return
}
}
val foundIndex = GeofencingConstants.LANDMARK_DATA.indexOfFirst {
it.id == fenceId
}
if ( -1 == foundIndex ) {
Log.e(TAG, "Unknown Geofence: Abort Mission")
return
}
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendGeofenceEnteredNotification(
context, foundIndex
)
}
}
}
ACTION_GEOFENCE_EVENT
.if (intent.action == ACTION_GEOFENCE_EVENT) {
}
geofencingEvent
e inicialize-a para GeofencingEvent
com a intenção passada.val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
val errorMessage = errorMessage(context, geofencingEvent.errorCode)
Log.e(TAG, errorMessage)
return
}
geofenceTransition
é ENTER
.if (geofencingEvent.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {}
triggeringGeofences
não estiver vazio, defina o fenceID
para o requestId
da primeira geocerca. Você teria apenas uma geocerca ativa por vez, portanto, se a matriz não estivesse vazia, haveria apenas uma para interagir. Se a matriz estiver vazia, registre uma mensagem e return
.val fenceId = when {
geofencingEvent.triggeringGeofences.isNotEmpty() ->
geofencingEvent.triggeringGeofences[0].requestId
else -> {
Log.e(TAG, "No Geofence Trigger Found! Abort mission!")
return
}
}
GeofenceUtil.kt
. Caso contrário, imprima um registro e return
.val foundIndex = GeofencingConstants.LANDMARK_DATA.indexOfFirst {
it.id == fenceId
}
if ( -1 == foundIndex ) {
Log.e(TAG, "Unknown Geofence: Abort Mission")
return
}
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendGeofenceEnteredNotification(
context, foundIndex
)
Pule esta seção se você não estiver usando um emulador.
Como o teste deste tutorial depende de uma caminhada, pode ser mais conveniente usar um local simulado no emulador. Nesta tarefa, você aprenderá como simular a localização em seu emulador.
Quando você não precisar mais de cercas geográficas, é uma prática recomendada removê-las, o que interrompe o monitoramento, para economizar bateria e ciclos de CPU.
HuntMainActivity.kt
, copie este código para o método removeGeofences()
. Cada etapa é explicada nos pontos abaixo.private fun removeGeofences() {
if (!foregroundAndBackgroundLocationPermissionApproved()) {
return
}
geofencingClient.removeGeofences(geofencePendingIntent)?.run {
addOnSuccessListener {
Log.d(TAG, getString(R.string.geofences_removed))
Toast.makeText(applicationContext, R.string.geofences_removed, Toast.LENGTH_SHORT)
.show()
}
addOnFailureListener {
Log.d(TAG, getString(R.string.geofences_not_removed))
}
}
}
if (!foregroundAndBackgroundLocationPermissionApproved()) {
return
}
removeGeofences()
no geofencingClient
e passe o geofencePendingIntent
. geofencingClient.removeGeofences(geofencePendingIntent)?.run {
}
onSuccessListener()
e informe ao usuário com um brinde que as cercas geográficas foram removidas com sucesso.addOnSuccessListener {
Log.d(TAG, getString(R.string.geofences_removed))
Toast.makeText(applicationContext, R.string.geofences_removed, Toast.LENGTH_SHORT)
.show()
}
onFailureListener()
onde você registra se as cercas geográficas não foram removidas.addOnFailureListener {
Log.d(TAG, getString(R.string.geofences_not_removed))
}
removeGeofences()
é chamado no método onDestroy()
incluído no código inicial.Agora que tudo está configurado, resta apenas uma coisa a fazer. Ganhar o jogo!
Navegue até o local vencedor zombando do local em seu emulador ou caminhando fisicamente até lá! Parabéns, você ganhou este tutorial!
Você pode adicionar pontos de referência para personalizar sua caça ao tesouro e adicionar mais cercas geográficas para fazer a caça ao tesouro durar mais tempo.
strings.xml
, adicione sua dica personalizada e local.<!-- Geofence Hints -->
<string name="lombard_street_hint">Go to the most crooked street in the City</string>
<!-- Geofence Locations -->
<string name="lombard_street_location">at Lombard Street</string>
GeofenceUtils.kt
, personalize os pontos de referência criando um LandmarkDataObject
com um ID de destino, dica de destino, localização de destino e latitude e longitude de destino. Adicione-o ao array LANDMARK_DATA
com seus próprios objetos de referência.val LANDMARK_DATA = arrayOf(
LandmarkDataObject(
"Lombard street",
R.string.lombard_street_hint,
R.string.lombard_street_location,
LatLng(37.801205, -122.426752))
)
Neste tutorial você aprendeu como:
onReceive()
.Cursos Udacity:
Documentação do desenvolvedor Android:
Outros recursos:
Para obter links para outros tutoriais neste curso, consulte a página inicial de tutoriais do Android avançado em Kotlin.