
Welcome to android runtime permissions example. With the introduction of Android 6.0 Marshmallow, Google has changed the way permissions are handled by the app. In this tutorial we’ll look into the new android runtime permissions that are introduced and how to handle them. If not handled properly, it can cause application crashes.
What are Android Runtime Permissions?
With the introduction of Android 6.0 (SDK 23), users are prompted for some specific permissions at runtime when they become necessary to use.
So the first question that comes to our mind is – Will the older apps run on Android Marshmallow? The answer is yes if the targetSdkVersion is 22 or less.
Thus android runtime permissions support backward compatibility. Now this doesn’t mean that we can work with old model of permissions by setting the sdk version to 22. A user using Marshmallow can revoke the dangerous permissions (we’ll discuss the dangerous and normal permissions later) from the Settings->Apps->Permissions. In the case we try to call some function that requires a permission which user has not granted yet, the function will suddenly throw an Exception(java.lang.SecurityException
) that will lead to the application crashing. Hence we need to implement this new android permissions model in our application.
Dangerous and Normal android permissions
Android defines some permissions as dangerous and some as normal. The common thing in both the types is that they need to be defined in the Manifest file.
From Android 6.0 only dangerous permissions are checked at runtime, normal permissions are not. An example of a normal permission is android.permission.INTERNET
.
Dangerous permissions are grouped into categories that make it easier for the user to understand what they are allowing the application to do. If the user accepts one permission in a group/category they accept the entire group. An example of dangerous permission is android.permission.FINE_LOCATION
and android.permission.COARSE_LOCATION
. Enabling anyone of the location permissions enables all.
Requesting Android Runtime Permissions
The method requestPermissions(String[] permissions, int requestCode);
is a public method that is used to request dangerous permissions. We can ask for multiple dangerous permissions by passing a string array of permissions.
Note: Android Permissions belonging to two different groups would prompt the user with an individual dialog for each of them. If they belong to the same group, then only one dialog prompt would be displayed. The results of the requests will be passed into the method onRequestPermissionResult
.
Example : Let’s say we want to access the camera and location in our app. Both are dangerous permissions. We’ll display a prompt requesting access to these permissions when the application is launched. Let’s add the permissions into a string array and call the requestPermissions
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/bt" android:text="Permission"></Button> </LinearLayout> </layout> |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
package com.abc.runtimepermissions; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.databinding.DataBindingUtil; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.View; import android.widget.Toast; import com.abc.runtimepermissions.databinding.ActivityMainBinding; import static android.Manifest.permission.READ_CONTACTS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; public static final int Requestcode=1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding= DataBindingUtil.setContentView (MainActivity.this,R.layout.activity_main); binding.bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(checkPermission()) { Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show(); }else { requestPermission(); } } }); } private void requestPermission() { ActivityCompat.requestPermissions(MainActivity.this, new String[]{WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE,RECORD_AUDIO, READ_CONTACTS,LOCATION_SERVICE},Requestcode); } public boolean checkPermission(){ int result=ContextCompat.checkSelfPermission(MainActivity.this, WRITE_EXTERNAL_STORAGE); int result1=ContextCompat.checkSelfPermission(MainActivity.this, READ_EXTERNAL_STORAGE); int result2=ContextCompat.checkSelfPermission(MainActivity.this, RECORD_AUDIO); int result3=ContextCompat.checkSelfPermission(MainActivity.this, READ_CONTACTS); int result4=ContextCompat.checkSelfPermission(MainActivity.this, LOCATION_SERVICE); return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED && result2==PackageManager.PERMISSION_GRANTED&& result3==PackageManager.PERMISSION_GRANTED && result4==PackageManager.PERMISSION_GRANTED; } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case Requestcode: if (grantResults.length> 0) { boolean StoragePermission = grantResults[0] == PackageManager.PERMISSION_GRANTED; boolean RecordPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED; boolean Readcontact = grantResults[2] == PackageManager.PERMISSION_GRANTED; boolean recordaudio = grantResults[3] == PackageManager.PERMISSION_GRANTED; boolean location = grantResults[4] == PackageManager.PERMISSION_GRANTED; if (StoragePermission && RecordPermission && Readcontact && recordaudio && location) { Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this,"Permission Denied",Toast.LENGTH_LONG).show(); } } break; } } } |
androidmanifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.abc.runtimepermissions"> <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |