-
-
Notifications
You must be signed in to change notification settings - Fork 661
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add NMEA message event stream and channel
- Loading branch information
Showing
9 changed files
with
301 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
geolocator_android/android/src/main/java/com/baseflow/geolocator/NmeaStreamHandlerImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.baseflow.geolocator; | ||
|
||
import android.content.Context; | ||
import android.util.Log; | ||
|
||
import androidx.annotation.Nullable; | ||
|
||
import com.baseflow.geolocator.location.NmeaClient; | ||
import com.baseflow.geolocator.location.NmeaMapper; | ||
|
||
import io.flutter.plugin.common.BinaryMessenger; | ||
import io.flutter.plugin.common.EventChannel; | ||
|
||
class NmeaStreamHandlerImpl implements EventChannel.StreamHandler { | ||
private static final String TAG = "FlutterGeolocator"; | ||
|
||
@Nullable private EventChannel channel; | ||
@Nullable private Context context; | ||
@Nullable private NmeaClient nmeaClient; | ||
|
||
public NmeaStreamHandlerImpl() {} | ||
|
||
/** | ||
* Registers this instance as event stream handler on the given {@code messenger}. | ||
* | ||
* <p>Stops any previously started and unstopped calls. | ||
* | ||
* <p>This should be cleaned with {@link #stopListening} once the messenger is disposed of. | ||
*/ | ||
void startListening(Context context, BinaryMessenger messenger) { | ||
if (channel != null) { | ||
Log.w(TAG, "Setting a event call handler before the last was disposed."); | ||
stopListening(); | ||
} | ||
|
||
channel = new EventChannel(messenger, "flutter.baseflow.com/geolocator_nmea_updates_android"); | ||
channel.setStreamHandler(this); | ||
this.context = context; | ||
this.nmeaClient = new NmeaClient(this.context); | ||
} | ||
|
||
/** | ||
* Clears this instance from listening to method calls. | ||
* | ||
* <p>Does nothing if {@link #startListening} hasn't been called, or if we're already stopped. | ||
*/ | ||
void stopListening() { | ||
if (channel == null) { | ||
Log.d(TAG, "Tried to stop listening when no MethodChannel had been initialized."); | ||
return; | ||
} | ||
|
||
disposeListeners(); | ||
channel.setStreamHandler(null); | ||
channel = null; | ||
} | ||
|
||
@Override | ||
public void onListen(Object arguments, EventChannel.EventSink events) { | ||
|
||
if (nmeaClient == null) { | ||
Log.e(TAG, "NMEA Client has not started correctly"); | ||
return; | ||
} | ||
|
||
nmeaClient.start(); | ||
nmeaClient.setCallback((message -> events.success(NmeaMapper.toHashMap(message)))); | ||
} | ||
|
||
@Override | ||
public void onCancel(Object arguments) { | ||
disposeListeners(); | ||
} | ||
|
||
private void disposeListeners() { | ||
Log.e(TAG, "Geolocator position updates stopped"); | ||
if (nmeaClient != null) { | ||
nmeaClient.setCallback(null); | ||
nmeaClient.stop(); | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
geolocator_android/android/src/main/java/com/baseflow/geolocator/location/NMEACallback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.baseflow.geolocator.location; | ||
|
||
public interface NMEACallback { | ||
void onMessage(String message); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
geolocator_android/android/src/main/java/com/baseflow/geolocator/location/NmeaMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.baseflow.geolocator.location; | ||
|
||
import java.util.HashMap; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
|
||
public class NmeaMapper { | ||
|
||
public static Map<String, Object> toHashMap(String message) { | ||
if (message == null) { | ||
return null; | ||
} | ||
|
||
Map<String, Object> nmeaMessage = new HashMap<>(); | ||
|
||
if (message.startsWith("$")) { | ||
|
||
nmeaMessage.put("nmeaMessage", message); | ||
nmeaMessage.putAll(tryParseGPGAAMessage(message)); | ||
} | ||
|
||
|
||
return nmeaMessage; | ||
} | ||
|
||
private static Map<String, Object> tryParseGPGAAMessage(String message) { | ||
|
||
Map<String, Object> parsedMessage = new HashMap<>(); | ||
|
||
String[] tokens = message.split(","); | ||
String type = tokens[0]; | ||
// Parse altitude above sea level, Detailed description of NMEA string here | ||
// http://aprs.gids.nl/nmea/#gga | ||
if (type.startsWith("$GPGGA") && tokens.length > 9) { | ||
|
||
parsedMessage.put("time", tokens[1]); | ||
parsedMessage.put("latitude", tokens[2] + ',' + tokens[3]); | ||
parsedMessage.put("longitude", tokens[4] + ',' + tokens[5]); | ||
parsedMessage.put("quality", Integer.parseInt(tokens[6])); | ||
parsedMessage.put("numberOfSatellites", Integer.parseInt(tokens[7])); | ||
parsedMessage.put("horizontalDilutionOfPrecision", Double.parseDouble(tokens[8])); | ||
parsedMessage.put("altitude", Double.parseDouble(tokens[9])); | ||
parsedMessage.put("heightAboveEllipsoid", Double.parseDouble(tokens[11])); | ||
} | ||
|
||
return parsedMessage; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/// Represents an NMEA message received from the platform. | ||
/// If the NMEA message represents a GPGGA sequence then the message | ||
/// will be parsed and the GPGGA fields will be populate as well. | ||
class NMEAMessage { | ||
|
||
/// Initializes a new [NMEAMessage] instance with default values. | ||
const NMEAMessage({ | ||
required this.nmeaMessage, | ||
this.time, | ||
this.latitude, | ||
this.longitude, | ||
this.quality, | ||
this.numberOfSatellites, | ||
this.altitude, | ||
this.heightAboveEllipsoid, | ||
}); | ||
|
||
/// The raw NMEA Message | ||
final String nmeaMessage; | ||
|
||
/// UTC time if the NMEA Message was GPGGA Sequence | ||
/// GPGGA formatting can be found here: http://aprs.gids.nl/nmea/#gga | ||
final String? time; | ||
|
||
/// Latitude if the NMEA Message was GPGGA Sequence | ||
/// GPGGA formatting can be found here: http://aprs.gids.nl/nmea/#gga | ||
final String? latitude; | ||
|
||
/// Longitude if the NMEA Message was GPGGA Sequence | ||
/// GPGGA formatting can be found here: http://aprs.gids.nl/nmea/#gga | ||
final String? longitude; | ||
|
||
/// Quality if the NMEA Message was GPGGA Sequence | ||
/// Valid values can be found here: http://aprs.gids.nl/nmea/#gga | ||
final int? quality; | ||
|
||
/// Number of Satellites if the NMEA Message was GPGGA Sequence | ||
final int? numberOfSatellites; | ||
|
||
/// Altitude above mean sea level in meters if the NMEA Message was GPGGA Sequence | ||
final double? altitude; | ||
|
||
/// Height of geoid above QGS84 ellipsoid | ||
/// if the NMEA Message was GPGGA Sequence | ||
final double? heightAboveEllipsoid; | ||
|
||
@override | ||
bool operator ==(Object other) { | ||
var areEqual = other is NMEAMessage && | ||
other.nmeaMessage == nmeaMessage && | ||
other.time == time && | ||
other.latitude == latitude && | ||
other.longitude == longitude && | ||
other.quality == quality && | ||
other.numberOfSatellites == numberOfSatellites && | ||
other.altitude == altitude && | ||
other.heightAboveEllipsoid == heightAboveEllipsoid; | ||
|
||
return areEqual; | ||
} | ||
|
||
@override | ||
int get hashCode => | ||
nmeaMessage.hashCode ^ | ||
time.hashCode ^ | ||
latitude.hashCode ^ | ||
longitude.hashCode ^ | ||
quality.hashCode ^ | ||
numberOfSatellites.hashCode ^ | ||
altitude.hashCode ^ | ||
heightAboveEllipsoid.hashCode; | ||
|
||
@override | ||
String toString() { | ||
return 'NMEA Message: $nmeaMessage'; | ||
} | ||
|
||
/// Converts the supplied [Map] to an instance of the [NMEAMessage] class. | ||
static NMEAMessage fromMap(dynamic message) { | ||
final Map<dynamic, dynamic> nmeaMap = message; | ||
|
||
if (!nmeaMap.containsKey('nmeaMessage')) { | ||
throw ArgumentError.value(nmeaMap, 'nmeaMap', | ||
'The supplied map doesn\'t contain the mandatory key `nmeaMessage`.'); | ||
} | ||
|
||
return NMEAMessage( | ||
nmeaMessage: nmeaMap['nmeaMessage'], | ||
time: nmeaMap['time'], | ||
latitude: nmeaMap['latitude'], | ||
longitude: nmeaMap['longitude'], | ||
quality: nmeaMap['quality'], | ||
numberOfSatellites: nmeaMap['numberOfSatellites'], | ||
altitude: nmeaMap['altitude'], | ||
heightAboveEllipsoid: nmeaMap['heightAboveEllipsoid'], | ||
); | ||
} | ||
|
||
/// Converts the [NMEAMessage] instance into a [Map] instance that can be | ||
/// serialized to JSON. | ||
Map<String, dynamic> toJson() => { | ||
'nmeaMessage': nmeaMessage, | ||
'time': time, | ||
'latitude': latitude, | ||
'longitude': longitude, | ||
'quality': quality, | ||
'numberOfSatellites': numberOfSatellites, | ||
'altitude': altitude, | ||
'heightAboveEllipsoid': heightAboveEllipsoid, | ||
}; | ||
} |
Oops, something went wrong.