Skip to content

Pick image for cropping from Camera or Gallery

sirpythagoras edited this page Dec 29, 2017 · 15 revisions

pick image

##TL;DR

  1. Add permissions to manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  1. Add CropImageActivity to manifest
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
  android:theme="@style/Base.Theme.AppCompat"/> <!-- optional (needed if default theme has no action bar) -->
  1. Start Crop Image Activity
private void startCropImageActivity() {
  CropImage.activity()
    .start(this);
}

Call pick image chooser from your activity

  1. Start pick-image-chooser
public void onSelectImageClick(View view) {
  CropImage.startPickImageActivity(this);
}
  1. Handle onActivityResult
@Override
@SuppressLint("NewApi")
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // handle result of pick image chooser
  if (requestCode == CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    Uri imageUri = CropImage.getPickImageResultUri(this, data); 

    // For API >= 23 we need to check specifically that we have permissions to read external storage.
    if (CropImage.isReadExternalStoragePermissionsRequired(this, imageUri)) {
      // request permissions and handle the result in onRequestPermissionsResult()
      mCropImageUri = imageUri;
      requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},   CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); 
    } else {
      // no permissions required or already granted, can start crop image activity
      startCropImageActivity(imageUri);
    }
  }
}
  1. Handle onRequestPermissionsResult
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
 if (requestCode == CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE) {
   if (mCropImageUri != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
     // required permissions granted, start crop image activity
     startCropImageActivity(mCropImageUri);
   } else {
     Toast.makeText(this, "Cancelling, required permissions are not granted", Toast.LENGTH_LONG).show();
   }
 }
}
  1. Start Crop Image Activity
private void startCropImageActivity(Uri imageUri) {
  CropImage.activity(imageUri)
    .start(this);
}

Details

Start pick image chooser

Create "chooser" intent using Intent.createChooser that will contains all the available Camera (MediaStore.ACTION_IMAGE_CAPTURE) and Gallery (Intent.ACTION_GET_CONTENT) activities on the device.
Your can start the activity quickly by CropImage.startPickImageActivity, get the chooser intent using CropImage.getPickImageChooserIntent or create the intent yourself possible separating camera and gallery intents.

There is an edge scenario starting with Marshmallow that if the app requests <uses-permission android:name="android.permission.CAMERA"/> in the manifests it must explicitly request them in runtime to be able to use the MediaStore.ACTION_IMAGE_CAPTURE action intent, otherwise selecting camera in the chooser won't work. So first a check is done to see if explicit camera permission is required.
From UX perspective it may be better, in this scenario, to separate capture image by camera and pick image from gallery to different UI triggers (buttons).

@SuppressLint("NewApi")
public void onSelectImageClick(View view) {
  if (CropImage.isExplicitCameraPermissionRequired(this)) {
    requestPermissions(new String[]{Manifest.permission.CAMERA}, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE);
  } else {
    CropImage.startPickImageActivity(this);
  }
}

Handle pick image activity result

The result of pick image activity is returned in onActivityResult method for the activity that started it.
To retrieve the picked image uri use CropImage.getPickImageResultUri that will handle if the user used Camera or Gallery activity.
Some Gallery activities may use external storage to pass the picked image so will require READ_EXTERNAL_STORAGE permissions to read it. For pre-Marshmallow (SDK 23) you need to request the permissions in the manifest:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

Starting from Marshmallow you need to request it at runtime. Because not all activities require external storage it is best to first check it using CropImage.isReadExternalStoragePermissionsRequired so only to request it if-and-only-if it is required. Calling requestPermissions method will request the permission from the user asynchronously so starting of the crop activity is postponed to onRequestPermissionsResult.

@Override
@SuppressLint("NewApi")
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // handle result of pick image chooser
  if (requestCode == CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    Uri imageUri = CropImage.getPickImageResultUri(this, data);

    // For API >= 23 we need to check specifically that we have permissions to read external storage.
    if (CropImage.isReadExternalStoragePermissionsRequired(this, imageUri)) {
      // request permissions and handle the result in onRequestPermissionsResult()
      mCropImageUri = imageUri;
      requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE);
    } else {
      // no permissions required or already granted, can start crop image activity
      startCropImageActivity(imageUri);
    }
  }
}

Handle permissions result (SDK >= 23)

Result of permissions request is returned in onRequestPermissionsResult method.
If the required permissions are not granted you can cancel gracefully or present an explanation dialog and request the permissions again, it either case you cannot continue.
For camera capture permissions you can start the pick image chooser activity, and the crop activity for external storage permissions.
The permission request was shown right after the user picked the image for cropping so it is obvious why the app required the permissions, no special dialog is required in that case, IMHO.

public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
  if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
      CropImage.startPickImageActivity(this);
    } else {
      Toast.makeText(this, "Cancelling, required permissions are not granted", Toast.LENGTH_LONG).show();
    }
  }
  if (requestCode == CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE) {
    if (mCropImageUri != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
      // required permissions granted, start crop image activity
      startCropImageActivity(mCropImageUri);
    } else {
      Toast.makeText(this, "Cancelling, required permissions are not granted", Toast.LENGTH_LONG).show();
    }
  }
}

Start image crop activity

Finally you can start the crop activity, be it the built-in CropImageActivity or your custom activity using CropImageView.

private void startCropImageActivity(Uri imageUri) {
  CropImage.activity(imageUri)
    .start(this);
}

External Resources

Android cropping image from camera or gallery