Skip to content
This repository was archived by the owner on Aug 16, 2025. It is now read-only.

Commit e8feac5

Browse files
committed
refactor MapActivity
1 parent b0e64c9 commit e8feac5

File tree

6 files changed

+466
-375
lines changed

6 files changed

+466
-375
lines changed
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package de.storchp.opentracks.osmplugin.dashboardapi
22

33
import android.net.Uri
4-
import java.util.ArrayList
54

65
object APIConstants {
76
const val LAT_LON_FACTOR = 1E6
@@ -11,12 +10,12 @@ object APIConstants {
1110

1211
const val ACTION_DASHBOARD_PAYLOAD = "$ACTION_DASHBOARD.Payload"
1312

14-
fun getTracksUri(uris: ArrayList<Uri>) = uris[0]
13+
fun getTracksUri(uris: List<Uri>) = uris[0]
1514

16-
fun getTrackpointsUri(uris: ArrayList<Uri>) = uris[1]
15+
fun getTrackpointsUri(uris: List<Uri>) = uris[1]
1716

1817
/**
1918
* Waypoints are only available in newer versions of OpenTracks.
2019
*/
21-
fun getWaypointsUri(uris: ArrayList<Uri>) = uris.getOrNull(2)
20+
fun getWaypointsUri(uris: List<Uri>) = uris.getOrNull(2)
2221
}
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
package de.storchp.opentracks.osmplugin.dashboardapi
2+
3+
import android.content.ContentResolver
4+
import android.content.Intent
5+
import android.database.ContentObserver
6+
import android.net.Uri
7+
import android.os.Handler
8+
import android.util.Log
9+
import de.storchp.opentracks.osmplugin.map.DEFAULT_TRACK_COLOR_MORE
10+
import de.storchp.opentracks.osmplugin.map.MapData
11+
import de.storchp.opentracks.osmplugin.map.MapUtils
12+
import de.storchp.opentracks.osmplugin.map.StyleColorCreator
13+
import de.storchp.opentracks.osmplugin.map.TrackColorMode
14+
import de.storchp.opentracks.osmplugin.map.TrackStatistics
15+
import de.storchp.opentracks.osmplugin.utils.PreferencesUtils
16+
import de.storchp.opentracks.osmplugin.utils.TrackpointsDebug
17+
import org.oscim.core.BoundingBox
18+
import org.oscim.core.GeoPoint
19+
import java.lang.Exception
20+
21+
private val TAG: String = DashboardReader::class.java.getSimpleName()
22+
23+
private const val EXTRAS_PROTOCOL_VERSION = "PROTOCOL_VERSION"
24+
private const val EXTRAS_OPENTRACKS_IS_RECORDING_THIS_TRACK =
25+
"EXTRAS_OPENTRACKS_IS_RECORDING_THIS_TRACK"
26+
private const val EXTRAS_SHOULD_KEEP_SCREEN_ON = "EXTRAS_SHOULD_KEEP_SCREEN_ON"
27+
private const val EXTRAS_SHOW_WHEN_LOCKED = "EXTRAS_SHOULD_KEEP_SCREEN_ON"
28+
private const val EXTRAS_SHOW_FULLSCREEN = "EXTRAS_SHOULD_FULLSCREEN"
29+
30+
typealias UpdateTrackStatistics = (TrackStatistics?) -> Unit
31+
typealias UpdateTrackpointsDebug = (TrackpointsDebug) -> Unit
32+
33+
class DashboardReader(
34+
intent: Intent,
35+
private val contentResolver: ContentResolver,
36+
private val mapData: MapData,
37+
private val updateTrackStatistics: UpdateTrackStatistics,
38+
private val updateTrackpointsDebug: UpdateTrackpointsDebug,
39+
) {
40+
41+
private val colorCreator = StyleColorCreator()
42+
private var trackColor = colorCreator.nextColor()
43+
private var trackpointsDebug = TrackpointsDebug()
44+
private var lastWaypointId = 0L
45+
private var lastTrackPointId = 0L
46+
private var contentObserver: OpenTracksContentObserver? = null
47+
var lastTrackId = 0L
48+
val keepScreenOn: Boolean
49+
val showOnLockScreen: Boolean
50+
val showFullscreen: Boolean
51+
val isOpenTracksRecordingThisTrack: Boolean
52+
val tracksUri: Uri
53+
val trackpointsUri: Uri
54+
val waypointsUri: Uri?
55+
val protocolVersion: Int
56+
57+
init {
58+
require(intent.isDashboardAction())
59+
val uris =
60+
intent.getParcelableArrayListExtra<Uri>(APIConstants.ACTION_DASHBOARD_PAYLOAD)!!
61+
protocolVersion = intent.getIntExtra(EXTRAS_PROTOCOL_VERSION, 1)
62+
tracksUri = APIConstants.getTracksUri(uris)
63+
trackpointsUri = APIConstants.getTrackpointsUri(uris)
64+
waypointsUri = APIConstants.getWaypointsUri(uris)
65+
keepScreenOn = intent.getBooleanExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, false)
66+
showOnLockScreen = intent.getBooleanExtra(EXTRAS_SHOW_WHEN_LOCKED, false)
67+
showFullscreen = intent.getBooleanExtra(EXTRAS_SHOW_FULLSCREEN, false)
68+
isOpenTracksRecordingThisTrack =
69+
intent.getBooleanExtra(EXTRAS_OPENTRACKS_IS_RECORDING_THIS_TRACK, false)
70+
71+
trackpointsDebug.protocolVersion = protocolVersion
72+
readTrackpoints(trackpointsUri, false, protocolVersion)
73+
readTracks(tracksUri)
74+
waypointsUri?.let { readWaypoints(it) }
75+
}
76+
77+
fun readTrackpoints(data: Uri, update: Boolean, protocolVersion: Int) {
78+
Log.i(TAG, "Loading trackpoints from $data")
79+
80+
val showPauseMarkers = PreferencesUtils.isShowPauseMarkers()
81+
val latLongs = mutableListOf<GeoPoint>()
82+
val tolerance = PreferencesUtils.getTrackSmoothingTolerance()
83+
84+
try {
85+
val trackpointsBySegments: TrackpointsBySegments =
86+
TrackpointReader.readTrackpointsBySegments(
87+
contentResolver,
88+
data,
89+
lastTrackPointId,
90+
protocolVersion
91+
)
92+
if (trackpointsBySegments.isEmpty()) {
93+
Log.d(TAG, "No new trackpoints received")
94+
return
95+
}
96+
97+
val average = trackpointsBySegments.calcAverageSpeed()
98+
val maxSpeed = trackpointsBySegments.calcMaxSpeed()
99+
val averageToMaxSpeed = maxSpeed - average
100+
var trackColorMode = PreferencesUtils.getTrackColorMode()
101+
if (isOpenTracksRecordingThisTrack && !trackColorMode.supportsLiveTrack) {
102+
trackColorMode = DEFAULT_TRACK_COLOR_MORE
103+
}
104+
105+
trackpointsBySegments.segments.map { trackpoints ->
106+
if (!update) {
107+
mapData.polyline = null // cut polyline on new segment
108+
if (tolerance > 0) { // smooth track
109+
return@map MapUtils.decimate(tolerance, trackpoints)
110+
}
111+
}
112+
return@map trackpoints
113+
}.forEach { trackpoints ->
114+
trackpoints.filter { it.latLong != null }.forEach { trackpoint ->
115+
lastTrackPointId = trackpoint.id
116+
117+
if (trackpoint.trackId != lastTrackId) {
118+
if (trackColorMode == TrackColorMode.BY_TRACK) {
119+
trackColor = colorCreator.nextColor()
120+
}
121+
lastTrackId = trackpoint.trackId
122+
mapData.resetCurrentPolyline()
123+
}
124+
125+
if (trackColorMode == TrackColorMode.BY_SPEED) {
126+
trackColor = MapUtils.getTrackColorBySpeed(
127+
average,
128+
averageToMaxSpeed,
129+
trackpoint
130+
)
131+
mapData.addNewPolyline(trackColor, trackpoint.latLong!!)
132+
} else {
133+
mapData.extendPolyline(trackColor, trackpoint.latLong!!)
134+
}
135+
136+
137+
if (trackpoint.isPause && showPauseMarkers) {
138+
mapData.addPauseMarker(trackpoint.latLong)
139+
}
140+
141+
if (!update) {
142+
mapData.endPos?.let { latLongs.add(it) }
143+
}
144+
145+
146+
}
147+
trackpointsBySegments.debug.trackpointsDrawn += trackpoints.size
148+
}
149+
trackpointsDebug.add(trackpointsBySegments.debug)
150+
} catch (e: Exception) {
151+
throw RuntimeException("Error reading trackpoints", e)
152+
}
153+
154+
mapData.setEndMarker()
155+
156+
var myPos: GeoPoint? = null
157+
if (update && mapData.endPos != null) {
158+
myPos = mapData.endPos
159+
mapData.renderMap()
160+
} else if (latLongs.isNotEmpty()) {
161+
mapData.boundingBox = BoundingBox(latLongs).extendMargin(1.2f).also {
162+
myPos = it.getCenterPoint()
163+
}
164+
}
165+
166+
if (myPos != null) {
167+
mapData.updateMapPositionAndRotation(myPos)
168+
}
169+
170+
updateTrackpointsDebug(trackpointsDebug)
171+
}
172+
173+
private fun readWaypoints(data: Uri) {
174+
try {
175+
WaypointReader.readWaypoints(contentResolver, data, lastWaypointId).forEach {
176+
lastWaypointId = it.id
177+
mapData.addWaypointMarker(it)
178+
}
179+
} catch (e: Exception) {
180+
Log.e(TAG, "Reading waypoints failed", e)
181+
}
182+
}
183+
184+
private fun readTracks(data: Uri) {
185+
val tracks = TrackReader.readTracks(contentResolver, data)
186+
val trackStatistics = if (tracks.isNotEmpty()) TrackStatistics(tracks) else null
187+
updateTrackStatistics(trackStatistics)
188+
}
189+
190+
fun hasTrackId() = lastTrackId > 0
191+
192+
fun startContentObserver() {
193+
contentObserver = OpenTracksContentObserver(
194+
tracksUri,
195+
trackpointsUri,
196+
waypointsUri,
197+
protocolVersion
198+
)
199+
200+
contentResolver.registerContentObserver(tracksUri, false, contentObserver!!)
201+
contentResolver.registerContentObserver(
202+
trackpointsUri,
203+
false,
204+
contentObserver!!
205+
)
206+
if (waypointsUri != null) {
207+
contentResolver.registerContentObserver(
208+
waypointsUri,
209+
false,
210+
contentObserver!!
211+
)
212+
}
213+
214+
}
215+
216+
fun unregisterContentObserver() {
217+
contentObserver?.let { contentResolver.unregisterContentObserver(it) }
218+
contentObserver = null
219+
}
220+
221+
private inner class OpenTracksContentObserver(
222+
private val tracksUri: Uri,
223+
private val trackpointsUri: Uri,
224+
private val waypointsUri: Uri?,
225+
private val protocolVersion: Int
226+
) : ContentObserver(Handler()) {
227+
228+
override fun onChange(selfChange: Boolean, uri: Uri?) {
229+
if (uri == null) {
230+
return // nothing can be done without an uri
231+
}
232+
if (tracksUri.toString().startsWith(uri.toString())) {
233+
readTracks(tracksUri)
234+
} else if (trackpointsUri.toString().startsWith(uri.toString())) {
235+
readTrackpoints(trackpointsUri, true, protocolVersion)
236+
} else if (waypointsUri?.toString()?.startsWith(uri.toString()) == true) {
237+
readWaypoints(waypointsUri)
238+
}
239+
}
240+
}
241+
242+
}
243+
244+
fun Intent.isDashboardAction(): Boolean {
245+
return APIConstants.ACTION_DASHBOARD == action
246+
}

0 commit comments

Comments
 (0)