Be patient..... we are fetching your source code.
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.
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.
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.
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.
Aah… If I must implement this, then let’s dig deeper
There are 2 types of Permission Categories:
- Dangerous
- 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-group.CAMERA |
|
android.permission-group.CONTACTS |
|
android.permission-group.LOCATION |
|
android.permission-group.MICROPHONE |
|
android.permission-group.PHONE |
|
android.permission-group.SENSORS |
|
android.permission-group.SMS |
|
android.permission-group.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>
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:
Permission Request Dialog:
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.
Easy Steps To Create iOS Native Popup Using Unity