bugfix: crash when rotating image on SD card on older devices
This commit is contained in:
parent
02dbb3fcbf
commit
829e97783e
2 changed files with 46 additions and 18 deletions
|
@ -141,8 +141,20 @@ public abstract class ImageProvider {
|
||||||
|
|
||||||
private void rotateJpeg(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
|
private void rotateJpeg(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
|
||||||
final String mimeType = MimeTypes.JPEG;
|
final String mimeType = MimeTypes.JPEG;
|
||||||
|
|
||||||
|
final DocumentFileCompat originalDocumentFile = StorageUtils.getDocumentFile(activity, path, uri);
|
||||||
|
if (originalDocumentFile == null) {
|
||||||
|
callback.onFailure(new Exception("failed to get document file for path=" + path + ", uri=" + uri));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// copy original file to a temporary file for editing
|
// copy original file to a temporary file for editing
|
||||||
final String editablePath = StorageUtils.copyFileToTemp(path);
|
final String editablePath = StorageUtils.copyFileToTemp(originalDocumentFile, path);
|
||||||
|
if (editablePath == null) {
|
||||||
|
callback.onFailure(new Exception("failed to create a temporary file for path=" + path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Env.requireAccessPermission(path)) {
|
if (Env.requireAccessPermission(path)) {
|
||||||
if (PermissionManager.getSdCardTreeUri(activity) == null) {
|
if (PermissionManager.getSdCardTreeUri(activity) == null) {
|
||||||
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
|
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
|
||||||
|
@ -171,8 +183,8 @@ public abstract class ImageProvider {
|
||||||
exif.setAttribute(ExifInterface.TAG_ORIENTATION, Integer.toString(newOrientationCode));
|
exif.setAttribute(ExifInterface.TAG_ORIENTATION, Integer.toString(newOrientationCode));
|
||||||
exif.saveAttributes();
|
exif.saveAttributes();
|
||||||
|
|
||||||
// copy the edited temporary file to the original DocumentFile
|
// copy the edited temporary file back to the original
|
||||||
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
|
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(originalDocumentFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
callback.onFailure(e);
|
callback.onFailure(e);
|
||||||
return;
|
return;
|
||||||
|
@ -205,8 +217,20 @@ public abstract class ImageProvider {
|
||||||
|
|
||||||
private void rotatePng(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
|
private void rotatePng(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
|
||||||
final String mimeType = MimeTypes.PNG;
|
final String mimeType = MimeTypes.PNG;
|
||||||
|
|
||||||
|
final DocumentFileCompat originalDocumentFile = StorageUtils.getDocumentFile(activity, path, uri);
|
||||||
|
if (originalDocumentFile == null) {
|
||||||
|
callback.onFailure(new Exception("failed to get document file for path=" + path + ", uri=" + uri));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// copy original file to a temporary file for editing
|
// copy original file to a temporary file for editing
|
||||||
final String editablePath = StorageUtils.copyFileToTemp(path);
|
final String editablePath = StorageUtils.copyFileToTemp(originalDocumentFile, path);
|
||||||
|
if (editablePath == null) {
|
||||||
|
callback.onFailure(new Exception("failed to create a temporary file for path=" + path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Env.requireAccessPermission(path)) {
|
if (Env.requireAccessPermission(path)) {
|
||||||
if (PermissionManager.getSdCardTreeUri(activity) == null) {
|
if (PermissionManager.getSdCardTreeUri(activity) == null) {
|
||||||
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
|
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
|
||||||
|
@ -229,8 +253,8 @@ public abstract class ImageProvider {
|
||||||
try (FileOutputStream fos = new FileOutputStream(editablePath)) {
|
try (FileOutputStream fos = new FileOutputStream(editablePath)) {
|
||||||
rotatedImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
rotatedImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
|
||||||
// copy the edited temporary file to the original DocumentFile
|
// copy the edited temporary file back to the original
|
||||||
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
|
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(originalDocumentFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
callback.onFailure(e);
|
callback.onFailure(e);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -220,18 +220,22 @@ public class StorageUtils {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static DocumentFileCompat getDocumentFile(@NonNull Activity activity, @NonNull String path, @NonNull Uri mediaUri) {
|
public static DocumentFileCompat getDocumentFile(@NonNull Activity activity, @NonNull String path, @NonNull Uri mediaUri) {
|
||||||
if (Env.requireAccessPermission(path)) {
|
if (Env.requireAccessPermission(path)) {
|
||||||
|
// need a document URI (not a media content URI) to open a `DocumentFile` output stream
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// cleanest API to get it
|
||||||
Uri docUri = MediaStore.getDocumentUri(activity, mediaUri);
|
Uri docUri = MediaStore.getDocumentUri(activity, mediaUri);
|
||||||
return DocumentFileCompat.fromSingleUri(activity, docUri);
|
if (docUri != null) {
|
||||||
} else {
|
return DocumentFileCompat.fromSingleUri(activity, docUri);
|
||||||
Uri sdCardTreeUri = PermissionManager.getSdCardTreeUri(activity);
|
}
|
||||||
String[] storageVolumeRoots = Env.getStorageVolumeRoots(activity);
|
|
||||||
Optional<DocumentFileCompat> docFile = StorageUtils.getSdCardDocumentFile(activity, sdCardTreeUri, storageVolumeRoots, path);
|
|
||||||
return docFile.orElse(null);
|
|
||||||
}
|
}
|
||||||
} else {
|
// fallback for older APIs
|
||||||
return DocumentFileCompat.fromFile(new File(path));
|
Uri sdCardTreeUri = PermissionManager.getSdCardTreeUri(activity);
|
||||||
|
String[] storageVolumeRoots = Env.getStorageVolumeRoots(activity);
|
||||||
|
Optional<DocumentFileCompat> docFile = StorageUtils.getSdCardDocumentFile(activity, sdCardTreeUri, storageVolumeRoots, path);
|
||||||
|
return docFile.orElse(null);
|
||||||
}
|
}
|
||||||
|
// good old `File`
|
||||||
|
return DocumentFileCompat.fromFile(new File(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the directory `DocumentFile` (from tree URI when scoped storage is required, `File` otherwise)
|
// returns the directory `DocumentFile` (from tree URI when scoped storage is required, `File` otherwise)
|
||||||
|
@ -277,15 +281,15 @@ public class StorageUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String copyFileToTemp(String path) {
|
public static String copyFileToTemp(@NonNull DocumentFileCompat documentFile, @NonNull String path) {
|
||||||
|
String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(path)).toString());
|
||||||
try {
|
try {
|
||||||
String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(path)).toString());
|
|
||||||
File temp = File.createTempFile("aves", '.' + extension);
|
File temp = File.createTempFile("aves", '.' + extension);
|
||||||
Utils.copyFile(new File(path), temp);
|
documentFile.copyTo(DocumentFileCompat.fromFile(temp));
|
||||||
temp.deleteOnExit();
|
temp.deleteOnExit();
|
||||||
return temp.getPath();
|
return temp.getPath();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(LOG_TAG, "failed to copy file at path=" + path);
|
Log.w(LOG_TAG, "failed to copy file from path=" + path);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue