diff --git a/android/app/build.gradle b/android/app/build.gradle index 96d85232d..822ed2f59 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -33,12 +33,21 @@ android { defaultConfig { applicationId "deckers.thibault.aves" - minSdkVersion 24 // Java 8 (stream, lambda, etc.) requires minSdkVersion 24 + // some Java 8 APIs (java.util.stream, etc.) require minSdkVersion 24 + // but Android Studio 4.0 desugaring features allow targeting older SDKs + minSdkVersion 23 targetSdkVersion 29 // same as compileSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } + compileOptions { + // enable support for Java 8 language APIs (stream, optional, etc.) + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildTypes { debug { applicationIdSuffix ".debug" @@ -56,6 +65,9 @@ flutter { } dependencies { + // enable support for Java 8 language APIs (stream, optional, etc.) + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5' + implementation 'com.drewnoakes:metadata-extractor:2.14.0' implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.google.guava:guava:29.0-android' diff --git a/android/app/src/main/java/deckers/thibault/aves/MainActivity.java b/android/app/src/main/java/deckers/thibault/aves/MainActivity.java index 12bd765b2..600701f39 100644 --- a/android/app/src/main/java/deckers/thibault/aves/MainActivity.java +++ b/android/app/src/main/java/deckers/thibault/aves/MainActivity.java @@ -71,11 +71,10 @@ public class MainActivity extends FlutterActivity { Intent data = new Intent(); data.setData(Uri.parse(resultUri)); setResult(RESULT_OK, data); - finish(); } else { setResult(RESULT_CANCELED); - finish(); } + finish(); } }); } diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageDecodeTask.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageDecodeTask.java index 15e9ebd18..00694b3a8 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageDecodeTask.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageDecodeTask.java @@ -1,7 +1,6 @@ package deckers.thibault.aves.channelhandlers; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; @@ -13,6 +12,8 @@ import android.provider.MediaStore; import android.util.Log; import android.util.Size; +import androidx.annotation.RequiresApi; + import com.bumptech.glide.Glide; import com.bumptech.glide.load.Key; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -104,7 +105,7 @@ public class ImageDecodeTask extends AsyncTask handler.post(() -> eventSink.success(entry))); // 350ms handler.post(() -> eventSink.endOfStream()); -// Log.d(LOG_TAG, "fetchAll complete in " + Duration.between(start, Instant.now()).toMillis() + "ms"); } } \ No newline at end of file diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/StorageHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/StorageHandler.java index 1b644ffef..8f283ba98 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/StorageHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/StorageHandler.java @@ -1,10 +1,12 @@ package deckers.thibault.aves.channelhandlers; import android.app.Activity; +import android.os.Build; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; import java.io.File; import java.util.ArrayList; @@ -30,27 +32,12 @@ public class StorageHandler implements MethodChannel.MethodCallHandler { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { switch (call.method) { case "getStorageVolumes": { - List> volumes = new ArrayList<>(); - StorageManager sm = activity.getSystemService(StorageManager.class); - if (sm != null) { - for (String path : Env.getStorageVolumes(activity)) { - try { - File file = new File(path); - StorageVolume volume = sm.getStorageVolume(file); - if (volume != null) { - Map volumeMap = new HashMap<>(); - volumeMap.put("path", path); - volumeMap.put("description", volume.getDescription(activity)); - volumeMap.put("isPrimary", volume.isPrimary()); - volumeMap.put("isRemovable", volume.isRemovable()); - volumeMap.put("isEmulated", volume.isEmulated()); - volumeMap.put("state", volume.getState()); - volumes.add(volumeMap); - } - } catch (IllegalArgumentException e) { - // ignore - } - } + List> volumes = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + volumes = getStorageVolumes(); + } else { + // TODO TLAD find alternative for Android (); } result.success(volumes); break; @@ -66,4 +53,31 @@ public class StorageHandler implements MethodChannel.MethodCallHandler { break; } } + + @RequiresApi(api = Build.VERSION_CODES.N) + private List> getStorageVolumes() { + List> volumes = new ArrayList<>(); + StorageManager sm = activity.getSystemService(StorageManager.class); + if (sm != null) { + for (String path : Env.getStorageVolumes(activity)) { + try { + File file = new File(path); + StorageVolume volume = sm.getStorageVolume(file); + if (volume != null) { + Map volumeMap = new HashMap<>(); + volumeMap.put("path", path); + volumeMap.put("description", volume.getDescription(activity)); + volumeMap.put("isPrimary", volume.isPrimary()); + volumeMap.put("isRemovable", volume.isRemovable()); + volumeMap.put("isEmulated", volume.isEmulated()); + volumeMap.put("state", volume.getState()); + volumes.add(volumeMap); + } + } catch (IllegalArgumentException e) { + // ignore + } + } + } + return volumes; + } } diff --git a/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java b/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java index dbedf36a3..eb6a335e8 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java @@ -224,9 +224,7 @@ public abstract class ImageProvider { values.put(MediaStore.MediaColumns.ORIENTATION, orientationDegrees); int updatedRowCount = contentResolver.update(uri, values, null, null); if (updatedRowCount > 0) { - MediaScannerConnection.scanFile(activity, new String[]{path}, new String[]{mimeType}, (p, u) -> { - callback.onSuccess(newFields); - }); + MediaScannerConnection.scanFile(activity, new String[]{path}, new String[]{mimeType}, (p, u) -> callback.onSuccess(newFields)); } else { Log.w(LOG_TAG, "failed to update fields in Media Store for uri=" + uri); callback.onSuccess(newFields); diff --git a/android/app/src/main/java/deckers/thibault/aves/utils/PermissionManager.java b/android/app/src/main/java/deckers/thibault/aves/utils/PermissionManager.java index 96f84ac89..841eda204 100644 --- a/android/app/src/main/java/deckers/thibault/aves/utils/PermissionManager.java +++ b/android/app/src/main/java/deckers/thibault/aves/utils/PermissionManager.java @@ -1,6 +1,5 @@ package deckers.thibault.aves.utils; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -12,6 +11,7 @@ import android.os.storage.StorageVolume; import android.provider.DocumentsContract; import android.util.Log; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.core.util.Pair; @@ -36,7 +36,7 @@ public class PermissionManager { return uriPermissionOptional.map(UriPermission::getUri).orElse(null); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public static void showSdCardAccessDialog(final Activity activity, final Runnable pendingRunnable) { new AlertDialog.Builder(activity) .setTitle("SD Card Access") @@ -77,19 +77,23 @@ public class PermissionManager { runnable.run(); } - public static boolean hasGrantedPermissionToVolumeRoot(Context context, String path) { boolean canAccess = false; Stream permittedUris = context.getContentResolver().getPersistedUriPermissions().stream().map(UriPermission::getUri); // e.g. content://com.android.externalstorage.documents/tree/12A9-8B42%3A StorageManager sm = context.getSystemService(StorageManager.class); if (sm != null) { - StorageVolume volume = sm.getStorageVolume(new File(path)); - if (volume != null) { - // primary storage doesn't have a UUID - String uuid = volume.isPrimary() ? "primary" : volume.getUuid(); - Uri targetVolumeTreeUri = getVolumeTreeUriFromUuid(uuid); - canAccess = permittedUris.anyMatch(uri -> uri.equals(targetVolumeTreeUri)); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + StorageVolume volume = sm.getStorageVolume(new File(path)); + if (volume != null) { + // primary storage doesn't have a UUID + String uuid = volume.isPrimary() ? "primary" : volume.getUuid(); + Uri targetVolumeTreeUri = getVolumeTreeUriFromUuid(uuid); + canAccess = permittedUris.anyMatch(uri -> uri.equals(targetVolumeTreeUri)); + } + } else { + // TODO TLAD find alternative for Android