read files with uri only, and fix to handle unknown MediaMetadataRetriever issues

This commit is contained in:
Thibault Deckers 2020-06-11 14:58:27 +09:00
parent cbacb923e7
commit a6eeba7744
6 changed files with 36 additions and 59 deletions

View file

@ -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) {

View file

@ -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<String, Map<String, String>> 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<String, Map<String, String>> 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<Integer, String> 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<String, Object> 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<String, Object> 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());
}
}

View file

@ -24,7 +24,7 @@ class VideoThumbnailFetcher implements DataFetcher<InputStream> {
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> 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) {

View file

@ -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);

View file

@ -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 <Q, we directly work with file paths if possible,
// as `FileInputStream` is faster than input stream from `ContentResolver`
return path != null ? new FileInputStream(path) : context.getContentResolver().openInputStream(uri);
return context.getContentResolver().openInputStream(uri);
}
public static MediaMetadataRetriever openMetadataRetriever(Context context, Uri uri, String path) {
public static MediaMetadataRetriever openMetadataRetriever(Context context, Uri uri) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
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);
}
retriever.setDataSource(context, uri);
return retriever;
}
// on Android <Q, we directly work with file paths if possible
if (path != null) {
retriever.setDataSource(path);
} else {
retriever.setDataSource(context, uri);
}
} catch (IllegalArgumentException e) {
Log.w(LOG_TAG, "failed to open MediaMetadataRetriever for uri=" + uri + ", path=" + path);
retriever.setDataSource(context, uri);
} catch (Exception e) {
Log.w(LOG_TAG, "failed to open MediaMetadataRetriever for uri=" + uri, e);
}
return retriever;
}

View file

@ -14,7 +14,6 @@ class MetadataService {
try {
final result = await platform.invokeMethod('getAllMetadata', <String, dynamic>{
'mimeType': entry.mimeType,
'path': entry.path,
'uri': entry.uri,
});
return result as Map;
@ -40,7 +39,6 @@ class MetadataService {
// 'xmpTitleDescription': XMP title or XMP description (string)
final result = await platform.invokeMethod('getCatalogMetadata', <String, dynamic>{
'mimeType': entry.mimeType,
'path': entry.path,
'uri': entry.uri,
}) as Map;
result['contentId'] = entry.contentId;
@ -61,7 +59,6 @@ class MetadataService {
// return map with string descriptions for: 'aperture' 'exposureTime' 'focalLength' 'iso'
final result = await platform.invokeMethod('getOverlayMetadata', <String, dynamic>{
'mimeType': entry.mimeType,
'path': entry.path,
'uri': entry.uri,
}) as Map;
return OverlayMetadata.fromMap(result);