diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 9b498cc..5d3d43c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -47,6 +47,9 @@
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 290af5b..b2cf09d 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -53,5 +53,9 @@
NSCameraUsageDescription
Provide camera permissions in order to make measurements
+ NSLocationWhenInUseUsageDescription
+ Provide location permissions to save coordinates with your photos
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ Provide location permissions to save coordinates with your photos
diff --git a/lib/application_wrapper.dart b/lib/application_wrapper.dart
index 62fbe83..c7e0176 100644
--- a/lib/application_wrapper.dart
+++ b/lib/application_wrapper.dart
@@ -6,6 +6,7 @@ import 'package:lightmeter/data/analytics/analytics.dart';
import 'package:lightmeter/data/analytics/api/analytics_firebase.dart';
import 'package:lightmeter/data/caffeine_service.dart';
import 'package:lightmeter/data/camera_info_service.dart';
+import 'package:lightmeter/data/geolocation_service.dart';
import 'package:lightmeter/data/haptics_service.dart';
import 'package:lightmeter/data/light_sensor_service.dart';
import 'package:lightmeter/data/permissions_service.dart';
@@ -71,6 +72,7 @@ class _ApplicationWrapperState extends State {
analytics: const LightmeterAnalytics(api: LightmeterAnalyticsFirebase()),
caffeineService: const CaffeineService(),
environment: widget.env.copyWith(hasLightSensor: hasLightSensor),
+ geolocationService: const GeolocationService(),
hapticsService: const HapticsService(),
lightSensorService: const LightSensorService(LocalPlatform()),
permissionsService: const PermissionsService(),
diff --git a/lib/data/geolocation_service.dart b/lib/data/geolocation_service.dart
new file mode 100644
index 0000000..0659914
--- /dev/null
+++ b/lib/data/geolocation_service.dart
@@ -0,0 +1,55 @@
+import 'package:geolocator/geolocator.dart';
+import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
+
+class GeolocationService {
+ const GeolocationService();
+
+ /// Gets the current position and returns Coordinates if successful
+ /// Returns null if location services are disabled or permission is denied
+ Future getCurrentPosition() async {
+ try {
+ // Check if location services are enabled
+ final isLocationServiceEnabled = await Geolocator.isLocationServiceEnabled();
+ if (!isLocationServiceEnabled) {
+ return null;
+ }
+
+ // Check location permission
+ final permission = await Geolocator.checkPermission();
+ if (permission == LocationPermission.denied) {
+ final requestedPermission = await Geolocator.requestPermission();
+ if (requestedPermission == LocationPermission.denied ||
+ requestedPermission == LocationPermission.deniedForever) {
+ return null;
+ }
+ }
+
+ // Get current position
+ final position = await Geolocator.getCurrentPosition(
+ locationSettings: const LocationSettings(
+ timeLimit: Duration(seconds: 10),
+ ),
+ );
+
+ return Coordinates(position.latitude, position.longitude);
+ } catch (e) {
+ // Return null if any error occurs (timeout, no GPS signal, etc.)
+ return null;
+ }
+ }
+
+ /// Checks if location services are enabled
+ Future isLocationServiceEnabled() async {
+ return await Geolocator.isLocationServiceEnabled();
+ }
+
+ /// Checks current location permission status
+ Future checkPermission() async {
+ return await Geolocator.checkPermission();
+ }
+
+ /// Requests location permission
+ Future requestPermission() async {
+ return await Geolocator.requestPermission();
+ }
+}
diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb
index 87ad0c3..5812d14 100644
--- a/lib/l10n/intl_de.arb
+++ b/lib/l10n/intl_de.arb
@@ -171,5 +171,7 @@
"ndFilter": "ND Filter",
"film": "Film",
"note": "Notiz",
- "notSet": "Nicht gesetzt"
+ "notSet": "Nicht gesetzt",
+ "location": "Standort",
+ "noMapsAppFound": "Keine Kartenanwendung gefunden."
}
\ No newline at end of file
diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb
index 66f3ca9..8e0e718 100644
--- a/lib/l10n/intl_en.arb
+++ b/lib/l10n/intl_en.arb
@@ -171,5 +171,7 @@
"ndFilter": "ND Filter",
"film": "Film",
"note": "Note",
- "notSet": "Not set"
+ "notSet": "Not set",
+ "location": "Location",
+ "noMapsAppFound": "No maps application found."
}
\ No newline at end of file
diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb
index 115b11a..9d50c60 100644
--- a/lib/l10n/intl_fr.arb
+++ b/lib/l10n/intl_fr.arb
@@ -162,5 +162,7 @@
"ndFilter": "Filtre ND",
"film": "Film",
"note": "Note",
- "notSet": "Non défini"
+ "notSet": "Non défini",
+ "location": "Emplacement",
+ "noMapsAppFound": "Aucune application de cartes trouvée."
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb
index 2aca4df..06d0e34 100644
--- a/lib/l10n/intl_ru.arb
+++ b/lib/l10n/intl_ru.arb
@@ -161,5 +161,7 @@
"ndFilter": "ND фильтр",
"film": "Плёнка",
"note": "Заметка",
- "notSet": "Не задано"
+ "notSet": "Не задано",
+ "location": "Местоположение",
+ "noMapsAppFound": "Приложение карт не найдено."
}
\ No newline at end of file
diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb
index b9d5531..c35e01e 100644
--- a/lib/l10n/intl_zh.arb
+++ b/lib/l10n/intl_zh.arb
@@ -159,5 +159,7 @@
"ndFilter": "ND 滤镜",
"film": "胶片",
"note": "备注",
- "notSet": "未设置"
+ "notSet": "未设置",
+ "location": "位置",
+ "noMapsAppFound": "未找到地图应用程序。"
}
\ No newline at end of file
diff --git a/lib/providers/logbook_photos_provider.dart b/lib/providers/logbook_photos_provider.dart
index d91b638..0e396ce 100644
--- a/lib/providers/logbook_photos_provider.dart
+++ b/lib/providers/logbook_photos_provider.dart
@@ -2,7 +2,7 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
-import 'package:lightmeter/providers/films_provider.dart';
+import 'package:lightmeter/providers/services_provider.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@@ -63,6 +63,10 @@ class LogbookPhotosProviderState extends State {
required int nd,
}) async {
if (context.isPro) {
+ // Get coordinates from geolocation service
+ final geolocationService = ServicesProvider.of(context).geolocationService;
+ final coordinates = await geolocationService.getCurrentPosition();
+
final photo = LogbookPhoto(
id: const UuidV8().generate(),
name: path,
@@ -70,9 +74,9 @@ class LogbookPhotosProviderState extends State {
ev: ev100,
iso: iso,
nd: nd,
- coordinates: null, // TODO
+ coordinates: coordinates,
);
- //await widget.storageService.addPhoto(photo);
+ await widget.storageService.addPhoto(photo);
_photos[photo.id] = photo;
setState(() {});
} else {
diff --git a/lib/providers/services_provider.dart b/lib/providers/services_provider.dart
index 1db019e..820c339 100644
--- a/lib/providers/services_provider.dart
+++ b/lib/providers/services_provider.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/data/analytics/analytics.dart';
import 'package:lightmeter/data/caffeine_service.dart';
+import 'package:lightmeter/data/geolocation_service.dart';
import 'package:lightmeter/data/haptics_service.dart';
import 'package:lightmeter/data/light_sensor_service.dart';
import 'package:lightmeter/data/permissions_service.dart';
@@ -13,6 +14,7 @@ class ServicesProvider extends InheritedWidget {
final LightmeterAnalytics analytics;
final CaffeineService caffeineService;
final Environment environment;
+ final GeolocationService geolocationService;
final HapticsService hapticsService;
final LightSensorService lightSensorService;
final PermissionsService permissionsService;
@@ -23,6 +25,7 @@ class ServicesProvider extends InheritedWidget {
required this.analytics,
required this.caffeineService,
required this.environment,
+ required this.geolocationService,
required this.hapticsService,
required this.lightSensorService,
required this.permissionsService,
diff --git a/lib/screens/logbook/screen_logbook.dart b/lib/screens/logbook/screen_logbook.dart
index 34b4aa8..232b48f 100644
--- a/lib/screens/logbook/screen_logbook.dart
+++ b/lib/screens/logbook/screen_logbook.dart
@@ -1,14 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart';
-import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/navigation/routes.dart';
import 'package:lightmeter/platform_config.dart';
-import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/logbook_photos_provider.dart';
import 'package:lightmeter/res/dimens.dart';
-import 'package:lightmeter/screens/equipment_profile_edit/flow_equipment_profile_edit.dart';
-import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart';
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
diff --git a/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart
index 2a794d9..5993b4a 100644
--- a/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart
+++ b/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart
@@ -23,8 +23,6 @@ class LogbookPhotoEditBloc extends Bloc createState() => LogbookPhotoCoordinatesListTileState();
+}
+
+class LogbookPhotoCoordinatesListTileState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ buildWhen: (previous, current) => previous.note != current.note,
+ builder: (context, state) {
+ final coords = state.coordinates;
+ final hasCoords = coords != null;
+ final text = hasCoords ? '${coords.latitude.toStringAsFixed(6)}, ${coords.longitude.toStringAsFixed(6)}' : '-';
+ return ListTile(
+ leading: const Icon(Icons.location_on_outlined),
+ title: Text(S.of(context).location),
+ trailing: Text(text),
+ onTap: hasCoords
+ ? () async {
+ final lat = coords.latitude;
+ final lng = coords.longitude;
+ final availableMaps = await MapLauncher.installedMaps;
+ if (availableMaps.isEmpty) {
+ // Fallback to Google Maps in browser
+ final url = Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng');
+ if (await canLaunchUrl(url)) {
+ await launchUrl(url, mode: LaunchMode.externalApplication);
+ } else if (mounted) {
+ _showSnackBar();
+ }
+ return;
+ }
+ await MapLauncher.showMarker(
+ mapType: availableMaps.first.mapType,
+ coords: Coords(lat, lng),
+ title: text,
+ description: state.note,
+ );
+ }
+ : null,
+ );
+ },
+ );
+ }
+
+ Future _showSnackBar() async {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(S.of(context).youDontHaveMailApp),
+ behavior: SnackBarBehavior.floating,
+ ),
+ );
+ }
+}
diff --git a/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart
index 51e95c7..0431520 100644
--- a/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart
+++ b/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart
@@ -6,6 +6,7 @@ import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/platform_config.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart';
+import 'package:lightmeter/screens/logbook_photo_edit/components/coordinates_list_tile/widget_list_tile_coordinates_logbook_photo.dart';
import 'package:lightmeter/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart';
import 'package:lightmeter/screens/logbook_photo_edit/event_logbook_photo_edit.dart';
import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart';
@@ -77,6 +78,7 @@ class _LogbookPhotoEditScreenState extends State {
child: const Column(
children: [
_DateListTile(),
+ LogbookPhotoCoordinatesListTile(),
_NoteListTile(),
_EvListTile(),
_IsoListTile(),
diff --git a/pubspec.yaml b/pubspec.yaml
index 395d9f3..8b6f49b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -26,6 +26,7 @@ dependencies:
flutter_localizations:
sdk: flutter
flutter_native_splash: 2.4.4
+ geolocator: 13.0.1
intl: 0.19.0
intl_utils: 2.8.7
light_sensor: 3.0.1
@@ -46,6 +47,7 @@ dependencies:
url_launcher_ios: 6.3.2
uuid: 4.5.1
vibration: 2.0.1
+ map_launcher: 3.2.0
dev_dependencies:
args: 2.6.0
@@ -65,6 +67,11 @@ dev_dependencies:
test: 1.25.7
dependency_overrides:
+ geolocator_android: 4.6.1
+ m3_lightmeter_iap:
+ path: /Users/vodemn/Documents/GitHub/Vodemn/m3_lightmeter_iap
+ m3_lightmeter_resources:
+ path: /Users/vodemn/Documents/GitHub/Vodemn/m3_lightmeter_resources
material_color_utilities: 0.11.1
flutter: