Lyn
Lyn

Reputation: 219

Geofence ENTER and EXIT in Android not getting triggered (with Flutter)

I'm building a Flutter app that requires Geofencing service, so I opt to use the Native Geofence library where I'll check for all necessary permission on Flutter then pass the regions thru the Method Channel from Flutter to Android.

I followed the tutorial documented by Android and develop a simple prototype. I manage to register all the geofences but whenever I ENTER or EXIT a region, I do not get notified. Below are the codes of my prototype. Can anyone help me?

In my AndroidManifest.xml I have declared

<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" />

<receiver android:name=".GeofenceBroadcastReceiver" android:enabled="true" android:exported="true"/>

MainActivity.kt

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        Geofence().initialize(this.applicationContext, this.activity, flutterEngine)
    }
}

Geofence.kt

class Geofence: MethodChannel.MethodCallHandler {
    private var channel: MethodChannel? = null
    private var context: Context? = null
    private var geofencingClient: GeofencingClient? = null
    private var geofenceList = mutableListOf<Geofence>()
    private val channelName = "com.example.geofence/geofence"

    fun initialize(context: Context, activity: Activity, flutterEngine: FlutterEngine) {
        this.context = context
        geofencingClient = LocationServices.getGeofencingClient(this.context!!)

        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)
        channel!!.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when(call.method) {
            "startService" -> {
                val args = call.arguments<ArrayList<*>>()
                startService(args!!)
                result.success(null)
            }

            else -> result.notImplemented()
        }
    }

    @SuppressLint("MissingPermission")
    private fun startService(regions: ArrayList<*>) {
        for (r in regions) {
            val o = r as HashMap<*, *>
            geofenceList.add(
                Geofence.Builder()
                    .setRequestId(o["id"] as String)
                    .setCircularRegion(o["latitude"] as Double, o["longitude"] as Double, (o["radius"] as Int).toFloat())
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
                    .build()
            )
        }
        Log.d("TAG", "startService: Geofence count: ${geofenceList.count()}")

        geofencingClient!!.addGeofences(getGeofencingRequest(), geofencePendingIntent).run {
            addOnSuccessListener {
                Log.d("TAG", "startService: Geofence Added Successfully")
            }
            addOnFailureListener{
                Log.e("TAG", "startService: Failed to add Geofence ${it.message}")
            }
        }
    }

    private fun getGeofencingRequest(): GeofencingRequest {
        Log.d("TAG", "getGeofencingRequest")
        return GeofencingRequest.Builder().apply {
            setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER or GeofencingRequest.INITIAL_TRIGGER_EXIT)
            addGeofences(geofenceList)
        }.build()
    }

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(context, GeofenceBroadcastReceiver::class.java)
        PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
    }
}

GeofenceBroadcastReceiver.kt

class GeofenceBroadcastReceiver: BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("TAG", "onReceive")
        val geofencingEvent = GeofencingEvent.fromIntent(intent!!)
        if (geofencingEvent!!.hasError()) {
            Log.e("TAG", "onReceive: ${GeofenceStatusCodes.getStatusCodeString(geofencingEvent.errorCode)}")
            return
        }

        val geofenceTransition = geofencingEvent.geofenceTransition

        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            Log.d("TAG", "onReceive: Geofence transition: $geofenceTransition")
        } else {
            Log.e("TAG", "onReceive: Error")
        }
    }
}

From the Flutter side I'll invoke startService which will pass a List of regions to Android thru the MethodChannel

Upvotes: 0

Views: 80

Answers (0)

Related Questions