info: split directories by parent if necessary, hide useless Exif dir, fixed XMP tag order

This commit is contained in:
Thibault Deckers 2020-11-19 19:29:40 +09:00
parent 258d06198d
commit 37d575a1b3
3 changed files with 47 additions and 23 deletions

View file

@ -103,7 +103,10 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
for (dir in metadata.directories.filter { it.tagCount > 0 && it !is FileTypeDirectory }) {
// directory name
val dirName = dir.name ?: ""
var dirName = dir.name
// optional parent to distinguish child directories of the same type
dir.parent?.name?.let { dirName = "$it/$dirName" }
val dirMap = metadataMap.getOrDefault(dirName, HashMap())
metadataMap[dirName] = dirMap

View file

@ -217,8 +217,11 @@ object ExifInterfaceHelper {
// so that we can rely on metadata-extractor descriptions
val dirs = DirType.values().map { Pair(it, it.createDirectory()) }.toMap()
// exclude Exif directory when it only includes image size
val isUselessExif: (Map<String, String>) -> Boolean = { it.size == 2 && it.containsKey("Image Height") && it.containsKey("Image Width") }
return HashMap<String, Map<String, String>>().apply {
put("Exif", describeDir(exif, dirs, baseTags))
put("Exif", describeDir(exif, dirs, baseTags).takeUnless(isUselessExif) ?: hashMapOf())
put("Exif Thumbnail", describeDir(exif, dirs, thumbnailTags))
put(Metadata.DIR_GPS, describeDir(exif, dirs, gpsTags))
put(Metadata.DIR_XMP, describeDir(exif, dirs, xmpTags))

View file

@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:aves/model/image_entry.dart';
import 'package:aves/services/metadata_service.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/utils/durations.dart';
import 'package:aves/widgets/common/aves_expansion_tile.dart';
@ -28,7 +29,7 @@ class MetadataSectionSliver extends StatefulWidget {
}
class _MetadataSectionSliverState extends State<MetadataSectionSliver> with AutomaticKeepAliveClientMixin {
List<_MetadataDirectory> _metadata = [];
Map<String, _MetadataDirectory> _metadata = {};
final ValueNotifier<String> _loadedMetadataUri = ValueNotifier(null);
final ValueNotifier<String> _expandedDirectoryNotifier = ValueNotifier(null);
@ -41,6 +42,10 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
static const xmpDirectory = 'XMP'; // from metadata-extractor
static const mediaDirectory = 'Media'; // additional media (video/audio/images) directory
// directory names may contain the name of their parent directory
// if so, they are separated by this character
static const parentChildSeparator = '/';
@override
void initState() {
super.initState();
@ -87,8 +92,6 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
if (_metadata.isEmpty) {
content = SizedBox.shrink();
} else {
final directoriesWithoutTitle = _metadata.where((dir) => dir.name.isEmpty).toList();
final directoriesWithTitle = _metadata.where((dir) => dir.name.isNotEmpty).toList();
content = Column(
children: AnimationConfiguration.toStaggeredList(
duration: Durations.staggeredAnimation,
@ -101,8 +104,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
),
children: [
SectionRow(AIcons.info),
...directoriesWithoutTitle.map(_buildDirTileWithoutTitle),
...directoriesWithTitle.map(_buildDirTileWithTitle),
..._metadata.entries.map((kv) => _buildDirTile(kv.key, kv.value)),
],
),
);
@ -118,11 +120,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
);
}
Widget _buildDirTileWithoutTitle(_MetadataDirectory dir) {
return InfoRowGroup(dir.tags, maxValueLength: Constants.infoGroupMaxValueLength);
}
Widget _buildDirTileWithTitle(_MetadataDirectory dir) {
Widget _buildDirTile(String title, _MetadataDirectory dir) {
if (dir.name == xmpDirectory) {
return _buildXmpDirTile(dir);
}
@ -151,7 +149,8 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
}
return AvesExpansionTile(
title: dir.name,
title: title,
color: stringToColor(dir.name),
expandedNotifier: _expandedDirectoryNotifier,
children: [
if (prefixChildren.isNotEmpty) Wrap(children: prefixChildren),
@ -173,7 +172,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
if (i == -1) return '';
return fullKey.substring(0, i);
}),
compareAsciiLowerCase,
compareAsciiUpperCase,
);
return AvesExpansionTile(
title: dir.name,
@ -188,10 +187,11 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
final ns = kv.key;
final hasNamespace = ns.isNotEmpty;
final i = hasNamespace ? ns.length + 1 : 0;
final tags = Map.fromEntries(kv.value.map((kv) => MapEntry(kv.key.substring(i), kv.value)));
final entries = kv.value.map((kv) => MapEntry(kv.key.substring(i), kv.value)).toList();
entries.sort((a, b) => compareAsciiUpperCaseNatural(a.key, b.key));
return [
if (hasNamespace) HighlightTitle(ns),
InfoRowGroup(tags, maxValueLength: Constants.infoGroupMaxValueLength),
InfoRowGroup(Map.fromEntries(entries), maxValueLength: Constants.infoGroupMaxValueLength),
];
}).toList(),
),
@ -202,7 +202,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
void _onMetadataChanged() {
_loadedMetadataUri.value = null;
_metadata = [];
_metadata = {};
_getMetadata();
}
@ -211,8 +211,16 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
if (_loadedMetadataUri.value == entry.uri) return;
if (isVisible) {
final rawMetadata = await MetadataService.getAllMetadata(entry) ?? {};
_metadata = rawMetadata.entries.map((dirKV) {
final directoryName = dirKV.key as String ?? '';
final directories = rawMetadata.entries.map((dirKV) {
var directoryName = dirKV.key as String ?? '';
String parent;
final parts = directoryName.split(parentChildSeparator);
if (parts.length > 1) {
parent = parts[0];
directoryName = parts[1];
}
final rawTags = dirKV.value as Map ?? {};
final tags = SplayTreeMap.of(Map.fromEntries(rawTags.entries.map((tagKV) {
final value = tagKV.value as String ?? '';
@ -220,12 +228,21 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
final tagName = tagKV.key as String ?? '';
return MapEntry(tagName, value);
}).where((kv) => kv != null)));
return _MetadataDirectory(directoryName, tags);
return _MetadataDirectory(directoryName, parent, tags);
}).toList();
final titledDirectories = directories.map((dir) {
var title = dir.name;
if (directories.where((dir) => dir.name == title).length > 1 && dir.parent?.isNotEmpty == true) {
title = '${dir.parent}/$title';
}
return MapEntry(title, dir);
}).toList()
..sort((a, b) => compareAsciiUpperCase(a.name, b.name));
..sort((a, b) => compareAsciiUpperCase(a.key, b.key));
_metadata = Map.fromEntries(titledDirectories);
_loadedMetadataUri.value = entry.uri;
} else {
_metadata = [];
_metadata = {};
_loadedMetadataUri.value = null;
}
_expandedDirectoryNotifier.value = null;
@ -237,7 +254,8 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
class _MetadataDirectory {
final String name;
final String parent;
final SplayTreeMap<String, String> tags;
const _MetadataDirectory(this.name, this.tags);
const _MetadataDirectory(this.name, this.parent, this.tags);
}