Author: Adam Evyčędo <git@apiote.xyz>
add structs for route planning
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 27ec7e12a54255f6867f2638aff16d32b741247c..a30a386b78e38c4f35b7e686668990530549dce9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -116,11 +116,11 @@ implementation("com.github.jershell:kbson:0.5.0") implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.work:work-runtime-ktx:2.10.0") implementation("com.github.doyaaaaaken:kotlin-csv-jvm:1.10.0") - implementation("commons-io:commons-io:2.17.0") + implementation("commons-io:commons-io:2.18.0") implementation("com.google.guava:guava:33.3.1-android") implementation(project(":fruchtfleisch")) - implementation("ch.acra:acra-http:5.11.4") - implementation("ch.acra:acra-notification:5.11.4") + implementation("ch.acra:acra-http:5.12.0") + implementation("ch.acra:acra-notification:5.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.moshi:moshi-kotlin:1.15.1") diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt index 44fdaca97dc060fc611777eb0ceeba7d71982a2c..bd47fc72e1ba1bcaa102c8766b5930c268ad4b87 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt @@ -5,35 +5,107 @@ package xyz.apiote.bimba.czwek.api import android.content.Context +import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.api.transitous.api.RoutingApi -import xyz.apiote.bimba.czwek.api.transitous.model.Mode +import xyz.apiote.bimba.czwek.repo.Colour +import xyz.apiote.bimba.czwek.repo.CongestionLevel +import xyz.apiote.bimba.czwek.repo.Event import xyz.apiote.bimba.czwek.repo.Journey import xyz.apiote.bimba.czwek.repo.Leg -import xyz.apiote.bimba.czwek.repo.StopsLeg +import xyz.apiote.bimba.czwek.repo.LineStub +import xyz.apiote.bimba.czwek.repo.LineType +import xyz.apiote.bimba.czwek.repo.OccupancyStatus +import xyz.apiote.bimba.czwek.repo.Place +import xyz.apiote.bimba.czwek.repo.Position import xyz.apiote.bimba.czwek.repo.TrafficResponseException -import xyz.apiote.bimba.czwek.repo.TransitLeg +import xyz.apiote.bimba.czwek.repo.Vehicle +import xyz.apiote.bimba.czwek.units.Metre +import xyz.apiote.bimba.czwek.units.Mps +import xyz.apiote.bimba.czwek.units.Second import java.time.ZoneId -suspend fun getJourney(from: Double, to: Double, context: Context) { +suspend fun getJourney(from: Place, to: Place, context: Context): List<Journey> { if (!isNetworkAvailable(context)) { throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net)) } return withContext(Dispatchers.IO) { - val response = RoutingApi().plan(from.toString(), to.toString()) - val journeys = response.itineraries.map { + Log.d("Journeys", "from: ${from.planString()}") + Log.d("Journeys", "to: ${to.planString()}") + val response = RoutingApi().plan(from.planString(), to.planString(), maxTransfers = null) + Log.d("Journeys", "${response.itineraries}") + response.itineraries.map { val legs: List<Leg> = it.legs.map { - if(it.mode in setOf(Mode.BUS)) { - StopsLeg() - } else { - TransitLeg() - } + Leg( + Event( + it.tripId ?: "", + null, + Time.fromOffsetTime(it.startTime, ZoneId.systemDefault()), + 0u, + it.realTime, + Vehicle( + it.tripId ?: "", + Position(0.0, 0.0), + 0u, + Mps(0), + LineStub( + it.routeShortName ?: "", // TODO is it nullable, yes e.g. for transit + LineType.fromTransitous2(it.mode), + Colour.fromHex(it.routeColor) + ), + it.headsign ?: "", // TODO is it nullable, yes e.g. for transit + CongestionLevel.UNKNOWN, + OccupancyStatus.UNKNOWN + ), + boarding = 0xffu, + alerts = emptyList(), + exact = true, + terminusArrival = false + ), + Event( + it.tripId ?: "", + Time.fromOffsetTime(it.endTime, ZoneId.systemDefault()), + null, + 0u, + it.realTime, + Vehicle( + it.tripId ?: "", + Position(0.0, 0.0), + 0u, + Mps(0), + LineStub( + it.routeShortName ?: "", // TODO is it nullable, yes e.g. for transit + LineType.fromTransitous2(it.mode), + Colour.fromHex(it.routeColor) + ), + it.headsign ?: "", // TODO is it nullable, yes e.g. for transit + CongestionLevel.UNKNOWN, + OccupancyStatus.UNKNOWN + ), + boarding = 0xffu, + alerts = emptyList(), + exact = true, + terminusArrival = false + ), + Place(it.from), + Place(it.to), + it.agencyName, + it.distance?.toDouble()?.let { Metre(it) }, + Second(it.duration), + (it.intermediateStops ?: emptyList()).map { Place(it) }, + /*it.legGeometry, + it.rental, + it.steps,*/ + ) } - Journey(it.startTime.atZoneSameInstant(ZoneId.systemDefault()), it.endTime.atZoneSameInstant( - ZoneId.systemDefault()), legs) + Journey( + it.startTime.atZoneSameInstant(ZoneId.systemDefault()), it.endTime.atZoneSameInstant( + ZoneId.systemDefault() + ), legs + ) } } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt index ac05a4944b6d577160c3793997583c9c7126ccf1..2ab2919eb3cea85c2c7d2ea6f36b1af166550b71 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt @@ -36,8 +36,8 @@ import com.google.openlocationcode.OpenLocationCode import xyz.apiote.bimba.czwek.AboutActivity import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.dashboard.ui.home.HomeFragment +import xyz.apiote.bimba.czwek.dashboard.ui.journey.JourneyFragment import xyz.apiote.bimba.czwek.dashboard.ui.map.MapFragment -import xyz.apiote.bimba.czwek.dashboard.ui.voyage.VoyageFragment import xyz.apiote.bimba.czwek.databinding.ActivityMainBinding import xyz.apiote.bimba.czwek.onboarding.FirstRunActivity import xyz.apiote.bimba.czwek.search.ResultsActivity @@ -279,8 +279,7 @@ startActivity(ResultsActivity.getIntent(this, mode, query)) } private fun setNavbarIcons(f: Fragment) { - // todo [voyage-planning] - // binding.bottomNavigation.menu[2].setIcon(R.drawable.voyage_outline) + binding.bottomNavigation.menu[2].setIcon(R.drawable.voyage_outline) binding.bottomNavigation.menu[1].setIcon(R.drawable.home_outline) binding.bottomNavigation.menu[0].setIcon(R.drawable.map_outline) when (f) { @@ -288,7 +287,7 @@ is HomeFragment -> { binding.bottomNavigation.menu[1].setIcon(R.drawable.home_black) } - is VoyageFragment -> { + is JourneyFragment -> { binding.bottomNavigation.menu[2].setIcon(R.drawable.voyage_black) } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..899dcf61b0afec2a3bca1178f13e1c1f669d46dd --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Adam Evyčędo +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package xyz.apiote.bimba.czwek.dashboard.ui.journey + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import xyz.apiote.bimba.czwek.databinding.FragmentVoyageBinding + +class JourneyFragment : Fragment() { + + private var _binding: FragmentVoyageBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val journeyViewModel = + ViewModelProvider(this)[JourneyViewModel::class.java] + + _binding = FragmentVoyageBinding.inflate(inflater, container, false) + val root: View = binding.root + + val textView: TextView = binding.textDashboard + journeyViewModel.text.observe(viewLifecycleOwner) { + textView.text = it + } + + journeyViewModel.getJourney(requireContext()) + + return root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..6daa824df19ef722f345bd453f8abbdb4bf4c23d --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyViewModel.kt @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Adam Evyčędo +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package xyz.apiote.bimba.czwek.dashboard.ui.journey + +import android.content.Context +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import xyz.apiote.bimba.czwek.repo.ChangeOption +import xyz.apiote.bimba.czwek.repo.Place +import xyz.apiote.bimba.czwek.repo.Position +import xyz.apiote.bimba.czwek.repo.Stop + +class JourneyViewModel : ViewModel() { + + private val _text = MutableLiveData<String>().apply { + value = "This is voyage Fragment" + } + val text: LiveData<String> = _text + + fun getJourney(context: Context) { + viewModelScope.launch { + Log.i("Journeys", "getting journeys") + val journeys = xyz.apiote.bimba.czwek.api.getJourney( + Place(Stop("", "", "", "", "", Position(0.0, 0.0), emptyList<ChangeOption>(), ""), 52.402815, 16.911795), + Place(Stop("", "", "", "", "", Position(0.0, 0.0), emptyList<ChangeOption>(), ""), 52.445433, 17.079231), + context + ) + Log.i("Journeys", "got ${journeys.size} journeys") + + journeys.forEach { + Log.i("Journeys", "$it") + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageFragment.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageFragment.kt deleted file mode 100644 index dd4e43a5b5dced04c16bdedb0ec15706f9b978bc..0000000000000000000000000000000000000000 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageFragment.kt +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Adam Evyčędo -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package xyz.apiote.bimba.czwek.dashboard.ui.voyage - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider -import xyz.apiote.bimba.czwek.databinding.FragmentVoyageBinding - -class VoyageFragment : Fragment() { - - private var _binding: FragmentVoyageBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val voyageViewModel = - ViewModelProvider(this)[VoyageViewModel::class.java] - - _binding = FragmentVoyageBinding.inflate(inflater, container, false) - val root: View = binding.root - - val textView: TextView = binding.textDashboard - voyageViewModel.text.observe(viewLifecycleOwner) { - textView.text = it - } - return root - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageViewModel.kt deleted file mode 100644 index 7791ee8b3f17def24bd3200525907f396708e7a1..0000000000000000000000000000000000000000 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/voyage/VoyageViewModel.kt +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: Adam Evyčędo -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package xyz.apiote.bimba.czwek.dashboard.ui.voyage - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class VoyageViewModel : ViewModel() { - - private val _text = MutableLiveData<String>().apply { - value = "This is voyage Fragment" - } - val text: LiveData<String> = _text -} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt index 2ec0e896acf1697ce72836b0a0a18b38cd4021fb..e4086f2b9190b63bde8d95d160183a1320ee4d3c 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt @@ -4,45 +4,38 @@ // SPDX-License-Identifier: GPL-3.0-or-later package xyz.apiote.bimba.czwek.repo +import xyz.apiote.bimba.czwek.api.transitous.model.Place +import xyz.apiote.bimba.czwek.units.DistanceUnit import xyz.apiote.bimba.czwek.units.TimeUnit import java.time.ZonedDateTime -class Journey(val startTime: ZonedDateTime, val endTime: ZonedDateTime, val legs: List<Leg>) { -} +data class Journey(val startTime: ZonedDateTime, val endTime: ZonedDateTime, val legs: List<Leg>) -interface Leg { - fun origin(): Stop - fun destination(): Stop - fun duration(): TimeUnit - // TODO shape -} +data class Leg( + val start: Event, + val end: Event, + val origin: xyz.apiote.bimba.czwek.repo.Place, + val destination: xyz.apiote.bimba.czwek.repo.Place, + val agencyName: String?, + val distance: DistanceUnit?, + val duration: TimeUnit, + val intermediateStops: List<xyz.apiote.bimba.czwek.repo.Place>, + /* TODO + val shape: EncodedPolyline, + val rental: Rental?, + val steps: List<StepInstruction>?,*/ +) -class TransitLeg(): Leg { - override fun origin(): Stop { - TODO("Not yet implemented") - } +class Place(val stop: Stop, val latitude: Double, val longitude: Double) { + constructor(place: Place) : this( + stop = Stop(place), + latitude = place.lat.toDouble(), + longitude = place.lon.toDouble() + ) - override fun destination(): Stop { - TODO("Not yet implemented") + fun planString(): String = if (stop.code == "") { + "${latitude},${longitude},0" + } else { + stop.code } - - override fun duration(): TimeUnit { - TODO("Not yet implemented") - } - } - -class StopsLeg(): Leg { - override fun origin(): Stop { - TODO("Not yet implemented") - } - - override fun destination(): Stop { - TODO("Not yet implemented") - } - - override fun duration(): TimeUnit { - TODO("Not yet implemented") - } - -} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt index 8448bc7210a18e9e0d352a657e4ef0db6139848a..6d17c0280d508948271195823838d292d570be3c 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt @@ -61,7 +61,8 @@ s.zone, null, Position(s.position), s.changeOptions.map { ChangeOption(it) }, - null) + null + ) constructor(s: StopV2) : this( s.code, @@ -71,7 +72,8 @@ s.zone, s.feedID, Position(s.position), s.changeOptions.map { ChangeOption(it) }, - null) + null + ) constructor(s: StopV3) : this( s.code, @@ -81,9 +83,10 @@ s.zone, s.feedID, Position(s.position), s.changeOptions.map { ChangeOption(it) }, - null) + null + ) - constructor(s: Match): this( + constructor(s: Match) : this( s.id, s.name, s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString() + s.name, @@ -94,8 +97,8 @@ emptyList(), s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString() ) - constructor(s: Place): this( - s.stopId!!, + constructor(s: Place) : this( + s.stopId ?: "", s.name, s.name, "", @@ -173,7 +176,12 @@ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) LineDecoration.COLOUR -> { - str.setSpan(background, str.getSpanStart(it), str.getSpanEnd(it), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + str.setSpan( + background, + str.getSpanStart(it), + str.getSpanEnd(it), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) // str.setSpan(foreground, str.getSpanStart(it), str.getSpanEnd(it), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } @@ -199,6 +207,7 @@ } enum class LineDecoration { NONE, ITALICS, COLOUR; + companion object { fun fromPreferences(context: Context) = when (PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/res/layout/fragment_voyage.xml b/app/src/main/res/layout/fragment_voyage.xml index d203fb59097298684b55267db3e3f7250717b285..1795bb39213178519ebf3fd623efc5197b05db82 100644 --- a/app/src/main/res/layout/fragment_voyage.xml +++ b/app/src/main/res/layout/fragment_voyage.xml @@ -11,7 +11,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tool="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tool:context="xyz.apiote.bimba.czwek.dashboard.ui.voyage.VoyageFragment"> + tool:context="xyz.apiote.bimba.czwek.dashboard.ui.journey.JourneyFragment"> <com.google.android.material.textview.MaterialTextView android:id="@+id/text_dashboard" diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index be717aeeddf1f9fb5849e9756457444ce8ea25d6..19ae61227a47d944e0f93cf24a0f06f7e2014d01 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -15,9 +15,8 @@- android:id="@+id/navigation_home" android:icon="@drawable/home_outline" android:title="@string/title_home" /> - <!-- todo [voyage planning] <item android:id="@+id/navigation_voyage" android:icon="@drawable/voyage_outline" - android:title="@string/title_voyage" />--> + android:title="Journey" /> </menu> \ No newline at end of file diff --git a/app/src/main/res/navigation/front_navigation.xml b/app/src/main/res/navigation/front_navigation.xml index 1049aa1728a3a4a9591aad523c1527025415d860..bf3bb598a0cfb6ad56cf4f2bf21faa7b892ca44b 100644 --- a/app/src/main/res/navigation/front_navigation.xml +++ b/app/src/main/res/navigation/front_navigation.xml @@ -26,7 +26,7 @@ tool:layout="@layout/fragment_map" /> <fragment android:id="@+id/navigation_voyage" - android:name="xyz.apiote.bimba.czwek.dashboard.ui.voyage.VoyageFragment" + android:name="xyz.apiote.bimba.czwek.dashboard.ui.journey.JourneyFragment" android:label="@string/title_journey" tool:layout="@layout/fragment_voyage" /> </navigation> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b00ba98ca26bb4cd3ff5911e98920bbbd4505df8..313313506c8e7518f9acce01caa8c71959f02a23 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -287,7 +287,7 @@
A community-run provider-neutral international public transport routing service. Coverage is available at https://transitous.org/sources/ <string name="transitous_attribution">Transitous (https://transitous.org) API provided by Spline (https://routing.spline.de). Localities (https://github.com/public-transport/transitous/tree/main/feeds) maintained by the community.</string> <string name="local_time">local time</string> <string name="arrow">Arrow pointing to the stop</string> - <string name="departure_arrived">arrived</string> + <string name="departure_arrived">arrived</string> <string name="arrival_approximate">approx. arr.</string> <string name="arrival">arrival</string> <string name="departure_approximate">approx. dep.</string> diff --git a/fruchtfleisch/build.gradle.kts b/fruchtfleisch/build.gradle.kts index df25428f756486a6fc35db26cf06914a5427ee12..1cd1d02cb45b7590029e35f93a3e10b5468f1431 100644 --- a/fruchtfleisch/build.gradle.kts +++ b/fruchtfleisch/build.gradle.kts @@ -8,8 +8,8 @@ kotlin("jvm") } dependencies { - testImplementation("org.junit.jupiter:junit-jupiter:5.11.2") - testImplementation("org.junit.jupiter:junit-jupiter:5.11.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.11.3") + testImplementation("org.junit.jupiter:junit-jupiter:5.11.3") //implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10") }