BeNav5RNC API
Android

Receiving messages

A guide to integrate RNC message reception into your Android app: both command responses and asynchronous notifications (guidance progress, alerts, …) sent by BeNav (com.benomad.benav).

This covers the receive path only.

How BeNav returns RNC messages#

BeNav emits every RNC message as a global broadcast.
There is a single channel for both responses and notifications:

Intent actioncom.benomad.ACTION_RNC
Extra keymessage (String)
Payloadthe JSON-RPC message (response or notification)
Broadcast typeglobal sendBroadcast(), no target package or permission

Because BeNav does not track who sent a command, responses are not routed back to a specific caller; they are broadcast to everyone listening.
To tell which response matches which command, correlate the JSON-RPC id field with the id you sent.

Integration#

1. Register a runtime receiver (not a manifest one)#

The broadcast is implicit (an action with no target package).

Because the sender (BeNav) is a different app, the receiver must be registered as exported.

kotlin
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.ContextCompat

object RncResponseListener {

    private const val ACTION = "com.benomad.ACTION_RNC"
    private const val EXTRA_MESSAGE = "message"

    /** Registers a receiver that forwards each RNC message (JSON String) to [onMessage]. */
    fun register(context: Context, onMessage: (String) -> Unit): BroadcastReceiver {
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                intent.getStringExtra(EXTRA_MESSAGE)?.let(onMessage)
            }
        }
        ContextCompat.registerReceiver(
            context,
            receiver,
            IntentFilter(ACTION),
            ContextCompat.RECEIVER_EXPORTED,
        )
        return receiver
    }

    fun unregister(context: Context, receiver: BroadcastReceiver) {
        runCatching { context.unregisterReceiver(receiver) }
    }
}

It needs androidx.core:core-ktx (for ContextCompat).
No manifest entry and no permission are required.

2. Tie registration to a lifecycle#

Register while you want to receive, and unregister when you don't.
The right scope depends on your needs:

  • Register for the lifetime of an Activity/Service, or
  • Register only while a screen is visible.

Registering for as long as the component lives means messages keep arriving even while BeNav is in the foreground and your app is in the background (your process stays alive).

Plain Activity:

kotlin
class YourActivity : ComponentActivity() {

    private var receiver: BroadcastReceiver? = null

    override fun onStart() {
        super.onStart()
        receiver = RncResponseListener.register(this) { message ->
            handleRncMessage(message)
        }
    }

    override fun onStop() {
        receiver?.let { RncResponseListener.unregister(this, it) }
        receiver = null
        super.onStop()
    }
}

Jetpack Compose:

kotlin
val context = LocalContext.current
DisposableEffect(Unit) {
    val receiver = RncResponseListener.register(context) { message ->
        handleRncMessage(message)
    }
    onDispose { RncResponseListener.unregister(context, receiver) }
}

onReceive runs on the main thread.
Keep onMessage light (or hand the JSON off to your own dispatcher/coroutine); don't do blocking work there.

3. Handle the JSON#

message is the raw JSON-RPC string.
Parse it with your JSON library of choice and route by shape:

  • A response has a result (success) or an error (failure) and an id that matches the command you sent; use that id to correlate.
  • A notification has no matching commandid; dispatch by its method.
kotlin
fun handleRncMessage(json: String) {
    // e.g. parse, then branch on "id" / "result" / "error" / "method"
}