Android M introduced a new concept of permissions model with its release. According to this new concept now apps would have to request permissions from the users at run time. In a way this is a very useful feature for the end users. Making their user experience very secure on an Android device. But inversely this feature imposes a great deal of effort on development. As the new permissions model also allows the user to deny the permission at runtime. Therefore as developers, we would have to handle all the use cases. Thankfully, requesting Android runtime permissions, is not that difficult, with a few lines of code we can build a full fledged permission handling mechanism. But before doing so lets understand the permission model first.
Understanding Android Run Time Permissions Model
Android system permissions are divided into many categories. But when it comes to permission request at runtime from Android 6.0 onward, system permissions are categorized into two categories. Normal and Dangerous, to access either of these two permissions, the first step is to declare them in the manifest, as shown below. The difference between these two is that, dangerous permissions are now granted by the user at run time. Otherwise your app would crash with security exception. On the other hand we don’t need to request permissions falling under the normal category.
<?xml version="1.0" encoding="utf-8"?> <manifest package="com.truiton.runtimepermissions" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
In the above android manifest, first permission android.permission.INTERNET
is a normal permission and other two android.permission.READ_CONTACTS
, android.permission.WRITE_EXTERNAL_STORAGE
are dangerous permissions. Therefore in the following example, we would request the user to grant the later two Android permissions at runtime.
There is another special category of permissions having permissions like
SYSTEM_ALERT_WINDOW and
WRITE_SETTINGS.
Although these permissions are neither categorized in normal or dangerous section, but still it is required that these are requested by user at runtime by firing an intent (not discussed in this article).
Requesting Android Runtime Permissions
From Android 6.0 onward, declaring a permission in manifest will not enable an app to use that permission. In a way its just a way to notify the system that your app can use this permission. But to actually use the permission you would have to make an Android runtime permission request. As mentioned above, in this example we would be requesting two permissions android.permission.READ_CONTACTS
, and android.permission.WRITE_EXTERNAL_STORAGE
. Usually a permission should be requested only when it is required. Hence when a permission is required in your code, please initiate the permission request process by using the following code:
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + ContextCompat .checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.READ_CONTACTS)) { Snackbar.make(findViewById(android.R.id.content), "Please Grant Permissions", Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission .WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_CONTACTS}, REQUEST_PERMISSIONS); } }).show(); } else { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission .WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_CONTACTS}, REQUEST_PERMISSIONS); } } else { //Call whatever you want myMethod(); }
This would initiate an Android runtime permissions request. If you have the permission, myMethod()
would be called. If you have been denied or granted the permission by user action, then the onRequestPermissionResult()
method would be called:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_PERMISSIONS: { if ((grantResults.length > 0) && (grantResults[0] + grantResults[1]) == PackageManager.PERMISSION_GRANTED) { //Call whatever you want myMethod(); } else { Snackbar.make(findViewById(android.R.id.content), "Enable Permissions from settings", Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse("package:" + getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); startActivity(intent); } }).show(); } return; } } }
This is an additional method which you need to implement in your activity, to receive the Android runtime permissions request callback. Once the callback is received in the above method, and if the permission is granted you can call the same myMethod()
to invoke the desired functionality. Also here in the above method we have taken care of the situation in which the permission is not granted. We would ask the user to launch the settings screen and enable permissions manually.
Android M Runtime Permissions – Design Pattern
By going through the above code you must have understood, requesting Android runtime permissions is very easy. But the implementation of this methodology in your existing code base could be a very time taking activity. Therefore a design pattern is required, where we can write the permission request code once and use it again and again.
To do so lets design an Abstract activity class, where the onRequestPermissionResult()
method can be implemented. This would enable us to deliver permission granted callbacks to the child activity by using an abstract method, resulting minimal code change.
package com.truiton.runtimepermissions; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.SparseIntArray; import android.view.View; /** * Created by MG on 03-04-2016. */ public abstract class RuntimePermissionsActivity extends AppCompatActivity { private SparseIntArray mErrorString; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mErrorString = new SparseIntArray(); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); int permissionCheck = PackageManager.PERMISSION_GRANTED; for (int permission : grantResults) { permissionCheck = permissionCheck + permission; } if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) { onPermissionsGranted(requestCode); } else { Snackbar.make(findViewById(android.R.id.content), mErrorString.get(requestCode), Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse("package:" + getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); startActivity(intent); } }).show(); } } public void requestAppPermissions(final String[] requestedPermissions, final int stringId, final int requestCode) { mErrorString.put(requestCode, stringId); int permissionCheck = PackageManager.PERMISSION_GRANTED; boolean shouldShowRequestPermissionRationale = false; for (String permission : requestedPermissions) { permissionCheck = permissionCheck + ContextCompat.checkSelfPermission(this, permission); shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || ActivityCompat.shouldShowRequestPermissionRationale(this, permission); } if (permissionCheck != PackageManager.PERMISSION_GRANTED) { if (shouldShowRequestPermissionRationale) { Snackbar.make(findViewById(android.R.id.content), stringId, Snackbar.LENGTH_INDEFINITE).setAction("GRANT", new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(RuntimePermissionsActivity.this, requestedPermissions, requestCode); } }).show(); } else { ActivityCompat.requestPermissions(this, requestedPermissions, requestCode); } } else { onPermissionsGranted(requestCode); } } public abstract void onPermissionsGranted(int requestCode); }
To request Android runtime permissions we would have to extend the above activity and call its requestAppPermissions()
method, as shown below:
package com.truiton.runtimepermissions; import android.Manifest; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.Toast; public class MainActivity extends RuntimePermissionsActivity { private static final int REQUEST_PERMISSIONS = 20; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MainActivity.super.requestAppPermissions(new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE}, R.string .runtime_permissions_txt , REQUEST_PERMISSIONS); } }); } @Override public void onPermissionsGranted(final int requestCode) { Toast.makeText(this, "Permissions Received.", Toast.LENGTH_LONG).show(); } }
In the above piece of code, we took the full advantage of an abstract class. Where we made all Android run time permission requests in the parent class, and called the abstract method onPermissionsGranted()
of that class, to return callbacks to the child activity. Full source code available here:
This way, when implementing Android runtime permissions model, to support Android M and above, minimal code needs to be written in the existing activities. As the permission request would be made through the parent class method requestAppPermissions()
, and results would be propagated back by the onPermissionsGranted()
method. Although if you have already implemented a base activity pattern in your app, implementing this pattern would be very easy, as the same class would be reused to implement this pattern. Hope this helped. For more updates please connect with us on Facebook, Twitter and Google+.
Born in New Delhi, India. A software engineer by profession, an android enthusiast and an evangelist. My motive here is to create a group of skilled developers, who can develop something new and good. Reason being programming is my passion, and also it feels good to make a device do something you want. In a very short span of time professionally I have worked with many tech firms. As of now too, I am employed as a senior engineer in a leading tech company. In total I may have worked on more than 20 projects professionally, and whenever I get spare time I share my thoughts here at Truiton.
Great article and very helpful, Thank you
Great and very helpful. Keep on doing this kind of tutorials
Thanks. This helped me.
it is good but how about if you have a service which starts before all the activities and it requires one or two of the permissions like Manifest.persmission.READ_CONTACTS.
what do you do in that state?
Thanks! Quite helpful!