Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have code to crop an image, like this :

public void doCrop(){
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setType("image/");
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent,0);
int size = list.size();
if (size == 0 ){
   Toast.makeText(this, "Cant find crop app").show();
   return;
} else{
   intent.setData(selectImageUri);
   intent.putExtra("outputX", 300);
   intent.putExtra("outputY", 300);
   intent.putExtra("aspectX", 1);
   intent.putExtra("aspectY", 1);
   intent.putExtra("scale", true);
   intent.putExtra("return-data", true);
   if (size == 1) {
       Intent i = new Intent(intent);
       ResolveInfo res = list.get(0);
       i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
       startActivityForResult(i, CROP_RESULT);
   }
}
}

public void onActivityResult (int requestCode, int resultCode, Intent dara){
   if (resultCode == RESULT_OK){
      if (requestCode == CROP_RESULT){
          Bundle extras = data.getExtras();
          if (extras != null){
              bmp = extras.getParcelable("data");
          }
          File f = new File(selectImageUri.getPath());
          if (f.exists()) f.delete();
          Intent inten3 = new Intent(this, tabActivity.class);
          startActivity(inten3);
      }
   }
}

from what i have read, the code intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); is use to set the resolution of crop result, but why i can't get the result image resolution higer than 300x300? when I set the intent.putExtra("outputX", 800); intent.putExtra("outputY", 800); the crop function has no result or crash, any idea for this situation?

the log cat say "! ! ! FAILED BINDER TRANSACTION ! ! !

like image 222
adi.zean Avatar asked Oct 06 '12 09:10

adi.zean


2 Answers

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!

like image 138
hcpl Avatar answered Nov 11 '22 22:11

hcpl


That Intent is not part of the public Android API and is not guaranteed to work on all devices. It was used in earlier versions of android 1.x and 2.x but it's not used anymore and is not recommended. That is probably why it's crashing all over the palce or working improperly.

Use methods such as Bitmap.createBitmap(..) or Bitmap.createScaledBitmap(..) to create a resized or cropped version of your original image. These are part of the Android API and are guaranteed to work.

See official docs here and here

To crop a bitmap, you can use Bitmap.createBitmap(Bitmap, int x, int y, int width, int height). For example, if you need to crop 10 pixels from each side of a bitmap then use this:

Bitmap croppedBitmap = Bitmap.createBitmap(originalBitmap, 10, 10, originalBitmap.getWidth() - 20, originalBitmap.getHeight() - 20);

If you need to show the selector to the user. Then you can do something like this:

private static final String TEMP_PHOTO_FILE = "temporary_holder.jpg";  

Intent photoPickerIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra("crop", "true");
photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
photoPickerIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(photoPickerIntent, REQ_CODE_PICK_IMAGE);


    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) {

    }
    return f;
    } else {
    return null;
    }
    }

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




protected void onActivityResult(int requestCode, int resultCode,
        Intent imageReturnedIntent) {
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    switch (requestCode) {
    case REQ_CODE_PICK_IMAGE:
        if (resultCode == RESULT_OK) {  
          if (imageReturnedIntent!=null){



               File tempFile = getTempFile();

              String filePath= Environment.getExternalStorageDirectory()
            + "/temporary_holder.jpg";
              System.out.println("path "+filePath);


    Bitmap selectedImage =  BitmapFactory.decodeFile(filePath);
    _image = (ImageView) findViewById(R.id.image);
    _image.setImageBitmap(selectedImage );

}
}
}

code from here

like image 22
Anup Cowkur Avatar answered Nov 11 '22 22:11

Anup Cowkur