Aprendiendo Geofences en menos de un día utilizando HMS.
Tags: android, hmscore, huaweimobileservices
Published: 2020-10-06 00:11:16

Si tuviste la oportunidad de desarrollar una aplicación antes de usar Geofences, entonces ya sabes que son una característica excelente que los desarrolladores podemos usar para comunicar datos geolocalizados relevantes a los usuarios. Nos comenzamos a preguntar en estos días, ¿qué tan difícil es implementarlos usando HMS en lugar de GMS? ¡La respuesta fue prácticamente NADA! Solo hay un par de cosas a considerar, como las clases, los métodos y los nombres de atributos, pero puedo asegurarle que no pasará más de un día para hacerlo.

Para crear una Geofence, se deben especificar dos cosas: cómo se debe activar (ENTER, DWELL y EXIT), y un área de cobertura circular dada por las coordenadas (latitud y longitud) y un radio. Además, se aplica el mismo principio para HSM y GMS, de cómo activarlos en los siguientes escenarios, pero se conocen como conversiones y transiciones respectivamente:

* Intro: el usuario ingresa a un área específica.
* Salir: el usuario sale de un área específica.
* Tiempo de espera: los usuarios permanecen en el área dentro de un período de tiempo específico.

Por lo tanto, debemos de tener en cuenta las limitaciones de usar un Geofence, ya sea ambos proveedores.

    Un máximo de 100 geofences activos por aplicación.
    Radio recomendada> = 200 m. Menos de este valor no se garantiza la precisión.

Para esta característica específica, desarrollamos una aplicación de demostración que puedes clonar del repositorio en Github. Si descargas el código, ten en cuenta que debes seguir el proceso de desarrollo de preparación para el Kit de mapas y el Kit de ubicación para probarlo. La aplicación mostrará una notificación cada vez que una Geofence sea activada por una conversión de ingreso.
![1 yeDpdtIug-IIu3OayIuYwA](https://dev-to-uploads.s3.amazonaws.com/i/vcvopzrjviy14fw74uf8.png)

Pasos de Implementación

1. Agrega las dependencias para tu proyecto siguiendo los pasos descritos en el Kit de mapas y el Kit de ubicación. Ten en cuenta que Maps no es obligatorio para usar Geofences, pero para los propósitos de esta aplicación, será útil para representar las áreas circulares.

2. Agrega permisos de aplicación al archivo AndroidManifest.xml
```
<uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 <uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
```
3. Agrega el Broadcast Receiver a tu archivo AndroidManifest.xml para manejarlo cada vez que la conversión de entrada active una geocerca.
```
<receiver
     android:name=".GeofenceBroadcastReceiver"
     android:enabled="true"
     android:exported="true" />
```
4. Inicializa una instancia de GeofenceService en la Actividad de tu aplicación.
```
//lateinit var geofencingClient: GeofencingClient  GMS
lateinit var geofencingService: GeofenceService   //HMS
...
override fun onCreate(savedInstanceState: Bundle?) {
    ...
    //geofencingClient = LocationServices.getGeofencingClient(this) GMS
    geofencingService = LocationServices.getGeofenceService(this) //HMS
    ...
}
```
5. Crea una lista de las geofences que agregaras a tu aplicación. Recuerda usar el método setRoundArea para establecer la latitud, longitud y radio. Además, el tipo de evento desencadenante descrito por el método setConversions en la clase Geofence.Builder. Si estás interesado en saber más sobre los métodos restantes, el siguiente enlace contiene la documentación oficial.
[Link](https://developer.huawei.com/consumer/en/doc/development/HMS-References/geofencebuilder#4)

```
//HMS
//LANDMARK_DATA is a list of object that contains dummy data for the geofence and the notification
val geofences: List<Geofence>
    get() = LANDMARK_DATA.map {
        val builder = Geofence.Builder()
        builder.setUniqueId(it.identificator)
        builder.setRoundArea(it.latitude, it.longitude, GEOFENCE_RADIUS_IN_METERS)
        builder.setValidContinueTime(expirationDuration)
        builder.setDwellDelayTime(loiteringDelay)
        builder.setConversions(
            GeofenceRequest.ENTER_INIT_CONVERSION )
        builder.setNotificationInterval(notificationResponsiveness)
        builder.build()
    }/*GMS
val geofences: List<Geofence>
    get() = this.LANDMARK_DATA.map {
        val builder = Geofence.Builder()
        builder.setRequestId(it.identificator)
        builder.setCircularRegion(it.latitude, it.longitude, GEOFENCE_RADIUS_IN_METERS)
        builder.setExpirationDuration(expirationDuration)
        builder.setLoiteringDelay(loiteringDelay)
        builder.setTransitionTypes(
           Geofence.GEOFENCE_TRANSITION_ENTER )
        builder.setNotificationResponsiveness(notificationResponsiveness)
        builder.build()
    }
 */
```
6. Crea una solicitud de Geofence usando la lista de geofence y el tipo de conversión con los métodos createGeofenceList y setCoordinateType. Además, se puede agregar una sola geocerca según las necesidades del proyecto utilizando el método createGeofence.
```
val request = getGeofencingRequest()
private fun getGeofencingRequest(): GeofenceRequest {
    return GeofenceRequest.Builder().apply {
        createGeofenceList(GeofencingConstants.geofences)
        setCoordinateType(Geofence.ENTER_GEOFENCE_CONVERSION)
    }.build()
}/*GMS
private fun getGeofencingRequest(): GeofencingRequest {
    return GeofencingRequest.Builder().apply {
        setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
        addGeofences(geofenceList)
    }.build()
}
*/
```
7. Define tu pending intent que activará el BroadcastReceiver.

```
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)
}
```
8. Agrega geofences a través de la instancia geofencingClient usando el método createGeofenceList usando la solicitud y la intención pendiente como parámetros.
```
//HMS
geofencingService.createGeofenceList(request, geofencePendingIntent).run {
    addOnCompleteListener {
        if (it.isSuccessful) {
            Toast.makeText(
                this@MainActivity,
                "Geofences added succesfully",
                Toast.LENGTH_SHORT
            ).show()
        } else {
            Toast.makeText(
                this@MainActivity,
                "Geofences adding fail ${it.exception.message}",
                Toast.LENGTH_SHORT
            ).show()
        }
    }
    addOnFailureListener {
        // Failed to add geofences
        // ...
    }/* GMS
    geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
        addOnSuccessListener {
            // Geofences added
            // ...
        }
        addOnFailureListener {
            // Failed to add geofences
            // ...
        }
    }
 */
}
```
9. Antes de llamar a addGeofenceList, asegúrese de otorgar los permisos adecuados y de activar los servicios de ubicación en su teléfono.

10. Manejar Geofence en BroadCastReceiver. Este es el lugar donde ocurre la magia, los datos se extraen de la intención y el visualizador a través de una notificación.

```
class GeofenceBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {if (intent?.action == ACTION_GEOFENCE_EVENT) {
            //val geofencingEvent = GeofencingEvent.fromIntent(intent) GMS
            val geofencingEvent =GeofenceData.getDataFromIntent(intent) //HMS
            //if (geofencingEvent.hasError()) { GMS
            if (geofencingEvent.isFailure) { //HMS
                val errorMessage = errorMessage(context!!, geofencingEvent.errorCode) //ERROR codes constansta re different in HMS and GMS
                Log.e(TAG, errorMessage)
                return
            }
            //if (geofencingEvent.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) { GMS
            if (geofencingEvent.conversion == Geofence.ENTER_GEOFENCE_CONVERSION) { //HMS
                Log.v(TAG, context?.getString(R.string.geofence_entered)!!)val fenceId = when {
                    //geofencingEvent.triggeringGeofences.isNotEmpty() -> GMS
                    geofencingEvent.convertingGeofenceList.isNotEmpty() ->//HMS
                        //geofencingEvent.triggeringGeofences[0].requestId  GMS
                        geofencingEvent.convertingGeofenceList[0].uniqueId  //HMS
                    else -> {
                        Log.e(TAG, "No Geofence Trigger Found! Abort mission!")
                        return
                    }
                }
                // Check geofence against the constants listed in GeofenceUtil.kt to see if the
                // user has entered any of the locations we track for geofences.
                val foundIndex = GeofencingConstants.LANDMARK_DATA.find { it.identificator==fenceId}val store = foundIndex?.store
                val promo = foundIndex?.promoval notificationManager = ContextCompat.getSystemService(
                    context,
                    NotificationManager::class.java
                ) as NotificationManagernotificationManager.sendGeofenceEnteredNotification(
                    context, store?:"No store", promo?:"No promo"
                )
            }
        }
    }
    companion object {
        private const val TAG = "GeofenceReceiver"
    }
}
```

11. Una vez que nuestra aplicación termine de usar las Geofences, quítalas para evitar el consumo innecesario de la batería debido al monitoreo de geofence. Se puede apreciar una gran diferencia entre HMS y Google, los procesos de eliminación se realizan a través de la identificación iterativa a través de una lista de Geofences utilizando su identificación individual, mientras que la solución GMS utiliza el PendingIntent asociado.
```
//HMS
geofencingService.deleteGeofenceList(GeofencingConstants.LANDMARK_DATA.map { it.identificator }).run{
    addOnCompleteListener {
        if (it.isSuccessful) {
            // Geofences removed
        } else {
            // Failed to remove geofences
        }
    }
}/*GMS
geofencingClient?.removeGeofences(geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences removed
        // ...
    }
    addOnFailureListener {
        // Failed to remove geofences
        // ...
    }
}
*/
```
Lo que hemos visto hasta ahora con este ejemplo es que la implementación de Geofences con HMS es totalmente alcanzable en unas pocas horas, incluso desde una perspectiva de migración. La única consideración que debemos tener en cuenta es validar las clases, métodos, atributos y constantes homólogos utilizados en cada clase.

Si te gustaría conocer mas acerca de este tema sigue estos links
[Link](https://developer.huawei.com/consumer/en/doc/hmssdk_location_conversion_guide)

[Link](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/location-faq)