Last active
January 6, 2024 00:46
-
-
Save lgawin/71839117f261cdc36701c7e42bebbd8b to your computer and use it in GitHub Desktop.
Helper for requesting roles with example usage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.role.RoleManager | |
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 | |
import kotlinx.coroutines.flow.map | |
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> = updates.map { checkRole(roleManager, role) } | |
init { | |
lifecycle.addObserver(this) | |
} | |
override fun onCreate(owner: LifecycleOwner) { | |
getRole = registry.register("$role-request-${UUID.randomUUID()}", owner, ActivityResultContracts.StartActivityForResult()) { | |
if (it.resultCode == ComponentActivity.RESULT_OK) { | |
updates.trigger() | |
} | |
} | |
} | |
override fun onResume(owner: LifecycleOwner) { | |
super.onResume(owner) | |
updates.trigger() | |
} | |
override fun request() { | |
getRole.launch(roleManager.createRequestRoleIntent(role)) | |
} | |
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() { | |
tryEmit(System.currentTimeMillis()) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | |
Thus I need ROLE_CALL_SCREENING role. | |
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?) { | |
super.onCreate(savedInstanceState) | |
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