info: show picture embedded in videos
This commit is contained in:
parent
097a051b37
commit
652a5383ea
6 changed files with 54 additions and 10 deletions
|
@ -190,6 +190,9 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
||||||
case "getContentResolverMetadata":
|
case "getContentResolverMetadata":
|
||||||
new Thread(() -> getContentResolverMetadata(call, new MethodResultWrapper(result))).start();
|
new Thread(() -> getContentResolverMetadata(call, new MethodResultWrapper(result))).start();
|
||||||
break;
|
break;
|
||||||
|
case "getEmbeddedPictures":
|
||||||
|
new Thread(() -> getEmbeddedPictures(call, new MethodResultWrapper(result))).start();
|
||||||
|
break;
|
||||||
case "getExifThumbnails":
|
case "getExifThumbnails":
|
||||||
new Thread(() -> getExifThumbnails(call, new MethodResultWrapper(result))).start();
|
new Thread(() -> getExifThumbnails(call, new MethodResultWrapper(result))).start();
|
||||||
break;
|
break;
|
||||||
|
@ -415,7 +418,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
||||||
metadataMap.put(KEY_LATITUDE, latitude);
|
metadataMap.put(KEY_LATITUDE, latitude);
|
||||||
metadataMap.put(KEY_LONGITUDE, longitude);
|
metadataMap.put(KEY_LONGITUDE, longitude);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,6 +533,26 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getEmbeddedPictures(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
|
Uri uri = Uri.parse(call.argument("uri"));
|
||||||
|
List<byte[]> pictures = new ArrayList<>();
|
||||||
|
MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, uri);
|
||||||
|
if (retriever != null) {
|
||||||
|
try {
|
||||||
|
byte[] picture = retriever.getEmbeddedPicture();
|
||||||
|
if (picture != null) {
|
||||||
|
pictures.add(picture);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.error("getVideoEmbeddedPictures-failure", "failed to get embedded picture for uri=" + uri, e);
|
||||||
|
} finally {
|
||||||
|
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
|
||||||
|
retriever.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.success(pictures);
|
||||||
|
}
|
||||||
|
|
||||||
private void getExifThumbnails(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
private void getExifThumbnails(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
Uri uri = Uri.parse(call.argument("uri"));
|
Uri uri = Uri.parse(call.argument("uri"));
|
||||||
List<byte[]> thumbnails = new ArrayList<>();
|
List<byte[]> thumbnails = new ArrayList<>();
|
||||||
|
|
|
@ -40,8 +40,8 @@ class VideoThumbnailFetcher implements DataFetcher<InputStream> {
|
||||||
}
|
}
|
||||||
callback.onDataReady(new ByteArrayInputStream(bos.toByteArray()));
|
callback.onDataReady(new ByteArrayInputStream(bos.toByteArray()));
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception e) {
|
||||||
callback.onLoadFailed(ex);
|
callback.onLoadFailed(e);
|
||||||
} finally {
|
} finally {
|
||||||
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
|
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
|
||||||
retriever.release();
|
retriever.release();
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class MetadataHelper {
|
||||||
DateFormat parser = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US);
|
DateFormat parser = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US);
|
||||||
parser.setTimeZone((timeZone != null) ? timeZone : TimeZone.getTimeZone("GMT"));
|
parser.setTimeZone((timeZone != null) ? timeZone : TimeZone.getTimeZone("GMT"));
|
||||||
date = parser.parse(dateString);
|
date = parser.parse(dateString);
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,18 @@ class MetadataService {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<Uint8List>> getEmbeddedPictures(String uri) async {
|
||||||
|
try {
|
||||||
|
final result = await platform.invokeMethod('getEmbeddedPictures', <String, dynamic>{
|
||||||
|
'uri': uri,
|
||||||
|
});
|
||||||
|
return (result as List).cast<Uint8List>();
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
debugPrint('getEmbeddedPictures failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
static Future<List<Uint8List>> getExifThumbnails(String uri) async {
|
static Future<List<Uint8List>> getExifThumbnails(String uri) async {
|
||||||
try {
|
try {
|
||||||
final result = await platform.invokeMethod('getExifThumbnails', <String, dynamic>{
|
final result = await platform.invokeMethod('getExifThumbnails', <String, dynamic>{
|
||||||
|
|
|
@ -36,8 +36,9 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
static const int maxValueLength = 140;
|
static const int maxValueLength = 140;
|
||||||
|
|
||||||
// directory names from metadata-extractor
|
// directory names from metadata-extractor
|
||||||
static const exifThumbnailDirectory = 'Exif Thumbnail';
|
static const exifThumbnailDirectory = 'Exif Thumbnail'; // from metadata-extractor
|
||||||
static const xmpDirectory = 'XMP';
|
static const xmpDirectory = 'XMP'; // from metadata-extractor
|
||||||
|
static const videoDirectory = 'Video'; // additional generic video directory
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -106,6 +107,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
SizedBox(height: 4),
|
SizedBox(height: 4),
|
||||||
if (dir.name == exifThumbnailDirectory) MetadataThumbnails(source: MetadataThumbnailSource.exif, entry: entry),
|
if (dir.name == exifThumbnailDirectory) MetadataThumbnails(source: MetadataThumbnailSource.exif, entry: entry),
|
||||||
if (dir.name == xmpDirectory) MetadataThumbnails(source: MetadataThumbnailSource.xmp, entry: entry),
|
if (dir.name == xmpDirectory) MetadataThumbnails(source: MetadataThumbnailSource.xmp, entry: entry),
|
||||||
|
if (dir.name == videoDirectory) MetadataThumbnails(source: MetadataThumbnailSource.embedded, entry: entry),
|
||||||
Container(
|
Container(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
padding: EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
padding: EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/services/metadata_service.dart';
|
import 'package:aves/services/metadata_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
enum MetadataThumbnailSource { exif, xmp }
|
enum MetadataThumbnailSource { embedded, exif, xmp }
|
||||||
|
|
||||||
class MetadataThumbnails extends StatefulWidget {
|
class MetadataThumbnails extends StatefulWidget {
|
||||||
final MetadataThumbnailSource source;
|
final MetadataThumbnailSource source;
|
||||||
|
@ -24,15 +24,22 @@ class MetadataThumbnails extends StatefulWidget {
|
||||||
class _MetadataThumbnailsState extends State<MetadataThumbnails> {
|
class _MetadataThumbnailsState extends State<MetadataThumbnails> {
|
||||||
Future<List<Uint8List>> _loader;
|
Future<List<Uint8List>> _loader;
|
||||||
|
|
||||||
|
ImageEntry get entry => widget.entry;
|
||||||
|
|
||||||
|
String get uri => entry.uri;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
switch (widget.source) {
|
switch (widget.source) {
|
||||||
|
case MetadataThumbnailSource.embedded:
|
||||||
|
_loader = MetadataService.getEmbeddedPictures(uri);
|
||||||
|
break;
|
||||||
case MetadataThumbnailSource.exif:
|
case MetadataThumbnailSource.exif:
|
||||||
_loader = MetadataService.getExifThumbnails(widget.entry.uri);
|
_loader = MetadataService.getExifThumbnails(uri);
|
||||||
break;
|
break;
|
||||||
case MetadataThumbnailSource.xmp:
|
case MetadataThumbnailSource.xmp:
|
||||||
_loader = MetadataService.getXmpThumbnails(widget.entry.uri);
|
_loader = MetadataService.getXmpThumbnails(uri);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +50,7 @@ class _MetadataThumbnailsState extends State<MetadataThumbnails> {
|
||||||
future: _loader,
|
future: _loader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasError && snapshot.connectionState == ConnectionState.done && snapshot.data.isNotEmpty) {
|
if (!snapshot.hasError && snapshot.connectionState == ConnectionState.done && snapshot.data.isNotEmpty) {
|
||||||
final turns = (widget.entry.orientationDegrees / 90).round();
|
final turns = (entry.orientationDegrees / 90).round();
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
return Container(
|
return Container(
|
||||||
alignment: AlignmentDirectional.topStart,
|
alignment: AlignmentDirectional.topStart,
|
||||||
|
|
Loading…
Reference in a new issue