Android geo-fence is not triggering without requestLocationUpdates

Viewed 436

I have a pretty standard geo-fencing setup. Exactly as described in this official doco.

So there's a GeofenceBroadcastReceiver with a onReceive method that is supposed to fire when geo-fence events occur.

Here's how the geo-fence areas are declared:

val list = workAreas.map {
    Geofence.Builder().setRequestId(it.id.toString())
        .setCircularRegion(it.latLng.latitude, it.latLng.longitude, it.radius)
        .setExpirationDuration(Geofence.NEVER_EXPIRE)
        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
        .build()
}
val request = GeofencingRequest.Builder()
    .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
    .addGeofences(list).build()
client.addGeofences(request, geofencePendingIntent).run {
    addOnSuccessListener {
    }
    addOnFailureListener {
    }
}

Ok. So the problem is that the geofencing event does not trigger when I enter a defined area. That is until I do something on the app that fires requestLocationUpdates on the fusedLocationProviderClient.

This is the snipped that does the job:

val request = LocationRequest.create().apply {
    interval = 5000
    fastestInterval = 5000
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    maxWaitTime = 1000
}

Looper.myLooper()?.let { looper ->
    fusedLocationProviderClient.requestLocationUpdates(
        request,
        locationCallback,
        looper
    )
}

This can't be right can it? You can't have location update running all time. And it wasn't how it used to work in the earlier versions (you know, with JobIntentService).

So am I missing anything here?

1 Answers

Check below files for your solution

Add Google Map API key in manifest and background location permission for android 11

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.recycler.geofencingdemo">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".GeofenceRegistrationService"
            android:enabled="true"
            android:exported="true"/>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <!--
             The API key for Google Maps-based APIs.
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_map_key" />
    </application>

</manifest>

GeofenceRegistrationService.kt



package com.recycler.geofencingdemo

import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.text.TextUtils
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofenceStatusCodes
import com.google.android.gms.location.GeofencingEvent
import com.google.android.gms.maps.model.LatLng
import java.util.*


class GeofenceRegistrationService : IntentService(TAG) {


    override fun onHandleIntent(intent: Intent?) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            Log.d(TAG, "GeofencingEvent error " + geofencingEvent.errorCode)
        } else {
            val transaction = geofencingEvent.geofenceTransition
            val geofences = geofencingEvent.triggeringGeofences
            val geofence = geofences[0]
            if (transaction == Geofence.GEOFENCE_TRANSITION_ENTER && geofence.requestId == Constants.GEOFENCE_ID) {
                Log.d(TAG, "You are inside Tacme")
            } else {
                Log.d(TAG, "You are outside Tacme")
            }
            val geofenceTransitionDetails = getGeofenceTrasitionDetails(transaction, geofences)
            Constants.AREA_LANDMARKS[Constants.GEOFENCE_ID]?.let { sendNotification(applicationContext,geofenceTransitionDetails, it) }
        }
    }

    // Create a detail message with Geofences received
    private fun getGeofenceTrasitionDetails(geoFenceTransition: Int, triggeringGeofences: List<Geofence>): String {
        // get the ID of each geofence triggered
        val triggeringGeofencesList = ArrayList<String?>()
        for (geofence in triggeringGeofences) {
            triggeringGeofencesList.add(geofence.requestId)
        }
        var status: String? = null
        if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) status = "Entering " else if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) status = "Exiting "
        return status + TextUtils.join(", ", triggeringGeofencesList)
    }

    fun sendNotification(context: Context, message: String, latLng: LatLng) {
        val notificationManager = context
                .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                && notificationManager.getNotificationChannel(Companion.NOTIFICATION_CHANNEL_ID) == null) {
            val name = context.getString(R.string.app_name)
            val channel = NotificationChannel(Companion.NOTIFICATION_CHANNEL_ID,
                    name,
                    NotificationManager.IMPORTANCE_DEFAULT)

            notificationManager.createNotificationChannel(channel)
        }

        val intent = MainActivity.newIntent(context.applicationContext, latLng)

        val stackBuilder = TaskStackBuilder.create(context)
                .addParentStack(MainActivity::class.java)
                .addNextIntent(intent)
        val notificationPendingIntent = stackBuilder
                .getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT)

        val notification = NotificationCompat.Builder(context, Companion.NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(message)
                .setContentIntent(notificationPendingIntent)
                .setAutoCancel(true)
                .build()

        notificationManager.notify(getUniqueId(), notification)
    }

    private fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt())

    companion object {
        private const val TAG = "GeoIntentService"

        // Handle errors
        private fun getErrorString(errorCode: Int): String {
            return when (errorCode) {
                GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE -> "GeoFence not available"
                GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES -> "Too many GeoFences"
                GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS -> "Too many pending intents"
                else -> "Unknown error."
            }
        }

        private const val NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel"
    }
}

Constants.kt

package com.recycler.geofencingdemo

import com.google.android.gms.maps.model.LatLng
import java.util.*


object Constants {
    //Location
    const val GEOFENCE_ID = "TACME"
    const val GEOFENCE_RADIUS_IN_METERS = 100f

    /**
     * Map for storing information about tacme in the dubai.
     */
    val AREA_LANDMARKS = HashMap<String, LatLng>()

    init {
        // Tacme
        AREA_LANDMARKS[GEOFENCE_ID] = LatLng(48.8603, 2.2914)
    }
}

Related