Android Marshmallow Runtime Permission Example

Android permissions has been divided into two groups from android 6.0 which build name is android marshmallow. And the related sdk version is 23. This article will tell you the difference between those groups with examples.

1. Android Permission Groups.

1.1 Normal Permissions.

One group of permission is called normal permissions which do not need user to allow at runtime,  you just need to declare them in AndroidManifest.xml file as usual. All the permissions except runtime permissions are all normal permissions.

1.2 Dangerous ( Runtime ) Permissions

Another group of permission is called dangerous permissions, they are all related to android OS core operations. They are also need to be declared in AndroidManifest.xml file, and they need user to allow when app run. This can give app user the choice of android permission selection.

Below picture list all android dangerous permissions. They are divided into groups, if you allow one permission in a group, then all the group permissions are granted to the android application. That means the android app can use all the permissions in the group.

android dangerous permissions list

2. Android Runtime Permission Work Flow.

android runtime permissions process diagram

From above picture we can see that if you run app with android sdk version smaller than 23, even your app require dangerous ( runtime ) permissions, the app do not need user to grant required permission to run.

But if the sdk version is bigger than 23 then when the app require dangerous permissions, then android os will popup a window to let app user to choose whether grant the permission to the app or not.

2.1 Android Runtime Permission Check And Require Code.

  1. Use below code to check whether the required permission has been granted to current app or not.
    ContextCompat.checkSelfPermission(context, runtimePermission) == PackageManager.PERMISSION_GRANTED
  2. If required runtime permission is not granted, use below code to require app user to allow.
    ActivityCompat.requestPermissions(activity, new String[]{runtimePermission}, requestCode);
  3. Overwrite below method in your activity, and in this method you can get the user grant result. The requestCode input parameter is just the requestCode you used in step 2.
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
        // If this is our permission request result.
        if(requestCode==PERMISSION_REQUEST_CODE)
        {
    
        }
    }
  4. If you do not require the dangerous permissions in sdk version 23 or higher before you use, then you will see below exceptions.
    java.lang.SecurityException: Permission Denial: starting Intent
    android runtime permission denied exception
  5. After you uninstall the android app, all the permissions granted to it will be removed also.
READ :   How To Resolve Errors When Change SDK Level In Android Studio

3. Android Runtime Permission Example.

android marshmallow runtime permission example

Because there are three normal android permissions declared in AndroidManifest.xml file, so those permissions can be listed when you click the first button.

If you want to manually deny runtime permissions that has been granted to app. You can follow below steps.

  1. Click Settings —> Apps.
  2. Click the application you want to operate.
  3. Click Permissions menu item.
  4. Toggle the permissions button to manually turn on / off related permissions.
    android marshmallow deny runtime permission manually

3.1 Android Manifest Xml File.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dev2qa.example">

    <!-- Normal android permissions. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <!-- Dangerous Runtime Permissions. -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <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=".permission.runtime.RuntimePermissionActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

3.2 Activity Java Code.

RuntimePermissionActivity.java

package com.dev2qa.example.permission.runtime;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.dev2qa.example.R;

import java.util.ArrayList;
import java.util.List;

public class RuntimePermissionActivity extends AppCompatActivity {

    private static final String TAG_RUNTIME_PERMISSION = "TAG_RUNTIME_PERMISSION";

