# 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 action** | `com.benomad.ACTION_RNC` |
| **Extra key** | `message` (String) |
| **Payload** | the JSON-RPC message (response or notification) |
| **Broadcast type** | global `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 command`id`; dispatch by its `method`.

```kotlin
fun handleRncMessage(json: String) {
    // e.g. parse, then branch on "id" / "result" / "error" / "method"
}
```
