diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1e9d191a..d0186501 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -668,6 +668,30 @@ } } }, + "locationAlarm_proximityLocation_notificationTitle_whenEnter": "{name} is within {proximity}m", + "@locationAlarm_proximityLocation_notificationTitle_whenEnter": { + "description": "Notification title when entering a proximity location; Try to keep this short, will be truncated to 80 characters", + "placeholders": { + "name": { + "type": "String" + }, + "proximity": { + "type": "int" + } + } + }, + "locationAlarm_proximityLocation_notificationTitle_whenLeave": "{name} is outside of {proximity}m", + "@locationAlarm_proximityLocation_notificationTitle_whenLeave": { + "description": "Notification title when leaving a proximity location; Try to keep this short, will be truncated to 80 characters", + "placeholders": { + "name": { + "type": "String" + }, + "proximity": { + "type": "int" + } + } + }, "locationAlarm_notification_description": "Tap for more information", "androidNotificationChannel_locationAlarms_name": "Location Alarms", "androidNotificationChannel_locationAlarms_description": "Receive notifications for location alarms", diff --git a/lib/services/location_alarm_service/LocationAlarmServiceBase.dart b/lib/services/location_alarm_service/LocationAlarmServiceBase.dart index 9c2a6157..1f7367b5 100644 --- a/lib/services/location_alarm_service/LocationAlarmServiceBase.dart +++ b/lib/services/location_alarm_service/LocationAlarmServiceBase.dart @@ -18,7 +18,7 @@ abstract class LocationAlarmServiceBase { LocationAlarmTriggerType check( final LocationPointService previousLocation, final LocationPointService nextLocation, { - final LocationPointService userLocation, + required final LocationPointService userLocation, }); String getStorageKey() => "location_alarm_service:$IDENTIFIER:$id"; diff --git a/lib/services/location_alarm_service/ProximityLocationAlarm.dart b/lib/services/location_alarm_service/ProximityLocationAlarm.dart new file mode 100644 index 00000000..48e86c09 --- /dev/null +++ b/lib/services/location_alarm_service/ProximityLocationAlarm.dart @@ -0,0 +1,153 @@ +import 'package:apple_maps_flutter/apple_maps_flutter.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:geolocator/geolocator.dart'; + +import 'package:locus/services/location_alarm_service/enums.dart'; + +import 'package:locus/services/location_point_service.dart'; + +import 'LocationAlarmServiceBase.dart'; + +enum ProximityLocationAlarmType { + whenEnter, + whenLeave, +} + +class ProximityLocationAlarm extends LocationAlarmServiceBase { + // Radius in meters + final int radius; + final ProximityLocationAlarmType type; + + const ProximityLocationAlarm({ + required this.radius, + required this.type, + required String id, + }) : super(id); + + @override + LocationAlarmType get IDENTIFIER => LocationAlarmType.proximityLocation; + + factory ProximityLocationAlarm.fromJSON( + final Map data, + ) => + ProximityLocationAlarm( + radius: data["radius"], + type: ProximityLocationAlarmType.values[data["alarmType"]], + id: data["id"], + ); + + factory ProximityLocationAlarm.create({ + required final int radius, + required final ProximityLocationAlarmType type, + }) => + ProximityLocationAlarm( + radius: radius, + type: type, + id: uuid.v4(), + ); + + LocationAlarmTriggerType _wasInside( + final LocationPointService location, + final LocationPointService userLocation, + ) { + final fullDistance = Geolocator.distanceBetween( + location.latitude, + location.longitude, + userLocation.latitude, + userLocation.longitude, + ); + + if (fullDistance < radius && location.accuracy < radius) { + return LocationAlarmTriggerType.yes; + } + + if (fullDistance - location.accuracy - radius > 0) { + return LocationAlarmTriggerType.no; + } + + return LocationAlarmTriggerType.maybe; + } + + @override + LocationAlarmTriggerType check( + LocationPointService previousLocation, + LocationPointService nextLocation, { + required LocationPointService userLocation, + }) { + final previousInside = _wasInside(previousLocation, userLocation); + final nextInside = _wasInside(nextLocation, userLocation); + + switch (type) { + case ProximityLocationAlarmType.whenEnter: + if (previousInside == LocationAlarmTriggerType.no && + nextInside == LocationAlarmTriggerType.yes) { + return LocationAlarmTriggerType.yes; + } + + if (previousInside == LocationAlarmTriggerType.maybe && + nextInside == LocationAlarmTriggerType.yes) { + return LocationAlarmTriggerType.yes; + } + + if (previousInside == LocationAlarmTriggerType.no && + nextInside == LocationAlarmTriggerType.maybe) { + return LocationAlarmTriggerType.maybe; + } + + if (previousInside == LocationAlarmTriggerType.maybe && + nextInside == LocationAlarmTriggerType.maybe) { + return LocationAlarmTriggerType.maybe; + } + break; + case ProximityLocationAlarmType.whenLeave: + if (previousInside == LocationAlarmTriggerType.yes && + nextInside == LocationAlarmTriggerType.no) { + return LocationAlarmTriggerType.yes; + } + + if (previousInside == LocationAlarmTriggerType.maybe && + nextInside == LocationAlarmTriggerType.no) { + return LocationAlarmTriggerType.yes; + } + + if (previousInside == LocationAlarmTriggerType.yes && + nextInside == LocationAlarmTriggerType.maybe) { + return LocationAlarmTriggerType.maybe; + } + + if (previousInside == LocationAlarmTriggerType.maybe && + nextInside == LocationAlarmTriggerType.maybe) { + return LocationAlarmTriggerType.maybe; + } + break; + } + + return LocationAlarmTriggerType.no; + } + + @override + String createNotificationTitle(AppLocalizations l10n, String viewName) { + switch (type) { + case ProximityLocationAlarmType.whenEnter: + return l10n.locationAlarm_proximityLocation_notificationTitle_whenEnter( + viewName, + radius, + ); + case ProximityLocationAlarmType.whenLeave: + return l10n.locationAlarm_proximityLocation_notificationTitle_whenLeave( + viewName, + radius, + ); + } + } + + @override + Map toJSON() { + return { + "_IDENTIFIER": IDENTIFIER.name, + "radius": radius, + "alarmType": type.index, + "id": id, + }; + } +} diff --git a/lib/services/location_alarm_service/RadiusBasedRegionLocationAlarm.dart b/lib/services/location_alarm_service/RadiusBasedRegionLocationAlarm.dart index 08686dcf..da15cc33 100644 --- a/lib/services/location_alarm_service/RadiusBasedRegionLocationAlarm.dart +++ b/lib/services/location_alarm_service/RadiusBasedRegionLocationAlarm.dart @@ -34,7 +34,8 @@ class RadiusBasedRegionLocationAlarm extends LocationAlarmServiceBase { LocationAlarmType get IDENTIFIER => LocationAlarmType.radiusBasedRegion; factory RadiusBasedRegionLocationAlarm.fromJSON( - final Map data) => + final Map data, + ) => RadiusBasedRegionLocationAlarm( center: LatLng.fromJson(data["center"]), radius: data["radius"], @@ -74,10 +75,14 @@ class RadiusBasedRegionLocationAlarm extends LocationAlarmServiceBase { switch (type) { case RadiusBasedRegionLocationAlarmType.whenEnter: return l10n.locationAlarm_radiusBasedRegion_notificationTitle_whenEnter( - viewName, zoneName); + viewName, + zoneName, + ); case RadiusBasedRegionLocationAlarmType.whenLeave: return l10n.locationAlarm_radiusBasedRegion_notificationTitle_whenLeave( - viewName, zoneName); + viewName, + zoneName, + ); } } diff --git a/lib/services/location_alarm_service/enums.dart b/lib/services/location_alarm_service/enums.dart index 200bc4e5..d3bec696 100644 --- a/lib/services/location_alarm_service/enums.dart +++ b/lib/services/location_alarm_service/enums.dart @@ -6,4 +6,5 @@ enum LocationAlarmTriggerType { enum LocationAlarmType { radiusBasedRegion, + proximityLocation, } diff --git a/lib/services/view_service.dart b/lib/services/view_service.dart index 8a98ecf8..66fa25e8 100644 --- a/lib/services/view_service.dart +++ b/lib/services/view_service.dart @@ -17,6 +17,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../api/get-locations.dart' as get_locations_api; import '../constants/values.dart'; import 'location_alarm_service/LocationAlarmServiceBase.dart'; +import 'location_alarm_service/ProximityLocationAlarm.dart'; import 'location_alarm_service/RadiusBasedRegionLocationAlarm.dart'; import 'location_alarm_service/enums.dart'; import 'task_service/mixins.dart'; @@ -102,6 +103,8 @@ class TaskView extends ChangeNotifier with LocationBase { switch (identifier) { case LocationAlarmType.radiusBasedRegion: return RadiusBasedRegionLocationAlarm.fromJSON(alarm); + case LocationAlarmType.proximityLocation: + return ProximityLocationAlarm.fromJSON(alarm); } }), ),