#1087 open external map app from map views

This commit is contained in:
Thibault Deckers 2024-08-04 19:41:25 +02:00
parent 2d5a7f6c27
commit 4d80dfe1d6
8 changed files with 109 additions and 56 deletions

View file

@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
- Viewer: display more items in tag/copy/move quick action choosers
- Collection: sort by duration
- Map: open external map app from map views
### Changed

View file

@ -8,6 +8,7 @@ extension ExtraMapActionView on MapAction {
final l10n = context.l10n;
return switch (this) {
MapAction.selectStyle => l10n.mapStyleTooltip,
MapAction.openMapApp => l10n.entryActionOpenMap,
MapAction.zoomIn => l10n.mapZoomInTooltip,
MapAction.zoomOut => l10n.mapZoomOutTooltip,
};
@ -18,6 +19,7 @@ extension ExtraMapActionView on MapAction {
IconData _getIconData() {
return switch (this) {
MapAction.selectStyle => AIcons.layers,
MapAction.openMapApp => AIcons.openOutside,
MapAction.zoomIn => AIcons.zoomIn,
MapAction.zoomOut => AIcons.zoomOut,
};

View file

@ -124,7 +124,13 @@ class MapButtonPanel extends StatelessWidget {
Padding(
padding: EdgeInsets.only(top: padding),
// key is expected by test driver
child: _buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')),
child: Column(
children: [
_buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')),
SizedBox(height: padding),
_buildButton(context, MapAction.openMapApp),
],
),
),
],
),

View file

@ -26,6 +26,8 @@ class MapActionDelegate {
),
onSelection: (v) => settings.mapStyle = v,
);
case MapAction.openMapApp:
OpenMapAppNotification().dispatch(context);
case MapAction.zoomIn:
controller?.zoomBy(1);
case MapAction.zoomOut:
@ -33,3 +35,5 @@ class MapActionDelegate {
}
}
}
class OpenMapAppNotification extends Notification {}

View file

@ -12,6 +12,7 @@ import 'package:aves/model/settings/enums/map_style.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/view/view.dart';
@ -28,6 +29,7 @@ import 'package:aves/widgets/common/map/geo_map.dart';
import 'package:aves/widgets/common/map/map_action_delegate.dart';
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart';
import 'package:aves/widgets/map/scroller.dart';
import 'package:aves/widgets/viewer/controls/notifications.dart';
@ -188,6 +190,8 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
_goToCollection(notification.filter);
} else if (notification is FilterNotification) {
_goToCollection(notification.filter);
} else if (notification is OpenMapAppNotification) {
_openMapApp();
} else {
return false;
}
@ -434,6 +438,15 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
);
}
Future<void> _openMapApp() async {
final latLng = _dotEntryNotifier.value?.latLng ?? _mapController.idleBounds?.projectedCenter;
if (latLng != null) {
await appService.openMap(latLng).then((success) {
if (!success) showNoMatchingAppDialog(context);
});
}
}
// overlay
void _toggleOverlay() => _overlayVisible.value = !_overlayVisible.value;

View file

@ -10,7 +10,9 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/map/geo_map.dart';
import 'package:aves/widgets/common/map/map_action_delegate.dart';
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/map/map_page.dart';
import 'package:aves/widgets/viewer/info/common.dart';
import 'package:aves_map/aves_map.dart';
@ -76,7 +78,15 @@ class _LocationSectionState extends State<LocationSection> {
if (!entry.hasGps) return const SizedBox();
final canNavigate = context.select<ValueNotifier<AppMode>, bool>((v) => v.value.canNavigate);
return Column(
return NotificationListener(
onNotification: (notification) {
if (notification is OpenMapAppNotification) {
_openMapApp();
return true;
}
return false;
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.showTitle) const SectionRow(icon: AIcons.location),
@ -133,6 +143,7 @@ class _LocationSectionState extends State<LocationSection> {
},
),
],
),
);
}
@ -155,6 +166,15 @@ class _LocationSectionState extends State<LocationSection> {
);
}
Future<void> _openMapApp() async {
final latLng = entry.latLng;
if (latLng != null) {
await appService.openMap(latLng).then((success) {
if (!success) showNoMatchingAppDialog(context);
});
}
}
void _onMetadataChanged() {
setState(() {});

View file

@ -6,6 +6,9 @@ import 'package:latlong2/latlong.dart';
class AvesMapController {
final StreamController _streamController = StreamController.broadcast();
ZoomedBounds? _idleBounds;
ZoomedBounds? get idleBounds => _idleBounds;
Stream<dynamic> get _events => _streamController.stream;
@ -38,7 +41,10 @@ class AvesMapController {
void zoomBy(double delta) => _streamController.add(MapControllerZoomEvent(delta));
void notifyIdle(ZoomedBounds bounds) => _streamController.add(MapIdleUpdate(bounds));
void notifyIdle(ZoomedBounds bounds) {
_idleBounds = bounds;
_streamController.add(MapIdleUpdate(bounds));
}
void notifyMarkerLocationChange() => _streamController.add(MapMarkerLocationChangeEvent());
}

View file

@ -1,5 +1,6 @@
enum MapAction {
selectStyle,
openMapApp,
zoomIn,
zoomOut,
}