diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java index 872073b71..d9d24bba2 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java @@ -198,12 +198,14 @@ public class ImageDecodeTask extends AsyncTask target = Glide.with(activity) .asBitmap() .load(uri) diff --git a/android/app/src/main/java/deckers/thibault/aves/model/SourceImageEntry.java b/android/app/src/main/java/deckers/thibault/aves/model/SourceImageEntry.java index a853c7672..8fd402b46 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/SourceImageEntry.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/SourceImageEntry.java @@ -59,7 +59,7 @@ public class SourceImageEntry { this.sourceMimeType = (String) map.get("sourceMimeType"); this.width = (int) map.get("width"); this.height = (int) map.get("height"); - this.rotationDegrees = (int) map.get("orientationDegrees"); + this.rotationDegrees = (int) map.get("rotationDegrees"); this.sizeBytes = toLong(map.get("sizeBytes")); this.title = (String) map.get("title"); this.dateModifiedSecs = toLong(map.get("dateModifiedSecs")); diff --git a/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java b/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java index 121533ccc..36491011a 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java @@ -163,7 +163,7 @@ public class MediaStoreImageProvider extends ImageProvider { put("uri", itemUri.toString()); put("path", path); put("sourceMimeType", mimeType); - put("orientationDegrees", orientationColumn != -1 ? cursor.getInt(orientationColumn) : 0); + put("rotationDegrees", orientationColumn != -1 ? cursor.getInt(orientationColumn) : 0); put("sizeBytes", cursor.getLong(sizeColumn)); put("title", cursor.getString(titleColumn)); put("dateModifiedSecs", dateModifiedSecs); diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt index 416e71994..c1e6baf88 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt @@ -2,16 +2,25 @@ package deckers.thibault.aves.utils object MimeTypes { const val IMAGE = "image" - const val DNG = "image/x-adobe-dng" // .dng + + const val BMP = "image/bmp" const val GIF = "image/gif" - const val HEIC = "image/heic" - const val HEIF = "image/heif" + const val ICO = "image/x-icon" const val JPEG = "image/jpeg" const val PNG = "image/png" - const val PSD = "image/x-photoshop" // .psd - const val SVG = "image/svg+xml" // .svg + const val WBMP = "image/vnd.wap.wbmp" const val WEBP = "image/webp" + + const val HEIC = "image/heic" + const val HEIF = "image/heif" + const val PSD = "image/x-photoshop" // .psd + + const val DNG = "image/x-adobe-dng" // .dng + + const val SVG = "image/svg+xml" // .svg + const val VIDEO = "video" + const val AVI = "video/avi" const val MP2T = "video/mp2t" // .m2ts const val MP4 = "video/mp4" diff --git a/lib/model/filters/mime.dart b/lib/model/filters/mime.dart index 3ed29003a..fc2671cf6 100644 --- a/lib/model/filters/mime.dart +++ b/lib/model/filters/mime.dart @@ -52,9 +52,9 @@ class MimeFilter extends CollectionFilter { static String displayType(String mime) { final patterns = [ RegExp('.*/'), // remove type, keep subtype - RegExp('(X-|VND.)'), // noisy prefixes + RegExp('(X-|VND.(WAP.)?)'), // noisy prefixes '+XML', // noisy suffix - RegExp('ADOBE[-\.]'), // for DNG, PSD... + RegExp('ADOBE\.'), // for PSD ]; mime = mime.toUpperCase(); patterns.forEach((pattern) => mime = mime.replaceFirst(pattern, '')); diff --git a/lib/model/image_entry.dart b/lib/model/image_entry.dart index 6bb40bb04..1b2fb754a 100644 --- a/lib/model/image_entry.dart +++ b/lib/model/image_entry.dart @@ -90,7 +90,7 @@ class ImageEntry { sourceMimeType: map['sourceMimeType'] as String, width: map['width'] as int ?? 0, height: map['height'] as int ?? 0, - orientationDegrees: map['orientationDegrees'] as int, + orientationDegrees: map['orientationDegrees'] as int ?? 0, sizeBytes: map['sizeBytes'] as int, sourceTitle: map['title'] as String, dateModifiedSecs: map['dateModifiedSecs'] as int, @@ -165,7 +165,7 @@ class ImageEntry { // guess whether this is a photo, according to file type (used as a hint to e.g. display megapixels) bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg].contains(mimeType) || isRaw; - bool get isRaw => [MimeTypes.dng].contains(mimeType); + bool get isRaw => MimeTypes.rawImages.contains(mimeType); bool get isVideo => mimeType.startsWith('video'); diff --git a/lib/model/mime_types.dart b/lib/model/mime_types.dart index bc9080026..dbe344c59 100644 --- a/lib/model/mime_types.dart +++ b/lib/model/mime_types.dart @@ -1,6 +1,6 @@ class MimeTypes { static const String anyImage = 'image/*'; - static const String dng = 'image/x-adobe-dng'; + static const String gif = 'image/gif'; static const String heic = 'image/heic'; static const String heif = 'image/heif'; @@ -9,8 +9,33 @@ class MimeTypes { static const String svg = 'image/svg+xml'; static const String webp = 'image/webp'; + static const String arw = 'image/x-sony-arw'; + static const String cr2 = 'image/x-canon-cr2'; + static const String crw = 'image/x-canon-crw'; + static const String dcr = 'image/x-kodak-dcr'; + static const String dng = 'image/x-adobe-dng'; + static const String erf = 'image/x-epson-erf'; + static const String k25 = 'image/x-kodak-k25'; + static const String kdc = 'image/x-kodak-kdc'; + static const String mrw = 'image/x-minolta-mrw'; + static const String nef = 'image/x-nikon-nef'; + static const String nrw = 'image/x-nikon-nrw'; + static const String orf = 'image/x-olympus-orf'; + static const String pef = 'image/x-pentax-pef'; + static const String raf = 'image/x-fuji-raf'; + static const String raw = 'image/x-panasonic-raw'; + static const String rw2 = 'image/x-panasonic-rw2'; + static const String sr2 = 'image/x-sony-sr2'; + static const String srf = 'image/x-sony-srf'; + static const String srw = 'image/x-samsung-srw'; + static const String x3f = 'image/x-sigma-x3f'; + static const String anyVideo = 'video/*'; + static const String avi = 'video/avi'; static const String mp2t = 'video/mp2t'; // .m2ts static const String mp4 = 'video/mp4'; + + // groups + static const List rawImages = [arw, cr2, crw, dcr, dng, erf, k25, kdc, mrw, nef, nrw, orf, pef, raf, raw, rw2, sr2, srf, srw, x3f]; } diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 742466b3b..80ae5f9e9 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -20,6 +20,8 @@ class Constants { static const pointNemo = Tuple2(-48.876667, -123.393333); + static const int infoGroupMaxValueLength = 140; + static const List androidDependencies = [ Dependency( name: 'CWAC-Document', diff --git a/lib/widgets/common/aves_expansion_tile.dart b/lib/widgets/common/aves_expansion_tile.dart new file mode 100644 index 000000000..85b76a31b --- /dev/null +++ b/lib/widgets/common/aves_expansion_tile.dart @@ -0,0 +1,41 @@ +import 'package:aves/widgets/common/highlight_title.dart'; +import 'package:expansion_tile_card/expansion_tile_card.dart'; +import 'package:flutter/material.dart'; + +class AvesExpansionTile extends StatelessWidget { + final String title; + final List children; + final ValueNotifier expandedNotifier; + + const AvesExpansionTile({ + @required this.title, + @required this.children, + this.expandedNotifier, + }); + + @override + Widget build(BuildContext context) { + return Theme( + data: Theme.of(context).copyWith( + // color used by the `ExpansionTileCard` for selected text and icons + accentColor: Colors.white, + ), + child: ExpansionTileCard( + key: Key('tilecard-$title'), + value: title, + expandedNotifier: expandedNotifier, + title: HighlightTitle( + title, + fontSize: 18, + ), + children: [ + Divider(thickness: 1, height: 1), + SizedBox(height: 4), + ...children, + ], + baseColor: Colors.grey[900], + expandedColor: Colors.grey[850], + ), + ); + } +} diff --git a/lib/widgets/fullscreen/debug.dart b/lib/widgets/fullscreen/debug.dart index b819e0767..7c3e0591d 100644 --- a/lib/widgets/fullscreen/debug.dart +++ b/lib/widgets/fullscreen/debug.dart @@ -5,11 +5,12 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_metadata.dart'; import 'package:aves/model/metadata_db.dart'; import 'package:aves/services/metadata_service.dart'; +import 'package:aves/utils/constants.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/image_providers/thumbnail_provider.dart'; import 'package:aves/widgets/common/image_providers/uri_picture_provider.dart'; import 'package:aves/widgets/fullscreen/info/info_page.dart'; -import 'package:aves/widgets/settings/settings_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:tuple/tuple.dart'; @@ -260,7 +261,7 @@ class _FullscreenDebugPageState extends State { static const millisecondTimestampKeys = ['datetaken', 'datetime']; Widget _buildContentResolverTabView() { - Widget builder(BuildContext context, AsyncSnapshot snapshot) { + Widget builder(BuildContext context, AsyncSnapshot snapshot, String title) { if (snapshot.hasError) return Text(snapshot.error.toString()); if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); final data = SplayTreeMap.of(snapshot.data.map((k, v) { @@ -277,21 +278,30 @@ class _FullscreenDebugPageState extends State { } return MapEntry(key, value); })); - return InfoRowGroup(data); + return AvesExpansionTile( + title: title, + children: [ + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup( + data, + maxValueLength: Constants.infoGroupMaxValueLength, + ), + ) + ], + ); } return ListView( - padding: EdgeInsets.all(16), + padding: EdgeInsets.all(8), children: [ - SectionTitle('Content Resolver (Media Store)'), FutureBuilder( future: _contentResolverMetadataLoader, - builder: builder, + builder: (context, snapshot) => builder(context, snapshot, 'Content Resolver'), ), - SectionTitle('Exif Interface'), FutureBuilder( future: _exifInterfaceMetadataLoader, - builder: builder, + builder: (context, snapshot) => builder(context, snapshot, 'Exif Interface'), ), ], ); diff --git a/lib/widgets/fullscreen/info/metadata_section.dart b/lib/widgets/fullscreen/info/metadata_section.dart index 03cdc0bc0..4096c050e 100644 --- a/lib/widgets/fullscreen/info/metadata_section.dart +++ b/lib/widgets/fullscreen/info/metadata_section.dart @@ -3,12 +3,12 @@ import 'dart:collection'; import 'package:aves/model/image_entry.dart'; import 'package:aves/services/metadata_service.dart'; -import 'package:aves/widgets/common/highlight_title.dart'; +import 'package:aves/utils/constants.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/fullscreen/info/info_page.dart'; import 'package:aves/widgets/fullscreen/info/metadata_thumbnail.dart'; import 'package:collection/collection.dart'; -import 'package:expansion_tile_card/expansion_tile_card.dart'; import 'package:flutter/material.dart'; class MetadataSectionSliver extends StatefulWidget { @@ -33,8 +33,6 @@ class _MetadataSectionSliverState extends State with Auto bool get isVisible => widget.visibleNotifier.value; - static const int maxValueLength = 140; - // directory names from metadata-extractor static const exifThumbnailDirectory = 'Exif Thumbnail'; // from metadata-extractor static const xmpDirectory = 'XMP'; // from metadata-extractor @@ -86,37 +84,22 @@ class _MetadataSectionSliverState extends State with Auto } if (index < untitledDirectoryCount + 1) { final dir = directoriesWithoutTitle[index - 1]; - return InfoRowGroup(dir.tags, maxValueLength: maxValueLength); + return InfoRowGroup(dir.tags, maxValueLength: Constants.infoGroupMaxValueLength); } final dir = directoriesWithTitle[index - 1 - untitledDirectoryCount]; - return Theme( - data: Theme.of(context).copyWith( - // color used by the `ExpansionTileCard` for selected text and icons - accentColor: Colors.white, - ), - child: ExpansionTileCard( - key: Key('tilecard-${dir.name}'), - value: dir.name, - expandedNotifier: _expandedDirectoryNotifier, - title: HighlightTitle( - dir.name, - fontSize: 18, + return AvesExpansionTile( + title: dir.name, + expandedNotifier: _expandedDirectoryNotifier, + children: [ + if (dir.name == exifThumbnailDirectory) MetadataThumbnails(source: MetadataThumbnailSource.exif, entry: entry), + if (dir.name == xmpDirectory) MetadataThumbnails(source: MetadataThumbnailSource.xmp, entry: entry), + if (dir.name == videoDirectory) MetadataThumbnails(source: MetadataThumbnailSource.embedded, entry: entry), + Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup(dir.tags, maxValueLength: Constants.infoGroupMaxValueLength), ), - children: [ - Divider(thickness: 1, height: 1), - SizedBox(height: 4), - if (dir.name == exifThumbnailDirectory) MetadataThumbnails(source: MetadataThumbnailSource.exif, entry: entry), - if (dir.name == xmpDirectory) MetadataThumbnails(source: MetadataThumbnailSource.xmp, entry: entry), - if (dir.name == videoDirectory) MetadataThumbnails(source: MetadataThumbnailSource.embedded, entry: entry), - Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), - child: InfoRowGroup(dir.tags, maxValueLength: maxValueLength), - ), - ], - baseColor: Colors.grey[900], - expandedColor: Colors.grey[850], - ), + ], ); }, childCount: 1 + _metadata.length,