mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-08-05 04:36:41 +00:00
save geolocation
This commit is contained in:
parent
1412ce2b3e
commit
39501ee4ac
16 changed files with 163 additions and 14 deletions
|
@ -47,6 +47,9 @@
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.MICROPHONE" tools:node="remove" />
|
<uses-permission android:name="android.permission.MICROPHONE" tools:node="remove" />
|
||||||
<uses-feature android:name="android.hardware.microphone" android:required="false" />
|
<uses-feature android:name="android.hardware.microphone" android:required="false" />
|
||||||
|
|
|
@ -53,5 +53,9 @@
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Provide camera permissions in order to make measurements</string>
|
<string>Provide camera permissions in order to make measurements</string>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>Provide location permissions to save coordinates with your photos</string>
|
||||||
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
|
<string>Provide location permissions to save coordinates with your photos</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:lightmeter/data/analytics/analytics.dart';
|
||||||
import 'package:lightmeter/data/analytics/api/analytics_firebase.dart';
|
import 'package:lightmeter/data/analytics/api/analytics_firebase.dart';
|
||||||
import 'package:lightmeter/data/caffeine_service.dart';
|
import 'package:lightmeter/data/caffeine_service.dart';
|
||||||
import 'package:lightmeter/data/camera_info_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/haptics_service.dart';
|
||||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
import 'package:lightmeter/data/permissions_service.dart';
|
||||||
|
@ -71,6 +72,7 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
|
||||||
analytics: const LightmeterAnalytics(api: LightmeterAnalyticsFirebase()),
|
analytics: const LightmeterAnalytics(api: LightmeterAnalyticsFirebase()),
|
||||||
caffeineService: const CaffeineService(),
|
caffeineService: const CaffeineService(),
|
||||||
environment: widget.env.copyWith(hasLightSensor: hasLightSensor),
|
environment: widget.env.copyWith(hasLightSensor: hasLightSensor),
|
||||||
|
geolocationService: const GeolocationService(),
|
||||||
hapticsService: const HapticsService(),
|
hapticsService: const HapticsService(),
|
||||||
lightSensorService: const LightSensorService(LocalPlatform()),
|
lightSensorService: const LightSensorService(LocalPlatform()),
|
||||||
permissionsService: const PermissionsService(),
|
permissionsService: const PermissionsService(),
|
||||||
|
|
55
lib/data/geolocation_service.dart
Normal file
55
lib/data/geolocation_service.dart
Normal file
|
@ -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<Coordinates?> 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<bool> isLocationServiceEnabled() async {
|
||||||
|
return await Geolocator.isLocationServiceEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks current location permission status
|
||||||
|
Future<LocationPermission> checkPermission() async {
|
||||||
|
return await Geolocator.checkPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests location permission
|
||||||
|
Future<LocationPermission> requestPermission() async {
|
||||||
|
return await Geolocator.requestPermission();
|
||||||
|
}
|
||||||
|
}
|
|
@ -171,5 +171,7 @@
|
||||||
"ndFilter": "ND Filter",
|
"ndFilter": "ND Filter",
|
||||||
"film": "Film",
|
"film": "Film",
|
||||||
"note": "Notiz",
|
"note": "Notiz",
|
||||||
"notSet": "Nicht gesetzt"
|
"notSet": "Nicht gesetzt",
|
||||||
|
"location": "Standort",
|
||||||
|
"noMapsAppFound": "Keine Kartenanwendung gefunden."
|
||||||
}
|
}
|
|
@ -171,5 +171,7 @@
|
||||||
"ndFilter": "ND Filter",
|
"ndFilter": "ND Filter",
|
||||||
"film": "Film",
|
"film": "Film",
|
||||||
"note": "Note",
|
"note": "Note",
|
||||||
"notSet": "Not set"
|
"notSet": "Not set",
|
||||||
|
"location": "Location",
|
||||||
|
"noMapsAppFound": "No maps application found."
|
||||||
}
|
}
|
|
@ -162,5 +162,7 @@
|
||||||
"ndFilter": "Filtre ND",
|
"ndFilter": "Filtre ND",
|
||||||
"film": "Film",
|
"film": "Film",
|
||||||
"note": "Note",
|
"note": "Note",
|
||||||
"notSet": "Non défini"
|
"notSet": "Non défini",
|
||||||
|
"location": "Emplacement",
|
||||||
|
"noMapsAppFound": "Aucune application de cartes trouvée."
|
||||||
}
|
}
|
|
@ -161,5 +161,7 @@
|
||||||
"ndFilter": "ND фильтр",
|
"ndFilter": "ND фильтр",
|
||||||
"film": "Плёнка",
|
"film": "Плёнка",
|
||||||
"note": "Заметка",
|
"note": "Заметка",
|
||||||
"notSet": "Не задано"
|
"notSet": "Не задано",
|
||||||
|
"location": "Местоположение",
|
||||||
|
"noMapsAppFound": "Приложение карт не найдено."
|
||||||
}
|
}
|
|
@ -159,5 +159,7 @@
|
||||||
"ndFilter": "ND 滤镜",
|
"ndFilter": "ND 滤镜",
|
||||||
"film": "胶片",
|
"film": "胶片",
|
||||||
"note": "备注",
|
"note": "备注",
|
||||||
"notSet": "未设置"
|
"notSet": "未设置",
|
||||||
|
"location": "位置",
|
||||||
|
"noMapsAppFound": "未找到地图应用程序。"
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.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:lightmeter/utils/context_utils.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
@ -63,6 +63,10 @@ class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
|
||||||
required int nd,
|
required int nd,
|
||||||
}) async {
|
}) async {
|
||||||
if (context.isPro) {
|
if (context.isPro) {
|
||||||
|
// Get coordinates from geolocation service
|
||||||
|
final geolocationService = ServicesProvider.of(context).geolocationService;
|
||||||
|
final coordinates = await geolocationService.getCurrentPosition();
|
||||||
|
|
||||||
final photo = LogbookPhoto(
|
final photo = LogbookPhoto(
|
||||||
id: const UuidV8().generate(),
|
id: const UuidV8().generate(),
|
||||||
name: path,
|
name: path,
|
||||||
|
@ -70,9 +74,9 @@ class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
|
||||||
ev: ev100,
|
ev: ev100,
|
||||||
iso: iso,
|
iso: iso,
|
||||||
nd: nd,
|
nd: nd,
|
||||||
coordinates: null, // TODO
|
coordinates: coordinates,
|
||||||
);
|
);
|
||||||
//await widget.storageService.addPhoto(photo);
|
await widget.storageService.addPhoto(photo);
|
||||||
_photos[photo.id] = photo;
|
_photos[photo.id] = photo;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/analytics/analytics.dart';
|
import 'package:lightmeter/data/analytics/analytics.dart';
|
||||||
import 'package:lightmeter/data/caffeine_service.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/haptics_service.dart';
|
||||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
import 'package:lightmeter/data/permissions_service.dart';
|
||||||
|
@ -13,6 +14,7 @@ class ServicesProvider extends InheritedWidget {
|
||||||
final LightmeterAnalytics analytics;
|
final LightmeterAnalytics analytics;
|
||||||
final CaffeineService caffeineService;
|
final CaffeineService caffeineService;
|
||||||
final Environment environment;
|
final Environment environment;
|
||||||
|
final GeolocationService geolocationService;
|
||||||
final HapticsService hapticsService;
|
final HapticsService hapticsService;
|
||||||
final LightSensorService lightSensorService;
|
final LightSensorService lightSensorService;
|
||||||
final PermissionsService permissionsService;
|
final PermissionsService permissionsService;
|
||||||
|
@ -23,6 +25,7 @@ class ServicesProvider extends InheritedWidget {
|
||||||
required this.analytics,
|
required this.analytics,
|
||||||
required this.caffeineService,
|
required this.caffeineService,
|
||||||
required this.environment,
|
required this.environment,
|
||||||
|
required this.geolocationService,
|
||||||
required this.hapticsService,
|
required this.hapticsService,
|
||||||
required this.lightSensorService,
|
required this.lightSensorService,
|
||||||
required this.permissionsService,
|
required this.permissionsService,
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
import 'package:lightmeter/platform_config.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/providers/logbook_photos_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.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:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
|
||||||
iso: photo.iso,
|
iso: photo.iso,
|
||||||
nd: photo.nd,
|
nd: photo.nd,
|
||||||
coordinates: photo.coordinates,
|
coordinates: photo.coordinates,
|
||||||
aperture: null,
|
|
||||||
shutterSpeed: null,
|
|
||||||
note: photo.note,
|
note: photo.note,
|
||||||
canSave: false,
|
canSave: false,
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart';
|
||||||
|
import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart';
|
||||||
|
import 'package:map_launcher/map_launcher.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class LogbookPhotoCoordinatesListTile extends StatefulWidget {
|
||||||
|
const LogbookPhotoCoordinatesListTile();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LogbookPhotoCoordinatesListTile> createState() => LogbookPhotoCoordinatesListTileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogbookPhotoCoordinatesListTileState extends State<LogbookPhotoCoordinatesListTile> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
|
||||||
|
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<void> _showSnackBar() async {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(S.of(context).youDontHaveMailApp),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/platform_config.dart';
|
import 'package:lightmeter/platform_config.dart';
|
||||||
import 'package:lightmeter/res/dimens.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/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/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/event_logbook_photo_edit.dart';
|
||||||
import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart';
|
import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart';
|
||||||
|
@ -77,6 +78,7 @@ class _LogbookPhotoEditScreenState extends State<LogbookPhotoEditScreen> {
|
||||||
child: const Column(
|
child: const Column(
|
||||||
children: [
|
children: [
|
||||||
_DateListTile(),
|
_DateListTile(),
|
||||||
|
LogbookPhotoCoordinatesListTile(),
|
||||||
_NoteListTile(),
|
_NoteListTile(),
|
||||||
_EvListTile(),
|
_EvListTile(),
|
||||||
_IsoListTile(),
|
_IsoListTile(),
|
||||||
|
|
|
@ -26,6 +26,7 @@ dependencies:
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_native_splash: 2.4.4
|
flutter_native_splash: 2.4.4
|
||||||
|
geolocator: 13.0.1
|
||||||
intl: 0.19.0
|
intl: 0.19.0
|
||||||
intl_utils: 2.8.7
|
intl_utils: 2.8.7
|
||||||
light_sensor: 3.0.1
|
light_sensor: 3.0.1
|
||||||
|
@ -46,6 +47,7 @@ dependencies:
|
||||||
url_launcher_ios: 6.3.2
|
url_launcher_ios: 6.3.2
|
||||||
uuid: 4.5.1
|
uuid: 4.5.1
|
||||||
vibration: 2.0.1
|
vibration: 2.0.1
|
||||||
|
map_launcher: 3.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
args: 2.6.0
|
args: 2.6.0
|
||||||
|
@ -65,6 +67,11 @@ dev_dependencies:
|
||||||
test: 1.25.7
|
test: 1.25.7
|
||||||
|
|
||||||
dependency_overrides:
|
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
|
material_color_utilities: 0.11.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Loading…
Reference in a new issue