    private static final int PERMISSION_REQUEST_CODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_runtime_permission);

        setTitle("dev2qa.com - Android Runtime Permission Example");

        // List all granted permissions.
        Button listGrantedPermissionButton = (Button)findViewById(R.id.runtime_permission_list_granted);
        listGrantedPermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // First get package name.
                String packageName = getPackageName();
                List<String> permissionList = getAllPermissions(packageName);

                StringBuffer grantedPermissionBuf = new StringBuffer();

                grantedPermissionBuf.append("Package ");
                grantedPermissionBuf.append(packageName);
                grantedPermissionBuf.append(" has been granted below permissions : \r\n\r\n");

                int size = permissionList.size();
                for(int i=0;i<size;i++)
                {
                    String permission = permissionList.get(i);
                    // If this permission has been granted.
                    if(hasRuntimePermission(getApplicationContext(), permission)) {
                        grantedPermissionBuf.append(permissionList.get(i));

                        if (i < size - 1) {
                            grantedPermissionBuf.append(",\r\n\r\n");
                        }
                    }
                }

                // Show user granted permissions list in a alert dialog.
                AlertDialog alertDialog = new AlertDialog.Builder(RuntimePermissionActivity.this).create();
                alertDialog.setMessage(grantedPermissionBuf.toString());
                alertDialog.show();
            }
        });

        // Process camera permission.
        Button cameraPermissionButton = (Button)findViewById(R.id.runtime_permission_camera);
        cameraPermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!hasRuntimePermission(getApplicationContext(), Manifest.permission.CAMERA)) {
                    requestRuntimePermission(RuntimePermissionActivity.this, Manifest.permission.CAMERA, PERMISSION_REQUEST_CODE);
                } else {
                    Toast.makeText(RuntimePermissionActivity.this, "Manifest.permission.CAMERA permission already has been granted", Toast.LENGTH_LONG).show();
                }
            }
        });

        // Process accounts permission.
        Button accountsPermissionButton = (Button)findViewById(R.id.runtime_permission_accounts);
        accountsPermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!hasRuntimePermission(getApplicationContext(), Manifest.permission.GET_ACCOUNTS)) {
                    requestRuntimePermission(RuntimePermissionActivity.this, Manifest.permission.GET_ACCOUNTS, PERMISSION_REQUEST_CODE);
                } else {
                    Toast.makeText(RuntimePermissionActivity.this, "Manifest.permission.GET_ACCOUNTS permission already has been granted", Toast.LENGTH_LONG).show();
                }
            }
        });

        // Process gps location permission.
        Button locationPermissionButton = (Button)findViewById(R.id.runtime_permission_location);
        locationPermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!hasRuntimePermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION)) {
                    requestRuntimePermission(RuntimePermissionActivity.this, Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_CODE);
                } else {
                    Toast.makeText(RuntimePermissionActivity.this, "Manifest.permission.ACCESS_FINE_LOCATION permission already has been granted", Toast.LENGTH_LONG).show();
                }
            }
        });

        // Process write external storage permission.
        Button storagePermissionButton = (Button)findViewById(R.id.runtime_permission_storage);
        storagePermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!hasRuntimePermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    requestRuntimePermission(RuntimePermissionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSION_REQUEST_CODE);
                } else {
                    Toast.makeText(RuntimePermissionActivity.this, "Manifest.permission.WRITE_EXTERNAL_STORAGE permission already has been granted", Toast.LENGTH_LONG).show();
                }
            }
        });


        // Process phone call permission.
        Button phoneCallPermissionButton = (Button)findViewById(R.id.runtime_permission_phone_call);
        phoneCallPermissionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!hasRuntimePermission(getApplicationContext(), Manifest.permission.CALL_PHONE)) {
                    requestRuntimePermission(RuntimePermissionActivity.this, Manifest.permission.CALL_PHONE, PERMISSION_REQUEST_CODE);
                } else {
                    Toast.makeText(RuntimePermissionActivity.this, "Manifest.permission.CALL_PHONE permission already has been granted", Toast.LENGTH_LONG).show();
                    makePhoneCall("10086");
                }
            }
        });
    }

    // Make a phone call.
    private void makePhoneCall(String phoneNumber)
    {
        // Prepare phone call string uri.
        String phoneCallString = "tel:"+phoneNumber;
        Uri phoneCallUri = Uri.parse(phoneCallString);

        // Prepare phone call intent.
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(phoneCallUri);

        // Start phone call activity and make phone call.
        try {
            startActivity(intent);
        }catch(SecurityException ex)
        {
            Log.e(TAG_RUNTIME_PERMISSION, "makePhoneCall: " + ex.getMessage(), ex);
        }
    }

    // This method is used to check whether current app has required runtime permission.
    private boolean hasRuntimePermission(Context context, String runtimePermission)
    {
        boolean ret = false;

        // Get current android os version.
        int currentAndroidVersion = Build.VERSION.SDK_INT;

        // Build.VERSION_CODES.M's value is 23.
        if(currentAndroidVersion > Build.VERSION_CODES.M)
        {
            // Only android version 23+ need to check runtime permission.
            if(ContextCompat.checkSelfPermission(context, runtimePermission) == PackageManager.PERMISSION_GRANTED)
            {
                ret = true;
            }
        }else
        {
            ret = true;
        }
        return ret;
    }

    /* Request app user to allow the needed runtime permission.
       It will popup a confirm dialog , user can click allow or deny. */
    private void requestRuntimePermission(Activity activity, String runtimePermission, int requestCode)
    {
        ActivityCompat.requestPermissions(activity, new String[]{runtimePermission}, requestCode);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        // If this is our permission request result.
        if(requestCode==PERMISSION_REQUEST_CODE)
        {
            if(grantResults.length > 0)
            {
                // Construct result message.
                StringBuffer msgBuf = new StringBuffer();
                int grantResult = grantResults[0];
                if(grantResult==PackageManager.PERMISSION_GRANTED)
                {
                    msgBuf.append("You granted below permissions, you can do the action again to use the permission : ");
                }else
                {
                    msgBuf.append("You denied below permissions : ");
                }

                // Add granted permissions to the message.
                if(permissions!=null)
                {
                    int length = permissions.length;
                    for(int i=0;i<length;i++)
                    {
                        String permission = permissions[i];
                        msgBuf.append(permission);

                        if(i<length-1) {
                            msgBuf.append(",");
                        }
                    }
                }

                // Show result message.
                Toast.makeText(getApplicationContext(), msgBuf.toString(), Toast.LENGTH_LONG).show();
            }
        }
    }


    // Return all this app required permissions defined in AndroidManifest.xml file..
    private List<String> getAllPermissions(String packageName)
    {
        List<String> ret = new ArrayList<String>();
        try {

            // Get package info.
            PackageManager packageManager = getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);

            // Loop to add all package requested permissions to string list.
            int length = packageInfo.requestedPermissions.length;
            for(int i=0;i<length;i++)
            {
                String requestPermission = packageInfo.requestedPermissions[i];
                ret.add(requestPermission);
            }
        }catch (PackageManager.NameNotFoundException ex)
        {
            Log.e(TAG_RUNTIME_PERMISSION, "getAllGrantedPermissions: " + ex.getMessage(), ex);
        }finally {
            return ret;
        }
    }
}

3.3 Layout Xml File.

activity_runtime_permission.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <Button
        android:id="@+id/runtime_permission_list_granted"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="List Granted Permissions"/>

    <Button
        android:id="@+id/runtime_permission_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Use Camera"/>

    <Button
        android:id="@+id/runtime_permission_accounts"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Accounts"/>

    <Button
        android:id="@+id/runtime_permission_location"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Location"/>

    <Button
        android:id="@+id/runtime_permission_storage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Write External Storage"/>

    <Button
        android:id="@+id/runtime_permission_phone_call"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Make Phone Call"/>

</LinearLayout>

Reference

  1. How To Change Minimum Sdk Version In Android Studio
  2. Android ADB Install / Uninstall App Examples
(Visited 1,185 times, 13 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.