THIS CONTENT DOWNLOAD SHORTLY

Objective

The main objective of this blog post is to implement permission request at runtime in Android, specifically for MarshMallow or higher version.

 

Have you ever tested your application with targetSDKVersion < 23 in MarshMallow?

Have you ever faced an issue that your application is running well on other devices, but causing crash on MarshMallow?

Are you familiar with "Requesting Permission at Runtime" concept?

Few days ago, I also faced the same issue on one of my applications. So, I thought to write a blog post on this issue.

"Are you targeting devices having MarshMallow OS? Then you have to implement Requesting Permissions at Run Time concept in your code."

But why it’s necessary to implement?

Because MarshMallow doesn’t ask for permission, when we install our application.

Not At Install Time

Ohh, then what it does for Permission Requests?

It introduces a new feature,

i.e. Requesting Permissions at Run Time.

It means you have to grant permission for external storage reading/writing, Sending/ Receiving SMS, accessing camera etc at runtime.

Run Time Permission

This concept is beneficial because users can stay informed about each & every permission which he/ she grants to the application.

User can revoke the granted permission through Settings >> Apps >> Take Photo App >> Permissions from his/her device.

Permission Path

If I don’t want to implement this new concept, then what?

If you don’t implement runtime permission, then your application will crash or will not work properly on the device having MarshMallow.

Marshmallow Error

Error

Aah… If I must implement this, then let’s dig deeper

There are 2 types of Permission Categories:

  1. Dangerous
  2. Normal

You can learn more about permissions over here

  • Both types of permissions are necessary to be defined in AndroidManifest.xml but only dangerous permissions require a runtime request.
  • Normal permissions have been requested at install time and cannot be revoked later. e.g.: android.permission.INTERNET

Here, one interesting thing is that dangerous permissions are grouped into the Permission Group, as displayed in the table.

Permission Group Permissions
android.permission-group.CALENDAR
  • android.permission.READ_CALENDAR
  • android.permission.WRITE_CALENDAR
android.permission-group.CAMERA
  • android.permission.CAMERA
android.permission-group.CONTACTS
  • android.permission.READ_CONTACTS
  • android.permission.WRITE_CONTACTS
  • android.permission.GET_ACCOUNTS
android.permission-group.LOCATION
  • android.permission.ACCESS_FINE_LOCATION
  • android.permission.ACCESS_COARSE_LOCATION
android.permission-group.MICROPHONE
  • android.permission.RECORD_AUDIO
android.permission-group.PHONE
  • android.permission.READ_PHONE_STATE
  • android.permission.CALL_PHONE
  • android.permission.READ_CALL_LOG
  • android.permission.WRITE_CALL_LOG com.
  • android.voicemail.permission.ADD_VOICEMAIL
  • android.permission.USE_SIP
  • android.permission.PROCESS_OUTGOING_CALLS
android.permission-group.SENSORS
  • android.permission.BODY_SENSORS
android.permission-group.SMS
  • android.permission.SEND_SMS
  • android.permission.RECEIVE_SMS
  • android.permission.READ_SMS
  • android.permission.RECEIVE_WAP_PUSH
  • android.permission.RECEIVE_MMS
  • android.permission.READ_CELL_BROADCASTS
android.permission-group.STORAGE
  • android.permission.READ_EXTERNAL_STORAGE
  • android.permission.WRITE_EXTERNAL_STORAGE

But what does it mean? It means that you need to request for only one permission from group.

If any permission from permission group is granted, then other permissions from the same group will be granted automatically. Likewise if any permission from permission group is denied, then entire group will be denied.

For Example,

  • Once READ_EXTERNAL_STORAGE is granted, application will also grant WRITE_EXTERNAL_STORAGE permission.

Hey wait, what about already launched Applications?
Yeah, Red Sign for Older Applications!!!

Now, If you understand above description properly then you might have question that what about already launched application? What do you think, will it crash?

Let’s see what will happen…

  • Application with targetSDKVersion less than 23 will be installed on MarshMallow device as usual & will ask for the required permissions at install time.
  • But here one risk exists, If user tries to manually revoke the permission, then system will warn that this permission is necessary to run that application properly.
  • Still if user revokes permission, then application will run properly on targetSDKVersion less than 23. But it will crash on SDK 23 or more.

I hope you are now pretty much aware with Runtime Permission concept in MarshMallow. Let’s understand this using an example.

 

Step 1 Getting Started

Create a simple project in Android Studio with blank activity. If you don’t know how to create new project in Android Studio.

Please check the following link:

 

Step 2 Update content_main.xml with following code

Create layout which contains 1 button to create calendar event

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   app:layout_behavior="@string/appbar_scrolling_view_behavior"
   tools:context="com.example.tag.runtimepermissiondemo.MainActivity"
   tools:showIn="@layout/activity_main">

   <Button
       android:id="@+id/showCalender"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/creareEvent" />
</RelativeLayout>

Layout1

 

Step 3 Update MainActivity.java with following code

MainActivity.java

public class MainActivity extends AppCompatActivity {

   public static final int MY_PERMISSIONS_REQUEST_WRITE_CALENDAR = 123;
   Context context;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
       setSupportActionBar(toolbar);

       context = MainActivity.this;
       Button showCalender = (Button) findViewById(R.id.showCalender);
       showCalender.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               //if we do this thing using INTENT then permission is not required
               boolean result = checkPermission();
               if (result) {
                   writeCalendarEvent();
               }
           }
       });
   }

   private void writeCalendarEvent() {
       final ContentValues event = new ContentValues();
       event.put(CalendarContract.Events.CALENDAR_ID, 1);

       event.put(CalendarContract.Events.TITLE, "title");
       event.put(CalendarContract.Events.DESCRIPTION, "description");
       event.put(CalendarContract.Events.EVENT_LOCATION, "location");

       event.put(CalendarContract.Events.DTSTART, 18000000);//startTimeMillis
       event.put(CalendarContract.Events.DTEND, 1800000000);//endTimeMillis
       event.put(CalendarContract.Events.ALL_DAY, 0);   // 0 for false, 1 for true
       event.put(CalendarContract.Events.HAS_ALARM, 1); // 0 for false, 1 for true

       String timeZone = TimeZone.getDefault().getID();
       event.put(CalendarContract.Events.EVENT_TIMEZONE, timeZone);

       Uri baseUri;
       if (Build.VERSION.SDK_INT >= 8) {
           baseUri = Uri.parse("content://com.android.calendar/events");
       } else {
           baseUri = Uri.parse("content://calendar/events");
       }

       getApplicationContext().getContentResolver().insert(baseUri, event);
       Toast.makeText(getApplicationContext(), "Event Created", Toast.LENGTH_SHORT).show();
   }

   @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   public boolean checkPermission()
   {
       int currentAPIVersion = Build.VERSION.SDK_INT;
       if(currentAPIVersion>=android.os.Build.VERSION_CODES.M)
       {
           if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
               if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.WRITE_CALENDAR)) {
                   AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
                   alertBuilder.setCancelable(true);
                   alertBuilder.setTitle("Permission necessary");
                   alertBuilder.setMessage("Write calendar permission is necessary to write event!!!");
                   alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                       @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
                       public void onClick(DialogInterface dialog, int which) {
                           ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.WRITE_CALENDAR}, MY_PERMISSIONS_REQUEST_WRITE_CALENDAR);
                       }
                   });
                   AlertDialog alert = alertBuilder.create();
                   alert.show();

               } else {
                   ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.WRITE_CALENDAR}, MY_PERMISSIONS_REQUEST_WRITE_CALENDAR);
               }
               return false;
           } else {
               return true;
           }
       } else {
           return true;
       }
   }

   @Override
   public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
       switch (requestCode) {
           case MY_PERMISSIONS_REQUEST_WRITE_CALENDAR:
               if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                   writeCalendarEvent();
               } else {
                   //code for deny
               }
               break;
       }
   }
}
 

3.1 Action on Button Click

Here, I am checking for runtime permission on onClick() of Button by calling checkPermission() method.

Button showCalender = (Button) findViewById(R.id.showCalender);
       showCalender.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
             //if we do this thing using INTENT then permission is not required
               boolean result = checkPermission();
               if (result) {
                   writeCalendarEvent();
               }
           }
       });

If permission has already been granted, then writeCalendarEvent() method will be called to write event.

If permission has not been granted then dialog will be displayed to:

  • Grand the permission or
  • Revoke the permission
 

3.2 Checking Permission at Runtime

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public boolean checkPermission()
{
   int currentAPIVersion = Build.VERSION.SDK_INT;
   if(currentAPIVersion>=android.os.Build.VERSION_CODES.M)
   {
       if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
           if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.WRITE_CALENDAR)) {
               AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
               alertBuilder.setCancelable(true);
               alertBuilder.setTitle("Permission necessary");
               alertBuilder.setMessage("Write calendar permission is necessary to write event!!!");
               alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                   @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
                   public void onClick(DialogInterface dialog, int which) {
                       ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.WRITE_CALENDAR}, MY_PERMISSIONS_REQUEST_WRITE_CALENDAR);
                   }
               });
               AlertDialog alert = alertBuilder.create();
               alert.show();

           } else {
               ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.WRITE_CALENDAR}, MY_PERMISSIONS_REQUEST_WRITE_CALENDAR);
           }
           return false;
       } else {
           return true;
       }
   } else {
       return true;
   }
}

checkPermission() will check runtime permission only if current API version has been MarshMallow or more.

It will display permission request dialog, If permission has not been granted.

Here, I have used ActivityCompat.shouldShowRequestPermissionRationale(), What’s that??

 

3.2.1 Rational Request Checking

If once user denied permission request, then it has been assumed that user cannot get the proper idea about why permission is required?

Therefore after that, permission description dialog will be displayed each time before permission request dialog.

Permission Description Dialog:

Rational Request Checking

Permission Request Dialog:

Permission Request

 

3.3 Writing Calendar Event

writeCalendarEvent() will write calendar event without using Intent.

Note

If you write calendar event via intent then WRITE_CALENDAR or READ_CALENDAR permissions are not required.

private void writeCalendarEvent() {
   final ContentValues event = new ContentValues();
   event.put(CalendarContract.Events.CALENDAR_ID, 1);

   event.put(CalendarContract.Events.TITLE, "title");
   event.put(CalendarContract.Events.DESCRIPTION, "description");
   event.put(CalendarContract.Events.EVENT_LOCATION, "location");

   event.put(CalendarContract.Events.DTSTART, 18000000);//startTimeMillis
   event.put(CalendarContract.Events.DTEND, 1800000000);//endTimeMillis
   event.put(CalendarContract.Events.ALL_DAY, 0);   // 0 for false, 1 for true
   event.put(CalendarContract.Events.HAS_ALARM, 1); // 0 for false, 1 for true

   String timeZone = TimeZone.getDefault().getID();
   event.put(CalendarContract.Events.EVENT_TIMEZONE, timeZone);

   Uri baseUri;
   if (Build.VERSION.SDK_INT >= 8) {
       baseUri = Uri.parse("content://com.android.calendar/events");
   } else {
       baseUri = Uri.parse("content://calendar/events");
   }

   getApplicationContext().getContentResolver().insert(baseUri, event);
   Toast.makeText(getApplicationContext(), "Event Created", Toast.LENGTH_SHORT).show();
}
 

3.4 Action on Allowing Permission Request

onRequestPermissionsResult() is inbuilt method, which receives Permission Request Dialog’s callback.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
   switch (requestCode) {
       case MY_PERMISSIONS_REQUEST_WRITE_CALENDAR:
           if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               writeCalendarEvent();
           } else {
               //code for deny
           }
           break;
   }
}

If permission has been granted then the value of grantResults[0] would be PERMISSION_GRANTED. And if permission has been revoked then the value of grantResults[0] would be PERMISSION_DENIED.

Here, if permission has been granted, then I am calling writeCalendarEvent() to write calendar event.

 

3.5 Add permission to manifest file

<uses-permission android:name="android.permission.WRITE_CALENDAR" />

For creating calendar event we need to add WRITE_CALENDAR permission.

You can refer the another example on storage permission at runtime from below link:

I hope you find this blog post very helpful while with Requesting Permissions at Runtime concept. Let me know in comment if you have any questions regarding Runtime Permission Request. I will reply you ASAP.

Learning Android sounds fun, right? Why not check out our other Android Tutorials?

Got an Idea of Android App Development? What are you still waiting for? Contact Us now and see the Idea live soon. Our company has been named as one of the best Android Application Development Company in India.

I am a passionate Android Developer.I like to develop different android applications with new concepts and ideas.