how to set the output image use com.android.camera.action.CROP

This question is all over stackoverflow. And I’m glad it is since I had to sort this out myself recently. I’ll do my best to mark some duplicates as I go but I prefer this one since it addresses the issue with the limited image size.

Short Answer

The short answer is not to use the return-data option. Read more about that option and how to retrieve the image then here: http://www.androidworks.com/crop_large_photos_with_android. The article does a great job listing the (known) configuration options for the Intent and how to use them.

Option #2: If you set return-data to “false”, you will not receive a
Bitmap back from the onActivityResult Intent in-line, instead you will
need to set MediaStore.EXTRA_OUTPUT to a Uri (of File scheme only)
where you want the Bitmap to be stored. This has some restrictions,
first you need to have a temp filesystem location in order to give the
file scheme URI, not a huge problem (except on some devices that don’t
have sdcards).

    /** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    thiz = this;
    setContentView(R.layout.main);
    mBtn = (Button) findViewById(R.id.btnLaunch);
    photo = (ImageView) findViewById(R.id.imgPhoto);
    mBtn.setOnClickListener(new OnClickListener(){

        public void onClick(View v) {
            try {
                // Launch picker to choose photo for selected contact
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
                intent.setType("image/*");
                intent.putExtra("crop", "true");
                intent.putExtra("aspectX", aspectX);
                intent.putExtra("aspectY", aspectY);
                intent.putExtra("outputX", outputX);
                intent.putExtra("outputY", outputY);
                intent.putExtra("scale", scale);
                intent.putExtra("return-data", return_data);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                intent.putExtra("noFaceDetection",!faceDetection); // lol, negative boolean noFaceDetection
                if (circleCrop) {
                    intent.putExtra("circleCrop", true);
                }

                startActivityForResult(intent, PHOTO_PICKED);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(thiz, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
            }
        }
    });
}

private Uri getTempUri() {
    return Uri.fromFile(getTempFile());
}

private File getTempFile() {
    if (isSDCARDMounted()) {
        File f = new File(Environment.getExternalStorageDirectory(),TEMP_PHOTO_FILE);
        try {
            f.createNewFile();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Toast.makeText(thiz, R.string.fileIOIssue, Toast.LENGTH_LONG).show();
        }
        return f;
    } else {
        return null;
    }
}

private boolean isSDCARDMounted(){
    String status = Environment.getExternalStorageState();    
    if (status.equals(Environment.MEDIA_MOUNTED))
        return true;
    return false;
}

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

    switch (requestCode) {
        case PHOTO_PICKED:
            if (resultCode == RESULT_OK) {
                if (data == null) {
                    Log.w(TAG, "Null data, but RESULT_OK, from image picker!");
                    Toast.makeText(this, R.string.no_photo_picked,
Toast.LENGTH_SHORT).show();
                    return;
                }

            final Bundle extras = data.getExtras();
            if (extras != null) {
                File tempFile = getTempFile();
                // new logic to get the photo from a URI
                if (data.getAction() != null) {
                    processPhotoUpdate(tempFile);
                }
            }
        }
        break;
    }
}

Code example from: http://www.androidworks.com/crop_large_photos_with_android

More Information

The long answer is not to use that intent at all. Continue reading to find out why.

Unofficial API

The core of the problem is the unofficial intent. Unofficial as in not part of the public API. Currently it works for most devices but most just isn’t enough. Also Google can change this intent anytime without letting you know. Breaking along all the apps using it. Much like how the calendar API was once unofficial. In fact this crop intent has already changed once. So avoid using this intent. There are alternatives. Feel free to ignore this advice.

Just to proof the “works for some devices statement” follow this link and enjoy frustrated Android developers discussing what should be considered part of the core android features (and isn’t): https://code.google.com/p/android/issues/detail?id=1480

Time for example code? Check this github project: https://github.com/lorensiuswlt/AndroidImageCrop

About Size Limit

Another issue I experienced while exploring this intent is the image crop size limit. This can easily be reproduced using the above example code and an image size of anything above 300 pixels. Basically what this original question is all about. In the best case your app will crash. But I’ve seen worse up to hanging devices that could only be reset by removing the battery.

Now if you remove that ‘return-data’ option you’ll be able to run it again. More information on how to get to the result is found in the short answer where I referenced this link already: http://www.androidworks.com/crop_large_photos_with_android

The Solution

So a lot of problems. Problems require a solution. The only decent solution until Google comes up with a public API for this is to provide your own crop intent. Just get your hands on a proper crop library like this one on github: https://github.com/lvillani/android-cropimage

The project lacks some documentation but since it’s an extract of the unofficial android crop intent you can use the examples listed on top to get started. Just make sure not to use the return-data option. Ah and check out the CropImageIntentBuilder class. That should let you easily create an intent for the cropping. Don’t forget to add this Activity to your manifest and the permissions to write to external data storage.

private void doCrop(File croppedResult){
        CropImageIntentBuilder builder = new CropImageIntentBuilder(600,600, croppedResult);
        // don't forget this, the error handling within the library is just ignoring if you do
        builder.setSourceImage(mImageCaptureUri);
        Intent  intent = builder.getIntent(getApplicationContext());
        // do not use return data for big images
        intent.putExtra("return-data", false);
        // start an activity and then get the result back in onActivtyResult
        startActivityForResult(intent, CROP_FROM_CAMERA);
    }

Using this library also opens the doors to more customisation. Good to know is the core bitmap functionally used for resizing: How to crop the parsed image in android?

And that’s it. Enjoy!

Leave a Comment