Taking coding permissions into account
Well, after all the explanations, we've reached the coding part, and this is where we will get our coding hands dirty. The following are key methods used for handling permissions:
Context.checkSelfPermission()
: This checks whether your app has been granted a permissionActivity.requestPermission()
: This requests a permission at runtime
Even if your app is not yet targeting Android Marshmallow, you should test your app and prepare to support it.
Testing permissions
In the Android Marshmallow permissions model, your app must ask the user for individual permissions at runtime. There is limited compatibility support for legacy apps, and you should test your app and also test a version to make sure it's supported.
You can use the following test guide and conduct app testing with the new behavior:
- Map your app's permissions
- Test flows with permissions granted and revoked
The adb
command shell can be quite helpful to check for permissions:
- Listing application permissions and status by group can be done using the following
adb
command:adb shell pm list permissions -g
- You can grant or revoke permissions using the following
adb
syntax:adb shell pm [grant|revoke] <permission.name>
- You can grant permissions and install
apk
using the followingadb
command:adb install -g <path_to_apk>
Coding for runtime permissions
When we want to adjust our application to the new model, we need to make sure that we organize our steps and leave no permission stranded:
- Check what platform the app is running on: When running a piece of code that is sensitive at the API level, we start by checking the version/API level that we are running on.
By now, you should be familiar with
Build.VERSION.SDK_INT
. - Check whether the app has the required permission: Here, we get ourselves a brand new API call:
Context.checkSelfPermission(String permission_name)
.With this, we silently check whether permissions are granted or not.
This method returns immediately, so any permission-related controls/flows should be dealt with by checking this first.
- Prompting for permissions: We have a new API call,
Activity.requestPermissions (String[] permissions, int requestCode)
. This call triggers the system to show the dialog requesting a permission. This method functions asynchronously.You can request more than one permission at once. The second argument is a simple request code returned in the callback so that you can recognize the calls. This is just like how we've been dealing with
startActivityForResult()
andonActivityResult()
for years.Another new API is
Activity.shouldShowRequestPermissionRationale(String permission)
.This method returns
true
when you have requested a permission and the user denied the request. It's considered a good practice after verifying that you explain to the user why you need that exact permission. The user can decide to turn down the permission request and select the Don't ask again option; then, this method will returnfalse
.
The following sample code checks whether the app has permission to read the user's contacts. It requests the permission if required, and the result callback returns to onRequestPermissionsResult
:
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, SAMPLE_MATRIXY_READ_CONTACTS); } //Now this is our callback @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case SAMPLE_MATRIXY_READ_CONTACTS: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission granted - we can continue the feature flow. } else { // permission denied! - we should disable the functionality that depends on this permission. } } }
Just to make sure we all know the constants used, here's the explanation:
public static final int PERMISSION_DENIED=-1
:Since it's API level 1, permission has not been granted to the given package
public static final int PERMISSION_GRANTED=0
:Since it's API level 1, permission has been granted to the given package.
If the user denies your permission request, your app should take the appropriate action, such as notifying the user why this permission is required or explaining that the feature can't work without it.
Note
Your app cannot assume user interaction has taken place because the user can choose to reject granting a permission along with the do not show again option; your permission request is automatically rejected and onRequestPermissionsResult
gets the result back.
Best practices and usage notes
The new permissions model has brought to life a smoother experience for users and a bit more code-handling for developers. It makes it easier to install and update apps and feel comfortable with what the apps are doing.
Minimalism is a great option
Don't be a permission hog! In our application life cycle, we should try to minimize our permission requests. Asking for a lot of permissions and maintaining them can seem hazardous for some, and we should try and make the feature smooth and ask for the smallest number of permissions as far as possible in order to allow relaxed, undisturbed usage. Consider using intents whenever possible—rely on other applications doing some of the work for us (fewer permissions means less friction, turning a good app into a great one).
Asking for too many permissions at once
Users can get distracted by too many dialogs popping up, asking them for more and more permissions. Instead, you should ask for permissions as and when you need them.
However, we have some exceptions to every rule. Your app may require a few permissions to begin with, such as a camera application showing the camera permissions right at the beginning. However, setting the photo to your contact can be done and requested only when the user triggers that specific action. Try to map your flow and make it easier for users to understand what is going on. Users will understand that you've requested permissions for contacts if they have asked to set information to a contact via your app.
One more suggestion: apps with a tutorial can integrate the essential permissions' request in the tutorial, allowing the users to better understand the flow and why each permission is used.
Honesty can be a great policy
When asking for a permission, the system shows a dialog stating which permission your app wants, but it doesn't say why. Consider users who hate being left in the dark thinking why this permission is needed now or users who deny the permissions due to speculation. Things can be even worse: sometimes, a user's cursor may be 2 cm away from the 1-star rating or the uninstall button.
This is why it's a good idea to explain why your app wants the permissions before calling requestPermissions()
.
Keep in mind that most developers will choose a tutorial but a lot of users may choose to skip tutorials whenever possible, so you must make sure that you can provide information about permissions, apart from the ones in the tutorial.