123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package ml.adamsprogs.bimba
- import android.content.Context
- import android.content.Intent
- import kotlinx.coroutines.*
- import kotlinx.coroutines.android.Main
- import ml.adamsprogs.bimba.datasources.VmClient
- import ml.adamsprogs.bimba.datasources.VmService
- import ml.adamsprogs.bimba.models.Departure
- import ml.adamsprogs.bimba.models.Plate
- import ml.adamsprogs.bimba.models.StopSegment
- import ml.adamsprogs.bimba.models.Timetable
- import ml.adamsprogs.bimba.models.suggestions.GtfsSuggestion
- import ml.adamsprogs.bimba.models.suggestions.StopSuggestion
- import java.util.*
- import kotlin.collections.HashMap
- //todo make singleton
- class ProviderProxy(context: Context? = null) {
- private val vmClient = VmClient.getVmClient()
- private var timetable: Timetable = Timetable.getTimetable(context)
- private var suggestions = emptyList<GtfsSuggestion>()
- private val requests = HashMap<String, Request>()
- var mode = if (timetable.isEmpty()) MODE_VM else MODE_FULL
- companion object {
- const val MODE_FULL = "mode_full"
- const val MODE_VM = "mode_vm"
- }
- fun getSuggestions(query: String = "", callback: (List<GtfsSuggestion>) -> Unit) {
- GlobalScope.launch {
- suggestions = getStopSuggestions(query) //+ getLineSuggestions(query) //todo<p:v+1> + bike stations, train stations, &c
- val filtered = filterSuggestions(query)
- launch(Dispatchers.Main) {
- callback(filtered)
- }
- }
- }
- private suspend fun getStopSuggestions(query: String): List<StopSuggestion> {
- val vmSuggestions = withContext(Dispatchers.Default) {
- vmClient.getStops(query)
- }
- return if (vmSuggestions.isEmpty() and !timetable.isEmpty()) {
- timetable.getStopSuggestions()
- } else {
- vmSuggestions
- }
- }
- private fun filterSuggestions(query: String): List<GtfsSuggestion> {
- return suggestions.filter {
- deAccent(it.name).contains(deAccent(query), true)
- }
- }
- private fun deAccent(str: String): String {
- var result = str.replace('ę', 'e', true)
- result = result.replace('ó', 'o', true)
- result = result.replace('ą', 'a', true)
- result = result.replace('ś', 's', true)
- result = result.replace('ł', 'l', true)
- result = result.replace('ż', 'z', true)
- result = result.replace('ź', 'z', true)
- result = result.replace('ć', 'c', true)
- result = result.replace('ń', 'n', true)
- return result
- }
- fun getSheds(name: String, callback: (Map<String, Set<String>>) -> Unit) {
- GlobalScope.launch {
- val vmSheds = vmClient.getSheds(name)
- val sheds = if (vmSheds.isEmpty() and !timetable.isEmpty()) {
- timetable.getHeadlinesForStop(name)
- } else {
- vmSheds
- }
- launch(Dispatchers.Main) {
- callback(sheds)
- }
- }
- }
- fun subscribeForDepartures(stopSegments: Set<StopSegment>, listener: OnDeparturesReadyListener, context: Context): String {
- stopSegments.forEach {
- val intent = Intent(context, VmService::class.java)
- intent.putExtra("stop", it.stop)
- intent.action = "request"
- context.startService(intent)
- }
- val uuid = UUID.randomUUID().toString()
- requests[uuid] = Request(listener, stopSegments)
- return uuid
- }
- fun subscribeForDepartures(stopCode: String, listener: OnDeparturesReadyListener, context: Context): String {
- val intent = Intent(context, VmService::class.java)
- intent.putExtra("stop", stopCode)
- intent.action = "request"
- context.startService(intent)
- val uuid = UUID.randomUUID().toString()
- requests[uuid] = Request(listener, setOf(StopSegment(stopCode, null)))
- return uuid
- }
- private fun constructSegmentDepartures(stopSegments: Set<StopSegment>): Deferred<Map<String, List<Departure>>> {
- return GlobalScope.async {
- if (timetable.isEmpty())
- emptyMap()
- else {
- timetable.getStopDeparturesBySegments(stopSegments)
- }
- }
- }
- private fun filterDepartures(departures: Map<String, List<Departure>>): List<Departure> {
- val now = Calendar.getInstance().secondsAfterMidnight()
- val lines = HashMap<String, Int>()
- val twoDayDepartures = (timetable.getServiceForToday()?.let {
- departures[it]
- } ?: emptyList()) +
- (timetable.getServiceForTomorrow()?.let { service ->
- departures[service]!!.map { it.copy().apply { tomorrow = true } }
- } ?: emptyList())
- return twoDayDepartures
- .filter { it.timeTill(now) >= 0 }
- .filter {
- val existed = lines[it.line] ?: 0
- if (existed < 3) {
- lines[it.line] = existed + 1
- true
- } else false
- }
- }
- fun unsubscribeFromDepartures(uuid: String, context: Context) {
- requests[uuid]?.unsubscribe(context)
- requests.remove(uuid)
- }
- fun refreshTimetable(context: Context) {
- timetable = Timetable.getTimetable(context, true)
- mode = MODE_FULL
- }
- fun getFullTimetable(stopCode: String): Map<String, List<Departure>> {
- return if (timetable.isEmpty())
- emptyMap()
- else
- timetable.getStopDepartures(stopCode)
- }
- fun getFullTimetable(stopSegments: Set<StopSegment>): Map<String, List<Departure>> {
- return if (timetable.isEmpty())
- emptyMap()
- else
- timetable.getStopDeparturesBySegments(stopSegments)
- }
- fun fillStopSegment(stopSegment: StopSegment, callback: (StopSegment?) -> Unit) {
- GlobalScope.launch {
- callback(fillStopSegment(stopSegment))
- }
- }
- suspend fun fillStopSegment(stopSegment: StopSegment): StopSegment? {
- if (stopSegment.plates != null)
- return stopSegment
- return if (timetable.isEmpty())
- vmClient.getDirections(stopSegment.stop)
- else
- timetable.getHeadlinesForStopCode(stopSegment.stop)
- }
- fun getStopName(stopCode: String, callback: (String?) -> Unit) {
- GlobalScope.launch {
- callback(getStopName(stopCode))
- }
- }
- suspend fun getStopName(stopCode: String): String? {
- return if (timetable.isEmpty())
- vmClient.getName(stopCode)
- else
- timetable.getStopName(stopCode)
- }
- fun describeService(service: String, context: Context): String? {
- return if (timetable.isEmpty())
- null
- else
- timetable.getServiceDescription(service, context)
- }
- fun getServiceFirstDay(service: String): Int {
- return timetable.getServiceFirstDay(service)
- }
- interface OnDeparturesReadyListener {
- fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?, code: Int)
- }
- inner class Request(private val listener: OnDeparturesReadyListener, private val segments: Set<StopSegment>) : MessageReceiver.OnVmListener {
- private val receiver = MessageReceiver.getMessageReceiver()
- private val receivedPlates = HashSet<Plate.ID>()
- private var cache: Deferred<Map<String, List<Departure>>>? = null
- init {
- receiver.addOnVmListener(this@Request)
- GlobalScope.launch {
- cache = constructSegmentDepartures(segments)
- }
- }
- override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String, code: Int) {
- GlobalScope.launch(Dispatchers.Main) {
- if ((plateId == null || vmDepartures == null) and (timetable.isEmpty())) {
- listener.onDeparturesReady(emptyList(), null, code)
- return@launch
- }
- if (plateId == null) {
- listener.onDeparturesReady(filterDepartures(cache!!.await()), null, code)
- } else {
- if (segments.any { plateId in it }) {
- if (vmDepartures != null) {
- listener.onDeparturesReady(vmDepartures.toList(), plateId, code)
- if (plateId !in receivedPlates)
- receivedPlates.add(plateId)
- } else {
- receivedPlates.remove(plateId)
- if (receivedPlates.isEmpty()) {
- listener.onDeparturesReady(filterDepartures(cache!!.await()), null, code)
- }
- }
- }
- }
- }
- }
- fun unsubscribe(context: Context) {
- segments.forEach {
- val intent = Intent(context, VmService::class.java)
- intent.putExtra("stop", it.stop)
- intent.action = "remove"
- context.startService(intent)
- }
- receiver.removeOnVmListener(this)
- }
- }
- }
|