From a29cc971b2c45a361dcf51fe3e0d564ea29f4c75 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Mon, 22 Feb 2021 11:33:27 +0900 Subject: [PATCH] improved reverse geocoding + misc fixes --- .../thibault/aves/utils/StorageUtils.kt | 2 +- lib/geo/countries.dart | 22 ++++++---- lib/geo/format.dart | 2 +- lib/model/entry.dart | 41 +++++++++---------- lib/model/source/collection_source.dart | 4 +- lib/model/source/location.dart | 18 +++++--- lib/model/source/media_store_source.dart | 26 +++++------- lib/model/source/tag.dart | 1 + lib/ref/mime_types.dart | 1 + lib/services/android_debug_service.dart | 10 ++--- lib/services/android_file_service.dart | 2 +- lib/services/image_file_service.dart | 6 +-- lib/services/metadata_service.dart | 8 ++-- lib/services/viewer_service.dart | 2 +- lib/widgets/debug/app_debug_page.dart | 4 +- lib/widgets/viewer/debug_page.dart | 2 +- lib/widgets/viewer/entry_vertical_pager.dart | 2 +- lib/widgets/viewer/info/basic_section.dart | 2 +- lib/widgets/viewer/overlay/bottom.dart | 2 +- 19 files changed, 84 insertions(+), 73 deletions(-) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt index 159db5884..43638d0d5 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt @@ -149,7 +149,7 @@ object StorageUtils { return paths.map { ensureTrailingSeparator(it) }.toTypedArray() } - // return physicalPaths based on phone model + // returns physicalPaths based on phone model @SuppressLint("SdCardPath") private val physicalPaths = arrayOf( "/storage/sdcard0", diff --git a/lib/geo/countries.dart b/lib/geo/countries.dart index f11a31eac..941f5204c 100644 --- a/lib/geo/countries.dart +++ b/lib/geo/countries.dart @@ -17,12 +17,12 @@ class CountryTopology { Future getTopology() => _topology != null ? SynchronousFuture(_topology) : rootBundle.loadString(topoJsonAsset).then(TopoJson().parse); - // return the country containing given coordinates + // returns the country containing given coordinates Future countryCode(LatLng position) async { return _countryOfNumeric(await numericCode(position)); } - // return the ISO 3166-1 numeric code of the country containing given coordinates + // returns the ISO 3166-1 numeric code of the country containing given coordinates Future numericCode(LatLng position) async { final topology = await getTopology(); if (topology == null) return null; @@ -31,7 +31,7 @@ class CountryTopology { return _getNumeric(topology, countries, position); } - // return a map of the given positions by country + // returns a map of the given positions by country Future>> countryCodeMap(Set positions) async { final numericMap = await numericCodeMap(positions); numericMap.remove(null); @@ -43,7 +43,7 @@ class CountryTopology { return codeMap; } - // return a map of the given positions by the ISO 3166-1 numeric code of the country containing them + // returns a map of the given positions by the ISO 3166-1 numeric code of the country containing them Future>> numericCodeMap(Set positions) async { final topology = await getTopology(); if (topology == null) return null; @@ -68,10 +68,18 @@ class CountryTopology { return null; } - static int _getNumeric(Topology topology, List countries, LatLng position) { + static int _getNumeric(Topology topology, List mruCountries, LatLng position) { final point = [position.longitude, position.latitude]; - final hit = countries.firstWhere((country) => country.containsPoint(topology, point), orElse: () => null); - final idString = (hit?.id as String); + final hit = mruCountries.firstWhere((country) => country.containsPoint(topology, point), orElse: () => null); + if (hit == null) return null; + + // promote hit countries, assuming given positions are likely to come from the same countries + if (mruCountries.first != hit) { + mruCountries.remove(hit); + mruCountries.insert(0, hit); + } + + final idString = (hit.id as String); final code = idString == null ? null : int.tryParse(idString); return code; } diff --git a/lib/geo/format.dart b/lib/geo/format.dart index 62a942c2a..eb9c601ac 100644 --- a/lib/geo/format.dart +++ b/lib/geo/format.dart @@ -21,7 +21,7 @@ String _decimal2sexagesimal(final double degDecimal) { return '$deg° $min′ ${roundToPrecision(sec, decimals: 2).toStringAsFixed(2)}″'; } -// return coordinates formatted as DMS, e.g. ['41° 24′ 12.2″ N', '2° 10′ 26.5″ E'] +// returns coordinates formatted as DMS, e.g. ['41° 24′ 12.2″ N', '2° 10′ 26.5″ E'] List toDMS(LatLng latLng) { if (latLng == null) return []; final lat = latLng.latitude; diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 024fdefa0..934e39cba 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -50,7 +50,7 @@ class AvesEntry { final Future> Function(Coordinates coordinates) _findAddresses = Geocoder.local.findAddressesFromCoordinates; // TODO TLAD make it dynamic if it depends on OS/lib versions - static const List undecodable = [MimeTypes.crw, MimeTypes.psd]; + static const List undecodable = [MimeTypes.crw, MimeTypes.djvu, MimeTypes.psd]; AvesEntry({ this.uri, @@ -289,6 +289,8 @@ class AvesEntry { static const ratioSeparator = '\u2236'; static const resolutionSeparator = ' \u00D7 '; + bool get isSized => (width ?? 0) > 0 && (height ?? 0) > 0; + String get resolutionText { final ws = width ?? '?'; final hs = height ?? '?'; @@ -369,11 +371,15 @@ class AvesEntry { return _durationText; } - bool get hasGps => _catalogMetadata?.latitude != null; + // returns whether this entry has GPS coordinates + // (0, 0) coordinates are considered invalid, as it is likely a default value + bool get hasGps => _catalogMetadata != null && _catalogMetadata.latitude != null && _catalogMetadata.longitude != null && (_catalogMetadata.latitude != 0 || _catalogMetadata.longitude != 0); bool get hasAddress => _addressDetails != null; - bool get hasPlace => _addressDetails?.place?.isNotEmpty == true; + // has a place, or at least the full country name + // derived from Google reverse geocoding addresses + bool get hasFineAddress => _addressDetails != null && (_addressDetails.place?.isNotEmpty == true || (_addressDetails.countryName?.length ?? 0) > 3); LatLng get latLng => hasGps ? LatLng(_catalogMetadata.latitude, _catalogMetadata.longitude) : null; @@ -449,22 +455,23 @@ class AvesEntry { addressChangeNotifier.notifyListeners(); } - Future locate() async { + Future locate({@required bool background}) async { + if (!hasGps) return; await _locateCountry(); if (await availability.canLocatePlaces) { - await locatePlace(background: false); + await locatePlace(background: background); } } // quick reverse geocoding to find the country, using an offline asset Future _locateCountry() async { - if (hasAddress) return; + if (!hasGps || hasAddress) return; final countryCode = await countryTopology.countryCode(latLng); setCountry(countryCode); } void setCountry(CountryCode countryCode) { - if (hasPlace || countryCode == null) return; + if (hasFineAddress || countryCode == null) return; addressDetails = AddressDetails( contentId: contentId, countryCode: countryCode.alpha2, @@ -474,16 +481,10 @@ class AvesEntry { // full reverse geocoding, requiring Play Services and some connectivity Future locatePlace({@required bool background}) async { - if (hasPlace) return; - - await catalog(background: background); - final latitude = _catalogMetadata?.latitude; - final longitude = _catalogMetadata?.longitude; - if (latitude == null || longitude == null || (latitude == 0 && longitude == 0)) return; - - final coordinates = Coordinates(latitude, longitude); + if (!hasGps || hasFineAddress) return; + final coordinates = latLng; try { - Future> call() => _findAddresses(coordinates); + Future> call() => _findAddresses(Coordinates(coordinates.latitude, coordinates.longitude)); final addresses = await (background ? servicePolicy.call( call, @@ -511,13 +512,11 @@ class AvesEntry { } Future findAddressLine() async { - final latitude = _catalogMetadata?.latitude; - final longitude = _catalogMetadata?.longitude; - if (latitude == null || longitude == null || (latitude == 0 && longitude == 0)) return null; + if (!hasGps) return null; - final coordinates = Coordinates(latitude, longitude); + final coordinates = latLng; try { - final addresses = await _findAddresses(coordinates); + final addresses = await _findAddresses(Coordinates(coordinates.latitude, coordinates.longitude)); if (addresses != null && addresses.isNotEmpty) { final address = addresses.first; return address.addressLine; diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index 1e4bd0243..37759a8c5 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -23,6 +23,8 @@ mixin SourceBase { List get sortedEntriesByDate; + ValueNotifier stateNotifier = ValueNotifier(SourceState.ready); + final StreamController _progressStreamController = StreamController.broadcast(); Stream get progressStream => _progressStreamController.stream; @@ -58,8 +60,6 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM return _sortedEntriesByDate; } - ValueNotifier stateNotifier = ValueNotifier(SourceState.ready); - List _savedDates; Future loadDates() async { diff --git a/lib/model/source/location.dart b/lib/model/source/location.dart index 0641340c2..c0f7b3715 100644 --- a/lib/model/source/location.dart +++ b/lib/model/source/location.dart @@ -35,9 +35,14 @@ mixin LocationMixin on SourceBase { // quick reverse geocoding to find the countries, using an offline asset Future _locateCountries() async { - final todo = visibleEntries.where((entry) => entry.hasGps && entry.addressDetails?.countryCode == null).toSet(); + final todo = visibleEntries.where((entry) => entry.hasGps && !entry.hasAddress).toSet(); if (todo.isEmpty) return; + stateNotifier.value = SourceState.locating; + var progressDone = 0; + final progressTotal = todo.length; + setProgress(done: progressDone, total: progressTotal); + // final stopwatch = Stopwatch()..start(); final countryCodeMap = await countryTopology.countryCodeMap(todo.map((entry) => entry.latLng).toSet()); final newAddresses = []; @@ -48,12 +53,13 @@ mixin LocationMixin on SourceBase { if (entry.hasAddress) { newAddresses.add(entry.addressDetails); } + setProgress(done: ++progressDone, total: progressTotal); }); if (newAddresses.isNotEmpty) { await metadataDb.saveAddresses(List.unmodifiable(newAddresses)); onAddressMetadataChanged(); } - // debugPrint('$runtimeType _locateCountries complete in ${stopwatch.elapsed.inSeconds}s'); + // debugPrint('$runtimeType _locateCountries complete in ${stopwatch.elapsed.inMilliseconds}ms'); } // full reverse geocoding, requiring Play Services and some connectivity @@ -61,7 +67,7 @@ mixin LocationMixin on SourceBase { if (!(await availability.canLocatePlaces)) return; // final stopwatch = Stopwatch()..start(); - final byLocated = groupBy(visibleEntries.where((entry) => entry.hasGps), (entry) => entry.hasPlace); + final byLocated = groupBy(visibleEntries.where((entry) => entry.hasGps), (entry) => entry.hasFineAddress); final todo = byLocated[false] ?? []; if (todo.isEmpty) return; @@ -85,6 +91,7 @@ mixin LocationMixin on SourceBase { final knownLocations = {}; byLocated[true]?.forEach((entry) => knownLocations.putIfAbsent(approximateLatLng(entry), () => entry.addressDetails)); + stateNotifier.value = SourceState.locating; var progressDone = 0; final progressTotal = todo.length; setProgress(done: progressDone, total: progressTotal); @@ -100,7 +107,7 @@ mixin LocationMixin on SourceBase { // so that we skip geocoding of following entries with the same coordinates knownLocations[latLng] = entry.addressDetails; } - if (entry.hasPlace) { + if (entry.hasFineAddress) { newAddresses.add(entry.addressDetails); if (newAddresses.length >= _commitCountThreshold) { await metadataDb.saveAddresses(List.unmodifiable(newAddresses)); @@ -147,7 +154,8 @@ mixin LocationMixin on SourceBase { _filterEntryCountMap.clear(); _filterRecentEntryMap.clear(); } else { - final countryCodes = entries.where((entry) => entry.hasPlace).map((entry) => entry.addressDetails.countryCode).toSet(); + final countryCodes = entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails.countryCode).toSet(); + countryCodes.remove(null); countryCodes.forEach(_filterEntryCountMap.remove); } } diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 4f9d72ba7..83b310368 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -13,7 +13,6 @@ import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; -import 'package:pedantic/pedantic.dart'; class MediaStoreSource extends CollectionSource { bool _initialized = false; @@ -103,25 +102,25 @@ class MediaStoreSource extends CollectionSource { updateDirectories(); } - final analytics = FirebaseAnalytics(); - unawaited(analytics.setUserProperty(name: 'local_item_count', value: (ceilBy(allEntries.length, 3)).toString())); - unawaited(analytics.setUserProperty(name: 'album_count', value: (ceilBy(rawAlbums.length, 1)).toString())); - - stateNotifier.value = SourceState.cataloguing; await catalogEntries(); - unawaited(analytics.setUserProperty(name: 'tag_count', value: (ceilBy(sortedTags.length, 1)).toString())); - - stateNotifier.value = SourceState.locating; await locateEntries(); - unawaited(analytics.setUserProperty(name: 'country_count', value: (ceilBy(sortedCountries.length, 1)).toString())); - stateNotifier.value = SourceState.ready; + + _reportCollectionDimensions(); debugPrint('$runtimeType refresh done, elapsed=${stopwatch.elapsed}'); }, onError: (error) => debugPrint('$runtimeType stream error=$error'), ); } + void _reportCollectionDimensions() { + final analytics = FirebaseAnalytics(); + analytics.setUserProperty(name: 'local_item_count', value: (ceilBy(allEntries.length, 3)).toString()); + analytics.setUserProperty(name: 'album_count', value: (ceilBy(rawAlbums.length, 1)).toString()); + analytics.setUserProperty(name: 'tag_count', value: (ceilBy(sortedTags.length, 1)).toString()); + analytics.setUserProperty(name: 'country_count', value: (ceilBy(sortedCountries.length, 1)).toString()); + } + // returns URIs to retry later. They could be URIs that are: // 1) currently being processed during bulk move/deletion // 2) registered in the Media Store but still being processed by their owner in a temporary location @@ -178,13 +177,8 @@ class MediaStoreSource extends CollectionSource { addEntries(newEntries); await metadataDb.saveEntries(newEntries); cleanEmptyAlbums(existingDirectories); - - stateNotifier.value = SourceState.cataloguing; await catalogEntries(); - - stateNotifier.value = SourceState.locating; await locateEntries(); - stateNotifier.value = SourceState.ready; } diff --git a/lib/model/source/tag.dart b/lib/model/source/tag.dart index 1d4a3b32c..9dbd72934 100644 --- a/lib/model/source/tag.dart +++ b/lib/model/source/tag.dart @@ -27,6 +27,7 @@ mixin TagMixin on SourceBase { final todo = visibleEntries.where((entry) => !entry.isCatalogued).toList(); if (todo.isEmpty) return; + stateNotifier.value = SourceState.cataloguing; var progressDone = 0; final progressTotal = todo.length; setProgress(done: progressDone, total: progressTotal); diff --git a/lib/ref/mime_types.dart b/lib/ref/mime_types.dart index 69dfe3800..c82db0619 100644 --- a/lib/ref/mime_types.dart +++ b/lib/ref/mime_types.dart @@ -18,6 +18,7 @@ class MimeTypes { static const cr2 = 'image/x-canon-cr2'; static const crw = 'image/x-canon-crw'; static const dcr = 'image/x-kodak-dcr'; + static const djvu = 'image/vnd.djvu'; static const dng = 'image/x-adobe-dng'; static const erf = 'image/x-epson-erf'; static const k25 = 'image/x-kodak-k25'; diff --git a/lib/services/android_debug_service.dart b/lib/services/android_debug_service.dart index 3815c5df8..cf89f8423 100644 --- a/lib/services/android_debug_service.dart +++ b/lib/services/android_debug_service.dart @@ -28,7 +28,7 @@ class AndroidDebugService { static Future getBitmapFactoryInfo(AvesEntry entry) async { try { - // return map with all data available when decoding image bounds with `BitmapFactory` + // returns map with all data available when decoding image bounds with `BitmapFactory` final result = await platform.invokeMethod('getBitmapFactoryInfo', { 'uri': entry.uri, }) as Map; @@ -41,7 +41,7 @@ class AndroidDebugService { static Future getContentResolverMetadata(AvesEntry entry) async { try { - // return map with all data available from the content resolver + // returns map with all data available from the content resolver final result = await platform.invokeMethod('getContentResolverMetadata', { 'mimeType': entry.mimeType, 'uri': entry.uri, @@ -55,7 +55,7 @@ class AndroidDebugService { static Future getExifInterfaceMetadata(AvesEntry entry) async { try { - // return map with all data available from the `ExifInterface` library + // returns map with all data available from the `ExifInterface` library final result = await platform.invokeMethod('getExifInterfaceMetadata', { 'mimeType': entry.mimeType, 'uri': entry.uri, @@ -70,7 +70,7 @@ class AndroidDebugService { static Future getMediaMetadataRetrieverMetadata(AvesEntry entry) async { try { - // return map with all data available from `MediaMetadataRetriever` + // returns map with all data available from `MediaMetadataRetriever` final result = await platform.invokeMethod('getMediaMetadataRetrieverMetadata', { 'uri': entry.uri, }) as Map; @@ -83,7 +83,7 @@ class AndroidDebugService { static Future getMetadataExtractorSummary(AvesEntry entry) async { try { - // return map with the mime type and tag count for each directory found by `metadata-extractor` + // returns map with the mime type and tag count for each directory found by `metadata-extractor` final result = await platform.invokeMethod('getMetadataExtractorSummary', { 'mimeType': entry.mimeType, 'uri': entry.uri, diff --git a/lib/services/android_file_service.dart b/lib/services/android_file_service.dart index 3b2096f42..09b3d609d 100644 --- a/lib/services/android_file_service.dart +++ b/lib/services/android_file_service.dart @@ -95,7 +95,7 @@ class AndroidFileService { return false; } - // return media URI + // returns media URI static Future scanFile(String path, String mimeType) async { debugPrint('scanFile with path=$path, mimeType=$mimeType'); try { diff --git a/lib/services/image_file_service.dart b/lib/services/image_file_service.dart index 8f0c33701..45208ca56 100644 --- a/lib/services/image_file_service.dart +++ b/lib/services/image_file_service.dart @@ -248,7 +248,7 @@ class ImageFileService { static Future rename(AvesEntry entry, String newName) async { try { - // return map with: 'contentId' 'path' 'title' 'uri' (all optional) + // returns map with: 'contentId' 'path' 'title' 'uri' (all optional) final result = await platform.invokeMethod('rename', { 'entry': _toPlatformEntryMap(entry), 'newName': newName, @@ -262,7 +262,7 @@ class ImageFileService { static Future rotate(AvesEntry entry, {@required bool clockwise}) async { try { - // return map with: 'rotationDegrees' 'isFlipped' + // returns map with: 'rotationDegrees' 'isFlipped' final result = await platform.invokeMethod('rotate', { 'entry': _toPlatformEntryMap(entry), 'clockwise': clockwise, @@ -276,7 +276,7 @@ class ImageFileService { static Future flip(AvesEntry entry) async { try { - // return map with: 'rotationDegrees' 'isFlipped' + // returns map with: 'rotationDegrees' 'isFlipped' final result = await platform.invokeMethod('flip', { 'entry': _toPlatformEntryMap(entry), }) as Map; diff --git a/lib/services/metadata_service.dart b/lib/services/metadata_service.dart index b7a830623..7954c1c84 100644 --- a/lib/services/metadata_service.dart +++ b/lib/services/metadata_service.dart @@ -11,7 +11,7 @@ import 'package:flutter/services.dart'; class MetadataService { static const platform = MethodChannel('deckers.thibault/aves/metadata'); - // return Map> (map of directories, each directory being a map of metadata label and value description) + // returns Map> (map of directories, each directory being a map of metadata label and value description) static Future getAllMetadata(AvesEntry entry) async { if (entry.isSvg) return null; @@ -33,7 +33,7 @@ class MetadataService { Future call() async { try { - // return map with: + // returns map with: // 'mimeType': MIME type as reported by metadata extractors, not Media Store (string) // 'dateMillis': date taken in milliseconds since Epoch (long) // 'isAnimated': animated gif/webp (bool) @@ -69,7 +69,7 @@ class MetadataService { if (entry.isSvg) return null; try { - // return map with values for: 'aperture' (double), 'exposureTime' (description), 'focalLength' (double), 'iso' (int) + // returns map with values for: 'aperture' (double), 'exposureTime' (description), 'focalLength' (double), 'iso' (int) final result = await platform.invokeMethod('getOverlayMetadata', { 'mimeType': entry.mimeType, 'uri': entry.uri, @@ -98,7 +98,7 @@ class MetadataService { static Future getPanoramaInfo(AvesEntry entry) async { try { - // return map with values for: + // returns map with values for: // 'croppedAreaLeft' (int), 'croppedAreaTop' (int), 'croppedAreaWidth' (int), 'croppedAreaHeight' (int), // 'fullPanoWidth' (int), 'fullPanoHeight' (int) final result = await platform.invokeMethod('getPanoramaInfo', { diff --git a/lib/services/viewer_service.dart b/lib/services/viewer_service.dart index 175874e6d..76c790375 100644 --- a/lib/services/viewer_service.dart +++ b/lib/services/viewer_service.dart @@ -6,7 +6,7 @@ class ViewerService { static Future getIntentData() async { try { - // return nullable map with 'action' and possibly 'uri' 'mimeType' + // returns nullable map with 'action' and possibly 'uri' 'mimeType' return await platform.invokeMethod('getIntentData') as Map; } on PlatformException catch (e) { debugPrint('getIntentData failed with code=${e.code}, exception=${e.message}, details=${e.details}'); diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart index ab8841b30..57d1caa86 100644 --- a/lib/widgets/debug/app_debug_page.dart +++ b/lib/widgets/debug/app_debug_page.dart @@ -64,7 +64,7 @@ class _AppDebugPageState extends State { final catalogued = visibleEntries.where((entry) => entry.isCatalogued); final withGps = catalogued.where((entry) => entry.hasGps); final withAddress = withGps.where((entry) => entry.hasAddress); - final withPlace = withGps.where((entry) => entry.hasPlace); + final withFineAddress = withGps.where((entry) => entry.hasFineAddress); return AvesExpansionTile( title: 'General', children: [ @@ -106,7 +106,7 @@ class _AppDebugPageState extends State { 'Catalogued': '${catalogued.length}', 'With GPS': '${withGps.length}', 'With address': '${withAddress.length}', - 'With place': '${withPlace.length}', + 'With fine address': '${withFineAddress.length}', }, ), ), diff --git a/lib/widgets/viewer/debug_page.dart b/lib/widgets/viewer/debug_page.dart index 8ee95bdc7..b5854fe88 100644 --- a/lib/widgets/viewer/debug_page.dart +++ b/lib/widgets/viewer/debug_page.dart @@ -107,7 +107,7 @@ class ViewerDebugPage extends StatelessWidget { InfoRowGroup({ 'hasGps': '${entry.hasGps}', 'hasAddress': '${entry.hasAddress}', - 'hasPlace': '${entry.hasPlace}', + 'hasFineAddress': '${entry.hasFineAddress}', 'latLng': '${entry.latLng}', 'geoUri': '${entry.geoUri}', }), diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index 3fe6e7405..d8441475e 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -151,7 +151,7 @@ class _ViewerVerticalPageViewState extends State { // make sure to locate the entry, // so that we can display the address instead of coordinates // even when initial collection locating has not reached this entry yet - entry.locate(); + entry.catalog(background: false).then((_) => entry.locate(background: false)); } else { Navigator.pop(context); } diff --git a/lib/widgets/viewer/info/basic_section.dart b/lib/widgets/viewer/info/basic_section.dart index 68888fd84..09c21761d 100644 --- a/lib/widgets/viewer/info/basic_section.dart +++ b/lib/widgets/viewer/info/basic_section.dart @@ -55,7 +55,7 @@ class BasicSection extends StatelessWidget { 'Title': title, 'Date': dateText, if (entry.isVideo) ..._buildVideoRows(), - if (!entry.isSvg) 'Resolution': rasterResolutionText, + if (!entry.isSvg && entry.isSized) 'Resolution': rasterResolutionText, 'Size': entry.sizeBytes != null ? formatFilesize(entry.sizeBytes) : Constants.infoUnknown, 'URI': uri, if (path != null) 'Path': path, diff --git a/lib/widgets/viewer/overlay/bottom.dart b/lib/widgets/viewer/overlay/bottom.dart index afe5ad60e..03d1f0567 100644 --- a/lib/widgets/viewer/overlay/bottom.dart +++ b/lib/widgets/viewer/overlay/bottom.dart @@ -386,7 +386,7 @@ class _DateRow extends StatelessWidget { Widget build(BuildContext context) { final date = entry.bestDate; final dateText = date != null ? '${DateFormat.yMMMd().format(date)} • ${DateFormat.Hm().format(date)}' : Constants.overlayUnknown; - final resolutionText = entry.isSvg ? entry.aspectRatioText : entry.resolutionText; + final resolutionText = entry.isSvg ? entry.aspectRatioText : entry.isSized ? entry.resolutionText : ''; return Row( children: [