info: split directories by parent if necessary, hide useless Exif dir, fixed XMP tag order
This commit is contained in:
parent
258d06198d
commit
37d575a1b3
3 changed files with 47 additions and 23 deletions
|
@ -103,7 +103,10 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
||||||
|
|
||||||
for (dir in metadata.directories.filter { it.tagCount > 0 && it !is FileTypeDirectory }) {
|
for (dir in metadata.directories.filter { it.tagCount > 0 && it !is FileTypeDirectory }) {
|
||||||
// directory name
|
// 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())
|
val dirMap = metadataMap.getOrDefault(dirName, HashMap())
|
||||||
metadataMap[dirName] = dirMap
|
metadataMap[dirName] = dirMap
|
||||||
|
|
||||||
|
|
|
@ -217,8 +217,11 @@ object ExifInterfaceHelper {
|
||||||
// so that we can rely on metadata-extractor descriptions
|
// so that we can rely on metadata-extractor descriptions
|
||||||
val dirs = DirType.values().map { Pair(it, it.createDirectory()) }.toMap()
|
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 {
|
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("Exif Thumbnail", describeDir(exif, dirs, thumbnailTags))
|
||||||
put(Metadata.DIR_GPS, describeDir(exif, dirs, gpsTags))
|
put(Metadata.DIR_GPS, describeDir(exif, dirs, gpsTags))
|
||||||
put(Metadata.DIR_XMP, describeDir(exif, dirs, xmpTags))
|
put(Metadata.DIR_XMP, describeDir(exif, dirs, xmpTags))
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:collection';
|
||||||
|
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/services/metadata_service.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/constants.dart';
|
||||||
import 'package:aves/utils/durations.dart';
|
import 'package:aves/utils/durations.dart';
|
||||||
import 'package:aves/widgets/common/aves_expansion_tile.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 {
|
class _MetadataSectionSliverState extends State<MetadataSectionSliver> with AutomaticKeepAliveClientMixin {
|
||||||
List<_MetadataDirectory> _metadata = [];
|
Map<String, _MetadataDirectory> _metadata = {};
|
||||||
final ValueNotifier<String> _loadedMetadataUri = ValueNotifier(null);
|
final ValueNotifier<String> _loadedMetadataUri = ValueNotifier(null);
|
||||||
final ValueNotifier<String> _expandedDirectoryNotifier = 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 xmpDirectory = 'XMP'; // from metadata-extractor
|
||||||
static const mediaDirectory = 'Media'; // additional media (video/audio/images) directory
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -87,8 +92,6 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
if (_metadata.isEmpty) {
|
if (_metadata.isEmpty) {
|
||||||
content = SizedBox.shrink();
|
content = SizedBox.shrink();
|
||||||
} else {
|
} else {
|
||||||
final directoriesWithoutTitle = _metadata.where((dir) => dir.name.isEmpty).toList();
|
|
||||||
final directoriesWithTitle = _metadata.where((dir) => dir.name.isNotEmpty).toList();
|
|
||||||
content = Column(
|
content = Column(
|
||||||
children: AnimationConfiguration.toStaggeredList(
|
children: AnimationConfiguration.toStaggeredList(
|
||||||
duration: Durations.staggeredAnimation,
|
duration: Durations.staggeredAnimation,
|
||||||
|
@ -101,8 +104,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
SectionRow(AIcons.info),
|
SectionRow(AIcons.info),
|
||||||
...directoriesWithoutTitle.map(_buildDirTileWithoutTitle),
|
..._metadata.entries.map((kv) => _buildDirTile(kv.key, kv.value)),
|
||||||
...directoriesWithTitle.map(_buildDirTileWithTitle),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -118,11 +120,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDirTileWithoutTitle(_MetadataDirectory dir) {
|
Widget _buildDirTile(String title, _MetadataDirectory dir) {
|
||||||
return InfoRowGroup(dir.tags, maxValueLength: Constants.infoGroupMaxValueLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDirTileWithTitle(_MetadataDirectory dir) {
|
|
||||||
if (dir.name == xmpDirectory) {
|
if (dir.name == xmpDirectory) {
|
||||||
return _buildXmpDirTile(dir);
|
return _buildXmpDirTile(dir);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +149,8 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
return AvesExpansionTile(
|
return AvesExpansionTile(
|
||||||
title: dir.name,
|
title: title,
|
||||||
|
color: stringToColor(dir.name),
|
||||||
expandedNotifier: _expandedDirectoryNotifier,
|
expandedNotifier: _expandedDirectoryNotifier,
|
||||||
children: [
|
children: [
|
||||||
if (prefixChildren.isNotEmpty) Wrap(children: prefixChildren),
|
if (prefixChildren.isNotEmpty) Wrap(children: prefixChildren),
|
||||||
|
@ -173,7 +172,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
if (i == -1) return '';
|
if (i == -1) return '';
|
||||||
return fullKey.substring(0, i);
|
return fullKey.substring(0, i);
|
||||||
}),
|
}),
|
||||||
compareAsciiLowerCase,
|
compareAsciiUpperCase,
|
||||||
);
|
);
|
||||||
return AvesExpansionTile(
|
return AvesExpansionTile(
|
||||||
title: dir.name,
|
title: dir.name,
|
||||||
|
@ -188,10 +187,11 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
final ns = kv.key;
|
final ns = kv.key;
|
||||||
final hasNamespace = ns.isNotEmpty;
|
final hasNamespace = ns.isNotEmpty;
|
||||||
final i = hasNamespace ? ns.length + 1 : 0;
|
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 [
|
return [
|
||||||
if (hasNamespace) HighlightTitle(ns),
|
if (hasNamespace) HighlightTitle(ns),
|
||||||
InfoRowGroup(tags, maxValueLength: Constants.infoGroupMaxValueLength),
|
InfoRowGroup(Map.fromEntries(entries), maxValueLength: Constants.infoGroupMaxValueLength),
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
@ -202,7 +202,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
|
|
||||||
void _onMetadataChanged() {
|
void _onMetadataChanged() {
|
||||||
_loadedMetadataUri.value = null;
|
_loadedMetadataUri.value = null;
|
||||||
_metadata = [];
|
_metadata = {};
|
||||||
_getMetadata();
|
_getMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,8 +211,16 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
if (_loadedMetadataUri.value == entry.uri) return;
|
if (_loadedMetadataUri.value == entry.uri) return;
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
final rawMetadata = await MetadataService.getAllMetadata(entry) ?? {};
|
final rawMetadata = await MetadataService.getAllMetadata(entry) ?? {};
|
||||||
_metadata = rawMetadata.entries.map((dirKV) {
|
final directories = rawMetadata.entries.map((dirKV) {
|
||||||
final directoryName = dirKV.key as String ?? '';
|
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 rawTags = dirKV.value as Map ?? {};
|
||||||
final tags = SplayTreeMap.of(Map.fromEntries(rawTags.entries.map((tagKV) {
|
final tags = SplayTreeMap.of(Map.fromEntries(rawTags.entries.map((tagKV) {
|
||||||
final value = tagKV.value as String ?? '';
|
final value = tagKV.value as String ?? '';
|
||||||
|
@ -220,12 +228,21 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
final tagName = tagKV.key as String ?? '';
|
final tagName = tagKV.key as String ?? '';
|
||||||
return MapEntry(tagName, value);
|
return MapEntry(tagName, value);
|
||||||
}).where((kv) => kv != null)));
|
}).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()
|
}).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;
|
_loadedMetadataUri.value = entry.uri;
|
||||||
} else {
|
} else {
|
||||||
_metadata = [];
|
_metadata = {};
|
||||||
_loadedMetadataUri.value = null;
|
_loadedMetadataUri.value = null;
|
||||||
}
|
}
|
||||||
_expandedDirectoryNotifier.value = null;
|
_expandedDirectoryNotifier.value = null;
|
||||||
|
@ -237,7 +254,8 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
|
||||||
|
|
||||||
class _MetadataDirectory {
|
class _MetadataDirectory {
|
||||||
final String name;
|
final String name;
|
||||||
|
final String parent;
|
||||||
final SplayTreeMap<String, String> tags;
|
final SplayTreeMap<String, String> tags;
|
||||||
|
|
||||||
const _MetadataDirectory(this.name, this.tags);
|
const _MetadataDirectory(this.name, this.parent, this.tags);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue