Android Runtime Permissions in Android M?

Android Runtime Permissions in Android M?

Before Android Marshmallow, users had to allow android runtime permissions at the installation time; if the user did not accept all the permissions, the app could not be installed. Android Marshmallow introduced a new mechanism; that it will not ask any of the users to grant any one of the android runtime permissions at installation time. Application has to ask user for a permission one-by-one at run time. In the following tutorial you will learn that how to request runtime permissions in android marshmallow.

android_runtime_permissions

Tutorial By: Muhammad Subhan

There are two types of permissions in Android.

Normal Permissions

Normal Permissions are those that will be granted automatically at install time and user can never revoke these permission for approval. These permissions just required to be declared in AndroidManifest.xml and will work fine. These includes INTERNET, WifiState, and NFC etc.

Simplified Permissions

All the other permissions will required user approval in Android Marshmallow. These permissions are bundled together in Permission Group. Permission Groups attempt to simplify permissions that are performing similar operations, such as Location, Contacts, Phone, Sensors, SMS, and Storage. If any permission in the group is granted, all the permissions in that group will also be granted automatically.

Flow Chart

android-flowchart-mobilesiri

Getting Started With Android Runtime Permissions

It is still necessary to add all the permissions in the AndroidManifest.xml, but the use of these permissions is only requested at runtime and not at install time.

<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CAMERA"/>

SDK Checking

Your compileSdkVersion and targetSdkVersion must be set to 23 or above in order to use runtime permissions as these are compiled only in Android Marshmallow or above.

<android {
    compileSdkVersion 23
buildToolsVersion "23.0.2"


defaultConfig {
        applicationId "com.mobilesiri.runtimepermissions"
minSdkVersion 14

targetSdkVersion 23
versionCode 1
versionName "1.0"
}

    buildTypes {
        release {
            minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

    }
}>

Ahh, what happen to the devices with less api than 23 as the minimumSdk is set to 14! So will the application crash? The Answer is no.

These new Runtime Permissions will work only when we targetedSdk is set to 23, which means it has already been tested on API level 23. This feature will work only on Android Marshmallow devices. If the targetedSdk is set to less than 23, it will be assumed that the application is not tested with new permissions yet and will switch to previous behavior. The same app will run with same old behavior on other devices also.

 

Check if Permission is Granted

To check whether the permission is granted or not, we have the method checkSelfPermission(),

It will return PERMISSION_GRANTED if the permission is granted or PERMISSION_DENIED if the permission is not granted.


 
int Permission= ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_CONTACTS);

if(Permission!= PackageManager.PERMISSION_GRANTED){

// permission is not granted, request the permission
}else {

// permission is granted, do
// functionality that depends on this permission.

Optionally Display Rationale

Good practice is to show the extra rationale to user in order to define the need of specific permission required. Sometimes user may confuse so it’s good to inform the user about why the permission is needed. We do this using Snackbar like this,

<Snackbar snackbar= Snackbar.make(layout, "You need to allow access to Contacts", Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
@Override
public void onClick(View v) {
         ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_CONTACTS}, contactPERMISSIONS);
     }
 });
 snackbar.show();>

This Message will be show before requesting permission so that user get known to the permission.

Request Permission

So finally requesting permission we have method requestPermission(), which will show the dialog box and prompt the user for the specific permission like this,

<ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_CONTACTS}, contactPERMISSIONS);>

Handle Permission Request

After the request, on RequestPermissionsResult() method will be called to inform the result, no matter what the user chosen Deny or Allow this method will be called and inform the result. We can override this method for our own implementation. The result will be in 3rd parameter which is grantResults array like this,

 <@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {        case contactPERMISSIONS: if (grantResults.length >0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {>

// permission was granted! Do the contact related things here.

} else {

// permission was not granted! Disable the things otherwise app will crash.
          } return;

    }
}

Conclusion

In this we learnt to develop app with requesting runtime permissions in Android Marshmallow. Although this code will works perfectly on Android Marshmallow Devices or above but, this code also works perfectly on previous versions of Android. Keep in mind that there are only few permissions that requires runtime permission flow. Most of the frequently used permissions such as INTERNET, are in the Normal Permissions and are automatically granted to app. Hope you find this helpful to understand the new runtime permissions.

 

The full Code is here,

<package com.mobilesiri.runtimepermissions;

import android.annotation.TargetApi;
import android.app.usage.UsageEvents;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
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.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.Toast;

import java.util.jar.Manifest;

