Line data Source code
1 : import 'dart:async'; 2 : import 'dart:math'; 3 : 4 : import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 5 : import 'package:vector_math/vector_math.dart'; 6 : 7 : import 'enums/enums.dart'; 8 : import 'implementations/method_channel_geolocator.dart'; 9 : import 'models/models.dart'; 10 : 11 : /// The interface that implementations of geolocator must implement. 12 : /// 13 : /// Platform implementations should extend this class rather than implement it 14 : /// as `geolocator` does not consider newly added methods to be breaking 15 : /// changes. Extending this class (using `extends`) ensures that the subclass 16 : /// will get the default implementation, while platform implementations that 17 : /// `implements` this interface will be broken by newly added 18 : /// [GeolocatorPlatform] methods. 19 : abstract class GeolocatorPlatform extends PlatformInterface { 20 : /// Constructs a GeolocatorPlatform. 21 6 : GeolocatorPlatform() : super(token: _token); 22 : 23 4 : static final Object _token = const Object(); 24 : 25 3 : static GeolocatorPlatform _instance = MethodChannelGeolocator(); 26 : 27 : /// The default instance of [GeolocatorPlatform] to use. 28 : /// 29 : /// Defaults to [MethodChannelGeolocator]. 30 2 : static GeolocatorPlatform get instance => _instance; 31 : 32 : /// Platform-specific plugins should set this with their own 33 : /// platform-specific class that extends [GeolocatorPlatform] when they 34 : /// register themselves. 35 1 : static set instance(GeolocatorPlatform instance) { 36 2 : PlatformInterface.verifyToken(instance, _token); 37 : _instance = instance; 38 : } 39 : 40 : /// Returns a [Future] indicating if the user allows the App to access 41 : /// the device's location. 42 1 : Future<LocationPermission> checkPermission() { 43 1 : throw UnimplementedError( 44 : 'checkPermission() has not been implemented.', 45 : ); 46 : } 47 : 48 : /// Request permission to access the location of the device. 49 : /// 50 : /// Returns a [Future] which when completes indicates if the user granted 51 : /// permission to access the device's location. 52 : /// Throws a [PermissionDefinitionsNotFoundException] when the required 53 : /// platform specific configuration is missing (e.g. in the 54 : /// AndroidManifest.xml on Android or the Info.plist on iOS). 55 : /// A [PermissionRequestInProgressException] is thrown if permissions are 56 : /// requested while an earlier request has not yet been completed. 57 1 : Future<LocationPermission> requestPermission() { 58 1 : throw UnimplementedError('requestPermission() has not been implemented.'); 59 : } 60 : 61 : /// Returns a [Future] containing a [bool] value indicating whether location 62 : /// services are enabled on the device. 63 1 : Future<bool> isLocationServiceEnabled() { 64 1 : throw UnimplementedError( 65 : 'isLocationServiceEnabled() has not been implemented.', 66 : ); 67 : } 68 : 69 : /// Returns the last known position stored on the users device. 70 : /// 71 : /// On Android you can force the plugin to use the old Android 72 : /// LocationManager implementation over the newer FusedLocationProvider by 73 : /// passing true to the [forceAndroidLocationManager] parameter. On iOS 74 : /// this parameter is ignored. 75 : /// When no position is available, null is returned. 76 : /// Throws a [PermissionDeniedException] when trying to request the device's 77 : /// location when the user denied access. 78 1 : Future<Position?> getLastKnownPosition({ 79 : bool forceAndroidLocationManager = false, 80 : }) { 81 1 : throw UnimplementedError( 82 : 'getLastKnownPosition() has not been implemented.', 83 : ); 84 : } 85 : 86 : /// Returns the current position taking the supplied [desiredAccuracy] into 87 : /// account. 88 : /// 89 : /// You can control the precision of the location updates by supplying the 90 : /// [desiredAccuracy] parameter (defaults to "best"). On Android you can 91 : /// force the use of the Android LocationManager instead of the 92 : /// FusedLocationProvider by setting the [forceAndroidLocationManager] 93 : /// parameter to true. The [timeLimit] parameter allows you to specify a 94 : /// timeout interval (by default no time limit is configured). 95 : /// 96 : /// Throws a [TimeoutException] when no location is received within the 97 : /// supplied [timeLimit] duration. 98 : /// Throws a [PermissionDeniedException] when trying to request the device's 99 : /// location when the user denied access. 100 : /// Throws a [LocationServiceDisabledException] when the user allowed access, 101 : /// but the location services of the device are disabled. 102 1 : Future<Position> getCurrentPosition({ 103 : LocationAccuracy desiredAccuracy = LocationAccuracy.best, 104 : bool forceAndroidLocationManager = false, 105 : Duration? timeLimit, 106 : }) { 107 1 : throw UnimplementedError('getCurrentPosition() has not been implemented.'); 108 : } 109 : 110 : /// Fires when the Location Service is manually disabled or enabled f.e. 111 : /// when Location Service in Settings is disabled, a event will be fired which 112 : /// returns a [LocationServiceStatus]. 113 1 : Stream<ServiceStatus> getServiceStatusStream() { 114 1 : throw UnimplementedError( 115 : 'getServiceStatusStream() has not been implemented.'); 116 : } 117 : 118 : /// Fires whenever the location changes inside the bounds of the 119 : /// [desiredAccuracy]. 120 : /// 121 : /// This event starts all location sensors on the device and will keep them 122 : /// active until you cancel listening to the stream or when the application 123 : /// is killed. 124 : /// 125 : /// ``` 126 : /// StreamSubscription<Position> positionStream = getPositionStream() 127 : /// .listen((Position position) { 128 : /// // Handle position changes 129 : /// }); 130 : /// 131 : /// // When no longer needed cancel the subscription 132 : /// positionStream.cancel(); 133 : /// ``` 134 : /// 135 : /// You can control the precision of the location updates by supplying the 136 : /// [desiredAccuracy] parameter (defaults to "best"). The [distanceFilter] 137 : /// parameter controls the minimum distance the device needs to move before 138 : /// the update is emitted (default value is 0 indicator no filter is used). 139 : /// On Android you can force the use of the Android LocationManager instead 140 : /// of the FusedLocationProvider by setting the [forceAndroidLocationManager] 141 : /// parameter to true. Using the [timeInterval] you can control the amount of 142 : /// time that needs to pass before the next position update is send. The 143 : /// [timeLimit] parameter allows you to specify a timeout interval (by 144 : /// default no time limit is configured). 145 : /// 146 : /// Throws a [TimeoutException] when no location is received within the 147 : /// supplied [timeLimit] duration. 148 : /// Throws a [PermissionDeniedException] when trying to request the device's 149 : /// location when the user denied access. 150 : /// Throws a [LocationServiceDisabledException] when the user allowed access, 151 : /// but the location services of the device are disabled. 152 1 : Stream<Position> getPositionStream({ 153 : LocationAccuracy desiredAccuracy = LocationAccuracy.best, 154 : int distanceFilter = 0, 155 : bool forceAndroidLocationManager = false, 156 : int timeInterval = 0, 157 : Duration? timeLimit, 158 : }) { 159 1 : throw UnimplementedError('getPositionStream() has not been implemented.'); 160 : } 161 : 162 : /// Opens the App settings page. 163 : /// 164 : /// Returns [true] if the app settings page could be opened, otherwise 165 : /// [false] is returned. 166 1 : Future<bool> openAppSettings() async { 167 1 : throw UnimplementedError('openAppSettings() has not been implemented.'); 168 : } 169 : 170 : /// Opens the location settings page. 171 : /// 172 : /// Returns [true] if the location settings page could be opened, otherwise 173 : /// [false] is returned. 174 1 : Future<bool> openLocationSettings() async { 175 1 : throw UnimplementedError( 176 : 'openLocationSettings() has not been implemented.'); 177 : } 178 : 179 : /// Calculates the distance between the supplied coordinates in meters. 180 : /// 181 : /// The distance between the coordinates is calculated using the Haversine 182 : /// formula (see https://en.wikipedia.org/wiki/Haversine_formula). The 183 : /// supplied coordinates [startLatitude], [startLongitude], [endLatitude] and 184 : /// [endLongitude] should be supplied in degrees. 185 1 : double distanceBetween( 186 : double startLatitude, 187 : double startLongitude, 188 : double endLatitude, 189 : double endLongitude, 190 : ) { 191 : var earthRadius = 6378137.0; 192 2 : var dLat = _toRadians(endLatitude - startLatitude); 193 2 : var dLon = _toRadians(endLongitude - startLongitude); 194 : 195 4 : var a = pow(sin(dLat / 2), 2) + 196 4 : pow(sin(dLon / 2), 2) * 197 3 : cos(_toRadians(startLatitude)) * 198 2 : cos(_toRadians(endLatitude)); 199 3 : var c = 2 * asin(sqrt(a)); 200 : 201 1 : return earthRadius * c; 202 : } 203 : 204 1 : static _toRadians(double degree) { 205 2 : return degree * pi / 180; 206 : } 207 : 208 : /// Calculates the initial bearing between two points 209 : /// 210 : /// The initial bearing will most of the time be different than the end 211 : /// bearing, see https://www.movable-type.co.uk/scripts/latlong.html#bearing. 212 : /// The supplied coordinates [startLatitude], [startLongitude], [endLatitude] 213 : /// and [endLongitude] should be supplied in degrees. 214 1 : double bearingBetween( 215 : double startLatitude, 216 : double startLongitude, 217 : double endLatitude, 218 : double endLongitude, 219 : ) { 220 1 : var startLongitudeRadians = radians(startLongitude); 221 1 : var startLatitudeRadians = radians(startLatitude); 222 1 : var endLongitudeRadians = radians(endLongitude); 223 1 : var endLatitudeRadians = radians(endLatitude); 224 : 225 3 : var y = sin(endLongitudeRadians - startLongitudeRadians) * 226 1 : cos(endLatitudeRadians); 227 4 : var x = cos(startLatitudeRadians) * sin(endLatitudeRadians) - 228 2 : sin(startLatitudeRadians) * 229 2 : cos(endLatitudeRadians) * 230 2 : cos(endLongitudeRadians - startLongitudeRadians); 231 : 232 2 : return degrees(atan2(y, x)); 233 : } 234 : }