Following the new Android announcements at Google I/O 2015, we previously took a look at the new Android Design Support Library. One other important announcement was the new approach to Permissions in Android M, which also affects the way they work in pre-M applications. Here at ribot we strive to keep up-to-date with the latest development approaches, so I decided to break down the new permissions model and here’s what happened.
Permissions on Android have always been an important area to consider when developing applications. Currently, users have awareness of these permissions, but no real control over them. On top of that, developers can encounter the issue of whether to add any new permissions on top of the existing ones, during updates, this can cause friction and have an impact on update statistics.
With the current permissions model, users must grant your app a variety of permissions at install time, before they’ve even used the application. This means the user must have a certain level of trust in your app before using it. For a lot of users this can be a crucial deciding point as to whether they’re going to install the app or not.
Personally, I have witnessed this on many occasions when installing apps. A recent case was upon attending a music festival which had a companion app available for viewing stage times — I thought this was great, so I instantly opened up the Google play Store to download it. After clicking install, I was presented with this rather daunting dialog (left). I was confused as to why this app (whose only function was displaying stage times alongside festival details) needed information on my contacts, access to my location, access to my files, my calendar and so on. The result of this was a lost install. There was no way I was installing this app when it was asking so much from me, before I even knew its purpose or had any reason to trust it. This experience was overwhelming before I had even used the app, which wasn’t the first time that this had occurred.
There are some cases where we as users have felt almost forced to install an application, giving in due to previous encounters. Take Facebook for example, a lot of users know and love Facebook. Whilst the permission list is huge, Facebook has already built a good level of trust with their users, so they’re going to install the app regardless of the number of permissions it’s asking for at the time of install.
Whilst some of these are genuinely needed as part of Facebook’s main functionality, there are likely to be some things in that list that you may just not want to grant permission to.
With Android-M, this list can be greatly reduced by
asking for permissions as and when they are required.
For example, asking for the camera permission when the user reaches the point of taking the photo would make sense. That way, if the user is never going to make use of that functionality within the app then they won’t need to grant that permission to the Facebook application at the time of install.
With Android-M, permissions will now be grouped together under a parent category, this category is what will be used when requesting a certain permission from the user. So for example, if you request access to the READ_CALENDAR permission then the user will be prompted to grant you the Calendar permission.
Many of these categories contain several permissions, which can be broken down into the following sets:
You’ll notice here that there is no longer an internet permission – great! One of the nice things is that you’re no longer required to ask for this permission, meaning you can make network requests without prompting the user to ask if it is OK to do so. This is due to this permission (along with many others) now being designated as PROTECTION_NORMAL, these are all automatically granted to the application if they are declared in the manifest.
The install flow of apps on the play store has always felt a little broken. Clicking Install would never actually install the app, you first have to accept a number of permissions before the actual install can take place. Thankfully, permissions will no longer be asked for at this point. So when you click Install in the Google Play Store it really will install the app, rather than prompting you to accept these numerous permissions first.
At runtime, there’ll be points in your application where you need to request permissions to use certain features. At this point, you’ll need to check with the system whether you have this required permission. As shown below, the system will make a number of checks before giving you, or the user, a path on which to continue.
- To begin with (1) we need to check if we have the permission(s) we require to perform an operation.
- Next (2) the system checks whether we have the permission we need, this is done when we call the checkSelfPermission() method.
- We can continue and handle the result (6) if we have the required permission, otherwise the system will next need to check if the user has already been asked to be granted this permission (3). The standard permission dialog will be displayed to the user (4) if they have not previously been asked to grant the permission.
- Otherwise, if the user hasn’t asked for the permission not to be asked for again (5) then the dialog will still be displayed to the user, this time with an additional checkbox to set this flag.
- However, if the flag has already been set at this step then the result will just be returned to the onRequestPermissionsResult() method (6) and the result handled within the code.
When we request a permission the user is shown a simple dialog, prompting them to either allow permission to the specificed feature or to deny it.
This is the dialog that the user will be shown the first time that the permission is requested. If they accept the request then you are granted permission to execute the desired task. However, as briefly explained above, denying the request can result in two different outcomes.
The first time the user denies the request for permission, the dialog is dismissed and you will need to handle this in some way visually. For example if you have requested access to Contacts in order to show the users contacts, then showing an error placeholder in place of the contact list would make the outcome of denying this permission clear.
Note: There is no penalty for the permission being denied the first time, so there is no need to carry out any form of double prompting.
If desired, you can use the shouldShowRequestPermissionRationale() method to check whether the application has previously requested the specified permission and been denied. This will allow error states to be correctly displayed on the screen before requesting the permission, allowing the explanation of why the permission is needed before requesting access again.
The next time that you request the same permission after it being denied for the first time, the user will be granted the option for this request not to occur again.
If the user selects this option, then you will not be able to request access to that permission again for the current install. There is nothing you can do about this, so if this is a permission that is crucial to the operation of your app then it is important that you make it clear as to why you need this permission the first time access is denied.
This is another reason as to why it is important to make it really clear to users as to why your app will require certain permissions to function. One approach to do so is providing some form of walkthrough or welcome screen, outlining the functionality of your application. This will really help users to be aware of why they will be asked for permissions, whilst also hopefully being given more trust to grant them.
It is also possible to request multiple permissions during the same request. In most cases this won’t be necessary and should not used unless it is really crucial. A good use-case for this is at app launch where the app can’t function without the required permissions. For example, an SMS app that requires access to contacts in order function would be able to ask for both the SMS and Contacts permissions when the app is launched.
The dialog looks pretty much the same as when requesting a single permission, other than the addition of the permission count. This count is another key reason not to flood the user with permission requests, seeing a high number here could have a negative effect on the experience and be overwhelming for the user.
Before checking for any of these permissions we first need to check the Build.VERSION.CODENAME that our application is running on. Currently, if this is equal to Android M (“MNC”) then we can go ahead and call the relevant permission methods.
Requesting a single Permission
In order to request a single permission we can simply use the checkSelfPermission() method to determine whether our application already has the required permission. As shown in the code example below, if we don’t have permission then we need to use the requestPermissions() method to request this permission for our application – passing in an array containing the permission that we‘re requesting access to.
When the requestPermissions() method is called, a dialog will be displayed to the user prompting their response to our request. Once responded, the onRequestPermissionsResult() method will be called – this is where we can check the users response to the permissions dialog and respond accordingly.
In this example we retrieve the permission level for our requested permission from the grantResults array, we access the first element here as we’ve only requested a single permission. If granted permission, then we’d give something back to the user by performing the action that requires the requested permission, now we have permission to do so.
On the other hand, if we’re denied permission then we should show some form of message to the user to notify them that they’re / we’re unable to carry out that certain action.
Requesting multiple permissions
In the case of requesting multiple permissions, we need to do things slightly differently. In the example below, I begin by checking if I have been granted the permissions that I wish to be given access to. If not, I add them to my list of desired permissions. Next, I pass my list of permissions as a parameter to the requestPermissions() method.
Declaring permissions only for M
In the case of only wishing to ask for a permission when the application is running on an Android-M device, it is possible to declare the permission for M builds only. This can be done by using:
This method behaves the same as a general permission declaration other than it’ll only be checked for M-builds, meaning that you are able to add new permissions to updated versions of applications, without the worry of breaking older pre-M builds. The permission will only be applicable to M-builds and will never be requested otherwise.
Toggling Permissions in Settings
Within the phones settings, users have the ability to both view and edit the given permissions for applications. As shown below, permissions are grouped by their category (left). Selecting one of these categories will take the user to the list of apps that have that permission specified in its manifest file (right).
Users can also access individual app permissions through Settings > Apps > Your App, from here we can select Permissions and the list of permission states will be shown.
At any point the user can access these settings and toggle the permissions on or off, this is where it can become an issue for any permissions that your application may be using. It is important to check if you have been granted a permission everytime you try to carry out a function that requires it.
If a permission has previously been granted and it is turned off, then your application is able to check this again – providing that the ‘Don’t ask again’ checkbox hasn’t been checked.
For applications built with a pre-M SDK, permissions can still be revoked from these settings screens. The issue here is that if these are revoked then we can’t request these permissions from within the app at all, because this functionality is only available for M builds. However, if the user does attempt to toggle off a permission for a pre-M application then they will be displayed a dialog (below) to notify them of the effect that this may have.
Applications built with a pre-M SDK will still behave mostly in the same way as before, with a few unfortunate exceptions.
At install time, the permissions behave exactly the same. The user will be displayed the dialog in which they can view and accept the permissions in order to install the application on their device. At this point, all of these permissions are granted. So on pre-M builds there is no such thing as granting permissions at runtime, they’re all granted at the time of install. The same goes for updates with new permissions, any new permissions will be granted when the user updates the app and accepts these new permissions at the time of install.
As mentioned previously, the user is still able to enter the system settings and disable these permissions. The user will be displayed the same error message in this case, but if they still decide to deny the permission then it’s not possible to request these at runtime. Currently if this occurs then any requests that take place that require these permissions will just be fed empty state data. For example:
- Requesting a list of contacts will return a “No contacts” response
- Request the users current location will return “Location not available”
- Attempting to save a contact will return “Contact saved”, when the contact would not have actually been saved
So whilst that is the case, it is important to ensure that within your pre-M applications you are correctly handling any error states that may occur in situations where revoked permissions can break functionality.
Do you even need permissions?
Remember that some functions can still be carried out with the use of an Intent, without the need to request permission to do so. This greatly improves the UX of your application as the user will not be prompted with any form of permission dialog when the need to perform the desired operation takes place.
- ACTION_INSERT – As long as it meets your apps requirements, this action can be used to replace a couple of permissions. Setting the required MIME Type and Intent Extras allows you to insert either calendar events or contacts. This could potentially remove the need for any of the Calendar and Contacts permissions.
- ACTION_IMAGE_CAPTURE – If your app simply needs to take a photo (or video, see: ACTION_VIDEO_CAPTURE) and return the result, then this action can be used to do so. The image will be returned to the specified location sent in the Intent Extras (EXTRA_OUTPUT). If sufficient, this intent will completely remove the need for the Camera permission. (See also: INTENT_ACTION_STILL_IMAGE_CAMERA)
- ACTION_PICK – This action can be used to both select a contact and select specific data (email, phone number, address) from the users contacts. When using this action your application is given temporary access to read the users contacts, removing the need to request the READ_CONTACTS permission.
- ACTION_VIEW – When used in conjuction with ACTION_PICK, this action can be used to view the details for a selected contact URI without requiring the Contacts permission. This action can also be used to start a Map intent by passing in a valid geo-location as the intent data.
- ACTION_EDIT – Again, if used in conjunction with ACTION_PICK, the returned contact URI can be used to edit the contacts details.
- ACTION_DIAL – This action can be used to open the dialer with a specific phone number. Whilst it requires the user to press the call button themselves (see ACTION_CALL), it does not require any phone related permissions to be used.
- ACTION_SENDTO – This action can be used to compose an SMS message to send to specific phone number. If this meets the requirements of your application then it can be used instead of requesting the SMS permission to send an SMS message. (See also: ACTION_SEND, ACTION_SEND_MULTIPLE)
When implementing this new approach to permissions there are a number of best practices that you should follow in-order to ensure that using your application is a pleasant experience, regardless of the permissions that may need to be requested along the way.
- Helping users to understand exactly why you’re going to be asking for certain permissions can really improve the experience of your application. The point of application launch can be a great time to make it clear what it is your application does, this can help the user to understand why they need to grant you permission to carry out certain actions.
- Be sure to ask for permissions as and when they are needed. When asking for these permissions it is important that they are done so at times when it makes sense to ask for a permission. For example, if your application requires the Camera permission to take a photo it’d make sense to ask for this permission when the user reaches the point where they want to take a photo (e.g pressing a “Take photo” button).
- As previously discussed, avoid requesting multiple permissions all at once as this can be overwhelming for the user and create a negative experience. You may want to ask for a couple of permissions at first launch if these are crucial to your app functioning. Otherwise, ask for permissions as and when they are needed.
- Always be sure to give something back to the user when they have granted you the permission that you have requested. For example, if the user has just given you access to the Location permission after navigating to a Map screen, then retrieve their current location and show this on the map.
- If you can carry out functionality with the use of Intent Actions and avoid asking for permissions then be sure to do this. Keeping permissions to a minimum (where possible) is essential and can really improve the overall experience of the application for the user.
Until next time…
And that’s all there is to it! Be sure to create a great UX when developing for the new permissions model and follow the best practices to help you do so. Don’t forget to really test these new permission methods, both manually and in your automated tests – that includes both your M and pre-M builds! Most importantly, have fun!