https://kotlinlang.org logo
#compose
Title
# compose
n

Nat Strangerweather

01/14/2021, 10:18 PM
Is it possible to pass a value from a BroadcastReceiver to a Composable? If so, how? If I use an intent and putExtra, my Composable is not in an Activity so I'm stuck. 🤔
n

nglauber

01/15/2021, 12:28 AM
You can do this (inside your composable function):
Copy code
val context = AmbientContext.current
onActive {
    val broadcast = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            //intent?.getStringExtra("param") ?: ""
        }
    }
    context.registerReceiver(broadcast, IntentFilter("SOME_ACTION"))

    onDispose {
        context.unregisterReceiver(broadcast)
    }
}
a

Adam Powell

01/15/2021, 12:36 AM
s/`onActive`/`DisposableEffect`/, but yes, in general, this.
(onActive, onCommit, onDispose, etc. are going away soon in favor of the newer Effect composables)
👍 7
n

nglauber

01/15/2021, 12:42 AM
@Adam Powell is this the recommended solution?
Copy code
val context = AmbientContext.current
val broadcast = remember {
    object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            pressed(intent?.getStringExtra("key") ?: "")
        }
    }
}
DisposableEffect(subject = broadcast, effect = {
    context.registerReceiver(broadcast, IntentFilter("SOME_ACTION"))

    onDispose {
        context.unregisterReceiver(broadcast)
    }
})
a

Adam Powell

01/15/2021, 12:44 AM
You can still keep the receiver inside the effect block (and I would) -
context
would be a valid subject here as it's what you're registering the receiver with. You should also consider what
pressed
is here. If it's a lambda param to the function you likely want something like:
Copy code
val currentPressed by rememberUpdatedState(pressed)
val context = AmbientContext.current
DisposableEffect(context) {
  val broadcast = ... currentPressed(...) ...
  ...
}
n

nglauber

01/15/2021, 12:45 AM
Great! this
pressed
is a lambda, but i just use this name to help here quickly… 😛
a

Adam Powell

01/15/2021, 12:46 AM
rememberUpdatedState
is specifically for this kind of usage, when you need composition to update something used by a long-lived object without recreating that long-lived object and potentially restarting some sort of heavyweight registration or subscription. Also useful for things like
LaunchedEffect
that suspend over time if you don't want to restart the whole coroutine.
👍 2
(and by extension,
Modifier.pointerInput {}
)
n

nglauber

01/15/2021, 12:49 AM
Great! I tried it here and worked fine 👏
👍 2
n

Nat Strangerweather

01/15/2021, 6:38 AM
Oh wow, thanks for this! I've been trying to solve this for about two weeks... Lol. 😂
So here:
Copy code
DisposableEffect(context) {
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val code = codesProvider.getInterruptionFilter(context!!)
            }
        }
        context.registerReceiver(broadcast, intentFilter)
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
How does
code
reach the actual Composable to be used?
n

nglauber

01/15/2021, 11:37 AM
once you do this and the
sendBroadcast
is called, your
code
will be received.
🙏 1
a

Adam Powell

01/15/2021, 3:47 PM
For perhaps more complete context based on the above,
Copy code
@Composable fun MyBroadcastReceiver(
    param1: T1,
    param2: T2,
    receiver: (code: Int) -> Unit
) {
    val context = AmbientContext.current
    val currentReceiver by rememberUpdatedState(receiver)
    DisposableEffect(context, param1, param2) {
        val intentFilter = IntentFilter().apply {
            // add actions/filter params based on param1/param2
        }
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val code = codesProvider.getInterruptionFilter(context!!)
                currentReceiver(code)
            }
        }
        context.registerReceiver(broadcast, intentFilter)
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}
👍 1
Note: 1. The use of
rememberUpdatedState
to let composition change the
receiver
callback that is invoked with
code
when the BroadcastReceiver receives a broadcast without unregistering and reregistering the underlying BR 2. Parameters to the constructed
IntentFilter
become keys to the
DisposableEffect
, because we do need to unregister and reregister the BR if the IntentFilter we need to build changes
functions like the above are common, and similar to the kinds of bridging functions between callbacks and coroutines that you would build with
suspendCancellableCoroutine
👍 1