As you know that in API 23 (Marshmallows) new Runtime Permission is added to android development. This feature make App installation more faster than before, because on Android Marshmallows above device your app will not ask for Permission at installation time. Permissions will be granted dynamically to your app when you actually need it. That’s a cool Idea. But if you have existing App and you try to install that app on Android Marshmallows device then your app will install without Permissions as expected but if you try to access Camera, Storage then your app will crash saying “No Permissions etc. Exception”. Because you haven’t yet defined runtime Permissions in your app. So you need to modify your app to ask for Permissions when required.
But maybe you tried adding some codes to make runtime Permission work on your app but you still got crash, then here is simple tutorial to embed runtime Permission in your app.
So in this demo I will make an App having an ImageView and Button, when I click on button it will capture Image and display in ImageView. So below is code without runtime permissions. And its working fine on Below Marshmallows devices. But getting crash in Android Marshmallows.
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"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"tools:context="com.edablogs.runtimepermissiondemo.MainActivity"><ImageViewandroid:layout_width="300dp"android:id="@+id/image"android:layout_centerHorizontal="true"android:layout_height="300dp" /><Buttonandroid:layout_width="match_parent"android:text="capture"android:id="@+id/btn"android:layout_below="@id/image"android:layout_height="wrap_content" /></RelativeLayout>
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.edablogs.runtimepermissiondemo"><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-featureandroid:glEsVersion="0x00020000"android:required="true" /><uses-featureandroid:name="android.hardware.camera"android:required="true" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
package com.edablogs.runtimepermissiondemo;import android.content.Intent;import android.content.pm.PackageManager;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;public class MainActivity extends AppCompatActivity {private Button btn;private ImageView imageView;// Request code for cameraprivate final int CAMERA_REQUEST_CODE = 100;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);imageView = (ImageView) findViewById(R.id.image);btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {dispatchTakePictureIntent();} else {Toast.makeText(MainActivity.this, "Camera not supported", Toast.LENGTH_LONG).show();}}});}private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// Ensure that there's a camera activity to handle the intentif (takePictureIntent.resolveActivity(getPackageManager()) != null) {// Create the File where the photo should goFile photoFile = null;try {photoFile = createImageFile();} catch (IOException ex) {// Error occurred while creating the FileLog.e("lpl", ex.getMessage());}// Continue only if the File was successfully createdif (photoFile != null) {takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE);}}}private void setPic() throws Exception {// Get the dimensions of the Viewint targetW = imageView.getWidth();int targetH = imageView.getHeight();// Get the dimensions of the bitmapBitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = true;BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);int photoW = bmOptions.outWidth;int photoH = bmOptions.outHeight;// Determine how much to scale down the imageint scaleFactor = Math.min(photoW/targetW, photoH/targetH);// Decode the image file into a Bitmap sized to fill the ViewbmOptions.inJustDecodeBounds = false;bmOptions.inSampleSize = scaleFactor;bmOptions.inPurgeable = true;Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);imageView.setImageBitmap(bitmap);}public String mCurrentPhotoPath;public File createImageFile() throws IOException {// Create an image file nameString folderName = "test";String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());String imageFileName = "JPEG_" + timeStamp + "_";File f = new File(Environment.getExternalStorageDirectory(), folderName);if (!f.exists()) {f.mkdirs();}File storageDir = new File(Environment.getExternalStorageDirectory()+"/"+folderName);File image = File.createTempFile(imageFileName, /* prefix */".jpg", /* suffix */storageDir /* directory */);// Save a file: path for use with ACTION_VIEW intentsmCurrentPhotoPath = image.getAbsolutePath();return image;}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == CAMERA_REQUEST_CODE && resultCode == RESULT_OK){try {setPic();} catch (Exception e) {e.printStackTrace();}}}}
So we need to do changes in java file only. So I will modify MainActivity.java file to add runtime permissions. below is code after adding runtime permissions into our app. I need only 2 permissions at this time, Camera and External Storage.
package com.edablogs.runtimepermissiondemo;import android.Manifest;import android.content.Intent;import android.content.pm.PackageManager;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;public class MainActivity extends AppCompatActivity {private Button btn;private ImageView imageView;// Request code for cameraprivate final int CAMERA_REQUEST_CODE = 100;// Request code for runtime permissionsprivate final int REQUEST_CODE_STORAGE_PERMS = 321;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);imageView = (ImageView) findViewById(R.id.image);btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {if (!hasPermissions()){// your app doesn't have permissions, ask for them.requestNecessaryPermissions();}else {// your app already have permissions allowed.// do what you want.dispatchTakePictureIntent();}} else {Toast.makeText(MainActivity.this, "Camera not supported", Toast.LENGTH_LONG).show();}}});}private boolean hasPermissions() {int res = 0;// list all permissions which you want to check are granted or not.String[] permissions = new String[] {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};for (String perms : permissions){res = checkCallingOrSelfPermission(perms);if (!(res == PackageManager.PERMISSION_GRANTED)){// it return false because your app dosen't have permissions.return false;}}// it return true, your app has permissions.return true;}private void requestNecessaryPermissions() {// make array of permissions which you want to ask from user.String[] permissions = new String[] {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// have arry for permissions to requestPermissions method.// and also send unique Request code.requestPermissions(permissions, REQUEST_CODE_STORAGE_PERMS);}}/* when user grant or deny permission then your app will check inonRequestPermissionsReqult about user's response. */@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grandResults) {// this boolean will tell us that user granted permission or not.boolean allowed = true;switch (requestCode) {case REQUEST_CODE_STORAGE_PERMS:for (int res : grandResults) {// if user granted all required permissions then 'allowed' will return true.allowed = allowed && (res == PackageManager.PERMISSION_GRANTED);}break;default:// if user denied then 'allowed' return false.allowed = false;break;}if (allowed) {// if user granted permissions then do your work.dispatchTakePictureIntent();}else {// else give any custom waring message.if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {Toast.makeText(MainActivity.this, "Camera Permissions denied", Toast.LENGTH_SHORT).show();}else if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)){Toast.makeText(MainActivity.this, "Storage Permissions denied", Toast.LENGTH_SHORT).show();}}}}private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// Ensure that there's a camera activity to handle the intentif (takePictureIntent.resolveActivity(getPackageManager()) != null) {// Create the File where the photo should goFile photoFile = null;try {photoFile = createImageFile();} catch (IOException ex) {// Error occurred while creating the FileLog.e("lpl", ex.getMessage());}// Continue only if the File was successfully createdif (photoFile != null) {takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE);}}}private void setPic() throws Exception {// Get the dimensions of the Viewint targetW = imageView.getWidth();int targetH = imageView.getHeight();// Get the dimensions of the bitmapBitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = true;BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);int photoW = bmOptions.outWidth;int photoH = bmOptions.outHeight;// Determine how much to scale down the imageint scaleFactor = Math.min(photoW/targetW, photoH/targetH);// Decode the image file into a Bitmap sized to fill the ViewbmOptions.inJustDecodeBounds = false;bmOptions.inSampleSize = scaleFactor;bmOptions.inPurgeable = true;Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);imageView.setImageBitmap(bitmap);}public String mCurrentPhotoPath;public File createImageFile() throws IOException {// Create an image file nameString folderName = "test";String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());String imageFileName = "JPEG_" + timeStamp + "_";File f = new File(Environment.getExternalStorageDirectory(), folderName);if (!f.exists()) {f.mkdirs();}File storageDir = new File(Environment.getExternalStorageDirectory()+"/"+folderName);File image = File.createTempFile(imageFileName, /* prefix */".jpg", /* suffix */storageDir /* directory */);// Save a file: path for use with ACTION_VIEW intentsmCurrentPhotoPath = image.getAbsolutePath();return image;}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == CAMERA_REQUEST_CODE && resultCode == RESULT_OK){try {setPic();} catch (Exception e) {e.printStackTrace();}}}}
In AndroidManifest.xml don’t remove Permissions, otherwise your app will not ask for runtime Permissions and also it will not work on below Android Marshmallows devices.
Here is Video Tutorial of Runtime Permissions.
and here is demo video how Runtime Permissions look when you embed this code in your app.
Quick Links
Legal Stuff
Social Media