public class MainActivity extends AppCompatActivity {

Switch contactbutton, camerabutton, locationbutton;
final private int contactPERMISSIONS = 1, cameraPERMISSIONS = 2, locationPERMISSIONS = 3;
private RelativeLayout layout;>

<@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contactbutton = (Switch) findViewById(R.id.switch1);
camerabutton = (Switch) findViewById(R.id.switch2);
locationbutton = (Switch) findViewById(R.id.switch3);
layout = (RelativeLayout) findViewById(R.id.coordinate);
contactbutton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
int Permission = ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_CONTACTS);
if (Permission != PackageManager.PERMISSION_GRANTED) {
Snackbar snackbar = Snackbar.make(layout, “You need to allow access to Contacts”, Snackbar.LENGTH_INDEFINITE).setAction(“OK”, new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_CONTACTS}, contactPERMISSIONS);
}
});
snackbar.show();

} else if (Permission == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, “automatically granted”, Toast.LENGTH_SHORT).show();
}
}
}
});
camerabutton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
int Permission = ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA);
if (Permission != PackageManager.PERMISSION_GRANTED) {
Snackbar snackbar = Snackbar.make(layout, “You need to allow access to Camera”, Snackbar.LENGTH_INDEFINITE).setAction(“OK”, new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.CAMERA}, cameraPERMISSIONS);
}
});
snackbar.show();

} else if (Permission == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, “automatically granted”, Toast.LENGTH_SHORT).show();
}
}
}
});
locationbutton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
int Permission = ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION);
if (Permission != PackageManager.PERMISSION_GRANTED) {
Snackbar snackbar = Snackbar.make(layout, “You need to allow access to Location”, Snackbar.LENGTH_INDEFINITE).setAction(“OK”, new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, locationPERMISSIONS);
}
});
snackbar.show();

} else if (Permission == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, “automatically granted”, Toast.LENGTH_SHORT).show();
}
}
}
});

}>

<@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case cameraPERMISSIONS:
case locationPERMISSIONS: {
if (grantResults.length >0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, “granted”, Toast.LENGTH_SHORT).show();

} else {
Toast.makeText(MainActivity.this, “not granted”, Toast.LENGTH_SHORT).show();
if (requestCode == contactPERMISSIONS) contactbutton.setChecked(false);
if (requestCode == cameraPERMISSIONS) camerabutton.setChecked(false);
if (requestCode == locationPERMISSIONS) locationbutton.setChecked(false);
}
return;
}
}
}
}>

Layout XML


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id=“@+id/textView”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentLeft=“true”
android:layout_alignParentStart=“true”
android:layout_alignParentTop=“true”
android:drawableLeft=“@android:drawable/ic_menu_call”
android:drawablePadding=“15dp”
android:gravity=“center”
android:paddingLeft=“20dp”
android:paddingTop=“15dp”
android:text=“Contacts”
android:textAppearance=“?android:attr/textAppearanceMedium”
android:textColor=“@android:color/secondary_text_light” />

<TextView
android:id=“@+id/textView2”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentLeft=“true”
android:layout_alignParentStart=“true”
android:layout_below=“@+id/textView”
android:drawableLeft=“@android:drawable/ic_menu_camera”
android:drawablePadding=“15dp”
android:gravity=“center”
android:paddingLeft=“20dp”
android:paddingTop=“12dp”
android:text=“Camera”
android:textAppearance=“?android:attr/textAppearanceMedium”
android:textColor=“@android:color/secondary_text_light” />

<TextView
android:id=“@+id/textView3”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentLeft=“true”
android:layout_alignParentStart=“true”
android:layout_below=“@+id/textView2”
android:drawableLeft=“@android:drawable/ic_menu_mylocation”
android:drawablePadding=“15dp”
android:gravity=“center”
android:paddingLeft=“20dp”
android:paddingTop=“12dp”
android:text=“Location”
android:textAppearance=“?android:attr/textAppearanceMedium”
android:textColor=“@android:color/secondary_text_light” />

<Switch
android:id=“@+id/switch1”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@+id/textView”
android:layout_alignParentEnd=“true”
android:layout_alignParentRight=“true”
android:layout_marginEnd=“32dp”
android:layout_marginRight=“39dp” />

<Switch
android:id=“@+id/switch2”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@+id/textView2”
android:layout_alignLeft=“@+id/switch1”
android:layout_alignStart=“@+id/switch1” />

<Switch
android:id=“@+id/switch3”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@+id/textView3”
android:layout_alignLeft=“@+id/switch2”
android:layout_alignStart=“@+id/switch2” />

</RelativeLayout>

Screen Shots

UI of main Activity with switches:

android_runtime_permissions

Defining info about Permission:

android_runtime_permissions

Requesting Android Runtime Permissions:

android_runtime_permissions

Permission granted by user:

android_runtime_permissions

MobileSiri.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to Amazon.com. Read about our Affiliates Disclosure Policy here. Amazon and the Amazon logo are trademarks of Amazon.com, Inc. or its affiliates.