Skip to content

Instantly share code, notes, and snippets.

@afollestad
Last active January 14, 2024 10:23
Show Gist options
  • Save afollestad/d6c0f6a80c99b3e7033caea997b2f061 to your computer and use it in GitHub Desktop.
Save afollestad/d6c0f6a80c99b3e7033caea997b2f061 to your computer and use it in GitHub Desktop.
A Lifecycle components aware BroadcastReceiver DSL (Kotlin)
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.Lifecycle.Event.ON_START
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import android.content.BroadcastReceiver as StockReceiver
/** @author Aidan Follestad (@afollestad) */
class BroadcastReceiver<T>(
context: T,
constructor: Builder.() -> Unit
) : LifecycleObserver where T : Context, T : LifecycleOwner {
private val appContext = context.applicationContext
private val filter: IntentFilter
private val instructions: List<Instructions>
init {
val builder = Builder()
constructor(builder)
filter = builder.filter()
instructions = builder.instructions()
context.lifecycle.addObserver(this)
}
private val broadcastReceiver = object : StockReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
for (ins in instructions) {
if (ins.matches(intent)) {
ins.execution()
.invoke(intent)
break
}
}
}
}
@OnLifecycleEvent(ON_START)
fun start() {
appContext.registerReceiver(broadcastReceiver, filter)
}
@OnLifecycleEvent(ON_DESTROY)
fun stop() = appContext.unregisterReceiver(broadcastReceiver)
}
import android.content.IntentFilter
/** @author Aidan Follestad (@afollestad) */
class Builder internal constructor() {
private val filter = IntentFilter()
private val instructions = mutableListOf<Instructions>()
fun onAction(
action: String,
execution: Execution
) {
filter.addAction(action)
instructions.add(OnAction(action, execution))
}
fun onDataScheme(
scheme: String,
execution: Execution
) {
filter.addDataScheme(scheme)
instructions.add(OnDataScheme(scheme, execution))
}
fun onCategory(
category: String,
execution: Execution
) {
filter.addCategory(category)
instructions.add(OnCategory(category, execution))
}
internal fun filter() = filter
internal fun instructions() = instructions
}
import android.content.Intent
typealias Execution = (Intent) -> Unit
/** @author Aidan Follestad (@afollestad) */
sealed class Instructions {
abstract fun matches(intent: Intent): Boolean
abstract fun execution(): Execution
data class OnAction(
val action: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.action == action
}
override fun execution() = execution
}
data class OnDataScheme(
val scheme: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.data?.scheme == scheme
}
override fun execution() = execution
}
data class OnCategory(
val category: String,
val execution: Execution
) : Instructions() {
override fun matches(intent: Intent): Boolean {
return intent.hasCategory(category)
}
override fun execution() = execution
}
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Will automatically unregister itself when the Activity pauses, and register again when it resumes.
BroadcastReceiver(this) {
onAction("com.yourapp.helloworld.SOME_ACTION") {
// Do something
}
onCategory("messages") {
// Do something
}
onDataScheme("file://") {
// Do something
}
}
}
}
@nhubbard
Copy link

This... is wonderful. Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment