18

So, I have below code that open camera, capture the image and save it on SDCard.

public void getPhotoFromCamera() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File mediaStorageDir = new File(
            Environment.getExternalStorageDirectory()
                    + File.separator
                    + getString(R.string.directory_name_corp_chat)
                    + File.separator
                    + getString(R.string.directory_name_temp)
    );

    if (!mediaStorageDir.exists()) {
        mediaStorageDir.mkdirs();
    }

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
            Locale.getDefault()).format(new Date());
    try {
        mediaFile = File.createTempFile(
                "TEMP_FULL_IMG_" + timeStamp,
                ".jpg",
                mediaStorageDir
        );
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mediaFile));
        startActivityForResult(takePictureIntent, PICK_FROM_CAMERA);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void performCrop(Uri picUri) {
    try {
        Intent cropIntent = new Intent("com.android.camera.action.CROP");
        cropIntent.setDataAndType(picUri, "image/*");
        cropIntent.putExtra("crop", "true");
        cropIntent.putExtra("aspectX", 1);
        cropIntent.putExtra("aspectY", 1);
        cropIntent.putExtra("outputX", 128);
        cropIntent.putExtra("outputY", 128);
        // retrieve data on return
        cropIntent.putExtra("return-data", true);

        File mediaStorageDir = new File(
                Environment.getExternalStorageDirectory()
                        + File.separator
                        + getString(R.string.directory_name_corp_chat)
                        + File.separator
                        + getString(R.string.directory_name_temp)
        );

        if (!mediaStorageDir.exists()) {
            mediaStorageDir.mkdirs();
        }

        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date());
        try {
            croppedFile = File.createTempFile(
                    "TEMP_CROPPED_IMG_" + timeStamp,
                    ".jpg",
                    mediaStorageDir
            );
            cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(croppedFile));
            startActivityForResult(cropIntent, PIC_CROP);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // respond to users whose devices do not support the crop action
    catch (ActivityNotFoundException anfe) {
        // display an error message
        String errorMessage = "Whoops - your device doesn't support the crop action!";
        Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
        toast.show();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == PICK_FROM_CAMERA) {
        if (resultCode == RESULT_OK) {
            performCrop(Uri.fromFile(mediaFile));
        } else {
            Log.i("Camera", "result cancel. Hence, deleting file: " + mediaFile.getPath());
            Log.i("File deleted ", mediaFile.delete() + "");
        }
    }

    if (requestCode == PICK_FROM_GALLERY) {
        if (resultCode == RESULT_OK) {
            performCrop(data.getData());
        } else {
            Log.i("Gallery", "result cancel");
        }
    }

    if (requestCode == PIC_CROP) {
        if (resultCode == RESULT_OK) {
            imageView.setImageBitmap(BitmapFactory.decodeFile(croppedFile.getAbsolutePath()));
            if (mediaFile != null) {
                Log.i("Camera", "result cancel. Hence, deleting file: " + mediaFile.getPath());
                Log.i("File deleted ", mediaFile.delete() + "");
            }
        } else {
            if (croppedFile != null) {
                Log.i("Camera", "result cancel. Hence, deleting file: " + croppedFile.getPath());
                Log.i("File deleted ", croppedFile.delete() + "");
            }
            if (mediaFile != null) {
                Log.i("Camera", "result cancel. Hence, deleting file: " + mediaFile.getPath());
                Log.i("File deleted ", mediaFile.delete() + "");
            }
        }
    }
}

Everything works perfect as expected below Android 6.0. But it doesn't work on Android 6.0 Marshmallow. In fact it doesn't even open the camera :(

I don't know whether I have to do something special for marshmallow. I am not getting any kind of error too, that I can post it here. Please help me out.

Thanks.

Chintan Soni
  • 24,761
  • 25
  • 106
  • 174

2 Answers2

34

So, I accomplished my task like as below:

For Checking permission I created a separate class as below:

public class MarshMallowPermission {

    public static final int RECORD_PERMISSION_REQUEST_CODE = 1;
    public static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 2;
    public static final int CAMERA_PERMISSION_REQUEST_CODE = 3;
    Activity activity;

    public MarshMallowPermission(Activity activity) {
        this.activity = activity;
    }

    public boolean checkPermissionForRecord(){
        int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO);
        if (result == PackageManager.PERMISSION_GRANTED){
            return true;
        } else {
            return false;
        }
    }

    public boolean checkPermissionForExternalStorage(){
        int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (result == PackageManager.PERMISSION_GRANTED){
            return true;
        } else {
            return false;
        }
    }

    public boolean checkPermissionForCamera(){
        int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
        if (result == PackageManager.PERMISSION_GRANTED){
            return true;
        } else {
            return false;
        }
    }

    public void requestPermissionForRecord(){
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.RECORD_AUDIO)){
           Toast.makeText(activity, "Microphone permission needed for recording. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.RECORD_AUDIO},RECORD_PERMISSION_REQUEST_CODE);
        }
    }

    public void requestPermissionForExternalStorage(){
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)){
            Toast.makeText(activity, "External Storage permission needed. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
        }
    }

    public void requestPermissionForCamera(){
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)){
            Toast.makeText(activity, "Camera permission needed. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.CAMERA},CAMERA_PERMISSION_REQUEST_CODE);
        }
    }
}

Then, for getting

...
MarshMallowPermission marshMallowPermission = new MarshMallowPermission(this);
...

public void getPhotoFromCamera() {

    if (!marshMallowPermission.checkPermissionForCamera()) {
        marshMallowPermission.requestPermissionForCamera();
    } else {
        if (!marshMallowPermission.checkPermissionForExternalStorage()) {
            marshMallowPermission.requestPermissionForExternalStorage();
        } else {
            Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            File mediaStorageDir = new File(
                    Environment.getExternalStorageDirectory()
                            + File.separator
                            + getString(R.string.directory_name_corp_chat)
                            + File.separator
                            + getString(R.string.directory_name_images)
            );

            if (!mediaStorageDir.exists()) {
                mediaStorageDir.mkdirs();
            }

            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                    Locale.getDefault()).format(new Date());
            try {
                mediaFile = File.createTempFile(
                        "IMG_" + timeStamp,  /* prefix */
                        ".jpg",         /* suffix */
                        mediaStorageDir      /* directory */
                );
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mediaFile));
                startActivityForResult(takePictureIntent, PICK_FROM_CAMERA);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
Chintan Soni
  • 24,761
  • 25
  • 106
  • 174
  • I had app working on a device with Lollipop, then failed on two devices running Marshmallow. I was getting a message on startup - It seems your device does not support camera (or it is locked). I traced the error through Camera.open() all the way to Camera.cameraInitVersion() where it was returning error of not being able to access Camera service. Didn't realize coding changes were needed for now permission model on Marshmallow. – medloh Nov 08 '15 at 15:14
  • @ChintanSoni is it Show one request Only for one time in App? – RushDroid Jan 06 '16 at 11:05
  • 1
    what is mediaFile? is it a File ? thanks for the example! – Carlos.V Mar 12 '16 at 19:09
  • @Carlos.V Yes. Its file object. – Chintan Soni Mar 12 '16 at 19:11
  • 2
    @ChintanSoni: how this works? Suppose for very first time the permission is not granted it goes on if statement if (!marshMallowPermission.checkPermissionForCamera()) returns false and then request for permission the code will stop here.The other part of the code wont be executed as it is return in else part. – Rajesh Gosemath Oct 14 '16 at 05:17
  • In addition to the code shown, your activity has to implement a onRequestPermissionsResult method, to handle the accepted permission requests. That method could call getPhotoFromCamera, for example. – Jose Gómez Feb 06 '17 at 18:33
7

Quoting developers.android.com:

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. This approach streamlines the app install process, since the user does not need to grant permissions when they install or update the app. It also gives the user more control over the app's functionality; for example, a user could choose to give a camera app access to the camera but not to the device location. The user can revoke the permissions at any time, by going to the app's Settings screen.

System permissions are divided into two categories, normal and dangerous:

  • Normal permissions do not directly risk the user's privacy. If your app lists a normal permission in its manifest, the system grants the permission automatically.

  • Dangerous permissions can give the app access to the user's confidential data. If your app lists a normal permission in its manifest, the system grants the permission automatically. If you list a dangerous permission, the user has to explicitly give approval to your app.

WRITE_EXTERNAL_STORAGE is in the Dangerous category, for this reason you need to request the permission to the user before call mediaStorageDir.mkdirs() or File.createTempFile otherwise you program crash with this exception:

W/System.err: java.io.IOException: open failed: EACCES (Permission denied)
W/System.err:     at java.io.File.createNewFile(File.java:939)
W/System.err:     at java.io.File.createTempFile(File.java:1004)
W/System.err:     at com.example.MainActivity.getPhotoFromCamera(MainActivity.java:98)
W/System.err:     at com.example.MainActivity.onCreate(MainActivity.java:48)
W/System.err:     at android.app.Activity.performCreate(Activity.java:6237)
W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.
W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.
W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476
W/System.err:     at android.app.ActivityThread.-wrap11(ActivityThread.java)
W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err:     at android.os.Looper.loop(Looper.java:148)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5417)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission 
W/System.err:     at libcore.io.Posix.open(Native Method)
W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
W/System.err:     at java.io.File.createNewFile(File.java:932)
W/System.err:   ... 15 more
Mattia Maestrini
  • 32,270
  • 15
  • 87
  • 94
  • Thank you.. For pointing out this feature of Marshmallow.. I don't know how did I forgot it ? Anyways.. Thanks.. I am trying to give the runtime permission and will update the question soon. – Chintan Soni Oct 10 '15 at 07:57
  • Dude, you freaking saved my tail. Thanks much. You are a gentleman and a scoundrel. ;) – LukeWaggoner Oct 11 '15 at 08:28
  • As per the guidelines it also says you can avoid asking for too many permissions by using intents to leverage other apps such as the camera to do the work. This means that the camera app would need to have the necessary permissions to take the picture and should return a result. Right? If that is the case then why is his camera not opening? I ask because I have an issue with my app where the result code is not RESULT_OK. I think it has to do with this permissions change. – Jarrett R Oct 21 '15 at 18:38
  • In this case the camera is not opening because the OP try to create a temp file **before** open the camera. If you have another issue I suggest to open a new question. – Mattia Maestrini Oct 22 '15 at 08:14
  • @ChintanSoni So have you tried to give the runtime permission? – Mattia Maestrini Nov 03 '15 at 06:41
  • @MattiaMaestrini Yes sir I achieved it following your suggestion. It took some time to understand the architecture behind the implementation but I did it.. Thanks for your guidance.. – Chintan Soni Nov 03 '15 at 11:37