About roles
Tags: android, jetpack
Published: 2021-02-18 16:18:53

Sometimes it’s quite a task to stay updated in Androidverse. When Feb 10 brought a bunch of Jetpack news I stumbled upon [Core-Role Version 1.0.0](https://developer.android.com/jetpack/androidx/releases/core#core-role-1.0.0) which I had not on my agenda so far. I was even more surprised realizing roles were a topic in api level 29 😎

Time for a short piece.

The release notes say:

> Added `RoleManagerCompat` containing the name and
> documentation for roles that might be available in the
> system.

Let's see what this `RoleManagerCompat` has up its sleeve. First a look at the docs:

> This class contains the name and documentation for 
> roles that might be available in the system.

Yes, we knew that already.

> The list of available roles might change with a system 
> app update, so apps should not make assumption about the
> availability of roles. Instead, they should always check
> if the role is available using
> `RoleManager.isRoleAvailable(String)` before trying
> to do anything with it.

Frankly, that did not enlighten me, either. Let's investigate. The source code starts like this:

![RoleManagerCompat source code snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soelugkckcoft2dgh6tn.png)

As it turns out, the whole class consists of constants representing a role, accompanied by a description of what an app must do in order to qualify for this role. Consider `ROLE_BROWSER` (the following screenshot is taken from the [current docs](https://developer.android.com/reference/androidx/core/role/RoleManagerCompat#ROLE_BROWSER)):

![Definition of ROLE_BROWSER](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adgl9kn66memussdb48b.png)

Now that we know what we need to do to be eligable holding this role - how do we acquire it? Please recall that when there is a `XyzCompat` class there is (almost) always a `Xyz` class. So, we do have `android.app.role.RoleManager`. It's available since api level 29. The [docs](https://developer.android.com/reference/android/app/role/RoleManager) say:

> This class provides information about and manages roles.
> A role is a unique name within the system associated with
> certain privileges. The list of available roles might
> change with a system app update, so apps should not make
> assumption about the availability of roles. Instead, they
> should always query if the role is available using
> `isRoleAvailable(java.lang.String)` before trying to do
> anything with it. Some predefined role names are available
> as constants in this class, and a list of possibly
> available roles can be found in the AndroidX Role library.

I know, you are tempted to say *I knew that already*.

> There can be multiple applications qualifying for a role,
> but only a subset of them can become role holders. To
> qualify for a role, an application must meet certain
> requirements, including defining certain components in its
> manifest. These requirements can be found in the AndroidX
> Libraries. Then the application will need user consent to
> become a role holder, which can be requested using
> `Activity.startActivityForResult(Intent, int)` with the
> Intent obtained from
> `createRequestRoleIntent(java.lang.String)`.

Let's translate this into code:

```kotlin
package com.thomaskuenneth.roledemo

import android.app.role.RoleManager
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.role.RoleManagerCompat

private const val REQUEST_ROLE_BROWSER = 123

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    getSystemService(RoleManager::class.java)?.run {
      if (isRoleAvailable(RoleManagerCompat.ROLE_BROWSER)) {
        if (!isRoleHeld(RoleManagerCompat.ROLE_BROWSER)) {
          val intent = createRequestRoleIntent(RoleManagerCompat.ROLE_BROWSER)
          startActivityForResult(intent, REQUEST_ROLE_BROWSER)
        } else
          toast(R.string.has_role)
      } else toast(R.string.role_not_available)
    }
  }

  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_ROLE_BROWSER && resultCode == RESULT_OK) {
      toast(R.string.role_acquired)
    }
  }

  private fun toast(resId: Int) {
    Toast.makeText(baseContext, resId, Toast.LENGTH_SHORT).show()
  }
}
```

So we get an instance of `android.app.role.RoleManager` by invoking `getSystemService()`. if `isRoleHeld()` returns `true` we are already in - we hold that role. If not, we need to knock at the door (ask the user) by calling `createRequestRoleIntent()` and `startActivityForResult()`. If the preconditions for acquiring a role are met the user will see something like this:

![The app RoleDemo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gy2nqe1kyqznvvz9wez8.png)

Please recall to always invoke `isRoleAvailable()` and check if the systems offers the rol your app wants to acquire. To conclude this short piece, here is another important quote from the docs:

> Upon becoming a role holder, the application may be granted
> certain privileges that are role specific. When the
> application loses its role, these privileges will also be
> revoked.

This bears an interesting question: can the app ask the system to revoke a role? So far I have not spotted a corresponding function/method. Did you? What are your thoughts about roles? I'd love to hear opinion in the comments

---

[source code](https://github.com/tkuenneth/android-demos/tree/master/misc/RoleDemo)