android: desugaring to allow targeting older API
This commit is contained in:
parent
41cf11bc55
commit
cdf435420f
9 changed files with 69 additions and 47 deletions
|
@ -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'
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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<ImageDecodeTask.Params, Void, Ima
|
|||
return new Result(p, data);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
private Bitmap getThumbnailBytesByResolver(Params params) throws IOException {
|
||||
ImageEntry entry = params.entry;
|
||||
int width = params.width;
|
||||
|
|
|
@ -5,14 +5,11 @@ import android.os.Handler;
|
|||
import android.os.Looper;
|
||||
|
||||
import deckers.thibault.aves.model.provider.MediaStoreImageProvider;
|
||||
import deckers.thibault.aves.utils.Utils;
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
|
||||
public class MediaStoreStreamHandler implements EventChannel.StreamHandler {
|
||||
public static final String CHANNEL = "deckers.thibault/aves/mediastore";
|
||||
|
||||
private static final String LOG_TAG = Utils.createLogTag(MediaStoreStreamHandler.class);
|
||||
|
||||
private EventChannel.EventSink eventSink;
|
||||
|
||||
@Override
|
||||
|
@ -26,11 +23,8 @@ public class MediaStoreStreamHandler implements EventChannel.StreamHandler {
|
|||
}
|
||||
|
||||
void fetchAll(Activity activity) {
|
||||
// Log.d(LOG_TAG, "fetchAll start");
|
||||
// Instant start = Instant.now();
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
new MediaStoreImageProvider().fetchAll(activity, (entry) -> handler.post(() -> eventSink.success(entry))); // 350ms
|
||||
handler.post(() -> eventSink.endOfStream());
|
||||
// Log.d(LOG_TAG, "fetchAll complete in " + Duration.between(start, Instant.now()).toMillis() + "ms");
|
||||
}
|
||||
}
|
|
@ -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<Map<String, Object>> 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<String, Object> 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<Map<String, Object>> volumes = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
volumes = getStorageVolumes();
|
||||
} else {
|
||||
// TODO TLAD find alternative for Android <N
|
||||
volumes = new ArrayList<>();
|
||||
}
|
||||
result.success(volumes);
|
||||
break;
|
||||
|
@ -66,4 +53,31 @@ public class StorageHandler implements MethodChannel.MethodCallHandler {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
private List<Map<String, Object>> getStorageVolumes() {
|
||||
List<Map<String, Object>> 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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Uri> 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 <N
|
||||
canAccess = true;
|
||||
}
|
||||
}
|
||||
return canAccess;
|
||||
|
|
|
@ -5,7 +5,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0-rc01'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Tue Apr 21 13:20:37 KST 2020
|
||||
#Mon May 25 13:52:11 KST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
|
|
Loading…
Reference in a new issue