From a6eeba7744ca57ae17c7086640329f20278fb0a3 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 11 Jun 2020 14:58:27 +0900 Subject: [PATCH] read files with uri only, and fix to handle unknown MediaMetadataRetriever issues --- .../channelhandlers/AppAdapterHandler.java | 8 +++- .../aves/channelhandlers/MetadataHandler.java | 43 +++++++------------ .../aves/decoder/VideoThumbnailFetcher.java | 2 +- .../thibault/aves/model/ImageEntry.java | 14 +++--- .../thibault/aves/utils/StorageUtils.java | 25 +++-------- lib/services/metadata_service.dart | 3 -- 6 files changed, 36 insertions(+), 59 deletions(-) diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/AppAdapterHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/AppAdapterHandler.java index 0bf3eb26b..c5453ecdb 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/AppAdapterHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/AppAdapterHandler.java @@ -8,6 +8,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.net.Uri; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -29,12 +30,15 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import deckers.thibault.aves.utils.Utils; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import static com.bumptech.glide.request.RequestOptions.centerCropTransform; public class AppAdapterHandler implements MethodChannel.MethodCallHandler { + private static final String LOG_TAG = Utils.createLogTag(AppAdapterHandler.class); + public static final String CHANNEL = "deckers.thibault/aves/app"; private Context context; @@ -162,11 +166,11 @@ public class AppAdapterHandler implements MethodChannel.MethodCallHandler { data = stream.toByteArray(); } } catch (Exception e) { - e.printStackTrace(); + Log.w(LOG_TAG, "failed to decode app icon for packageName=" + packageName, e); } Glide.with(context).clear(target); } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Log.w(LOG_TAG, "failed to get app info for packageName=" + packageName, e); return; } if (data != null) { diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java index 4e4588db3..40ae50fad 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java @@ -8,6 +8,7 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.text.format.Formatter; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -30,7 +31,6 @@ import com.drew.metadata.gif.GifAnimationDirectory; import com.drew.metadata.webp.WebpDirectory; import com.drew.metadata.xmp.XmpDirectory; -import java.io.FileNotFoundException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; @@ -42,10 +42,13 @@ import deckers.thibault.aves.utils.Constants; import deckers.thibault.aves.utils.MetadataHelper; import deckers.thibault.aves.utils.MimeTypes; import deckers.thibault.aves.utils.StorageUtils; +import deckers.thibault.aves.utils.Utils; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; public class MetadataHandler implements MethodChannel.MethodCallHandler { + private static final String LOG_TAG = Utils.createLogTag(MetadataHandler.class); + public static final String CHANNEL = "deckers.thibault/aves/metadata"; // catalog metadata @@ -105,12 +108,11 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } private void getAllMetadata(MethodCall call, MethodChannel.Result result) { - String path = call.argument("path"); String uri = call.argument("uri"); Map> metadataMap = new HashMap<>(); - try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri), path)) { + try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri))) { Metadata metadata = ImageMetadataReader.readMetadata(is); for (Directory dir : metadata.getDirectories()) { if (dir.getTagCount() > 0) { @@ -136,7 +138,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } } catch (XMPException e) { - e.printStackTrace(); + Log.w(LOG_TAG, "failed to read XMP directory for uri=" + uri, e); } } } @@ -144,15 +146,12 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { result.success(metadataMap); } catch (ImageProcessingException e) { getAllVideoMetadataFallback(call, result); - } catch (FileNotFoundException e) { - result.error("getAllMetadata-filenotfound", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); } catch (Exception e) { - result.error("getAllMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); + result.error("getAllMetadata-exception", "failed to get metadata for uri=" + uri, e.getMessage()); } } private void getAllVideoMetadataFallback(MethodCall call, MethodChannel.Result result) { - String path = call.argument("path"); String uri = call.argument("uri"); Map> metadataMap = new HashMap<>(); @@ -160,7 +159,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { // unnamed fallback directory metadataMap.put("", dirMap); - MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri), path); + MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri)); try { for (Map.Entry kv : Constants.MEDIA_METADATA_KEYS.entrySet()) { Integer key = kv.getKey(); @@ -179,7 +178,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } result.success(metadataMap); } catch (Exception e) { - result.error("getAllVideoMetadataFallback-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); + result.error("getAllVideoMetadataFallback-exception", "failed to get metadata for uri=" + uri, e.getMessage()); } finally { // cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs retriever.release(); @@ -188,12 +187,11 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { private void getCatalogMetadata(MethodCall call, MethodChannel.Result result) { String mimeType = call.argument("mimeType"); - String path = call.argument("path"); String uri = call.argument("uri"); Map metadataMap = new HashMap<>(); - try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri), path)) { + try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri))) { if (!MimeTypes.MP2T.equals(mimeType)) { Metadata metadata = ImageMetadataReader.readMetadata(is); @@ -233,7 +231,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { putLocalizedTextFromXmp(metadataMap, KEY_XMP_TITLE_DESCRIPTION, xmpMeta, XMP_DESCRIPTION_PROP_NAME); } } catch (XMPException e) { - e.printStackTrace(); + Log.w(LOG_TAG, "failed to read XMP directory for uri=" + uri, e); } } @@ -251,7 +249,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } if (isVideo(mimeType)) { - MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri), path); + MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri)); try { String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE); String rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); @@ -287,25 +285,20 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } } catch (Exception e) { - result.error("getCatalogMetadata-exception", "failed to get video metadata for uri=" + uri + ", path=" + path, e.getMessage()); + result.error("getCatalogMetadata-exception", "failed to get video metadata for uri=" + uri, e.getMessage()); } finally { // cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs retriever.release(); } } result.success(metadataMap); - } catch (ImageProcessingException e) { - result.error("getCatalogMetadata-imageprocessing", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); - } catch (FileNotFoundException e) { - result.error("getCatalogMetadata-filenotfound", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); } catch (Exception e) { - result.error("getCatalogMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); + result.error("getCatalogMetadata-exception", "failed to get metadata for uri=" + uri, e.getMessage()); } } private void getOverlayMetadata(MethodCall call, MethodChannel.Result result) { String mimeType = call.argument("mimeType"); - String path = call.argument("path"); String uri = call.argument("uri"); Map metadataMap = new HashMap<>(); @@ -315,7 +308,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { return; } - try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri), path)) { + try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri))) { Metadata metadata = ImageMetadataReader.readMetadata(is); ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); if (directory != null) { @@ -327,12 +320,8 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } result.success(metadataMap); - } catch (ImageProcessingException e) { - result.error("getOverlayMetadata-imageprocessing", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); - } catch (FileNotFoundException e) { - result.error("getOverlayMetadata-filenotfound", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); } catch (Exception e) { - result.error("getOverlayMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage()); + result.error("getOverlayMetadata-exception", "failed to get metadata for uri=" + uri, e.getMessage()); } } diff --git a/android/app/src/main/java/deckers/thibault/aves/decoder/VideoThumbnailFetcher.java b/android/app/src/main/java/deckers/thibault/aves/decoder/VideoThumbnailFetcher.java index 7c8ec33be..111d6468e 100644 --- a/android/app/src/main/java/deckers/thibault/aves/decoder/VideoThumbnailFetcher.java +++ b/android/app/src/main/java/deckers/thibault/aves/decoder/VideoThumbnailFetcher.java @@ -24,7 +24,7 @@ class VideoThumbnailFetcher implements DataFetcher { @Override public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { - MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(model.getContext(), model.getUri(), null); + MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(model.getContext(), model.getUri()); try { byte[] picture = retriever.getEmbeddedPicture(); if (picture != null) { diff --git a/android/app/src/main/java/deckers/thibault/aves/model/ImageEntry.java b/android/app/src/main/java/deckers/thibault/aves/model/ImageEntry.java index b1b45f541..742782aaf 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/ImageEntry.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/ImageEntry.java @@ -119,7 +119,7 @@ public class ImageEntry { // metadata retrieval - // expects entry with: uri/path, mimeType + // expects entry with: uri, mimeType // finds: width, height, orientation/rotation, date, title, duration public ImageEntry fillPreCatalogMetadata(Context context) { fillByMediaMetadataRetriever(context); @@ -130,10 +130,10 @@ public class ImageEntry { return this; } - // expects entry with: uri/path, mimeType + // expects entry with: uri, mimeType // finds: width, height, orientation/rotation, date, title, duration private void fillByMediaMetadataRetriever(Context context) { - MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, uri, path); + MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, uri); try { String width = null, height = null, rotation = null, durationMillis = null; if (isImage()) { @@ -180,12 +180,12 @@ public class ImageEntry { } } - // expects entry with: uri/path, mimeType + // expects entry with: uri, mimeType // finds: width, height, orientation, date private void fillByMetadataExtractor(Context context) { if (MimeTypes.SVG.equals(mimeType)) return; - try (InputStream is = StorageUtils.openInputStream(context, uri, path)) { + try (InputStream is = StorageUtils.openInputStream(context, uri)) { Metadata metadata = ImageMetadataReader.readMetadata(is); if (MimeTypes.JPEG.equals(mimeType)) { @@ -242,12 +242,12 @@ public class ImageEntry { } } - // expects entry with: uri/path + // expects entry with: uri // finds: width, height private void fillByBitmapDecode(Context context) { if (MimeTypes.SVG.equals(mimeType)) return; - try (InputStream is = StorageUtils.openInputStream(context, uri, path)) { + try (InputStream is = StorageUtils.openInputStream(context, uri)) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); diff --git a/android/app/src/main/java/deckers/thibault/aves/utils/StorageUtils.java b/android/app/src/main/java/deckers/thibault/aves/utils/StorageUtils.java index b6dcb4096..6d2de469b 100644 --- a/android/app/src/main/java/deckers/thibault/aves/utils/StorageUtils.java +++ b/android/app/src/main/java/deckers/thibault/aves/utils/StorageUtils.java @@ -42,41 +42,28 @@ public class StorageUtils { return uri != null && ContentResolver.SCHEME_CONTENT.equalsIgnoreCase(uri.getScheme()) && MediaStore.AUTHORITY.equalsIgnoreCase(uri.getHost()); } - public static InputStream openInputStream(Context context, Uri uri, String path) throws FileNotFoundException { + public static InputStream openInputStream(Context context, Uri uri) throws FileNotFoundException { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // we get a permission denial if we require original from a provider other than the media store if (isMediaStoreContentUri(uri)) { uri = MediaStore.setRequireOriginal(uri); } - return context.getContentResolver().openInputStream(uri); } - - // on Android = Build.VERSION_CODES.Q) { // we get a permission denial if we require original from a provider other than the media store if (isMediaStoreContentUri(uri)) { uri = MediaStore.setRequireOriginal(uri); } - retriever.setDataSource(context, uri); - return retriever; } - - // on Android