
Android Marshmallow 6.0 final stable version is released a few weeks back. Android Marshmallow brought some new API changes and one of the most important API change is the addition of new granular permissions. Let’s dive into the new permission system.
Normal and Dangerous Permissions
In Marshmallow these dangerous permissions should be granted at run time and the normal permissions are granted automatically. For Lollipop (API level 22) or lower android versions the permissions specified in manifest are granted at installation.
Let’s create a sample application with Android studio to get clear knowledge of how these permissions work.
Here I have created a new Android Studio project with learn2crack.marshmallowpermissions package. Use your preferred package name. And my main layout is activity_main.xml and main Activity is MainActivity.java. Dont forget to select the target SDK version as Marshmallow (API level 23).
New versions of Android Studio adds the AppCompat library and Android Design Support library dependencies automatically in your build.gradle file on new project creation. If not add the following two lines to the dependencies section of the app’s build.gradle file.
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
The dependencies section would look similar to
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
}
You can download the complete project as zip or fork from our Github repository.
Add the following permission to AndroidManifest.xml
android.permission.ACCESS_FINE_LOCATION
The above added permission is a dangerous permission which we will request at run time.
New version of Android Studio automatically adds the CoordinatorLayout With Floating Action Button and Toolbar to the main layout file on project creation. The CoordinatorLayout is a new type of layout which is added in the Android Design Support library. It provides more control over the touch events in the layout and child views. To make the main layout simple and neat the other layout components such as button are sepearted into another layout as content_main.xml
The content_main.xml layout has a parent RelativeLayout with two child Button, one to check permission and other to request permission. The complete content_main.xml layout is given by,
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context=".MainActivity">
<Button
android:id="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Check Permission"/>
<Button
android:id="@+id/request_permission"
android:layout_below="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Request Permission"/>
</RelativeLayout>
While the activity_main.xml inclues the content_main.xml layout using
The complete activty_main.xml layout is given by,
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>
Our Activity extends to AppCompatActivity and implements OnClickListener interface. Our app has two buttons. One is to check whether the permission is already granted or not. Another is used to request permission if permission is not granted.
The permission is checked by calling ContextCompat.checkSelfPermission() method and the result is verified with PackageManager.PERMISSION_GRANTED. Here I display a Snackbar to show whether the permission is granted or not. Snackbar is a new view which is displayed at the bottom similar to toast and also we can add action button to Snackbar.
The permission is requested using ActivityCompat.requestPermissions() method by passing
the Activity, permissions as String array and request code. And the result is handled using the onRequestPermissionsResult() method. The request code is verified and the result is diplayed in Snackbar whether permission is granted or denied.
If the user once denied the permission you cannot request permission again. The user need to grant you permisiion explicitly in App Settings. For that you can explain to the user why that permission is required. It can be checked using ActivityCompat.shouldShowRequestPermissionRationale() method by passing Activity and the permission. If it returns true the user have denied the permission and you can request the user to allow the permission or you can disable that paricular function.
The permission check process is wrapped in checkPermission() method. The request permission process is wrapped in requestPermission() method.
The complete MainActivity.java class is given by,
package learn2crack.marshmallowpermissions;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
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.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Context context;
private Activity activity;
private static final int PERMISSION_REQUEST_CODE = 1;
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
context = getApplicationContext();
activity = this;
Button check_permission = (Button)findViewById(R.id.check_permission);
Button request_permission = (Button)findViewById(R.id.request_permission);
check_permission.setOnClickListener(this);
request_permission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
view = v;
int id = v.getId();
switch (id){
case R.id.check_permission:
if (checkPermission()) {
Snackbar.make(view,"Permission already granted.",Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view,"Please request permission.",Snackbar.LENGTH_LONG).show();
}
break;
case R.id.request_permission:
if (!checkPermission()) {
requestPermission();
} else {
Snackbar.make(view,"Permission already granted.",Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPermission(){
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
if (result == PackageManager.PERMISSION_GRANTED){
return true;
} else {
return false;
}
}
private void requestPermission(){
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,Manifest.permission.ACCESS_FINE_LOCATION)){
Toast.makeText(context,"GPS permission allows us to access location data. Please allow in App Settings for additional functionality.",Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Snackbar.make(view,"Permission Granted, Now you can access location data.",Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view,"Permission Denied, You cannot access location data.",Snackbar.LENGTH_LONG).show();
}
break;
}
}
}
Happy Coding đŸ™‚