metadata: use URI when path is unavailable
This commit is contained in:
parent
b0699df136
commit
8d798c6f08
5 changed files with 131 additions and 17 deletions
|
@ -2,6 +2,7 @@ package deckers.thibault.aves.channelhandlers;
|
|||
|
||||
import android.content.Context;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.net.Uri;
|
||||
import android.text.format.Formatter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -71,9 +72,15 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
return mimeType != null && mimeType.startsWith(Constants.MIME_VIDEO);
|
||||
}
|
||||
|
||||
private InputStream getInputStream(String path, String uri) throws FileNotFoundException {
|
||||
// FileInputStream is faster than input stream from ContentResolver
|
||||
return path != null ? new FileInputStream(path) : context.getContentResolver().openInputStream(Uri.parse(uri));
|
||||
}
|
||||
|
||||
private void getAllMetadata(MethodCall call, MethodChannel.Result result) {
|
||||
String path = call.argument("path");
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
String uri = call.argument("uri");
|
||||
try (InputStream is = getInputStream(path, uri)) {
|
||||
Map<String, Map<String, String>> metadataMap = new HashMap<>();
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
for (Directory dir : metadata.getDirectories()) {
|
||||
|
@ -109,14 +116,15 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
} catch (ImageProcessingException e) {
|
||||
getAllVideoMetadataFallback(call, result);
|
||||
} catch (FileNotFoundException e) {
|
||||
result.error("getAllMetadata-filenotfound", "failed to get metadata for path=" + path, e.getMessage());
|
||||
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 path=" + path, e.getMessage());
|
||||
result.error("getAllMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void getAllVideoMetadataFallback(MethodCall call, MethodChannel.Result result) {
|
||||
String path = call.argument("path");
|
||||
String uri = call.argument("uri");
|
||||
try {
|
||||
Map<String, Map<String, String>> metadataMap = new HashMap<>();
|
||||
Map<String, String> dirMap = new HashMap<>();
|
||||
|
@ -124,7 +132,11 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
metadataMap.put("", dirMap);
|
||||
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
retriever.setDataSource(path);
|
||||
if (path != null) {
|
||||
retriever.setDataSource(path);
|
||||
} else {
|
||||
retriever.setDataSource(context, Uri.parse(uri));
|
||||
}
|
||||
for (Map.Entry<Integer, String> kv : Constants.MEDIA_METADATA_KEYS.entrySet()) {
|
||||
Integer key = kv.getKey();
|
||||
String value = retriever.extractMetadata(key);
|
||||
|
@ -144,14 +156,15 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
|
||||
result.success(metadataMap);
|
||||
} catch (Exception e) {
|
||||
result.error("getAllVideoMetadataFallback-exception", "failed to get metadata for path=" + path, e.getMessage());
|
||||
result.error("getAllVideoMetadataFallback-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void getCatalogMetadata(MethodCall call, MethodChannel.Result result) {
|
||||
String path = call.argument("path");
|
||||
String mimeType = call.argument("mimeType");
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
String path = call.argument("path");
|
||||
String uri = call.argument("uri");
|
||||
try (InputStream is = getInputStream(path, uri)) {
|
||||
Map<String, Object> metadataMap = new HashMap<>();
|
||||
if (!Constants.MIME_MP2T.equalsIgnoreCase(mimeType)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
|
@ -197,7 +210,11 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
if (isVideo(call.argument("mimeType"))) {
|
||||
try {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
retriever.setDataSource(path);
|
||||
if (path != null) {
|
||||
retriever.setDataSource(path);
|
||||
} else {
|
||||
retriever.setDataSource(context, Uri.parse(uri));
|
||||
}
|
||||
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
|
||||
String rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
|
||||
String locationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
|
||||
|
@ -233,16 +250,16 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.error("getCatalogMetadata-exception", "failed to get video metadata for path=" + path, e.getMessage());
|
||||
result.error("getCatalogMetadata-exception", "failed to get video metadata for uri=" + uri + ", path=" + path, e.getMessage());
|
||||
}
|
||||
}
|
||||
result.success(metadataMap);
|
||||
} catch (ImageProcessingException e) {
|
||||
result.error("getCatalogMetadata-imageprocessing", "failed to get metadata for path=" + path, e.getMessage());
|
||||
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 path=" + path, e.getMessage());
|
||||
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 path=" + path, e.getMessage());
|
||||
result.error("getCatalogMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,7 +272,8 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
}
|
||||
|
||||
String path = call.argument("path");
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
String uri = call.argument("uri");
|
||||
try (InputStream is = getInputStream(path, uri)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
|
||||
if (directory != null) {
|
||||
|
@ -274,11 +292,11 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
}
|
||||
result.success(metadataMap);
|
||||
} catch (ImageProcessingException e) {
|
||||
result.error("getOverlayMetadata-imageprocessing", "failed to get metadata for path=" + path, e.getMessage());
|
||||
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 path=" + path, e.getMessage());
|
||||
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 path=" + path, e.getMessage());
|
||||
result.error("getOverlayMetadata-exception", "failed to get metadata for uri=" + uri + ", path=" + path, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ public class ImageProviderFactory {
|
|||
return new MediaStoreImageProvider();
|
||||
// case Constants.DOWNLOADS_AUTHORITY:
|
||||
// return new DownloadImageProvider();
|
||||
default:
|
||||
return new UnknownContentImageProvider();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package deckers.thibault.aves.model.provider;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.drew.imaging.ImageMetadataReader;
|
||||
import com.drew.imaging.ImageProcessingException;
|
||||
import com.drew.metadata.Metadata;
|
||||
import com.drew.metadata.MetadataException;
|
||||
import com.drew.metadata.exif.ExifIFD0Directory;
|
||||
import com.drew.metadata.jpeg.JpegDirectory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static deckers.thibault.aves.utils.MetadataHelper.getOrientationDegreesForExifCode;
|
||||
|
||||
class UnknownContentImageProvider extends ImageProvider {
|
||||
@Override
|
||||
public void fetchSingle(final Activity activity, final Uri uri, final String mimeType, final ImageOpCallback callback) {
|
||||
int width = 0, height = 0, orientationDegrees = 0;
|
||||
Long sourceDateTakenMillis = null;
|
||||
|
||||
// check metadata first
|
||||
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
JpegDirectory jpegDir = metadata.getFirstDirectoryOfType(JpegDirectory.class);
|
||||
if (jpegDir != null) {
|
||||
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
|
||||
width = jpegDir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
|
||||
}
|
||||
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) {
|
||||
height = jpegDir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
|
||||
}
|
||||
}
|
||||
ExifIFD0Directory exifDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
|
||||
if (exifDir != null) {
|
||||
if (exifDir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
|
||||
orientationDegrees = getOrientationDegreesForExifCode(exifDir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
|
||||
}
|
||||
if (exifDir.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
|
||||
sourceDateTakenMillis = exifDir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime();
|
||||
}
|
||||
}
|
||||
} catch (IOException | ImageProcessingException | MetadataException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// fallback to decoding the image bounds
|
||||
if (width <= 0 || height <= 0) {
|
||||
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
width = options.outWidth;
|
||||
height = options.outHeight;
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
callback.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> entry = new HashMap<>();
|
||||
entry.put("uri", uri.toString());
|
||||
entry.put("path", null);
|
||||
entry.put("contentId", null);
|
||||
entry.put("mimeType", mimeType);
|
||||
entry.put("width", width);
|
||||
entry.put("height", height);
|
||||
entry.put("orientationDegrees", orientationDegrees);
|
||||
entry.put("sizeBytes", null);
|
||||
entry.put("title", null);
|
||||
entry.put("dateModifiedSecs", null);
|
||||
entry.put("sourceDateTakenMillis", sourceDateTakenMillis);
|
||||
entry.put("bucketDisplayName", null);
|
||||
entry.put("durationMillis", null);
|
||||
callback.onSuccess(entry);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getAllMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'path': entry.path,
|
||||
'uri': entry.uri,
|
||||
});
|
||||
return result as Map;
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -30,6 +31,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getCatalogMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'path': entry.path,
|
||||
'uri': entry.uri,
|
||||
}) as Map;
|
||||
result['contentId'] = entry.contentId;
|
||||
return CatalogMetadata.fromMap(result);
|
||||
|
@ -45,6 +47,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getOverlayMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'path': entry.path,
|
||||
'uri': entry.uri,
|
||||
}) as Map;
|
||||
return OverlayMetadata.fromMap(result);
|
||||
} on PlatformException catch (e) {
|
||||
|
|
|
@ -144,6 +144,10 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
|
|||
builder: (c, orientation, child) {
|
||||
final twoColumns = orientation == Orientation.landscape && maxWidth / 2 > _subRowMinWidth;
|
||||
final subRowWidth = twoColumns ? min(_subRowMinWidth, maxWidth / 2) : maxWidth;
|
||||
final positionTitle = [
|
||||
if (position != null) position,
|
||||
if (entry.title != null) entry.title,
|
||||
].join(' – ');
|
||||
final hasShootingDetails = details != null && !details.isEmpty;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -151,7 +155,7 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
|
|||
children: [
|
||||
SizedBox(
|
||||
width: maxWidth,
|
||||
child: Text('${position != null ? '$position – ' : ''}${entry.title ?? '?'}', strutStyle: Constants.overflowStrutStyle),
|
||||
child: Text(positionTitle, strutStyle: Constants.overflowStrutStyle),
|
||||
),
|
||||
if (entry.hasGps)
|
||||
Container(
|
||||
|
|
Loading…
Reference in a new issue