map shortcut, geo: uri handling

This commit is contained in:
Thibault Deckers 2024-10-02 01:11:02 +02:00
parent fb0b4dcab2
commit ec59e348c5
15 changed files with 143 additions and 55 deletions

View file

@ -183,6 +183,13 @@
<data android:scheme="content" /> <data android:scheme="content" />
<data android:scheme="file" /> <data android:scheme="file" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="geo" />
</intent-filter>
<!-- <!--
<intent-filter> <intent-filter>
<action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.EDIT" />

View file

@ -313,6 +313,13 @@ open class MainActivity : FlutterFragmentActivity() {
"com.android.camera.action.REVIEW", "com.android.camera.action.REVIEW",
"com.android.camera.action.SPLIT_SCREEN_REVIEW" -> { "com.android.camera.action.SPLIT_SCREEN_REVIEW" -> {
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri -> (intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
if (uri.scheme == "geo") {
return hashMapOf(
INTENT_DATA_KEY_ACTION to INTENT_ACTION_VIEW_GEO,
INTENT_DATA_KEY_URI to uri.toString(),
)
}
// MIME type is optional // MIME type is optional
val type = intent.type ?: intent.resolveType(this) val type = intent.type ?: intent.resolveType(this)
val fields = hashMapOf<String, Any?>( val fields = hashMapOf<String, Any?>(
@ -484,7 +491,16 @@ open class MainActivity : FlutterFragmentActivity() {
.setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_search else R.drawable.ic_shortcut_search)) .setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_search else R.drawable.ic_shortcut_search))
.setIntent( .setIntent(
Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java) Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java)
.putExtra(EXTRA_KEY_PAGE, "/search") .putExtra(EXTRA_KEY_PAGE, SEARCH_PAGE_ROUTE_NAME)
)
.build()
val map = ShortcutInfoCompat.Builder(this, "map")
.setShortLabel(getString(R.string.map_shortcut_short_label))
.setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_map else R.drawable.ic_shortcut_map))
.setIntent(
Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java)
.putExtra(EXTRA_KEY_PAGE, MAP_PAGE_ROUTE_NAME)
) )
.build() .build()
@ -493,21 +509,12 @@ open class MainActivity : FlutterFragmentActivity() {
.setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_movie else R.drawable.ic_shortcut_movie)) .setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_movie else R.drawable.ic_shortcut_movie))
.setIntent( .setIntent(
Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java) Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java)
.putExtra(EXTRA_KEY_PAGE, "/collection") .putExtra(EXTRA_KEY_PAGE, COLLECTION_PAGE_ROUTE_NAME)
.putExtra("filters", arrayOf("{\"type\":\"mime\",\"mime\":\"video/*\"}")) .putExtra("filters", arrayOf("{\"type\":\"mime\",\"mime\":\"video/*\"}"))
) )
.build() .build()
val safeMode = ShortcutInfoCompat.Builder(this, "safeMode") val shortcutInfoList = listOf(videos, search, map)
.setShortLabel(getString(R.string.safe_mode_shortcut_short_label))
.setIcon(IconCompat.createWithResource(this, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_safe_mode else R.drawable.ic_shortcut_safe_mode))
.setIntent(
Intent(Intent.ACTION_MAIN, null, this, MainActivity::class.java)
.putExtra(EXTRA_KEY_SAFE_MODE, true)
)
.build()
val shortcutInfoList = listOf(videos, search, safeMode)
ShortcutManagerCompat.setDynamicShortcuts(this, shortcutInfoList) ShortcutManagerCompat.setDynamicShortcuts(this, shortcutInfoList)
Log.i(LOG_TAG, "set shortcuts: ${shortcutInfoList.joinToString(", ") { v -> v.id }}") Log.i(LOG_TAG, "set shortcuts: ${shortcutInfoList.joinToString(", ") { v -> v.id }}")
} }
@ -537,6 +544,7 @@ open class MainActivity : FlutterFragmentActivity() {
const val INTENT_ACTION_SEARCH = "search" const val INTENT_ACTION_SEARCH = "search"
const val INTENT_ACTION_SET_WALLPAPER = "set_wallpaper" const val INTENT_ACTION_SET_WALLPAPER = "set_wallpaper"
const val INTENT_ACTION_VIEW = "view" const val INTENT_ACTION_VIEW = "view"
const val INTENT_ACTION_VIEW_GEO = "view_geo"
const val INTENT_ACTION_WIDGET_OPEN = "widget_open" const val INTENT_ACTION_WIDGET_OPEN = "widget_open"
const val INTENT_ACTION_WIDGET_SETTINGS = "widget_settings" const val INTENT_ACTION_WIDGET_SETTINGS = "widget_settings"
@ -560,6 +568,11 @@ open class MainActivity : FlutterFragmentActivity() {
const val EXTRA_KEY_SAFE_MODE = "safeMode" const val EXTRA_KEY_SAFE_MODE = "safeMode"
const val EXTRA_KEY_WIDGET_ID = "widgetId" const val EXTRA_KEY_WIDGET_ID = "widgetId"
// dart page routes
const val COLLECTION_PAGE_ROUTE_NAME = "/collection"
const val MAP_PAGE_ROUTE_NAME = "/map"
const val SEARCH_PAGE_ROUTE_NAME = "/search"
// request code to pending runnable // request code to pending runnable
val pendingStorageAccessResultHandlers = ConcurrentHashMap<Int, PendingStorageAccessResultHandler>() val pendingStorageAccessResultHandlers = ConcurrentHashMap<Int, PendingStorageAccessResultHandler>()

View file

@ -1,16 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:tint="@color/ic_shortcut_foreground"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="1.7226"
android:scaleY="1.7226"
android:translateX="33.3288"
android:translateY="33.3288">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM19.46,9.12l-2.78,1.15c-0.51,-1.36 -1.58,-2.44 -2.95,-2.94l1.15,-2.78C16.98,5.35 18.65,7.02 19.46,9.12zM12,15c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3s3,1.34 3,3S13.66,15 12,15zM9.13,4.54l1.17,2.78c-1.38,0.5 -2.47,1.59 -2.98,2.97L4.54,9.13C5.35,7.02 7.02,5.35 9.13,4.54zM4.54,14.87l2.78,-1.15c0.51,1.38 1.59,2.46 2.97,2.96l-1.17,2.78C7.02,18.65 5.35,16.98 4.54,14.87zM14.88,19.46l-1.15,-2.78c1.37,-0.51 2.45,-1.59 2.95,-2.97l2.78,1.17C18.65,16.98 16.98,18.65 14.88,19.46z" />
</group>
</vector>

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="@color/ic_shortcut_background"
android:pathData="M0,24 A1,1 0 1,1 48,24 A1,1 0 1,1 0,24" />
<group
android:translateX="12"
android:translateY="12">
<path
android:fillColor="@color/ic_shortcut_foreground"
android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z" />
</group>
</vector>

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:tint="@color/ic_shortcut_foreground"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="1.7226"
android:scaleY="1.7226"
android:translateX="33.3288"
android:translateY="33.3288">
<path
android:fillColor="@android:color/white"
android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z" />
</group>
</vector>

View file

@ -1,16 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="@color/ic_shortcut_background"
android:pathData="M0,24 A1,1 0 1,1 48,24 A1,1 0 1,1 0,24" />
<group
android:translateX="12"
android:translateY="12">
<path
android:fillColor="@color/ic_shortcut_foreground"
android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM19.46,9.12l-2.78,1.15c-0.51,-1.36 -1.58,-2.44 -2.95,-2.94l1.15,-2.78C16.98,5.35 18.65,7.02 19.46,9.12zM12,15c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3s3,1.34 3,3S13.66,15 12,15zM9.13,4.54l1.17,2.78c-1.38,0.5 -2.47,1.59 -2.98,2.97L4.54,9.13C5.35,7.02 7.02,5.35 9.13,4.54zM4.54,14.87l2.78,-1.15c0.51,1.38 1.59,2.46 2.97,2.96l-1.17,2.78C7.02,18.65 5.35,16.98 4.54,14.87zM14.88,19.46l-1.15,-2.78c1.37,-0.51 2.45,-1.59 2.95,-2.97l2.78,1.17C18.65,16.98 16.98,18.65 14.88,19.46z" />
</group>
</vector>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_shortcut_background" /> <background android:drawable="@color/ic_shortcut_background" />
<foreground android:drawable="@drawable/ic_shortcut_safe_mode_foreground" /> <foreground android:drawable="@drawable/ic_shortcut_map_foreground" />
</adaptive-icon> </adaptive-icon>

View file

@ -3,7 +3,7 @@
<string name="app_name">Aves</string> <string name="app_name">Aves</string>
<string name="app_widget_label">Photo Frame</string> <string name="app_widget_label">Photo Frame</string>
<string name="wallpaper">Wallpaper</string> <string name="wallpaper">Wallpaper</string>
<string name="safe_mode_shortcut_short_label">Safe mode</string> <string name="map_shortcut_short_label">Map</string>
<string name="search_shortcut_short_label">Search</string> <string name="search_shortcut_short_label">Search</string>
<string name="videos_shortcut_short_label">Videos</string> <string name="videos_shortcut_short_label">Videos</string>
<string name="analysis_channel_name">Media scan</string> <string name="analysis_channel_name">Media scan</string>

View file

@ -7,6 +7,7 @@ class IntentActions {
static const search = 'search'; static const search = 'search';
static const setWallpaper = 'set_wallpaper'; static const setWallpaper = 'set_wallpaper';
static const view = 'view'; static const view = 'view';
static const viewGeo = 'view_geo';
static const widgetOpen = 'widget_open'; static const widgetOpen = 'widget_open';
static const widgetSettings = 'widget_settings'; static const widgetSettings = 'widget_settings';
} }

View file

@ -248,7 +248,8 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
Future<void> _goToViewer(CollectionLens collection, AvesEntry entry) async { Future<void> _goToViewer(CollectionLens collection, AvesEntry entry) async {
// track viewer entry for dynamic hero placeholder // track viewer entry for dynamic hero placeholder
WidgetsBinding.instance.addPostFrameCallback((_) => context.read<ViewerEntryNotifier>().value = entry); final viewerEntryNotifier = context.read<ViewerEntryNotifier>();
WidgetsBinding.instance.addPostFrameCallback((_) => viewerEntryNotifier.value = entry);
final selection = context.read<Selection<AvesEntry>>(); final selection = context.read<Selection<AvesEntry>>();
await Navigator.maybeOf(context)?.push( await Navigator.maybeOf(context)?.push(
@ -284,7 +285,7 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
// TODO TLAD fix timing when transition is incomplete, e.g. when going back while going to the viewer // TODO TLAD fix timing when transition is incomplete, e.g. when going back while going to the viewer
await Future.delayed(ADurations.pageTransitionExact * timeDilation); await Future.delayed(ADurations.pageTransitionExact * timeDilation);
} }
context.read<ViewerEntryNotifier>().value = null; viewerEntryNotifier.value = null;
} }
} }

