android: reviewed storage access

This commit is contained in:
Thibault Deckers 2020-05-29 15:42:34 +09:00
parent 3a657c12f0
commit 0a3b625f44
4 changed files with 24 additions and 36 deletions

View file

@ -83,8 +83,7 @@ public abstract class ImageProvider {
return;
}
if (Env.isOnSdCard(activity, oldPath)) {
// rename with DocumentFile
if (Env.requireAccessPermission(oldPath)) {
Uri sdCardTreeUri = PermissionManager.getSdCardTreeUri(activity);
if (sdCardTreeUri == null) {
Runnable runnable = () -> rename(activity, oldPath, oldMediaUri, mimeType, newFilename, callback);
@ -173,21 +172,14 @@ public abstract class ImageProvider {
private void rotateJpeg(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
final String mimeType = MimeTypes.JPEG;
String editablePath = path;
boolean onSdCard = Env.isOnSdCard(activity, path);
if (onSdCard) {
// copy original file to a temporary file for editing
final String editablePath = StorageUtils.copyFileToTemp(path);
if (Env.requireAccessPermission(path)) {
if (PermissionManager.getSdCardTreeUri(activity) == null) {
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
new Handler(Looper.getMainLooper()).post(() -> PermissionManager.showSdCardAccessDialog(activity, runnable));
return;
}
// copy original file to a temporary file for editing
editablePath = StorageUtils.copyFileToTemp(path);
}
if (editablePath == null) {
callback.onFailure();
return;
}
boolean rotated = false;
@ -211,10 +203,8 @@ public abstract class ImageProvider {
exif.setAttribute(ExifInterface.TAG_ORIENTATION, Integer.toString(newOrientationCode));
exif.saveAttributes();
// if the image is on the SD card, copy the edited temporary file to the original DocumentFile
if (onSdCard) {
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
}
// copy the edited temporary file to the original DocumentFile
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
rotated = true;
} catch (IOException e) {
Log.w(LOG_TAG, "failed to edit EXIF to rotate image at path=" + path, e);
@ -251,21 +241,14 @@ public abstract class ImageProvider {
private void rotatePng(final Activity activity, final String path, final Uri uri, boolean clockwise, final ImageOpCallback callback) {
final String mimeType = MimeTypes.PNG;
String editablePath = path;
boolean onSdCard = Env.isOnSdCard(activity, path);
if (onSdCard) {
// copy original file to a temporary file for editing
final String editablePath = StorageUtils.copyFileToTemp(path);
if (Env.requireAccessPermission(path)) {
if (PermissionManager.getSdCardTreeUri(activity) == null) {
Runnable runnable = () -> rotate(activity, path, uri, mimeType, clockwise, callback);
new Handler(Looper.getMainLooper()).post(() -> PermissionManager.showSdCardAccessDialog(activity, runnable));
return;
}
// copy original file to a temporary file for editing
editablePath = StorageUtils.copyFileToTemp(path);
}
if (editablePath == null) {
callback.onFailure();
return;
}
Bitmap originalImage = BitmapFactory.decodeFile(path);
@ -284,10 +267,8 @@ public abstract class ImageProvider {
try (FileOutputStream fos = new FileOutputStream(editablePath)) {
rotatedImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
// if the image is on the SD card, copy the edited temporary file to the original DocumentFile
if (onSdCard) {
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
}
// copy the edited temporary file to the original DocumentFile
DocumentFileCompat.fromFile(new File(editablePath)).copyTo(DocumentFileCompat.fromSingleUri(activity, uri));
rotated = true;
} catch (IOException e) {
Log.e(LOG_TAG, "failed to save rotated image to path=" + path, e);

View file

@ -180,7 +180,7 @@ public class MediaStoreImageProvider extends ImageProvider {
public ListenableFuture<Object> delete(final Activity activity, final String path, final Uri mediaUri) {
SettableFuture<Object> future = SettableFuture.create();
if (Env.isOnSdCard(activity, path)) {
if (Env.requireAccessPermission(path)) {
Uri sdCardTreeUri = PermissionManager.getSdCardTreeUri(activity);
if (sdCardTreeUri == null) {
Runnable runnable = () -> {
@ -235,7 +235,7 @@ public class MediaStoreImageProvider extends ImageProvider {
if (uuid != null) {
// the UUID returned may be uppercase
// but it should be lowercase to work with the MediaStore
volumeName = volume.getUuid().toLowerCase();
volumeName = uuid.toLowerCase();
}
}
}

View file

@ -5,6 +5,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import androidx.annotation.NonNull;
public class Env {
private static String[] mStorageVolumeRoots;
private static String mExternalStorage;
@ -38,12 +40,17 @@ public class Env {
private static String getExternalStorage() {
if (mExternalStorage == null) {
mExternalStorage = Environment.getExternalStorageDirectory().toString();
mExternalStorage = Environment.getExternalStorageDirectory().getAbsolutePath();
if (!mExternalStorage.endsWith("/")) {
mExternalStorage += "/";
}
}
return mExternalStorage;
}
public static boolean isOnSdCard(final Activity activity, String path) {
return path != null && !getExternalStorage().equals(new PathSegments(path, getStorageVolumeRoots(activity)).getStorage());
public static boolean requireAccessPermission(@NonNull String path) {
boolean onPrimaryVolume = path.startsWith(getExternalStorage());
// TODO TLAD on Android R, we should require access permission even on primary
return !onPrimaryVolume;
}
}

View file

@ -212,7 +212,7 @@ public class StorageUtils {
@Nullable
public static DocumentFileCompat getDocumentFile(Activity activity, @NonNull String path, @NonNull Uri mediaUri) {
if (Env.isOnSdCard(activity, path)) {
if (Env.requireAccessPermission(path)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Uri docUri = MediaStore.getDocumentUri(activity, mediaUri);
return DocumentFileCompat.fromSingleUri(activity, docUri);