Photo by James Coleman on Unsplash

Android Q to the Res-Q!

An intro to RoleManager! And a terrible pun that has nothing to do with Resources!

Android Q Preview was released recently, inviting developers to start testing and supporting new APIs! One of these APIs is RoleManager , and as we can gather from the Q-preview documentation:

Android Q introduces a standard facility, roles, that allows the OS to grant apps elevated access to system functions based on well-understood use cases. Semantically, each role represents a common use case, such as playing music, viewing photos in a gallery, or sending SMS messages. If an app loses its role, this elevated access is also revoked. (Link)

In plain english, a role is basically a type of app that executes a specific function (e.g. web browser). The roles exposed in the Q preview are Browser, Dialer, Emergency, Gallery, Home, Music, and SMS. As a developer, you can now request to hold one of these roles for your app, if it falls into one of those buckets.

Why is this API helpful?

This now lets us proactively query the system to see if our app is the default. If not, it lets us request permission, from the user, to hold that role. We now have more control over checking and responding to these sorts of events. No more silly hacks, no more trying to resolve your package name with the PackageManager, no more searching for options in settings! (🎉🎉🎉)

What’s the catch?

For the RoleManager to resolve a request properly, your Manifest must specify categories, permissions, or additional intent filters for the role to properly be held. The request will be automatically canceled if you fail to do so. For example, if we have an Internet Browser, we must define the CATEGORY_APP_BROWSER inside the IntentFilter for your Launcher activity. If we do not, and we request a role via RoleManager , that request will be denied without ever prompting the user.

How do we use it? Well, It’s quite simple!

Step 0. We add any necessary categories, IntentFilters, or Permissions in your Manifest!

Step 1. Answer this question: Do we have an app that fits into a pre-defined role? If yes, let’s go to Step 2. If no, let’s enjoy some ice cream!

Step 2. Is this Role available on the current system? We can check this via the RoleManager class.

val roleManager = getSystemService(RoleManager::class.java)

val isRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER)

Step 3. If this Role is available, is our app holding that role currently? We can check this via the RoleManager too!

val roleManager = getSystemService(RoleManager::class.java)

val isRoleHeld = roleManager.isRoleHeld(RoleManager.ROLE_BROWSER)

Step 4. If we are not holding this role, is now an appropriate time to ask the user to request access for this role? If yes, we use RoleManager for its magical powers of Intent creation 🎩🐰🎉.

val roleManager = getSystemService(RoleManager::class.java)

val roleRequestIntent = roleManager.createRequestRoleIntent(

RoleManager.ROLE_BROWSER)

startActivityForResult(roleRequestIntent, ROLE_REQUEST_CODE)

Step 5. If the user grants permission for us to hold that role, onActivityResult will be called with RESULT_OK . RESULT_CANCELED will return if the user denied our request, or if conditions for holding that role have not been met (e.g. missing category in your manifest).

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

if (requestCode == ROLE_REQUEST_CODE) {

if (resultCode == RESULT_OK) {

// Oh yes :)

} else {

// Oh no :(

}

}

}

Step 6. Done. From there is just a matter of refreshing our UI to reflect the current state of the system!