How to implement Runtime Permissions in Android Marshmallow 6.0
For app developers and publishers, the biggest change they will need to integrate and test is how Android will now handle app permissions. If you are unfamiliar with permissions in Android, when an app is downloaded from Google Play, the user can see what that app is allowed to do with the phone. Some apps request permission to access user’s contacts or calendar, Wi-Fi and cellular data, camera access and so forth.
Before we start, Learn What is Android Runtime Permission?
How to make your application support new Runtime Permission
Now it's time to make our application support new Runtime Permission
perfectly. Start with setting
compileSdkVersion
and
targetSdkVersion
to 23 (or higher) in
app/build.gradle
file.
android { compileSdkVersion 23 ... defaultConfig { ... targetSdkVersion 23 ... }
Declare permissions in AndroidManifest.xml
It is required to declare the permission in
AndroidManifest.xml
file in order for app to ask for the
permission.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> ... <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ... <application ... </application> ... </manifest>
How to ask for the runtime permission programatically?
Now the question arises, how an application can request for or get
permission? Or rather, what workflow the developer should follow to request
and get permission for the app? Let us have a look.
In this example, we try to get write storage permission with a function
below.
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //Show Information about why you need the permission AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Need Storage Permission"); builder.setMessage("This app needs storage permission."); builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT); } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.show(); } else if (permissionStatus.getBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,false)) { //Previously Permission Request was cancelled with 'Dont Ask Again', // Redirect to Settings after showing Information about why you need the permission AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Need Storage Permission"); builder.setMessage("This app needs storage permission."); builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); sentToSettings = true; Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, REQUEST_PERMISSION_SETTING); Toast.makeText(getBaseContext(), "Go to Permissions to Grant Storage", Toast.LENGTH_LONG).show(); } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.show(); } else { //just request the permission ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT); } SharedPreferences.Editor editor = permissionStatus.edit(); editor.putBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,true); editor.commit(); } else { //You already have the permission, just go ahead. proceedAfterPermission(); }
Explanation :
We checked for permission with
checkSelfPermission()
Method to check if the
app already has permission to write on external storage. If it has then
continue to else part, otherwise go to next step.
Here we used
shouldShowRequestPermissionRationale()
method to check if we
should show the explanation for the permission or not, if this returns true
then we showed an alert dialog with explanation, and on the positive button
click we requested the permission. If shouldShowRequestPermissionRationale
returns false
then we requested permission straight forward.
We have successfully requested permission so far. We just need to override
the method
onRequestPermissionsResult
to receive the result. In that method
first we checked the requested code with our declared constant requestCode
== EXTERNAL_STORAGE_PERMISSION_CONSTANT
then checked if length of
grantResult is greater than 0, so that it contains the user’s decision, then
we cheked if the value of 0 index of the grant result, if the value is equal
to PackageManager.PERMISSION_GRANTED
then it means that the user has
authorised the permission and we can continue our work, otherwise in the
else part we can again request for permission with explanation.
Add
SharedPreferences
, think of a situation where the user has denied
permission by checking “Never Ask Again”. In that scenario the
shouldShowRequestPermissionRationale
method will return false
and
requestPermissions
method will do just nothing. So we need to remember
whether we have priorly requested for permission or not, even after the app
restarts. For that purpose we will use SharedPreferences
, we will use the
permission string as the key and boolean true or false as value.
Take a look at What is Android Runtime Permission to know more about what is Runtime Permissions.