import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/viewer/info/common.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart'; import 'package:aves/widgets/viewer/info/notifications.dart'; import 'package:collection/collection.dart'; import 'package:tuple/tuple.dart'; abstract class XmpGoogleNamespace extends XmpNamespace { XmpGoogleNamespace(String ns, Map rawProps) : super(ns, rawProps); List> get dataProps; @override Map linkifyValues(List props) { return Map.fromEntries(dataProps .map((t) { final dataPropPath = t.item1; final mimePropPath = t.item2; final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath); final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath); return (dataProp != null && mimeProp != null) ? MapEntry( dataProp.displayKey, InfoLinkHandler( linkText: (context) => context.l10n.viewerInfoOpenLinkText, onTap: (context) => OpenEmbeddedDataNotification.xmp( propPath: dataProp.path, mimeType: mimeProp.value, ).dispatch(context), )) : null; }) .where((kv) => kv != null) .cast>()); } } class XmpGAudioNamespace extends XmpGoogleNamespace { static const ns = 'GAudio'; XmpGAudioNamespace(Map rawProps) : super(ns, rawProps); @override List> get dataProps => const [Tuple2('$ns:Data', '$ns:Mime')]; @override String get displayTitle => 'Google Audio'; } class XmpGDepthNamespace extends XmpGoogleNamespace { static const ns = 'GDepth'; XmpGDepthNamespace(Map rawProps) : super(ns, rawProps); @override List> get dataProps => const [ Tuple2('$ns:Data', '$ns:Mime'), Tuple2('$ns:Confidence', '$ns:ConfidenceMime'), ]; @override String get displayTitle => 'Google Depth'; } class XmpGImageNamespace extends XmpGoogleNamespace { static const ns = 'GImage'; XmpGImageNamespace(Map rawProps) : super(ns, rawProps); @override List> get dataProps => const [Tuple2('$ns:Data', '$ns:Mime')]; @override String get displayTitle => 'Google Image'; } class XmpGCameraNamespace extends XmpNamespace { static const ns = 'GCamera'; static const videoOffsetKey = 'GCamera:MicroVideoOffset'; static const videoDataKey = 'Data'; late bool _isMotionPhoto; XmpGCameraNamespace(Map rawProps) : super(ns, rawProps) { _isMotionPhoto = rawProps.keys.any((key) => key == videoOffsetKey); } @override Map get buildProps { return _isMotionPhoto ? Map.fromEntries({ const MapEntry(videoDataKey, '[skipped]'), ...rawProps.entries, }) : rawProps; } @override Map linkifyValues(List props) { return { videoDataKey: InfoLinkHandler( linkText: (context) => context.l10n.viewerInfoOpenLinkText, onTap: (context) => OpenEmbeddedDataNotification.motionPhotoVideo().dispatch(context), ), }; } }