diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImage.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImage.java index 38b1baee..de611299 100644 --- a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImage.java +++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImage.java @@ -50,6 +50,7 @@ * Added value you get out-of-the-box is some edge case handling that you may miss otherwise, like the * stupid-ass Android camera result URI that may differ from version to version and from device to device. */ +@SuppressWarnings("WeakerAccess, unused") public final class CropImage { //region: Fields and Consts @@ -164,8 +165,10 @@ public static Intent getPickImageChooserIntent(@NonNull Context context, CharSeq List allIntents = new ArrayList<>(); PackageManager packageManager = context.getPackageManager(); - // collect all camera intents - allIntents.addAll(getCameraIntents(context, packageManager)); + // collect all camera intents if Camera permission is available + if (!isExplicitCameraPermissionRequired(context)) { + allIntents.addAll(getCameraIntents(context, packageManager)); + } List galleryIntents = getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments); if (galleryIntents.size() == 0) { @@ -350,13 +353,10 @@ public static boolean isUriRequiresPermissions(@NonNull Context context, @NonNul * Result will be recieved in {@link Activity#onActivityResult(int, int, Intent)} and can be retrieved * using {@link #getActivityResult(Intent)}. * - * @param uri the image Android uri source to crop + * @param uri the image Android uri source to crop or null to start a picker * @return builder for Crop Image Activity */ - public static ActivityBuilder activity(@NonNull Uri uri) { - if (uri == null || uri.equals(Uri.EMPTY)) { - throw new IllegalArgumentException("Uri must be non null or empty"); - } + public static ActivityBuilder activity(@Nullable Uri uri) { return new ActivityBuilder(uri); } @@ -380,6 +380,7 @@ public static final class ActivityBuilder { /** * The image to crop source Android uri. */ + @Nullable private final Uri mSource; /** @@ -387,7 +388,7 @@ public static final class ActivityBuilder { */ private final CropImageOptions mOptions; - private ActivityBuilder(@NonNull Uri source) { + private ActivityBuilder(@Nullable Uri source) { mSource = source; mOptions = new CropImageOptions(); } diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java index bb7f0321..ed62b9fa 100644 --- a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java +++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java @@ -12,17 +12,23 @@ package com.theartofdev.edmodo.cropper; +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; +import android.widget.Toast; import java.io.File; import java.io.IOException; @@ -38,12 +44,18 @@ public class CropImageActivity extends AppCompatActivity implements CropImageVie */ private CropImageView mCropImageView; + /** + * Persist URI image to crop URI if specific permissions are required + */ + private Uri mCropImageUri; + /** * the options that were set for the crop image */ private CropImageOptions mOptions; @Override + @SuppressLint("NewApi") public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.crop_image_activity); @@ -51,11 +63,24 @@ public void onCreate(Bundle savedInstanceState) { mCropImageView = (CropImageView) findViewById(R.id.cropImageView); Intent intent = getIntent(); - Uri source = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_SOURCE); + mCropImageUri = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_SOURCE); mOptions = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_OPTIONS); if (savedInstanceState == null) { - mCropImageView.setImageUriAsync(source); + if (mCropImageUri == null || mCropImageUri.equals(Uri.EMPTY)) { + if (CropImage.isExplicitCameraPermissionRequired(this)) { + // request permissions and handle the result in onRequestPermissionsResult() + requestPermissions(new String[]{Manifest.permission.CAMERA}, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); + } else { + CropImage.startPickImageActivity(this); + } + } else if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) { + // request permissions and handle the result in onRequestPermissionsResult() + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); + } else { + // no permissions required or already grunted, can start crop image activity + mCropImageView.setImageUriAsync(mCropImageUri); + } } ActionBar actionBar = getSupportActionBar(); @@ -140,6 +165,51 @@ public void onBackPressed() { setResultCancel(); } + @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) { + if (resultCode == Activity.RESULT_CANCELED) { + //User cancelled the picker. We don't have anything to crop + setResultCancel(); + } + + if (resultCode == Activity.RESULT_OK) { + mCropImageUri = CropImage.getPickImageResultUri(this, data); + + // For API >= 23 we need to check specifically that we have permissions to read external storage. + if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) { + // request permissions and handle the result in onRequestPermissionsResult() + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); + } else { + // no permissions required or already grunted, can start crop image activity + mCropImageView.setImageUriAsync(mCropImageUri); + } + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull 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 + mCropImageView.setImageUriAsync(mCropImageUri); + } else { + Toast.makeText(this, "Cancelling, required permissions are not granted", Toast.LENGTH_LONG).show(); + setResultCancel(); + } + } + + if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) { + //Irrespective of whether camera permission was given or not, we show the picker + //The picker will not add the camera intent if permission is not available + CropImage.startPickImageActivity(this); + } + } + @Override public void onSetImageUriComplete(CropImageView view, Uri uri, Exception error) { if (error == null) { diff --git a/quick-start/src/main/AndroidManifest.xml b/quick-start/src/main/AndroidManifest.xml index c8fbab2b..2f109e5b 100644 --- a/quick-start/src/main/AndroidManifest.xml +++ b/quick-start/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> + = 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}, 0); - } else { - // no permissions required or already grunted, can start crop image activity - startCropImageActivity(imageUri); - } - } - // handle result of CropImageActivity if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { CropImage.ActivityResult result = CropImage.getActivityResult(data); @@ -78,16 +53,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - 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 crop image activity for the given image. */