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:
- https://developer.android.com/training/basics/intents/result
- https://stackoverflow.com/questions/5309190/android-pick-images-from-gallery
- https://developer.android.com/reference/androidx/core/content/FileProvider
- https://developer.android.com/training/camera/photobasics (I copied
createImageFile()
from here)
Can you send the complete code