218 lines
7.7 KiB
Kotlin
218 lines
7.7 KiB
Kotlin
package org.pupes.mhdrunpathfinder
|
|
|
|
import android.app.Activity
|
|
import android.os.Handler
|
|
import android.os.Looper
|
|
import android.widget.Toast
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.res.painterResource
|
|
import androidx.compose.ui.unit.dp
|
|
import kotlinx.serialization.Serializable
|
|
import okhttp3.OkHttpClient
|
|
import okhttp3.Request
|
|
import ovh.plrapps.mapcompose.api.addMarker
|
|
import ovh.plrapps.mapcompose.api.removeMarker
|
|
import ovh.plrapps.mapcompose.ui.state.markers.model.RenderingStrategy
|
|
import java.net.URL
|
|
|
|
@Serializable
|
|
data class GTFSRoute(
|
|
val agency_id: String?,
|
|
val is_night: Boolean,
|
|
val route_color: String?,
|
|
val route_desc: String? = null,
|
|
val route_id: String?,
|
|
val route_long_name: String?,
|
|
val route_short_name: String?,
|
|
val route_text_color: String?,
|
|
val route_type: Int,
|
|
val route_url: String?,
|
|
val is_regional: Boolean,
|
|
val is_substitute_transport: Boolean
|
|
)
|
|
|
|
// Type alias for array of GTFS routes
|
|
typealias GTFSRoutes = MutableList<GTFSRoute>
|
|
|
|
var gtfsRoutes: GTFSRoutes = mutableListOf()
|
|
|
|
|
|
@Serializable
|
|
data class GTFSStopGeometry(
|
|
val coordinates: List<Double>,
|
|
val type: String?
|
|
)
|
|
|
|
@Serializable
|
|
data class GTFSStopProperties(
|
|
val location_type: Int,
|
|
val parent_station: String? = null,
|
|
val platform_code: String? = null,
|
|
val stop_id: String,
|
|
val stop_name: String?,
|
|
val wheelchair_boarding: Int,
|
|
val zone_id: String? = null,
|
|
val level_id: String? = null
|
|
)
|
|
|
|
@Serializable
|
|
data class GTFSStop(
|
|
val geometry: GTFSStopGeometry,
|
|
val properties: GTFSStopProperties,
|
|
val type: String?
|
|
)
|
|
|
|
@Serializable
|
|
data class GTFSStops(
|
|
val features: MutableList<GTFSStop>,
|
|
val type: String?
|
|
)
|
|
|
|
var gtfsStops: GTFSStops = GTFSStops(mutableListOf<GTFSStop>(), "FeatureCollection")
|
|
|
|
|
|
// OkHttpClient for making HTTP requests
|
|
val gtfshttpClient = OkHttpClient.Builder()
|
|
.build()
|
|
|
|
private val GTFSMainHandler = Handler(Looper.getMainLooper())
|
|
private var GTFSTrackingThread: Thread? = null
|
|
private var GTFSIsTracking = false
|
|
|
|
private fun fetchGTFSPosition(gtfsUrl: URL,api_key: String, callee: Activity) {
|
|
try {
|
|
|
|
val request = Request.Builder()
|
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${"/"}")
|
|
.header("User-Agent", "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)")
|
|
.build()
|
|
|
|
gtfshttpClient.newCall(request).execute().use { response ->
|
|
var message = if (response.isSuccessful) {
|
|
response.body?.string() ?: "Empty response"
|
|
} else {
|
|
"Error: ${response.code} ${response.message}"
|
|
}
|
|
|
|
// TODO: Handle response
|
|
}
|
|
} catch (e: Exception) {
|
|
// Post UI update to main thread
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, e.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Start the timer with a URL:
|
|
fun startGTFSTracking(url: String, api_key: String, callee: Activity) {
|
|
if (GTFSIsTracking) return
|
|
|
|
GTFSIsTracking = true
|
|
GTFSTrackingThread = Thread {
|
|
val gtfsUrl = URL(url);
|
|
try {
|
|
if(gtfsRoutes.isEmpty()) {
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, "Fetching routes...", Toast.LENGTH_SHORT).show()}
|
|
|
|
var messageParsed: GTFSRoutes;
|
|
var offset = 0;
|
|
do {
|
|
val request = Request.Builder()
|
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${callee.getString(R.string.routes_path)}?offset=${offset}")
|
|
.header(
|
|
"User-Agent",
|
|
"${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)"
|
|
)
|
|
.header("X-Access-Token", api_key).build();
|
|
|
|
gtfshttpClient.newCall(request).execute().use { response ->
|
|
var message = if (response.isSuccessful) {
|
|
response.body?.string() ?: "Empty response"
|
|
} else {
|
|
"Error: ${response.code} ${response.message}"
|
|
}
|
|
messageParsed = json.decodeFromString<GTFSRoutes>(message)
|
|
gtfsRoutes.addAll(messageParsed)
|
|
offset += messageParsed.size
|
|
}} while (messageParsed.isNotEmpty())
|
|
}
|
|
if(gtfsStops.features.isEmpty()) {
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, "Fetching stops...", Toast.LENGTH_SHORT).show()}
|
|
|
|
var messageParsed: GTFSStops;
|
|
var offset = 0;
|
|
do {
|
|
val request = Request.Builder()
|
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${callee.getString(R.string.stops_path)}?offset=${offset}")
|
|
.header(
|
|
"User-Agent",
|
|
"${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)"
|
|
)
|
|
.header("X-Access-Token", api_key).build();
|
|
|
|
gtfshttpClient.newCall(request).execute().use { response ->
|
|
var message = if (response.isSuccessful) {
|
|
response.body?.string() ?: "Empty response"
|
|
} else {
|
|
"Error: ${response.code} ${response.message}"
|
|
}
|
|
messageParsed = json.decodeFromString<GTFSStops>(message)
|
|
gtfsStops.features.addAll(messageParsed.features)
|
|
}
|
|
|
|
offset += messageParsed.features.size;
|
|
}while(messageParsed.features.isNotEmpty())
|
|
}
|
|
// put all stops on the map
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, "Drawing stops...", Toast.LENGTH_SHORT).show()}
|
|
for (stop in gtfsStops!!.features) {
|
|
|
|
if (stop.properties.parent_station == null) {
|
|
mapState.addMarker(stop.properties.stop_id, x = longitudeToXNormalized(stop.geometry.coordinates[0]), y = latitudeToYNormalized(stop.geometry.coordinates[1]), renderingStrategy = RenderingStrategy.LazyLoading("default")) {
|
|
Icon(
|
|
painter = painterResource(id = R.drawable.outline_directions_bus_24),
|
|
contentDescription = null,
|
|
modifier = Modifier.size(12.dp),
|
|
tint = Color(0xFF0000FF)
|
|
)
|
|
}}
|
|
}
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, "Done drawing stops", Toast.LENGTH_SHORT).show()}
|
|
|
|
} catch(e: Exception) {
|
|
GTFSMainHandler.post {
|
|
Toast.makeText(callee, e.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
|
|
}
|
|
GTFSMainHandler.post { stopGTFSTracking() }
|
|
return@Thread
|
|
}
|
|
while (GTFSIsTracking) {
|
|
fetchGTFSPosition(gtfsUrl,api_key, callee)
|
|
try {
|
|
Thread.sleep(1000) // Wait 1 second between requests
|
|
} catch (e: InterruptedException) {
|
|
break
|
|
}
|
|
}
|
|
}.apply { start() }
|
|
}
|
|
|
|
fun stopGTFSTracking() {
|
|
GTFSIsTracking = false
|
|
for (stop in gtfsStops!!.features) {
|
|
if (stop.properties.parent_station == null) {
|
|
mapState.removeMarker(stop.properties.stop_id);
|
|
}
|
|
}
|
|
GTFSTrackingThread?.interrupt()
|
|
GTFSTrackingThread = null
|
|
} |