Can't get geofence to trigger the exit or enter event Android

24 Views Asked by At

I am trying to create an app that reads geofences from a text file. I have successfully worked out the file reading and the geofence creation. I can't get the geofence events, enter or exit, to get triggered. I have tested on the emulator and on a real phone with mock locations and with real gps. I am attaching what is in my manifest.xml, my broadcast receiver class and my main activity.kt. It would be great is some one can point out what I am missing. Thanks.

ON the application section of androidmanifest.xml

<receiver android:name=".GeofenceBroadcastReceiver" android:exported="true">
      <intent-filter> -->
           <action android:name="com.example.locationtest.ACTION_RECEIVE_GEOFENCE"/>-->
      </intent-filter>-->
</receiver>

class GeofenceBroadcastReceiver : BroadcastReceiver() {

@RequiresApi(Build.VERSION_CODES.O)
override fun onReceive(context: Context?, intent: Intent?) {
    Log.d("TAG","GRV Geofence event happened1")
    val geofencingEvent = GeofencingEvent.fromIntent(intent)
    if (geofencingEvent.hasError()) {
        val errorMessage = "Geofence error: ${geofencingEvent.errorCode}"
        Log.d("TAG","GRV $errorMessage")
        // Log or handle the error accordingly
        return
    }
    Log.d("TAG","GRV Geofence event happened2")

    // Get the transition type (ENTER, EXIT, or DWELL)
    val geofenceTransition = geofencingEvent.geofenceTransition

    val triggeringGeofences = geofencingEvent.triggeringGeofences

    val triggeringGeofencesIds = triggeringGeofences.map { it.requestId }

    val now = LocalDateTime.now()
    val formatter = DateTimeFormatter.ofPattern("MMdd")
    val formatter2 =DateTimeFormatter.ofPattern("HH:mm:ss")
    val daystamp = now.format(formatter).takeLast(4)
    val timestamp = now.format(formatter2).takeLast(8)

    // Check which transition type has triggered this event
    when (geofenceTransition) {
        Geofence.GEOFENCE_TRANSITION_ENTER -> {

            triggeringGeofencesIds.forEach { geofenceId ->
                val eventdesc = timestamp +  "#" + geofenceId + "#" + "ENTR"
                if (context != null) {
                    writeToExternalStorageFile(context, eventdesc, daystamp + "geofenceEvents.txt" )
                }
                Log.d("TAG","GRV Geofence with ID $geofenceId triggered a transition.")
            }
        }
        Geofence.GEOFENCE_TRANSITION_EXIT -> {

            triggeringGeofencesIds.forEach { geofenceId ->
                val eventdesc = timestamp +  "#" + geofenceId + "#" + "EXIT"
                if (context != null) {
                    writeToExternalStorageFile(context, eventdesc, daystamp + "geofenceEvents.txt" )
                }
                Log.d("TAG","GRV Geofence with ID $geofenceId triggered a transition.")
            }
        }
        /*Geofence.GEOFENCE_TRANSITION_DWELL -> {
            // Handle geofence dwell (if you've set this transition type)
        }
        else -> {
            // Log or handle the error accordingly
        }*/
    }

    // This is where you would start an activity, send a notification, etc.
    // For example, to send a notification:
    // sendNotification(context, "Geofence Transition Detected", "You have entered/exited a geofence.")
}

}

class MainActivity : ComponentActivity() {

private val LOCATION_PERMISSION_REQUEST_CODE = 1
lateinit var geofencingClient: GeofencingClient

@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.d("TAG","GRV Entro")
    geofencingClient = LocationServices.getGeofencingClient(this)
    checkAndRequestPermissions()
    setContentView(R.layout.activity_main)
    Log.d("TAG","GRV Entro1")
    removeAllGeofences(this)
    val geofences = readFileLinesToArray(this, "GeofenceList.txt")
    if (geofences != null) {
        Log.d("TAG","GRV Leyendo geocercas")
        processGeofenceData(this, geofences, 100.00F)
    }

    val editText = findViewById<EditText>(R.id.editTextInput)
    val button = findViewById<Button>(R.id.buttonSubmit)
    val textViewResult = findViewById<TextView>(R.id.textViewResult)
    val toggleButton = findViewById<ToggleButton>(R.id.toggleButton)

    // Button Click Listener
    button.setOnClickListener {
        val inputText = editText.text.toString()

        val funciono = generateAndWriteLocationInfo(this, inputText)
        removeAllGeofences(this)
        val geofences = readFileLinesToArray(this, "GeofenceList.txt")
        if (geofences != null) {
            processGeofenceData(this, geofences, 100.00F)
        }
        textViewResult.text = "Result: ${funciono.toString()}"
        Toast.makeText(this, "Input: ${funciono.toString()}", Toast.LENGTH_SHORT).show()
    }

