Skip to content

Instantly share code, notes, and snippets.

Last active January 6, 2024 00:46
Show Gist options
  • Save lgawin/71839117f261cdc36701c7e42bebbd8b to your computer and use it in GitHub Desktop.
Save lgawin/71839117f261cdc36701c7e42bebbd8b to your computer and use it in GitHub Desktop.
Helper for requesting roles with example usage
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
sealed interface RoleAvailability {
data object NotAvailable : RoleAvailability
data object Available : RoleAvailability
data object Held : RoleAvailability
interface RoleObserver {
val role: String
val availability: Flow<RoleAvailability>
fun request()
fun ComponentActivity.roleObserver(role: String): RoleObserver = DefaultRoleObserver(
roleManager = getSystemService(ComponentActivity.ROLE_SERVICE) as RoleManager,
role = role,
registry = activityResultRegistry,
lifecycle = lifecycle,
internal class DefaultRoleObserver(
private val roleManager: RoleManager,
override val role: String,
private val registry: ActivityResultRegistry,
lifecycle: Lifecycle,
) : DefaultLifecycleObserver, RoleObserver {
private lateinit var getRole: ActivityResultLauncher<Intent>
private val updates = MutableStateFlow(System.currentTimeMillis())
override val availability: Flow<RoleAvailability> = { checkRole(roleManager, role) }
init {
override fun onCreate(owner: LifecycleOwner) {
getRole = registry.register("$role-request-${UUID.randomUUID()}", owner, ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == ComponentActivity.RESULT_OK) {
override fun onResume(owner: LifecycleOwner) {
override fun request() {
private fun checkRole(roleManager: RoleManager, role: String) = when {
roleManager.isRoleHeld(role) -> RoleAvailability.Held
roleManager.isRoleAvailable(role) -> RoleAvailability.Available
else -> RoleAvailability.NotAvailable
private fun MutableStateFlow<Long>.trigger() {
class SettingsActivity : ComponentActivity() {
Example usage:
I want my application to act as a call screening app, so I want to set is as default callerID & spam app.
On start check if role is held, then show app content.
When role is not held but available show some rationale & when request clicked, invoke system popup.
When role is not available, show error.
override fun onCreate(savedInstanceState: Bundle?) {
val callScreeningRole = roleObserver(RoleManager.ROLE_CALL_SCREENING)
setContent {
AppTheme {
val callScreeningRoleAvailability by callScreeningRole.availability.collectAsState(RoleAvailability.Held)
when (callScreeningRoleAvailability) {
RoleAvailability.Available -> RequestRoleScreen("Some rationale", onClick = {callScreeningRole.request()})
RoleAvailability.Held -> AppScreen()
RoleAvailability.NotAvailable -> ErrorScreen()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment