ArthurHub / Android-Image-Cropper

Image Cropping Library for Android, optimized for Camera / Gallery.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A workaround to make this library work for Android 11 or higher

Mohaymin opened this issue · comments

This solution might not be feasible for every use-cases, and migrating to the latest library (see #858) my be the best solution. But I'm sharing my approach anyways.

The problem I faced: My objective was to select a photo from Gallery, load it in the image cropper and finally save the cropped photo. But I encountered exception in Android 11+ devices while loading the image in cropper.

The solution I used: Instead of loading the selected image directly into the cropper, I created a copy of the image file and loaded the copied file into the image-cropper.

I used ActivityResultLauncher to select the image from gallery. I created a util class named MyFileUtils for my own convenience. I'll include the snippets of relevant methods of those classes below it. I created an activity named EditImageActivity where I load the copied file into the image cropper. Here's how I created the ActivityResultLauncher to select image from gallery:

private ActivityResultLauncher<Intent> galleryActivityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
//         the datatype of result is androidx.activity.result.ActivityResult
            if (result.getResultCode() == Activity.RESULT_OK) {

                Intent data = result.getData();

                if (data == null) {
                    Log.d(TAG, "initializeActivityLaunchers: intent data is null");
                    return;
                }
                Uri selectedImageUri = data.getData();
                if (null != selectedImageUri) {
                    // Get the path from the Uri
                    File copyOfSelectedFile = null;
                    try {
                        InputStream inputStream = getContentResolver().openInputStream(selectedImageUri);
                        copyOfSelectedFile = MyFileUtils.createImageFile(DoubtHistoryActivity.this);
                        MyFileUtils.copyInputStreamToFile(inputStream, copyOfSelectedFile);
                        // refer to the following code snippet for implementation of MyFileUtils' methods
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return;
                    }

                    selectedImageUri = Uri.fromFile(copyOfSelectedFile);

                    Intent intent = new Intent(TheCurrentActivity.this, EditImageActivity.class);

                    intent.putExtra("FILE_PATH", selectedImageUri.toString())
                            .putExtra(...);
                    startActivity(intent);
                    finish();
                } else {
                    Log.d(TAG, "initializeActivityLaunchers: selected image uri is null");
                    // maybe the user returned from gallery without choosing any image
                }
            }
        });

The following snippet contains implementation of MyFileUtils.createImageFile(..) and MyFileUtils.copyInputStreamToFile(..):

    public static File createImageFile(Activity associatedActivity) throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "Question_" + timeStamp + "_";
        File storageDir = associatedActivity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        Log.d(TAG, "createImageFile: storageDir: " + storageDir.getAbsolutePath());
        // Save a file: path for use with ACTION_VIEW intents
        File temporaryFile = File.createTempFile(
                imageFileName,  /* prefix */
                ".png",         /* suffix */
//                ".jpg",         /* suffix */
//                ".jpeg",         /* suffix */
                storageDir      /* directory */
        );
        temporaryFile.deleteOnExit();
        return temporaryFile;
    }


    // Copy an InputStream to a File.
    public static void copyInputStreamToFile(InputStream in, File file) {
        OutputStream out = null;

        try {
            out = new FileOutputStream(file);
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Ensure that the InputStreams are closed even if there's an exception.
            try {
                if (out != null) {
                    out.close();
                }

                // If you want to close the "in" InputStream yourself then remove this
                // from here but ensure that you close it yourself eventually.
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Inside the EditImageActivity, I have the path of the copied image file. So I convert the path string to path Uri and load it in the image cropper in the following way:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_image);
        Intent intent = getIntent();
        String sourcePath = intent.getStringExtra("FILE_PATH");
        Uri sourceUri = Uri.parse(sourcePath);
        CropImage.activity(sourceUri)
                .setAllowFlipping(false)
                .start(this);
    }

I went through the following links to come up with this solution:

Can you send the complete code