11package com.mapbox.maps.testapp.utils
22
3- import android.os.Handler
4- import android.os.Looper
53import com.mapbox.geojson.LineString
64import com.mapbox.geojson.Point
75import com.mapbox.maps.plugin.locationcomponent.LocationConsumer
86import com.mapbox.maps.plugin.locationcomponent.LocationProvider
97import com.mapbox.turf.TurfConstants
108import com.mapbox.turf.TurfMeasurement
119import com.mapbox.turf.TurfMisc
12- import java.util.concurrent.*
10+ import kotlinx.coroutines.CoroutineScope
11+ import kotlinx.coroutines.Dispatchers
12+ import kotlinx.coroutines.Job
13+ import kotlinx.coroutines.SupervisorJob
14+ import kotlinx.coroutines.cancelAndJoin
15+ import kotlinx.coroutines.delay
16+ import kotlinx.coroutines.isActive
17+ import kotlinx.coroutines.launch
18+ import kotlinx.coroutines.withContext
19+ import java.util.concurrent.CopyOnWriteArraySet
1320
1421/* *
1522 * A location provider implementation that takes in a line string as route and animate the location
1623 * updates along the route.
1724 */
1825class SimulateRouteLocationProvider (
1926 val route : LineString ,
20- private val handler : Handler = Handler (Looper .getMainLooper())
2127) : LocationProvider {
22- private val totalRouteLength = TurfMeasurement .length(route, TurfConstants .UNIT_CENTIMETERS )
28+ private val scope = CoroutineScope (SupervisorJob () + Dispatchers .Default )
29+ private var emitLocationsJob: Job ? = null
30+ private val totalRouteLength by lazy { TurfMeasurement .length(route, TurfConstants .UNIT_CENTIMETERS ) }
2331 private val routeStartPoint = route.coordinates().first()
2432 private val locationConsumers = CopyOnWriteArraySet <LocationConsumer >()
2533 private var isFakeLocationEmitting = false
34+ private val iterator = route.coordinates().toMutableList().iterator()
35+
2636 override fun registerLocationConsumer (locationConsumer : LocationConsumer ) {
2737 locationConsumers.add(locationConsumer)
2838 if (! isFakeLocationEmitting) {
@@ -34,35 +44,34 @@ class SimulateRouteLocationProvider(
3444 override fun unRegisterLocationConsumer (locationConsumer : LocationConsumer ) {
3545 locationConsumers.remove(locationConsumer)
3646 if (locationConsumers.isEmpty()) {
47+ emitLocationsJob?.cancel()
3748 isFakeLocationEmitting = false
3849 }
3950 }
4051
41- private val pointList = route.coordinates().toMutableList()
42- private val iterator = pointList.iterator()
43- private var lastLocation: Point =
44- if (iterator.hasNext()) iterator.next() else Point .fromLngLat(0.0 , 0.0 )
45- private var lastBearing = 0.0
46-
4752 private fun emitFakeLocations () {
48- handler.postDelayed(
49- {
50- if (iterator.hasNext()) {
51- val point = iterator.next().insertProgressInfo()
52- val bearing = TurfMeasurement .bearing(lastLocation, point)
53- lastLocation = point
54- lastBearing = bearing
55- iterator.remove()
53+ val previousEmitLocationsJob = emitLocationsJob
54+ emitLocationsJob = scope.launch {
55+ // Make sure previous job is cancelled before starting a new one
56+ previousEmitLocationsJob?.cancelAndJoin()
57+ var lastLocation: Point = if (iterator.hasNext()) {
58+ iterator.next()
59+ } else {
60+ Point .fromLngLat(0.0 , 0.0 )
61+ }
62+ while (isActive && iterator.hasNext()) {
63+ val point = iterator.next().insertProgressInfo()
64+ val bearing = TurfMeasurement .bearing(lastLocation, point)
65+ lastLocation = point
66+ iterator.remove()
5667
68+ withContext(Dispatchers .Main ) {
5769 locationConsumers.forEach { it.onLocationUpdated(point) }
5870 locationConsumers.forEach { it.onBearingUpdated(bearing) }
5971 }
60- if (iterator.hasNext() && isFakeLocationEmitting) {
61- emitFakeLocations()
62- }
63- },
64- LOCATION_UPDATE_INTERVAL_MS
65- )
72+ delay(LOCATION_UPDATE_INTERVAL_MS )
73+ }
74+ }
6675 }
6776
6877 // use altitude of Point to pass through progress data, and use internal animator to interpolate
0 commit comments