diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 8d97731e2..75c0838b5 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:aves/geo/countries.dart'; import 'package:aves/model/entry_cache.dart'; @@ -178,6 +179,8 @@ class AvesEntry { return _extension; } + bool get isMissingAtPath => path != null && !File(path!).existsSync(); + // the MIME type reported by the Media Store is unreliable // so we use the one found during cataloguing if possible String get mimeType => _catalogMetadata?.mimeType ?? sourceMimeType; diff --git a/lib/services/metadata/metadata_fetch_service.dart b/lib/services/metadata/metadata_fetch_service.dart index 3729b393a..7c487b978 100644 --- a/lib/services/metadata/metadata_fetch_service.dart +++ b/lib/services/metadata/metadata_fetch_service.dart @@ -40,7 +40,9 @@ class PlatformMetadataFetchService implements MetadataFetchService { }); if (result != null) return result as Map; } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); + if (!entry.isMissingAtPath) { + await reportService.recordError(e, stack); + } } return {}; } @@ -118,7 +120,9 @@ class PlatformMetadataFetchService implements MetadataFetchService { } return MultiPageInfo.fromPageMaps(entry, pageMaps); } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); + if (!entry.isMissingAtPath) { + await reportService.recordError(e, stack); + } } return null; } diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 9d723b7fe..91145e206 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -348,7 +348,7 @@ class _CollectionAppBarState extends State with SingleTickerPr // we compute the default name beforehand // because some filter labels need localization final sortedFilters = List.from(filters)..sort(); - defaultName = sortedFilters.first.getLabel(context); + defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' '); } final result = await showDialog>( context: context, diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 975f4d343..04aaf83ae 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -57,7 +57,7 @@ class GeoMap extends StatefulWidget { } class _GeoMapState extends State { - // as of google_maps_flutter v2.0.6, Google Maps initialization is blocking + // as of google_maps_flutter v2.0.6, Google map initialization is blocking // cf https://github.com/flutter/flutter/issues/28493 // it is especially severe the first time, but still significant afterwards // so we prevent loading it while scrolling or animating @@ -68,14 +68,21 @@ class _GeoMapState extends State { List get entries => widget.entries; + // cap initial zoom to avoid a zoom change + // when toggling overlay on Google map initial state + static const double minInitialZoom = 3; + @override void initState() { super.initState(); final initialEntry = widget.initialEntry; final points = (initialEntry != null ? [initialEntry] : entries).map((v) => v.latLng!).toSet(); - _boundsNotifier = ValueNotifier(ZoomedBounds.fromPoints( + final bounds = ZoomedBounds.fromPoints( points: points.isNotEmpty ? points : {Constants.wonders[Random().nextInt(Constants.wonders.length)]}, collocationZoom: settings.infoMapZoom, + ); + _boundsNotifier = ValueNotifier(bounds.copyWith( + zoom: max(bounds.zoom, minInitialZoom), )); _defaultMarkerCluster = _buildFluster(); } diff --git a/lib/widgets/common/map/google/map.dart b/lib/widgets/common/map/google/map.dart index aea422c19..f76734750 100644 --- a/lib/widgets/common/map/google/map.dart +++ b/lib/widgets/common/map/google/map.dart @@ -109,7 +109,7 @@ class _EntryGoogleMapState extends State with WidgetsBindingObse case AppLifecycleState.detached: break; case AppLifecycleState.resumed: - // workaround for blank Google Maps when resuming app + // workaround for blank Google map when resuming app // cf https://github.com/flutter/flutter/issues/40284 _googleMapController?.setMapStyle(null); break; diff --git a/lib/widgets/common/map/google/marker_generator.dart b/lib/widgets/common/map/google/marker_generator.dart index bc572cad0..b296d0575 100644 --- a/lib/widgets/common/map/google/marker_generator.dart +++ b/lib/widgets/common/map/google/marker_generator.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:provider/provider.dart'; -// generate bitmap from widget, for Google Maps +// generate bitmap from widget, for Google map class MarkerGeneratorWidget extends StatefulWidget { final List markers; final bool Function(T markerKey) isReadyToRender; diff --git a/lib/widgets/common/map/leaflet/map.dart b/lib/widgets/common/map/leaflet/map.dart index 9dbf3aa41..f48aba615 100644 --- a/lib/widgets/common/map/leaflet/map.dart +++ b/lib/widgets/common/map/leaflet/map.dart @@ -66,7 +66,7 @@ class _EntryLeafletMapState extends State with TickerProviderSt ZoomedBounds get bounds => boundsNotifier.value; - // duration should match the uncustomizable Google Maps duration + // duration should match the uncustomizable Google map duration static const _cameraAnimationDuration = Duration(milliseconds: 600); @override diff --git a/lib/widgets/common/map/marker.dart b/lib/widgets/common/map/marker.dart index 6a82a2814..a25cba8ff 100644 --- a/lib/widgets/common/map/marker.dart +++ b/lib/widgets/common/map/marker.dart @@ -39,7 +39,7 @@ class ImageMarker extends StatelessWidget { ) : const SizedBox(); - // need to be sized for the Google Maps marker generator + // need to be sized for the Google map marker generator child = SizedBox( width: extent, height: extent, diff --git a/lib/widgets/common/map/zoomed_bounds.dart b/lib/widgets/common/map/zoomed_bounds.dart index 1a277a0fa..db65719c9 100644 --- a/lib/widgets/common/map/zoomed_bounds.dart +++ b/lib/widgets/common/map/zoomed_bounds.dart @@ -63,5 +63,19 @@ class ZoomedBounds extends Equatable { ); } + ZoomedBounds copyWith({ + LatLng? sw, + LatLng? ne, + double? zoom, + double? rotation, + }) { + return ZoomedBounds( + sw: sw ?? this.sw, + ne: ne ?? this.ne, + zoom: zoom ?? this.zoom, + rotation: rotation ?? this.rotation, + ); + } + bool contains(LatLng point) => GeoUtils.contains(sw, ne, point); } diff --git a/lib/widgets/map/map_info_row.dart b/lib/widgets/map/map_info_row.dart index d3923a454..bfb7c32b4 100644 --- a/lib/widgets/map/map_info_row.dart +++ b/lib/widgets/map/map_info_row.dart @@ -110,7 +110,7 @@ class _AddressRowState extends State<_AddressRow> { child: Container( alignment: AlignmentDirectional.centerStart, // addresses can include non-latin scripts with inconsistent line height, - // which is especially an issue for relayout/painting of heavy Google maps, + // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout height: Theme.of(context).textTheme.bodyText2!.fontSize! * context.select((mq) => mq.textScaleFactor) * 2, child: ValueListenableBuilder( diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 4960fe20e..e4c6d2140 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -382,7 +382,7 @@ class _MapPageContentState extends State with SingleTickerProvid void _toggleOverlay() => _overlayVisible.value = !_overlayVisible.value; - // TODO TLAD [map] as of Flutter v2.5.1 / google_maps_flutter v2.0.10, toggling overlay changes the size of the map, which is an issue for Google Maps on Android 12 + // TODO TLAD [map] as of Flutter v2.5.1 / google_maps_flutter v2.0.10, toggling overlay changes the size of the map, which is an issue for Google map on Android 12 // cf https://github.com/flutter/flutter/issues/90556 Future _onOverlayVisibleChange({bool animate = true}) async { if (_overlayVisible.value) { diff --git a/lib/widgets/viewer/debug/debug_page.dart b/lib/widgets/viewer/debug/debug_page.dart index ece06d17e..7089a8581 100644 --- a/lib/widgets/viewer/debug/debug_page.dart +++ b/lib/widgets/viewer/debug/debug_page.dart @@ -68,6 +68,7 @@ class ViewerDebugPage extends StatelessWidget { 'sourceTitle': entry.sourceTitle ?? '', 'sourceMimeType': entry.sourceMimeType, 'mimeType': entry.mimeType, + 'isMissingAtPath': '${entry.isMissingAtPath}', }, ), const Divider(),