Documentation

Broadcast Receivers

A receiver is a .wh file with no markup in src/receivers/. Whitehall writes the BroadcastReceiver and its manifest entry.

A receiver

// src/receivers/+BootReceiver.wh
val on = ["BOOT_COMPLETED"]
val exported = true

fun onReceive() {
  $dispatch(Startup.bringUpDaemon, constraints: ["network"])
}

Options

Options are top-level vals, the way a SvelteKit route file declares export const prerender. The set is closed, so a misspelled option is a build error rather than a receiver that silently never fires.

OptionMeaning
onIntent actions to listen for. Required.
exportedWhether the system and other apps can reach it. Defaults to false.
permissionA permission the sender must hold. Uses the $permission names.

A bare action is qualified for you — "BOOT_COMPLETED" becomes android.intent.action.BOOT_COMPLETED. Anything containing a dot is left alone, so your own actions work as written:

val on = ["com.example.app.REPLY"]

onReceive may not block

A receiver has ten seconds in the foreground and sixty in the background before Android reports an ANR. Whitehall owns this function body, so rather than let you write something that cannot finish, it refuses to compile it:

error: `BootReceiver.onReceive` may not block
   = `Thread.sleep` sleeps on the broadcast thread
   = a broadcast receiver has 60s before Android reports an ANR
   = help: hand off and return -- `$dispatch(SomeWork)`

Refused: Thread.sleep, runBlocking, $await, $fetch, a suspend fun declared in the same file, and a suspend onReceive.

A receiver's whole job is to hand off and return. $dispatch is the handoff, and it has no deadline — Android schedules it, retries it, and honors your constraints.

Exported, and why it is checked

A system broadcast comes from outside your app, so a receiver that is not exported never fires — silently, with nothing at build or install time to tell you. Listening for a system action without exporting is an error rather than a default:

error: receiver `BootReceiver` listens for `android.intent.action.BOOT_COMPLETED`
       but is not exported
   = the system sends that broadcast, so an unexported receiver never fires
   = help: add `val exported = true`

Your own actions need no export, since the sender is the app itself.

Services

Services have no .wh equivalent yet. Write the Kotlin and drop it in src/android/, which is copied into the app's own package — see Project Structure.

See Also