Android BroadcastReceiver To Listen For Custom Actions
Create an Android BroadcastReceiver to listen for custom actions sent by app to execute further necessary processing.

In this post I will demonstrate how to implement an Android BroadcastReceiver to listen for custom actions.

Generally, most common uses for Android BroadcastReceivers are to notify your app that some action has changed on the device.

In another case, maybe your app relies on specific device features to be enabled. If your app makes a request to pair with a device via bluetooth, it is important to ensure that bluetooth is turned on. If it is off, then you could create a BroadcastReceiver to monitor when bluetooth is enabled so your app can move forward with its functionality.

With that being said, you are not limited to system features for the BroadcastReceiver to listen to. You can create a BroadcastReceiver to listen for custom actions for further processing in your app.

Check out the link below from a previous post for more about BroadcastReceivers. It demonstrates how to implement a basic Android BroadcastReceiver.

Android BroadcastReceiver To Listen For Custom Actions

Code

Create Your BroadcastReceiver

First, have your BroadcastReceiver look for the custom action you specified. In this example, a string is passed along with the broadcast to display back to the UI.
class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {

        if(intent?.action == "com.itgeek25.samplebroadcastreceiver.custom_action"){
            intent.getStringExtra("message")?.let { DataRepo.updateState(it) }
        }
    }

}

Implement A Singleton Repository

Next, create a single source of trust for the app. I implemented a singleton repository. It will contain the string received as an intent extra from the sendBroadcast() method.
object DataRepo {

    private val _text: MutableStateFlow<String> = MutableStateFlow<String>("")
    val text: StateFlow<String> = _text.asStateFlow()

    fun updateState(state : String){
        _text.value = state
    }

}

Monitor UI State Updates Using A ViewModel

Afterwards, I created a ViewModel that has the reference of the string held by the repository.
class ViewModel : ViewModel() {

    val state: StateFlow<String> = DataRepo.text

}

class ViewModelFactory : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return if(modelClass.isAssignableFrom(com.itgeek25.samplebroadcastreceiver.viewmodel.ViewModel::class.java)){
            com.itgeek25.samplebroadcastreceiver.viewmodel.ViewModel() as T
        } else {
            throw IllegalArgumentException("ViewModel Not Found!!")
        }
    }
}

Build Your MainActivity

Finally, create your UI registering your BroadcastReceiver. Don't forget to unregister it in either the onStop or onDestroy method.
class MainActivity : ComponentActivity() {

    lateinit var viewModel: ViewModel

    var mBroadcastReceiver = MyReceiver()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val factory = ViewModelFactory()
        viewModel = ViewModelProvider(
            this,
            factory
        )[ViewModel::class.java]

        ContextCompat.registerReceiver(applicationContext, mBroadcastReceiver, IntentFilter().also { it.addAction("com.itgeek25.samplebroadcastreceiver.custom_action") },
            ContextCompat.RECEIVER_NOT_EXPORTED)

        setContent {

            var text by remember { mutableStateOf("") }
            val br_text = viewModel.state.collectAsStateWithLifecycle().value

            SampleBroadcastReceiverTheme {

                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(modifier = Modifier
                        .fillMaxSize()
                        .padding(innerPadding),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally) {
                        Text(
                            modifier = Modifier,
                            style = MaterialTheme.typography.titleLarge,
                            text = br_text,
                        )
                        TextField(
                            value = text,
                            onValueChange = {
                                text = it
                            },
                            placeholder = {
                                Text(text = "Enter your message")
                            })
                        Button(onClick = {
                            sendBroadcast(Intent("com.itgeek25.samplebroadcastreceiver.custom_action").also {
                                it.putExtra("message", text)
                            })
                        }) {
                            Text("Click To Send Broadcast")
                        }
                    }
                }
            }
        }
    }

    override fun onStop() {
        super.onStop()
        if(mBroadcastReceiver != null){
           unregisterReceiver(mBroadcastReceiver)
        }
    }
}

Bonus

There is an alternative way to register your BroadcastReceiver in your app. You can add it to your AndroidManifest.xml. The benefit of doing it this way will be that Android will take care of register / unregister of the BroadcastReceiver for the app.

Add BroadcastReceiver To Your AndroidManifest.XML

You just need to add the reference to the class as a receiver object inside the application tags.

<receiver android:name=".broadcastreceiver.MyReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="com.itgeek25.samplebroadcastreceiver.custom_action" />
            </intent-filter>
</receiver>

Edit The MainActivity

Lastly in this approach you will not need to add references to the BroadcastReceiver class. The only thing that is necessary is setting the class of the BroadcastReceiver in the Intent being passed to from the sendBroadcast() method.

class MainActivity : ComponentActivity() {

    lateinit var viewModel: ViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val factory = ViewModelFactory()
        viewModel = ViewModelProvider(
            this,
            factory
        )[ViewModel::class.java]

        setContent {

            var text by remember { mutableStateOf("") }
            val br_text = viewModel.state.collectAsStateWithLifecycle().value

            SampleBroadcastReceiverTheme {

                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(modifier = Modifier
                        .fillMaxSize()
                        .padding(innerPadding),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally) {
                        Text(
                            modifier = Modifier,
                            style = MaterialTheme.typography.titleLarge,
                            text = br_text,
                        )
                        TextField(
                            value = text,
                            onValueChange = {
                                text = it
                            },
                            placeholder = {
                                Text(text = "Enter your message")
                            })
                        Button(onClick = {
                            sendBroadcast(Intent("com.itgeek25.samplebroadcastreceiver.custom_action").also {
                                it.putExtra("message", text)
                                it.setClass(this@MainActivity, MyReceiver::class.java)
                            })
                        }) {
                            Text("Click To Send Broadcast")
                        }
                    }
                }
            }
        }
    }
}