@@ -38,7 +38,6 @@ import com.arcgismaps.mapping.Viewpoint
3838import com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources.databinding.ActivityMainBinding
3939import com.google.android.material.floatingactionbutton.FloatingActionButton
4040import com.google.android.material.snackbar.Snackbar
41- import kotlinx.coroutines.Dispatchers
4241import kotlinx.coroutines.launch
4342import java.io.BufferedReader
4443import java.io.File
@@ -62,7 +61,10 @@ class MainActivity : AppCompatActivity() {
6261 // create a timer to simulate a stream of NMEA data
6362 private var timer = Timer ()
6463
65- // index of list of locations
64+ // list of nmea location sentences
65+ private var nmeaSentences: MutableList <String >? = null
66+
67+ // index of nmea location sentence
6668 private var locationIndex = 0
6769
6870 // set up data binding for the activity
@@ -113,114 +115,81 @@ class MainActivity : AppCompatActivity() {
113115 )
114116 )
115117
116- // set the NMEA location data source onto the map view's location display
118+ // set the nmea location data source onto the map view's location display
117119 val locationDisplay = mapView.locationDisplay
118120 locationDisplay.dataSource = nmeaLocationDataSource
119121 locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode .Recenter )
120122
121123 // disable map view interaction, the location display will automatically center on the mock device location
122124 mapView.interactionOptions.isPanEnabled = false
123125 mapView.interactionOptions.isZoomEnabled = false
126+
127+ // read nmea location sentences from file
128+ nmeaSentences = getNMEASentenceList()
129+ // collects the accuracy for each location change
130+ collectLocationChanges()
131+ // collects satellite changes and display satellite information
132+ setupSatelliteChangedListener()
124133 }
125134
126135 /* *
127- * Control the start/stop status of the NMEA location data source
136+ * Reads NMEA location sentences from the .nmea file and
137+ * returns it as a [MutableList]
128138 */
129- fun playPauseClick (view : View ) {
130- lifecycleScope.launch {
131- if (nmeaLocationDataSource.status.value != LocationDataSourceStatus .Started ) {
132- // start location data source
133- startNMEALocationDataSource()
134- setButtonStatus(true )
135- } else {
136- // stop receiving and displaying location data
137- nmeaLocationDataSource.stop().onSuccess {
138- nmeaLocationDataSource = NmeaLocationDataSource (SpatialReference .wgs84())
139- mapView.locationDisplay.dataSource = nmeaLocationDataSource
140- }
141- setButtonStatus(false )
142- clearInformation()
139+ private fun getNMEASentenceList (): MutableList <String >? {
140+ // create list of nmea location sentences
141+ val nmeaSentences: MutableList <String > = mutableListOf ()
142+ val simulatedNmeaDataFile = File (" $provisionPath /Redlands.nmea" )
143+ if (! simulatedNmeaDataFile.exists()) {
144+ showError(" NMEA file does not exist" )
145+ return null
146+ }
147+ // read the nmea file contents using a buffered reader and store the mock data sentences in a list
148+ return try {
149+ // create a buffered reader using the .nmea file
150+ val bufferedReader = BufferedReader (FileReader (simulatedNmeaDataFile.path))
151+ var line = bufferedReader.readLine()
152+ while (line != null ) {
153+ // add carriage return for nmea location data source parser
154+ nmeaSentences.add(line + " \n " )
155+ line = bufferedReader.readLine()
143156 }
157+ bufferedReader.close()
158+ nmeaSentences
159+ } catch (e: Exception ) {
160+ showError(" Error creating NMEA sentences " + e.message)
161+ null
144162 }
145163 }
146164
147165 /* *
148- * Sets the FAB button to "Start"/"Stop" based on [isShowingLocation]
166+ * Control the start/stop status of the NMEA location data source
149167 */
150- private fun setButtonStatus (isShowingLocation : Boolean ) = if (isShowingLocation) {
151- playPauseFAB.setImageDrawable(
152- AppCompatResources .getDrawable(
153- this , R .drawable.ic_round_pause_24
154- )
155- )
156- } else {
157- playPauseFAB.setImageDrawable(
158- AppCompatResources .getDrawable(
159- this , R .drawable.ic_round_play_arrow_24
160- )
161- )
168+ fun playPauseClick (view : View ) = lifecycleScope.launch {
169+ if (nmeaLocationDataSource.status.value != LocationDataSourceStatus .Started ) {
170+ // initialize the location data source and prepare to begin receiving location updates when data is pushed
171+ // as updates are received, they will be displayed on the map
172+ nmeaLocationDataSource.start().onFailure {
173+ showError(" NmeaLocationDataSource failed to start: ${it.message} " )
174+ }
175+ // starts the NMEA mock data sentences
176+ nmeaSentences?.let { startNMEAMockData(it) }
177+ setButtonStatus(true )
178+ } else {
179+ // stop receiving and displaying location data
180+ nmeaLocationDataSource.stop()
181+ // cancel up the timer task
182+ timer.cancel()
183+ setButtonStatus(false )
184+ clearInformation()
185+ }
162186 }
163187
164188 /* *
165189 * Initializes the location data source, reads the mock data NMEA sentences, and displays location updates from that file
166190 * on the location display. Data is pushed to the data source using a timeline to simulate live updates, as they would
167191 * appear if using real-time data from a GPS dongle
168192 */
169- private fun startNMEALocationDataSource () {
170- val simulatedNmeaDataFile = File (" $provisionPath /Redlands.nmea" )
171- if (simulatedNmeaDataFile.exists()) {
172- try {
173- // read the nmea file contents using a buffered reader and store the mock data sentences in a list
174- val bufferedReader = BufferedReader (FileReader (simulatedNmeaDataFile.path))
175- // add carriage return for NMEA location data source parser
176- val nmeaSentences: MutableList <String > = mutableListOf ()
177- var line = bufferedReader.readLine()
178- while (line != null ) {
179- nmeaSentences.add(line + " \n " )
180- line = bufferedReader.readLine()
181- }
182- bufferedReader.close()
183-
184- lifecycleScope.apply {
185- launch {
186- // initialize the location data source and prepare to begin receiving location updates when data is pushed
187- // as updates are received, they will be displayed on the map
188- nmeaLocationDataSource.start()
189- }
190- launch {
191- // collect the accuracy for each location change
192- nmeaLocationDataSource.locationChanged.collect {
193- // convert from meters to foot
194- val horizontalAccuracy = it.horizontalAccuracy * 3.28084
195- val verticalAccuracy = it.verticalAccuracy * 3.28084
196- accuracyTV.text =
197- getString(R .string.accuracy) + " Horizontal-%.1fft, Vertical-%.1fft" .format(
198- horizontalAccuracy, verticalAccuracy
199- )
200- }
201- }
202- launch {
203- // handle when LocationDataSource status is changed
204- nmeaLocationDataSource.status.collect {
205- if (it == LocationDataSourceStatus .Started ) {
206- // add a satellite changed listener to the NMEA location data source and display satellite information
207- setupSatelliteChangedListener()
208- // starts the NMEA mock data sentences
209- startNMEAMockData(nmeaSentences)
210- }
211- if (it == LocationDataSourceStatus .Stopped ) {
212- timer.cancel()
213- }
214- }
215- }
216- }
217- } catch (e: Exception ) {
218- showError(" Error while setting up NmeaLocationDataSource: " + e.message)
219- }
220- } else {
221- showError(" NMEA File not found" )
222- }
223- }
224193
225194 /* *
226195 * Push the mock data NMEA sentences into the data source every 250 ms
@@ -240,49 +209,82 @@ class MainActivity : AppCompatActivity() {
240209 }
241210
242211 /* *
243- * Obtains NMEA satellite information from the NMEA location data source, and displays satellite information on the app
212+ * Sets the FAB button to "Start"/"Stop" based on [isShowingLocation]
213+ */
214+ private fun setButtonStatus (isShowingLocation : Boolean ) = if (isShowingLocation) {
215+ playPauseFAB.setImageDrawable(
216+ AppCompatResources .getDrawable(
217+ this , R .drawable.ic_round_pause_24
218+ )
219+ )
220+ } else {
221+ playPauseFAB.setImageDrawable(
222+ AppCompatResources .getDrawable(
223+ this , R .drawable.ic_round_play_arrow_24
224+ )
225+ )
226+ }
227+
228+ /* *
229+ * Collects location changes of the NMEA location data source,
230+ * and displays the location accuracy
231+ */
232+ private fun collectLocationChanges () = lifecycleScope.launch {
233+ nmeaLocationDataSource.locationChanged.collect { nmeaLocation ->
234+ // convert from meters to foot
235+ val horizontalAccuracy = nmeaLocation.horizontalAccuracy * 3.28084
236+ val verticalAccuracy = nmeaLocation.verticalAccuracy * 3.28084
237+ accuracyTV.text =
238+ getString(R .string.accuracy) + " Horizontal-%.1fft, Vertical-%.1fft" .format(
239+ horizontalAccuracy, verticalAccuracy
240+ )
241+ }
242+ }
243+
244+ /* *
245+ * Obtains NMEA satellite information from the NMEA location data source,
246+ * and displays satellite information on the app
244247 */
245- private fun setupSatelliteChangedListener () {
246- lifecycleScope.launch {
247- nmeaLocationDataSource.satellitesChanged.collect { nmeaSatelliteInfoList ->
248- val uniqueSatelliteIDs = mutableListOf<Int >()
249- var satelliteSystems = " "
250- // set the text of the satellite count label
251- satelliteCountTV.text =
252- getString(R .string.satellite_count) + nmeaSatelliteInfoList.size
253- // get the system of the first satellite
254- when (nmeaSatelliteInfoList.first().system) {
255- NmeaGnssSystem .Bds -> {
256- satelliteSystems = " BDS"
257- }
258- NmeaGnssSystem .Galileo -> {
259- satelliteSystems = " Galileo"
260- }
261- NmeaGnssSystem .Glonass -> {
262- satelliteSystems = " Glonass"
263- }
264- NmeaGnssSystem .Gps -> {
265- satelliteSystems = " GPS"
266- }
267- NmeaGnssSystem .NavIc -> {
268- satelliteSystems = " NavIc"
269- }
270- NmeaGnssSystem .Qzss -> {
271- satelliteSystems = " Qzss"
272- }
273- NmeaGnssSystem .Unknown -> {
274- satelliteSystems = " Unknown"
275- }
248+ private fun setupSatelliteChangedListener () = lifecycleScope.launch {
249+ nmeaLocationDataSource.satellitesChanged.collect { nmeaSatelliteInfoList ->
250+ val uniqueSatelliteIDs = mutableListOf<Int >()
251+ var satelliteSystems = " "
252+ // set the text of the satellite count label
253+ satelliteCountTV.text =
254+ getString(R .string.satellite_count) + nmeaSatelliteInfoList.size
255+ // get the system of the first satellite
256+ when (nmeaSatelliteInfoList.first().system) {
257+ NmeaGnssSystem .Bds -> {
258+ satelliteSystems = " BDS"
276259 }
277- // get the satellite IDs from the info list
278- nmeaSatelliteInfoList.forEach { satelliteInfo ->
279- uniqueSatelliteIDs.add(satelliteInfo.id)
260+ NmeaGnssSystem .Galileo -> {
261+ satelliteSystems = " Galileo"
280262 }
281- // display the satellite system and id information
282- systemTypeTV.text = getString(R .string.system) + satelliteSystems
283- satelliteIDsTV.text = getString(R .string.satellite_ids) + uniqueSatelliteIDs
263+ NmeaGnssSystem .Glonass -> {
264+ satelliteSystems = " Glonass"
265+ }
266+ NmeaGnssSystem .Gps -> {
267+ satelliteSystems = " GPS"
268+ }
269+ NmeaGnssSystem .NavIc -> {
270+ satelliteSystems = " NavIc"
271+ }
272+ NmeaGnssSystem .Qzss -> {
273+ satelliteSystems = " Qzss"
274+ }
275+ NmeaGnssSystem .Unknown -> {
276+ satelliteSystems = " Unknown"
277+ }
278+ }
279+ // get the satellite IDs from the info list
280+ nmeaSatelliteInfoList.forEach { satelliteInfo ->
281+ uniqueSatelliteIDs.add(satelliteInfo.id)
284282 }
283+ // display the satellite system and id information
284+ systemTypeTV.text = getString(R .string.system) + satelliteSystems
285+ satelliteIDsTV.text = getString(R .string.satellite_ids) + uniqueSatelliteIDs
285286 }
287+
286288 }
287289
288290 /* *
0 commit comments