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.

Introdução

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.

Tipos de transição de 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 cerca geográfica tem muitas aplicações, incluindo:

A imagem abaixo mostra os locais da geocerca denotados por marcadores e os raios ao redor deles.

O que você precisará

O que você já deveria saber

O que você aprenderá

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:

Baixe o Zip

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

Etapa 1: execute o aplicativo inicial

  1. Execute o aplicativo inicializador em um emulador ou em seu próprio dispositivo. Você deverá ver uma tela inicial com um Android segurando um mapa do tesouro.

Etapa 2: familiarize-se com o código

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:

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.

  1. Adicione as permissões ao manifesto do Android.
  2. Crie um método que verifique as permissões.
  3. Solicite essas permissões chamando esse método.
  4. Lide com o resultado da solicitação de permissões ao usuário.

Etapa 1: adicionar permissões ao AndroidManifest

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.

  1. Abra AndroidManifest.xml.
  2. Adicione permissões para 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" />

Etapa 2: verifique se o dispositivo está executando o Android Q (API 29) ou posterior

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.

  1. Abra HuntMainActivity.kt.
  2. Acima do método 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

Etapa 3: crie um método para verificar as permissões

Em seu aplicativo, você precisa verificar se as permissões foram concedidas e, em caso negativo, solicite-as.

  1. Em 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
}
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

Etapa 4: solicitar permissões

  1. Copie o seguinte código para o método 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
   )
}
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
}
ActivityCompat.requestPermissions(
   this@HuntMainActivity,
   permissionsArray,
   resultCode
)

Etapa 5: manipular permissões

Depois que o usuário responde à solicitação de permissão, você precisa tratar com sua resposta em onRequestPermissionsResult().

  1. Copie este código para o método 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()
   }
}
  1. Se a matriz grantResults estiver vazia, a interação foi interrompida e a solicitação de permissão cancelada.
  2. Se o valor do array grantResults em LOCATION_PERMISSION_INDEX tiver PERMISSION_DENIED, isso significa que o usuário negou as permissões de primeiro plano.
  3. Se o código de solicitação for igual a 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()
else {
   checkDeviceLocationSettingsAndStartGeofence()
}
  1. Execute seu aplicativo! Você deverá ver um pop-up solicitando que você conceda permissões. Escolha Allow all the time ou Allow se você estiver executando uma API inferior a 29.

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.

  1. Copie esse código para o método 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()
       }
   }
}
   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()
   }
}
  1. Em 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)
   }
}
  1. Para testar isso, desligue a localização do dispositivo e execute o aplicativo. Você deverá ver um pop-up conforme mostrado abaixo. Pressione OK.

Agora que você concluiu a verificação de que as permissões apropriadas foram concedidas, adicione algumas geocerca!

Etapa 1: criar uma intenção pendente

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.

  1. Em 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)
}

Etapa 2: Adicionar um cliente de cerca geográfica

Um GeofencingClient é o principal ponto de entrada para interagir com as APIs de geofencing.

  1. No método onCreate(), instancie o geofencingClient que já está declarado no código inicial.
geofencingClient = LocationServices.getGeofencingClient(this)

Etapa 3: Adicionar geocerca

  1. Copie este código para o método 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
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)
   }
}
  1. Execute seu aplicativo. Sua tela deve exibir uma pista e um brinde que informa que a geocerca foi adicionada.

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.

Etapa 1: substituir o método onReceive()

  1. Em 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
           )
       }
   }
}
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) {}
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
)
  1. Tente você mesmo entrando em uma geocerca ou emulando sua localização para estar na geocerca (instruções na próxima etapa). Quando você entra, uma notificação deve aparecer.

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.

Etapa 1: simule sua localização

  1. Na barra de menus ao lado do emulador, toque nos três pontos (...) na parte inferior para abrir o plano de Extended controls.

  1. Selecione Location.

  1. Na barra de pesquisa do mapa, insira um local, como a Ponte Golden Gate. O marcador de local é exibido no local que você inseriu.

  1. No canto inferior direito do painel, pressione o botão Set Location.

  1. Vá para o aplicativo do Google Maps e a notificação deve aparecer. Isso pode demorar alguns segundos.

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.

Etapa 1: remover geofences

  1. Em 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
   }
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))
}
  1. O método 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!

Etapa 1: Ganhe 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.

  1. Em 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>
  1. Em 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:

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.