View file

@ -10,6 +10,7 @@ import 'package:aves/widgets/common/map/map_action_delegate.dart';
import 'package:aves_map/aves_map.dart'; import 'package:aves_map/aves_map.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -42,6 +43,12 @@ class MapButtonPanel extends StatelessWidget {
tooltip: MaterialLocalizations.of(context).backButtonTooltip, tooltip: MaterialLocalizations.of(context).backButtonTooltip,
); );
} }
case MapNavigationButton.close:
navigationButton = MapOverlayButton(
icon: const CloseButtonIcon(),
onPressed: SystemNavigator.pop,
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
);
case MapNavigationButton.map: case MapNavigationButton.map:
if (openMapPage != null) { if (openMapPage != null) {
navigationButton = MapOverlayButton( navigationButton = MapOverlayButton(

View file

@ -36,6 +36,7 @@ class GeoMap extends StatefulWidget {
final List<AvesEntry> entries; final List<AvesEntry> entries;
final Size availableSize; final Size availableSize;
final LatLng? initialCenter; final LatLng? initialCenter;
final double? initialZoom;
final ValueNotifier<bool> isAnimatingNotifier; final ValueNotifier<bool> isAnimatingNotifier;
final ValueNotifier<LatLng?>? dotLocationNotifier; final ValueNotifier<LatLng?>? dotLocationNotifier;
final ValueNotifier<double>? overlayOpacityNotifier; final ValueNotifier<double>? overlayOpacityNotifier;
@ -62,6 +63,7 @@ class GeoMap extends StatefulWidget {
required this.entries, required this.entries,
required this.availableSize, required this.availableSize,
this.initialCenter, this.initialCenter,
this.initialZoom,
required this.isAnimatingNotifier, required this.isAnimatingNotifier,
this.dotLocationNotifier, this.dotLocationNotifier,
this.overlayOpacityNotifier, this.overlayOpacityNotifier,
@ -313,6 +315,8 @@ class _GeoMapState extends State<GeoMap> {
); );
} }
} }
final initialZoom = widget.initialZoom ?? settings.infoMapZoom;
if (bounds == null) { if (bounds == null) {
LatLng? centerToSave; LatLng? centerToSave;
final initialCenter = widget.initialCenter; final initialCenter = widget.initialCenter;
@ -320,7 +324,7 @@ class _GeoMapState extends State<GeoMap> {
// fit map for specified center and user zoom // fit map for specified center and user zoom
bounds = ZoomedBounds.fromPoints( bounds = ZoomedBounds.fromPoints(
points: {initialCenter}, points: {initialCenter},
collocationZoom: settings.infoMapZoom, collocationZoom: initialZoom,
); );
centerToSave = initialCenter; centerToSave = initialCenter;
} else { } else {
@ -345,7 +349,7 @@ class _GeoMapState extends State<GeoMap> {
} }
bounds = ZoomedBounds.fromPoints( bounds = ZoomedBounds.fromPoints(
points: {center}, points: {center},
collocationZoom: settings.infoMapZoom, collocationZoom: initialZoom,
); );
} }

View file

@ -1,13 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/app_mode.dart'; import 'package:aves/app_mode.dart';
import 'package:aves/model/app/intent.dart';
import 'package:aves/model/app/permissions.dart'; import 'package:aves/model/app/permissions.dart';
import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/entry/extensions/catalog.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/app/intent.dart'; import 'package:aves/model/filters/location.dart';
import 'package:aves/model/settings/enums/home_page.dart'; import 'package:aves/model/settings/enums/home_page.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
@ -29,6 +30,7 @@ import 'package:aves/widgets/editor/entry_editor_page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart'; import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart'; import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/map/map_page.dart';
import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/settings/home_widget_settings_page.dart'; import 'package:aves/widgets/settings/home_widget_settings_page.dart';
import 'package:aves/widgets/settings/screen_saver_settings_page.dart'; import 'package:aves/widgets/settings/screen_saver_settings_page.dart';
@ -38,6 +40,7 @@ import 'package:aves/widgets/wallpaper_page.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -62,12 +65,14 @@ class _HomePageState extends State<HomePage> {
String? _initialRouteName, _initialSearchQuery; String? _initialRouteName, _initialSearchQuery;
Set<CollectionFilter>? _initialFilters; Set<CollectionFilter>? _initialFilters;
String? _initialExplorerPath; String? _initialExplorerPath;
(LatLng, double)? _initialLocationZoom;
List<String>? _secureUris; List<String>? _secureUris;
static const allowedShortcutRoutes = [ static const allowedShortcutRoutes = [
AlbumListPage.routeName, AlbumListPage.routeName,
CollectionPage.routeName, CollectionPage.routeName,
ExplorerPage.routeName, ExplorerPage.routeName,
MapPage.routeName,
SearchPage.routeName, SearchPage.routeName,
]; ];
@ -118,6 +123,32 @@ class _HomePageState extends State<HomePage> {
case IntentActions.view: case IntentActions.view:
appMode = AppMode.view; appMode = AppMode.view;
_secureUris = (intentData[IntentDataKeys.secureUris] as List?)?.cast<String>(); _secureUris = (intentData[IntentDataKeys.secureUris] as List?)?.cast<String>();
case IntentActions.viewGeo:
error = true;
if (intentUri != null) {
final geoUri = Uri.tryParse(intentUri);
if (geoUri != null) {
// e.g. `geo:44.4361283,26.1027248?z=4.0(Bucharest)`
// cf https://en.wikipedia.org/wiki/Geo_URI_scheme
// cf https://developer.android.com/guide/components/intents-common#ViewMap
final coordinates = geoUri.path.split(',');
if (coordinates.length == 2) {
final lat = double.tryParse(coordinates[0]);
final lon = double.tryParse(coordinates[1]);
if (lat != null && lon != null) {
double? zoom;
final zoomString = geoUri.queryParameters['z'];
if (zoomString != null) {
zoom = double.tryParse(zoomString);
}
_initialRouteName = MapPage.routeName;
_initialLocationZoom = (LatLng(lat, lon), zoom ?? settings.infoMapZoom);
error = false;
}
}
}
}
break;
case IntentActions.edit: case IntentActions.edit:
appMode = AppMode.edit; appMode = AppMode.edit;
case IntentActions.setWallpaper: case IntentActions.setWallpaper:
@ -361,6 +392,18 @@ class _HomePageState extends State<HomePage> {
return buildRoute((context) => const AlbumListPage()); return buildRoute((context) => const AlbumListPage());
case TagListPage.routeName: case TagListPage.routeName:
return buildRoute((context) => const TagListPage()); return buildRoute((context) => const TagListPage());
case MapPage.routeName:
return buildRoute((context) {
final mapCollection = CollectionLens(
source: source,
filters: {LocationFilter.located},
);
return MapPage(
collection: mapCollection,
initialLocation: _initialLocationZoom?.$1,
initialZoom: _initialLocationZoom?.$2,
);
});
case ExplorerPage.routeName: case ExplorerPage.routeName:
final path = _initialExplorerPath ?? settings.homeCustomExplorerPath; final path = _initialExplorerPath ?? settings.homeCustomExplorerPath;
return buildRoute((context) => ExplorerPage(path: path)); return buildRoute((context) => ExplorerPage(path: path));

View file

@ -43,15 +43,19 @@ import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MapPage extends StatelessWidget { class MapPage extends StatelessWidget {
static const routeName = '/collection/map'; static const routeName = '/map';
final CollectionLens collection; final CollectionLens collection;
final LatLng? initialLocation;
final double? initialZoom;
final AvesEntry? initialEntry; final AvesEntry? initialEntry;
final MappedGeoTiff? overlayEntry; final MappedGeoTiff? overlayEntry;
const MapPage({ const MapPage({
super.key, super.key,
required this.collection, required this.collection,
this.initialLocation,
this.initialZoom,
this.initialEntry, this.initialEntry,
this.overlayEntry, this.overlayEntry,
}); });
@ -70,6 +74,8 @@ class MapPage extends StatelessWidget {
bottom: true, bottom: true,
child: _Content( child: _Content(
collection: collection, collection: collection,
initialLocation: initialLocation,
initialZoom: initialZoom,
initialEntry: initialEntry, initialEntry: initialEntry,
overlayEntry: overlayEntry, overlayEntry: overlayEntry,
), ),
@ -81,11 +87,15 @@ class MapPage extends StatelessWidget {
class _Content extends StatefulWidget { class _Content extends StatefulWidget {
final CollectionLens collection; final CollectionLens collection;
final LatLng? initialLocation;
final double? initialZoom;
final AvesEntry? initialEntry; final AvesEntry? initialEntry;
final MappedGeoTiff? overlayEntry; final MappedGeoTiff? overlayEntry;
const _Content({ const _Content({
required this.collection, required this.collection,
this.initialLocation,
this.initialZoom,
this.initialEntry, this.initialEntry,
this.overlayEntry, this.overlayEntry,
}); });
@ -252,10 +262,11 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
} }
Widget _buildMap() { Widget _buildMap() {
final canPop = Navigator.maybeOf(context)?.canPop() == true;
Widget child = MapTheme( Widget child = MapTheme(
interactive: true, interactive: true,
showCoordinateFilter: true, showCoordinateFilter: true,
navigationButton: MapNavigationButton.back, navigationButton: canPop ? MapNavigationButton.back : MapNavigationButton.close,
scale: _overlayScale, scale: _overlayScale,
child: GeoMap( child: GeoMap(
// key is expected by test driver // key is expected by test driver
@ -264,7 +275,8 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
collectionListenable: openingCollection, collectionListenable: openingCollection,
entries: openingCollection.sortedEntries, entries: openingCollection.sortedEntries,
availableSize: MediaQuery.sizeOf(context), availableSize: MediaQuery.sizeOf(context),
initialCenter: widget.initialEntry?.latLng ?? widget.overlayEntry?.center, initialCenter: widget.initialLocation ?? widget.initialEntry?.latLng ?? widget.overlayEntry?.center,
initialZoom: widget.initialZoom,
isAnimatingNotifier: _isPageAnimatingNotifier, isAnimatingNotifier: _isPageAnimatingNotifier,
dotLocationNotifier: _dotLocationNotifier, dotLocationNotifier: _dotLocationNotifier,
overlayOpacityNotifier: _overlayOpacityNotifier, overlayOpacityNotifier: _overlayOpacityNotifier,

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum MapNavigationButton { back, map, none } enum MapNavigationButton { back, close, map, none }
class MapThemeData { class MapThemeData {
final bool interactive, showCoordinateFilter; final bool interactive, showCoordinateFilter;