    // Toggle Button Listener
    toggleButton.setOnCheckedChangeListener { _, isChecked ->
        if (isChecked) {
            // The toggle is enabled
            Toast.makeText(this, "Toggle On", Toast.LENGTH_SHORT).show()
        } else {
            // The toggle is disabled
            Toast.makeText(this, "Toggle Off", Toast.LENGTH_SHORT).show()
        }
    }

}

private fun checkAndRequestPermissions() {
    Log.d("TAG","Entra a permisos")
    val fineLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    val backgroundLocationPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
    } else {
        PackageManager.PERMISSION_GRANTED // Background location isn't needed/requested before Android 10 (Q)
    }

    val listPermissionsNeeded = mutableListOf<String>()

    if (fineLocationPermission != PackageManager.PERMISSION_GRANTED) {
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION)
    }
    if (backgroundLocationPermission != PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Note: Background location permission should be requested separately and only after fine location permission has been granted.
        listPermissionsNeeded.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
    }

    if (listPermissionsNeeded.isNotEmpty()) {
        ActivityCompat.requestPermissions(this, listPermissionsNeeded.toTypedArray(), LOCATION_PERMISSION_REQUEST_CODE)
    }
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    when (requestCode) {
        LOCATION_PERMISSION_REQUEST_CODE -> {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d("TAG","Permiso proporcionado")
                // Permission was granted. You can now access the location.
            } else {
                Log.d("TAG","Permiso negado")
                // Permission denied. You can notify the user and disable features that require the permission.
            }
            return
        }
    }
}

fun processGeofenceData(context: Context, geofenceData: Array<String>, radius: Float) {
    geofenceData.forEach { data ->
        val parts = data.split("#")
        Log.d("TAG","Procesando Geocercas")
        if (parts.size >= 4) {
            val geofenceId = parts[0]
            // Assuming the address is not used directly for geofence creation here
            val latitude = parts[2].toDoubleOrNull()
            val longitude = parts[3].toDoubleOrNull()

            if (latitude != null && longitude != null) {
                createAndActivateGeofence(context, geofenceId, latitude, longitude, radius)
            } else {
                Log.d("TAG","GRV Invalid latitude or longitude for $geofenceId")
            }
        }
    }
}



   private val geofencePendingIntent: PendingIntent by lazy {
        Log.d("TAG","GRV pendingIntent")
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java).apply {
            action = "com.example.locationtest.ACTION_RECEIVE_GEOFENCE"}
            Log.d("TAG","GRV pendingIntent2")
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().

        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

    }

    fun createAndActivateGeofence(context: Context, geofenceId: String, latitude: Double, longitude: Double, radius: Float) {
        //val geofencingClient: GeofencingClient = LocationServices.getGeofencingClient(context)
        Log.d("TAG","GRV Armando Geocercas")
        val geofence = Geofence.Builder()
            .setRequestId(geofenceId)
            .setCircularRegion(latitude, longitude, radius)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
            .build()

        val geofencingRequest = GeofencingRequest.Builder().apply {
            setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            addGeofence(geofence)
        }.build()

//        val intent = Intent(context, GeofenceBroadcastReceiver::class.java).apply {
//            action = "com.example.locationtest.ACTION_RECEIVE_GEOFENCE"
//        }
//        val geofencePendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

        // Corrected permission check
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent).run {
                addOnSuccessListener {
                    // Handle success
                    Log.d("TAG","GRV Geofence Added: $geofenceId")
                }
                addOnFailureListener {
                    // Handle failure
                    Log.d("TAG","GRV Failed to add geofence: $geofenceId")
                }
            }
        } else {
            Log.d("TAG","GRV Location permission not granted")
            // Here you should actually request the permissions if not already granted,
            // not just print a message. This could be a call to requestPermissions
            // if within an Activity, or a signal to the user to enable permissions via app settings.
        }
    }

    fun removeAllGeofences(context: Context) {
        //val geofencingClient: GeofencingClient = LocationServices.getGeofencingClient(context)

        // The PendingIntent that was used to add the geofences
        val intent = Intent(context, GeofenceBroadcastReceiver::class.java).apply {
            action = "com.example.locationtest.ACTION_RECEIVE_GEOFENCE" // Use the same action as when adding the geofences
        }
        val geofencePendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

        // Check for location permissions for completeness, though it's not strictly necessary for removal
        if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
            geofencingClient.removeGeofences(geofencePendingIntent).run {
                addOnSuccessListener {
                    // Handle success
                    Log.d("TAG","GRV All geofences successfully removed.")
                }
                addOnFailureListener {
                    // Handle failure
                    Log.d("TAG","GRV Failed to remove geofences.")
                }
            }
        } else {
            Log.d("TAG","GRV Location permission not granted")
            // Handle lack of permission (e.g., request permissions here)
        }
    }

}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    LocationTestTheme {
        Greeting("Android")
    }
}
0

There are 0 best solutions below