Author: Adam Evyčędo <git@apiote.xyz>
Merge branch 'develop' into translations
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/.gitignore b/.gitignore index 73d061c3eff9885fd9b5f102f37943513d0b118f..51ff5c25c1cacc26bc5d708f8334716cf8fb77a3 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ converter/local/*.db converter/local/metadata.yml converter/local/__pycache__ converter/local/config.py +.kotlin diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index eaf4987492797d556a095a211eee2fbebc873db6..752ca17fa23bfb6f4fe112ab5b255bf899742ada 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -18,6 +18,16 @@ == [3.6.1] – 2024-09-04 === Fixed +* phantom feed names in search results + +=== Added + +* stops and departures from Transitous + +== [3.6.1] – 2024-09-04 + +=== Fixed + * add empty default cache credentials == [3.6] – 2024-08-30 diff --git a/README.adoc b/README.adoc index b484f6d8db8a2df8d8fad83be524e87818df188e..9fd6689891dbe5915ae419d699d378944a4df0c4 100644 --- a/README.adoc +++ b/README.adoc @@ -4,7 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later = Bimba Adam Evyčędo <me@apiote.xyz> -v3.6.1 2024-09-04 +v3.7.1 2024-11-30 :toc: Bimba is a FLOSS public transport passenger companion; a timetable in your pocket. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 14ae30d07aba7f11774548ce2cf91f69b4d641db..fa1aa641706e77412d05ae850be974e2a8165252 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,19 +11,19 @@ } android { namespace = "xyz.apiote.bimba.czwek" - compileSdk = 34 // https://gitlab.com/fdroid/fdroiddata/-/issues/3299#note_1989808414 - buildToolsVersion = - "34.0.0" // https://gitlab.com/fdroid/fdroiddata/-/issues/3299#note_1989808414 + // NOTE apksigner with `--alignment-preserved` https://gitlab.com/fdroid/fdroiddata/-/issues/3299#note_1989808414 + compileSdk = 35 + buildToolsVersion = "35.0.0" defaultConfig { applicationId = "xyz.apiote.bimba.czwek" minSdk = 21 targetSdk = 35 - versionCode = 31 - versionName = "3.6.1" + versionCode = 33 + versionName = "3.7.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - resourceConfigurations += listOf("en", "pl", "it", "de", "fr", "en-rUS") + resourceConfigurations += listOf("en", "de", "en-rGB", "en-rUS", "et", "fr", "it", "pl") } buildTypes { @@ -50,26 +50,26 @@ } } dependencies { - implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.core:core-ktx:1.15.0") implementation("androidx.appcompat:appcompat:1.7.0") implementation("com.google.android.material:material:1.12.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.4") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.6") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6") - implementation("androidx.navigation:navigation-fragment-ktx:2.8.2") - implementation("androidx.navigation:navigation-ui-ktx:2.8.2") + implementation("androidx.constraintlayout:constraintlayout:2.2.0") + implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7") + implementation("androidx.navigation:navigation-fragment-ktx:2.8.4") + implementation("androidx.navigation:navigation-ui-ktx:2.8.4") implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.core:core-splashscreen:1.0.1") implementation("com.google.openlocationcode:openlocationcode:1.0.4") implementation("org.osmdroid:osmdroid-android:6.1.20") implementation("org.yaml:snakeyaml:2.3") - implementation("androidx.activity:activity-ktx:1.9.2") + implementation("androidx.activity:activity-ktx:1.9.3") implementation("com.otaliastudios:zoomlayout:1.9.0") implementation("dev.bandb.graphview:graphview:0.8.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3") implementation("com.github.jershell:kbson:0.5.0") implementation("androidx.preference:preference-ktx:1.2.1") - implementation("androidx.work:work-runtime-ktx:2.9.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("com.google.guava:guava:33.3.1-android") @@ -77,7 +77,7 @@ implementation(project(":fruchtfleisch")) implementation("ch.acra:acra-http:5.11.4") implementation("ch.acra:acra-notification:5.11.4") - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.2.1") diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousDepartures.kt b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousDepartures.kt index 66fa6ae249f756a186b7405f5794b29726efd1db..3c52446a49d0db59620b89fa7ac64a29bd774cf5 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousDepartures.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousDepartures.kt @@ -12,16 +12,15 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.api.responses.ErrorResponse -import xyz.apiote.bimba.czwek.repo.Alert import xyz.apiote.bimba.czwek.repo.Colour import xyz.apiote.bimba.czwek.repo.CongestionLevel -import xyz.apiote.bimba.czwek.repo.Departure +import xyz.apiote.bimba.czwek.repo.Event 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.Position import xyz.apiote.bimba.czwek.repo.Stop -import xyz.apiote.bimba.czwek.repo.StopDepartures +import xyz.apiote.bimba.czwek.repo.StopEvents import xyz.apiote.bimba.czwek.repo.TrafficResponseException import xyz.apiote.bimba.czwek.repo.Vehicle import xyz.apiote.bimba.czwek.units.Mps @@ -37,7 +36,7 @@ context: Context, stop: String, date: LocalDate?, limit: Int? -): StopDepartures { +): StopEvents { if (!isNetworkAvailable(context)) { throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net)) } @@ -75,7 +74,7 @@ throw TrafficResponseException(result.error.statusCode, "", result.error) } } else { return withContext(Dispatchers.IO) { - val departures = mutableListOf<Departure>() + val events = mutableListOf<Event>() var stopID = "" var stopName = "" var latitude = 0.0 @@ -190,10 +189,17 @@ ZonedDateTime.ofInstant( Instant.ofEpochSecond(eventTimestamp), ZoneId.systemDefault() ) - departures.add( - Departure( - ID = hash, - time = Time( + events.add( + Event( + id = hash, + arrivalTime = Time( + t.hour.toUInt(), + t.minute.toUInt(), + t.second.toUInt(), + (t.dayOfYear - ZonedDateTime.now().dayOfYear).toByte(), + ZoneId.systemDefault().id + ), + departureTime = Time( t.hour.toUInt(), t.minute.toUInt(), t.second.toUInt(), @@ -247,8 +253,8 @@ } } } - return@withContext StopDepartures( - departures, + return@withContext StopEvents( + events, Stop(stopID, stopName, stopName, "", "transitous", Position(latitude, longitude), listOf()), listOf() ) diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/Favourites.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/Favourites.kt index 2b1d50a476c8181b6758489d60b61fe772080d09..11c0a85d49513a4f0720a5ca7a1854bc429b73c9 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/Favourites.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/Favourites.kt @@ -14,7 +14,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.departures.DeparturesActivity -import xyz.apiote.bimba.czwek.repo.Departure +import xyz.apiote.bimba.czwek.repo.Event import xyz.apiote.bimba.czwek.repo.Favourite import java.time.ZoneId import java.time.ZonedDateTime @@ -23,7 +23,7 @@ import java.util.Optional class BimbaFavouritesAdapter( private var favourites: List<Favourite>, - private var departures: Map<String, Optional<Departure>>, + private var departures: Map<String, Optional<Event>>, private val inflater: LayoutInflater, private val context: Context ) : @@ -34,9 +34,9 @@ private set inner class DiffUtilCallback( private val oldFavourites: List<Favourite>, - private val oldDepartures: Map<String, Optional<Departure>?>, + private val oldDepartures: Map<String, Optional<Event>?>, private val newFavourites: List<Favourite>, - private val newDepartures: Map<String, Optional<Departure>?> + private val newDepartures: Map<String, Optional<Event>?> ) : DiffUtil.Callback() { override fun getOldListSize() = oldFavourites.size @@ -57,9 +57,9 @@ return false } val favouritesSame = oldFav.feedName == newFav.feedName && - oldFav.stopName == newFav.stopName && - oldFav.sequence == newFav.sequence && - oldFav.lines == newFav.lines + oldFav.stopName == newFav.stopName && + oldFav.sequence == newFav.sequence && + oldFav.lines == newFav.lines if (!favouritesSame) { return false @@ -73,14 +73,14 @@ if ((oldDeparture!!.isEmpty && !newDeparture!!.isEmpty) || (!oldDeparture!!.isEmpty && newDeparture!!.isEmpty)) { return false } - return oldDeparture!!.get().ID == newDeparture!!.get().ID && - oldDeparture!!.get().vehicle.Line == newDeparture!!.get().vehicle.Line && - oldDeparture!!.get().vehicle.Headsign == newDeparture!!.get().vehicle.Headsign && - oldDeparture!!.get().statusText( - context, - false, - lastUpdate - ) == newDeparture!!.get().statusText(context, false) + return oldDeparture!!.get().id == newDeparture!!.get().id && + oldDeparture!!.get().vehicle.Line == newDeparture!!.get().vehicle.Line && + oldDeparture!!.get().vehicle.Headsign == newDeparture!!.get().vehicle.Headsign && + oldDeparture!!.get().statusText( + context, + false, + lastUpdate + ) == newDeparture!!.get().statusText(context, false) } } @@ -111,7 +111,7 @@ this.favourites = favourites diff.dispatchUpdatesTo(this) } - fun updateDepartures(departures: Map<String, Optional<Departure>>) { + fun updateDepartures(departures: Map<String, Optional<Event>>) { this.departures = departures notifyDataSetChanged() lastUpdate = ZonedDateTime.now() @@ -151,6 +151,10 @@ class FavouriteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val root: View = itemView.findViewById(R.id.favourite) val feedName: TextView = itemView.findViewById(R.id.feed_name) val lineIcon: ImageView = itemView.findViewById(R.id.line_icon) + val arrivalStatus: TextView = itemView.findViewById(R.id.arrival_status) + val arrivalTime: TextView = itemView.findViewById(R.id.arrival_time) + val arrivalTimeFull: TextView = itemView.findViewById(R.id.arrival_full_time) + val departureStatus: TextView = itemView.findViewById(R.id.departure_status) val departureTime: TextView = itemView.findViewById(R.id.departure_time) val departureTimeFull: TextView = itemView.findViewById(R.id.departure_full_time) val lineName: TextView = itemView.findViewById(R.id.departure_line) @@ -162,33 +166,32 @@ fun bind( favourite: Favourite, holder: FavouriteViewHolder, context: Context, - departure: Optional<Departure>? + event: Optional<Event>? ) { - if (departure == null) { + if (event == null) { holder.feedName.text = favourite.feedName holder.stopHeadline.text = favourite.stopName holder.lineIcon.setImageDrawable(null) holder.lineName.text = context.getString(R.string.loading) - holder.departureTime.text = "" - holder.departureTimeFull.text = "" + holder.arrivalTime.text = "" + holder.arrivalTimeFull.text = "" holder.headsign.text = "" - } else if (departure.isEmpty) { + } else if (event.isEmpty) { holder.feedName.text = favourite.feedName holder.stopHeadline.text = favourite.stopName holder.lineIcon.setImageDrawable(null) holder.lineName.text = context.getString(R.string.no_departures).lowercase() - holder.departureTime.text = "" - holder.departureTimeFull.text = "" + holder.arrivalTime.text = "" + holder.arrivalTimeFull.text = "" holder.headsign.text = "" } else { - val vehicle = departure.get().vehicle + val statusTexts = event.get().statusText(context, false) + val vehicle = event.get().vehicle holder.feedName.text = favourite.feedName holder.stopHeadline.text = favourite.stopName holder.lineIcon.setImageDrawable(vehicle.Line.icon(context)) holder.lineIcon.contentDescription = vehicle.Line.kind.name holder.lineName.text = vehicle.Line.name - holder.departureTime.text = departure.get().statusText(context, false) - holder.departureTimeFull.text = departure.get().timeString(context) holder.headsign.text = context.getString(R.string.departure_headsign, vehicle.Headsign) holder.headsign.contentDescription = @@ -196,10 +199,87 @@ context.getString( R.string.departure_headsign_content_description, vehicle.Headsign ) + + with(event.get()) { + if (arrivalTime == departureTime) { + holder.arrivalStatus.visibility = View.GONE + holder.arrivalTime.visibility = View.GONE + holder.arrivalTimeFull.visibility = View.GONE + holder.departureTime.apply { + text = statusTexts.second + visibility = View.VISIBLE + } + holder.departureTimeFull.apply { + text = departureTimeString(context) + visibility = View.VISIBLE + } + if (!exact) { + holder.departureStatus.apply { + text = context.getString(R.string.approximately) + visibility = View.VISIBLE + } + } else { + holder.departureStatus.visibility = View.GONE + } + } else { + if (arrivalTime != null) { + holder.arrivalTimeFull.apply { + visibility = View.VISIBLE + text = arrivalTimeString(context) + } + holder.arrivalTime.apply { + visibility = View.VISIBLE + text = statusTexts.first + } + holder.arrivalStatus.apply { + visibility = View.VISIBLE + text = if (!exact) { + context.getString(R.string.arrival_approximate) + } else { + context.getString(R.string.arrival) + } + } + } else { + holder.arrivalStatus.visibility = View.GONE + holder.arrivalTime.visibility = View.GONE + holder.arrivalTimeFull.visibility = View.GONE + } + if (departureTime != null) { + holder.departureTimeFull.apply { + visibility = View.VISIBLE + text = departureTimeString(context) + } + holder.departureTime.apply { + visibility = View.VISIBLE + text = statusTexts.second + } + holder.departureStatus.apply { + visibility = View.VISIBLE + text = if (!exact) { + context.getString(R.string.departure_approximate) + } else { + context.getString(R.string.departure) + } + } + } else { + holder.departureStatus.visibility = View.GONE + holder.departureTime.visibility = View.GONE + holder.departureTimeFull.visibility = View.GONE + } + } + } } holder.root.setOnClickListener { - context.startActivity(DeparturesActivity.getIntent(context, favourite.stopCode, favourite.stopName, favourite.feedID, favourite.lines.toTypedArray())) + context.startActivity( + DeparturesActivity.getIntent( + context, + favourite.stopCode, + favourite.stopName, + favourite.feedID, + favourite.lines.toTypedArray() + ) + ) } } } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeViewModel.kt index ba6da66f9308e7dc91bd81aca46cff37c3dffee1..def65cf9bf6e3e4d1f37c37e65d5121123a5824d 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeViewModel.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeViewModel.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch -import xyz.apiote.bimba.czwek.repo.Departure +import xyz.apiote.bimba.czwek.repo.Event import xyz.apiote.bimba.czwek.repo.Favourite import xyz.apiote.bimba.czwek.repo.FeedInfo import xyz.apiote.bimba.czwek.repo.OfflineRepository @@ -36,8 +36,8 @@ var feeds: Map? = null var feedsSettings: FeedsSettings? = null private val mutableFavourites = MutableLiveData<List<Favourite>>() val favourites: LiveData<List<Favourite>> = mutableFavourites - private val mutableDepartures = MutableLiveData<Map<String, Optional<Departure>>>() - val departures: LiveData<Map<String, Optional<Departure>>> = mutableDepartures + private val mutableDepartures = MutableLiveData<Map<String, Optional<Event>>>() + val departures: LiveData<Map<String, Optional<Event>>> = mutableDepartures fun getQueryables(query: String, context: Context) { viewModelScope.launch { @@ -89,10 +89,10 @@ context, 12 // XXX heuristics ) stopDepartures?.let { sDs -> - if (sDs.departures.isEmpty()) { + if (sDs.events.isEmpty()) { Pair(favourite.feedID+favourite.stopCode, Optional.empty()) } else { - Pair(favourite.feedID+favourite.stopCode, Optional.ofNullable(sDs.departures.find { departure -> + Pair(favourite.feedID+favourite.stopCode, Optional.ofNullable(sDs.events.find { departure -> favourite.lines.isEmpty() or favourite.lines.contains( departure.vehicle.Line.name ) diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt index 6aa86b0cb2362798e410558411e5229ae8ba873e..a9ff0719d69c398d56d1f3e06ad2aa3cc8072187 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt @@ -73,7 +73,7 @@ vehicle.Headsign ) } - content.findViewById<TextView>(R.id.time).visibility = View.GONE + content.findViewById<TextView>(R.id.arrival_time).visibility = View.GONE content.findViewById<TextView>(R.id.local_time).visibility = View.GONE content.findViewById<MapView>(R.id.map).visibility = View.GONE diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt index 17c08de21676990de2632a77c932c099454c4626..01f592aa9c53a29de8ef478f6d1ff9f504d97335 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt @@ -34,13 +34,12 @@ import org.osmdroid.views.MapView import org.osmdroid.views.overlay.Marker import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.gestures.RotationGestureOverlay -import org.w3c.dom.Text import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.dpToPixelI import xyz.apiote.bimba.czwek.repo.Alert import xyz.apiote.bimba.czwek.repo.CongestionLevel -import xyz.apiote.bimba.czwek.repo.Departure -import xyz.apiote.bimba.czwek.repo.DepartureItem +import xyz.apiote.bimba.czwek.repo.Event +import xyz.apiote.bimba.czwek.repo.EventItem import xyz.apiote.bimba.czwek.repo.OccupancyStatus import xyz.apiote.bimba.czwek.repo.Vehicle import xyz.apiote.bimba.czwek.units.UnitSystem @@ -50,75 +49,118 @@ class BimbaDepartureViewHolder(itemView: View) : ViewHolder(itemView) { val root: View = itemView.findViewById(R.id.departure) val lineIcon: ImageView = itemView.findViewById(R.id.line_icon) + val arrivalTime: TextView = itemView.findViewById(R.id.arrival_time) + val arrivalStatus: TextView = itemView.findViewById(R.id.arrival_status) val departureTime: TextView = itemView.findViewById(R.id.departure_time) + val departureStatus: TextView = itemView.findViewById(R.id.departure_status) val lineName: TextView = itemView.findViewById(R.id.departure_line) val headsign: TextView = itemView.findViewById(R.id.departure_headsign) - val timeStatus: ImageView = itemView.findViewById(R.id.time_status) + val eventStatus: ImageView = itemView.findViewById(R.id.event_status) companion object { fun bind( - departure: Departure, + event: Event, holder: BimbaDepartureViewHolder?, context: Context?, showAsTime: Boolean, - onClickListener: (Departure) -> Unit, + onClickListener: (Event) -> Unit, showingTerminusArrivals: String ) { holder?.root?.setOnClickListener { - onClickListener(departure) + onClickListener(event) } - holder?.lineIcon?.setImageDrawable(departure.vehicle.Line.icon(context!!)) - holder?.lineIcon?.contentDescription = departure.vehicle.Line.kind.name - holder?.lineName?.text = departure.vehicle.Line.name + holder?.lineIcon?.setImageDrawable(event.vehicle.Line.icon(context!!)) + holder?.lineIcon?.contentDescription = event.vehicle.Line.kind.name + holder?.lineName?.text = event.vehicle.Line.name holder?.headsign?.text = - context?.getString(R.string.departure_headsign, departure.vehicle.Headsign) + context?.getString(R.string.departure_headsign, event.vehicle.Headsign) holder?.headsign?.contentDescription = context?.getString( R.string.departure_headsign_content_description, - departure.vehicle.Headsign + event.vehicle.Headsign ) when { - departure.isRealtime -> { - holder?.timeStatus?.setImageResource(R.drawable.radar) - holder?.timeStatus?.contentDescription = - context?.getString(R.string.realtime_content_description) - holder?.timeStatus?.let { + event.isRealtime -> { + holder?.eventStatus?.let { + it.contentDescription = + context?.getString(R.string.realtime_content_description) + it.setImageResource(R.drawable.radar) TooltipCompat.setTooltipText( it, context?.getString(R.string.realtime_content_description) ) + // TODO all at the same time with setCurrentFraction based on wall clock + /*ObjectAnimator.ofPropertyValuesHolder(it, PropertyValuesHolder.ofFloat("alpha", 0.0f)) + .apply { + setDuration(1000) + repeatCount = ObjectAnimator.INFINITE + repeatMode = ObjectAnimator.REVERSE + } + .start()*/ } } - departure.exact -> { - holder?.timeStatus?.setImageResource(R.drawable.calendar) - holder?.timeStatus?.contentDescription = - context?.getString(R.string.exact_content_description) - holder?.timeStatus?.let { + event.exact -> { + // FIXME clear animation + holder?.eventStatus?.let { + it.setImageResource(R.drawable.calendar) + it.contentDescription = + context?.getString(R.string.schedule_content_description) TooltipCompat.setTooltipText( it, - context?.getString(R.string.exact_content_description) + context?.getString(R.string.schedule_content_description) ) } } + } - else -> { - holder?.timeStatus?.setImageResource(R.drawable.inexact) - holder?.timeStatus?.contentDescription = - context?.getString(R.string.inexact_content_description) - holder?.timeStatus?.let { - TooltipCompat.setTooltipText( - it, - context?.getString(R.string.inexact_content_description) - ) + val statusTexts = event.statusText(context, showAsTime) + if (event.arrivalTime == event.departureTime) { + if (!event.exact) { + holder?.arrivalStatus?.apply{ + visibility = View.VISIBLE + text = context?.getString(R.string.approximately) + } + } else { + holder?.arrivalStatus?.visibility = View.INVISIBLE + } + holder?.arrivalTime?.apply{ + text = statusTexts.second + visibility = View.VISIBLE + } + holder?.departureStatus?.visibility = View.GONE + holder?.departureTime?.visibility = View.GONE + } else { + if (statusTexts.first != null) { + holder?.arrivalTime?.visibility = View.VISIBLE + holder?.arrivalTime?.text = statusTexts.first + holder?.arrivalStatus?.visibility = View.VISIBLE + holder?.arrivalStatus?.text = if (!event.exact) { + context?.getString(R.string.arrival_approximate) + } else { + context?.getString(R.string.arrival) } + } else { + holder?.arrivalTime?.visibility = View.GONE + holder?.arrivalStatus?.visibility = View.GONE + } + if (statusTexts.second != null) { + holder?.departureTime?.visibility = View.VISIBLE + holder?.departureTime?.text = statusTexts.second + holder?.departureStatus?.visibility = View.VISIBLE + holder?.departureStatus?.text = if (!event.exact) { + context?.getString(R.string.departure_approximate) + } else { + context?.getString(R.string.departure) + } + } else { + holder?.departureTime?.visibility = View.GONE + holder?.departureStatus?.visibility = View.GONE } } - - holder?.departureTime?.text = departure.statusText(context, showAsTime) holder?.root?.alpha = - if (departure.terminusArrival && showingTerminusArrivals == BimbaDeparturesAdapter.TERMINUS_ARRIVAL_GREY_OUT) { + if (event.terminusArrival && showingTerminusArrivals == BimbaDeparturesAdapter.TERMINUS_ARRIVAL_GREY_OUT) { .5f } else { 1f @@ -160,8 +202,8 @@ class BimbaDeparturesAdapter( private val inflater: LayoutInflater, private val context: Context?, - private var items: List<DepartureItem>, - private val onClickListener: ((Departure) -> Unit), + private var items: List<EventItem>, + private val onClickListener: ((Event) -> Unit), ) : RecyclerView.Adapter<ViewHolder>() { @@ -188,8 +230,8 @@ private set private var showAsTime: Boolean = false inner class DiffUtilCallback( - private val oldDepartures: List<DepartureItem>, - private val newDepartures: List<DepartureItem>, + private val oldDepartures: List<EventItem>, + private val newDepartures: List<EventItem>, private val showAsTimeChanged: Boolean, ) : DiffUtil.Callback() { override fun getOldListSize() = oldDepartures.size @@ -197,23 +239,23 @@ override fun getNewListSize() = newDepartures.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = - (oldDepartures[oldItemPosition].departure?.ID - ?: ALERT_ITEM_ID) == (newDepartures[newItemPosition].departure?.ID ?: ALERT_ITEM_ID) + (oldDepartures[oldItemPosition].event?.id + ?: ALERT_ITEM_ID) == (newDepartures[newItemPosition].event?.id ?: ALERT_ITEM_ID) override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldDeparture = oldDepartures[oldItemPosition] val newDeparture = newDepartures[newItemPosition] - return if (oldDeparture.departure != null && newDeparture.departure != null) { - !oldDeparture.departure.terminusArrival && - oldDeparture.departure.terminusArrival == newDeparture.departure.terminusArrival && - oldDeparture.departure.exact == newDeparture.departure.exact && - oldDeparture.departure.vehicle.Line == newDeparture.departure.vehicle.Line && - oldDeparture.departure.vehicle.Headsign == newDeparture.departure.vehicle.Headsign && - oldDeparture.departure.statusText( + return if (oldDeparture.event != null && newDeparture.event != null) { + !oldDeparture.event.terminusArrival && + oldDeparture.event.terminusArrival == newDeparture.event.terminusArrival && + oldDeparture.event.exact == newDeparture.event.exact && + oldDeparture.event.vehicle.Line == newDeparture.event.vehicle.Line && + oldDeparture.event.vehicle.Headsign == newDeparture.event.vehicle.Headsign && + oldDeparture.event.statusText( context, false, lastUpdate - ) == newDeparture.departure.statusText(context, false) && !showAsTimeChanged + ) == newDeparture.event.statusText(context, false) && !showAsTimeChanged } else if (oldDeparture.alert.isNotEmpty() && newDeparture.alert.isEmpty()) { oldDeparture.alert == newDeparture.alert } else { @@ -226,12 +268,12 @@ private var departuresPositions: MutableMap = HashMap() init { items.forEachIndexed { i, departure -> - departuresPositions[departure.departure?.ID ?: ALERT_ITEM_ID] = i + departuresPositions[departure.event?.id ?: ALERT_ITEM_ID] = i } } override fun getItemViewType(position: Int): Int { - return if (items[position].departure != null) { + return if (items[position].event != null) { 0 } else { 1 @@ -251,7 +293,7 @@ override fun onBindViewHolder(holder: ViewHolder, position: Int) { if (holder is BimbaDepartureViewHolder) { BimbaDepartureViewHolder.bind( - items[position].departure!!, + items[position].event!!, holder, context, showAsTime, @@ -265,7 +307,7 @@ } override fun getItemCount(): Int = items.size - fun get(id: String): DepartureItem? { + fun get(id: String): EventItem? { val position = departuresPositions[id] return if (position == null) { null @@ -275,7 +317,7 @@ } } fun update( - departures: List<DepartureItem>, + departures: List<EventItem>, showAsTime: Boolean, areNewObserved: Boolean = false, leaveAlert: Boolean = false @@ -287,7 +329,7 @@ departures } val newPositions: MutableMap<String, Int> = HashMap() newDepartures.forEachIndexed { i, departure -> - newPositions[departure.departure?.ID ?: ALERT_ITEM_ID] = i + newPositions[departure.event?.id ?: ALERT_ITEM_ID] = i } val diff = DiffUtil.calculateDiff( DiffUtilCallback( @@ -312,7 +354,7 @@ update(this.items, showAsTime) } } -class DepartureBottomSheet(private var departure: Departure) : BottomSheetDialogFragment() { +class DepartureBottomSheet(private var event: Event) : BottomSheetDialogFragment() { companion object { const val TAG = "DepartureBottomSheet" } @@ -329,33 +371,69 @@ cancelCallback?.let { it() } } fun departureID(): String { - return departure.ID + return event.id } - fun update(departure: Departure) { - this.departure = departure + fun update(event: Event) { + this.event = event view?.let { context?.let { ctx -> setContent(it, ctx, true) } } } private fun setContent(view: View, ctx: Context, updating: Boolean = false) { view.apply { - findViewById<TextView>(R.id.time).text = departure.timeString(ctx) + if (event.arrivalTime == event.departureTime) { + if (!event.exact) { + findViewById<TextView>(R.id.arrival_status).apply { + visibility = View.VISIBLE + text = context.getString(R.string.approximately) + } + } else { + findViewById<TextView>(R.id.arrival_status).visibility = View.GONE + } + findViewById<TextView>(R.id.arrival_time).apply { + text = event.arrivalTimeString(ctx) + visibility = View.VISIBLE + } + findViewById<TextView>(R.id.departure_status).visibility = View.GONE + findViewById<TextView>(R.id.departure_time).visibility = View.GONE + } else { + if (event.arrivalTime != null) { + findViewById<TextView>(R.id.arrival_time).visibility = View.VISIBLE + findViewById<TextView>(R.id.arrival_time).text = event.arrivalTimeString(ctx) + findViewById<TextView>(R.id.arrival_status).visibility = View.VISIBLE + findViewById<TextView>(R.id.arrival_status).text = if (!event.exact) { + context?.getString(R.string.arrival_approximate) + } else { + context?.getString(R.string.arrival) + } + } + if (event.departureTime != null) { + findViewById<TextView>(R.id.departure_time).visibility = View.VISIBLE + findViewById<TextView>(R.id.departure_time).text = event.departureTimeString(ctx) + findViewById<TextView>(R.id.departure_status).visibility = View.VISIBLE + findViewById<TextView>(R.id.departure_status).text = if (!event.exact) { + context?.getString(R.string.departure_approximate) + } else { + context?.getString(R.string.departure) + } + } + } findViewById<TextView>(R.id.local_time).visibility = - if (departure.time.Zone == ZoneId.systemDefault().id) { + if (event.timeZone() == ZoneId.systemDefault().id) { View.GONE } else { View.VISIBLE } findViewById<ImageView>(R.id.rt_icon).apply { - visibility = if (departure.isRealtime) { + visibility = if (event.isRealtime) { View.VISIBLE } else { View.GONE } } findViewById<ImageView>(R.id.wheelchair_icon).apply { - visibility = if (departure.vehicle.let { + visibility = if (event.vehicle.let { it.getCapability(Vehicle.Capability.LOW_FLOOR) || it.getCapability(Vehicle.Capability.LOW_ENTRY) || it.getCapability( Vehicle.Capability.RAMP ) @@ -369,41 +447,41 @@ findViewById<TextView>(R.id.line).apply { contentDescription = getString( R.string.vehicle_headsign_content_description, - departure.vehicle.Line.name, - departure.vehicle.Headsign + event.vehicle.Line.name, + event.vehicle.Headsign ) text = getString( R.string.vehicle_headsign, - departure.vehicle.Line.name, - departure.vehicle.Headsign + event.vehicle.Line.name, + event.vehicle.Headsign ) } - departure.boardingText(ctx).let { + event.boardingText(ctx).let { findViewById<TextView>(R.id.boarding_text).text = it - findViewById<ImageView>(R.id.boarding_icon).visibility = if (it == "") { - View.GONE - } else { - View.VISIBLE - } + findViewById<ImageView>(R.id.boarding_icon).visibility = if (it == "") { + View.GONE + } else { + View.VISIBLE + } } UnitSystem.getSelected(requireContext()).let { us -> findViewById<TextView>(R.id.speed_text).apply { text = - us.toString(context, us.speedUnit(departure.vehicle.Speed)) + us.toString(context, us.speedUnit(event.vehicle.Speed)) contentDescription = - us.speedUnit(departure.vehicle.Speed).contentDescription(requireContext(), us.base) + us.speedUnit(event.vehicle.Speed).contentDescription(requireContext(), us.base) } } findViewById<LinearLayout>(R.id.congestion).visibility = - if (departure.vehicle.congestionLevel == CongestionLevel.UNKNOWN) View.GONE else View.VISIBLE - findViewById<TextView>(R.id.congestion_text).text = departure.vehicle.congestion(ctx) + if (event.vehicle.congestionLevel == CongestionLevel.UNKNOWN) View.GONE else View.VISIBLE + findViewById<TextView>(R.id.congestion_text).text = event.vehicle.congestion(ctx) findViewById<LinearLayout>(R.id.occupancy).visibility = - if (departure.vehicle.occupancyStatus == OccupancyStatus.UNKNOWN) View.GONE else View.VISIBLE - findViewById<TextView>(R.id.occupancy_text).text = departure.vehicle.occupancy(ctx) + if (event.vehicle.occupancyStatus == OccupancyStatus.UNKNOWN) View.GONE else View.VISIBLE + findViewById<TextView>(R.id.occupancy_text).text = event.vehicle.occupancy(ctx) findViewById<ImageView>(R.id.ac).let { TooltipCompat.setTooltipText( @@ -411,7 +489,7 @@ it, getString(R.string.air_condition_content_description) ) it.visibility = - if (departure.vehicle.getCapability(Vehicle.Capability.AC)) View.VISIBLE else View.GONE + if (event.vehicle.getCapability(Vehicle.Capability.AC)) View.VISIBLE else View.GONE } findViewById<ImageView>(R.id.bike).let { @@ -420,7 +498,7 @@ it, getString(R.string.bicycles_allowed_content_description) ) it.visibility = - if (departure.vehicle.getCapability(Vehicle.Capability.BIKE)) { + if (event.vehicle.getCapability(Vehicle.Capability.BIKE)) { View.VISIBLE } else { View.GONE @@ -433,7 +511,7 @@ it, getString(R.string.voice_announcements_content_description) ) it.visibility = - if (departure.vehicle.getCapability(Vehicle.Capability.VOICE)) { + if (event.vehicle.getCapability(Vehicle.Capability.VOICE)) { View.VISIBLE } else { View.GONE @@ -444,7 +522,7 @@ TooltipCompat.setTooltipText( ticketImage, getString(R.string.tickets_sold_content_description) ) - ticketImage.visibility = if (departure.vehicle.let { + ticketImage.visibility = if (event.vehicle.let { it.getCapability(Vehicle.Capability.TICKET_DRIVER) || it.getCapability(Vehicle.Capability.TICKET_MACHINE) }) { View.VISIBLE @@ -458,15 +536,15 @@ it, getString(R.string.usb_charging_content_description) ) it.visibility = - if (departure.vehicle.getCapability(Vehicle.Capability.USB_CHARGING)) { + if (event.vehicle.getCapability(Vehicle.Capability.USB_CHARGING)) { View.VISIBLE } else { View.GONE } } - if (departure.alerts.isNotEmpty()) { - findViewById<MaterialTextView>(R.id.alerts_text).text = departure.alerts.map { + if (event.alerts.isNotEmpty()) { + findViewById<MaterialTextView>(R.id.alerts_text).text = event.alerts.map { it.header.ifEmpty { getString(R.string.alert_header) } @@ -477,7 +555,7 @@ setOnClickListener { MaterialAlertDialogBuilder(context) .setTitle(R.string.alerts) .setPositiveButton(R.string.ok) { _, _ -> } - .setMessage(departure.alerts.map { it.description }.filter { it != "" } + .setMessage(event.alerts.map { it.description }.filter { it != "" } .joinToString(separator = "\n")) .show() } @@ -485,14 +563,14 @@ } } findViewById<MapView>(R.id.map).let { map -> - if (departure.vehicle.Position.isZero()) { + if (event.vehicle.Position.isZero()) { map.visibility = View.GONE return@let } map.controller.apply { GeoPoint( - departure.vehicle.location().latitude, - departure.vehicle.location().longitude + event.vehicle.location().latitude, + event.vehicle.location().longitude ).let { geoPoint -> if (updating) { animateTo( @@ -511,11 +589,11 @@ } val marker = Marker(map).apply { position = GeoPoint( - departure.vehicle.location().latitude, - departure.vehicle.location().longitude + event.vehicle.location().latitude, + event.vehicle.location().longitude ) setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER) - icon = context?.let { ctx -> departure.vehicle.icon(ctx, 2f) } + icon = context?.let { ctx -> event.vehicle.icon(ctx, 2f) } setOnClickListener {} } map.overlays.add(marker) diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt index 0b725721dcfca87b8b52eaa06e4407b36784c410..8ca35eb6214c46644d0d28ee8f635bd4054a37d5 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt @@ -28,6 +28,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.datepicker.MaterialDatePicker @@ -45,7 +46,7 @@ import xyz.apiote.bimba.czwek.databinding.ActivityDeparturesBinding import xyz.apiote.bimba.czwek.departures.BimbaDeparturesAdapter.Companion.TERMINUS_ARRIVAL_GREY_OUT import xyz.apiote.bimba.czwek.departures.BimbaDeparturesAdapter.Companion.TERMINUS_ARRIVAL_HIDE import xyz.apiote.bimba.czwek.departures.BimbaDeparturesAdapter.Companion.TERMINUS_ARRIVAL_SHOWING_KEY -import xyz.apiote.bimba.czwek.repo.DepartureItem +import xyz.apiote.bimba.czwek.repo.EventItem import xyz.apiote.bimba.czwek.repo.Favourite import xyz.apiote.bimba.czwek.repo.OfflineRepository import xyz.apiote.bimba.czwek.repo.Stop @@ -168,7 +169,7 @@ } viewModel.linesFilter.observe(this) { // TODO if is before we got departures, do nothing - val departures = viewModel.departures.value?.departures ?: emptyList() + val departures = viewModel.departures.value?.events ?: emptyList() updateItems(departures .filter { d -> it.values.all { !it } or (it[d.vehicle.Line.name] ?: false) @@ -177,20 +178,20 @@ .filter { d -> viewModel.showingTerminusArrivals != TERMINUS_ARRIVAL_HIDE || !d.terminusArrival } .filter { d -> - val t = LocalTime.of(d.time.Hour.toInt(), d.time.Minute.toInt()) + val t = LocalTime.of(d.filterTime().Hour.toInt(), d.filterTime().Minute.toInt()) t >= viewModel.startTime && t <= viewModel.endTime - }.map { DepartureItem(it) }, + }.map { EventItem(it) }, null, true ) } viewModel.departures.observe(this) { stopDepartures -> - val items = mutableListOf<DepartureItem>() + val items = mutableListOf<EventItem>() if (stopDepartures.alerts.isNotEmpty()) { - items.add(DepartureItem(stopDepartures.alerts)) + items.add(EventItem(stopDepartures.alerts)) } - items.addAll(stopDepartures.departures + items.addAll(stopDepartures.events .filter { d -> viewModel.linesFilter.value?.let { filter -> filter.values.all { !it } or (filter[d.vehicle.Line.name] ?: false) @@ -200,15 +201,15 @@ .filter { d -> viewModel.showingTerminusArrivals != TERMINUS_ARRIVAL_HIDE || !d.terminusArrival } .filter { d -> - val t = LocalTime.of(d.time.Hour.toInt(), d.time.Minute.toInt()) + val t = LocalTime.of(d.filterTime().Hour.toInt(), d.filterTime().Minute.toInt()) t >= viewModel.startTime && t <= viewModel.endTime - }.map { DepartureItem(it) }) + }.map { EventItem(it) }) updateItems(items, stopDepartures.stop) viewModel.openBottomSheet?.departureID()?.let { adapter.get(it) } - ?.let { it.departure?.let { departure -> viewModel.openBottomSheet?.update(departure) } } + ?.let { it.event?.let { departure -> viewModel.openBottomSheet?.update(departure) } } - val lines = stopDepartures.departures.map { it.vehicle.Line.name }.sortedWith { s1, s2 -> + val lines = stopDepartures.events.map { it.vehicle.Line.name }.sortedWith { s1, s2 -> val s1n = s1.toIntOrNull() val s2n = s2.toIntOrNull() if (s1n != null && s2n != null) { @@ -374,6 +375,7 @@ } } binding.departuresRecycler.layoutManager = LinearLayoutManager(this) + binding.departuresRecycler.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) binding.departuresRecycler.itemAnimator = null binding.departuresRecycler.addOnScrollListener( object : RecyclerView.OnScrollListener() { @@ -518,7 +520,7 @@ binding.errorText.visibility = View.GONE } private fun updateItems( - departures: List<DepartureItem>, + departures: List<EventItem>, stop: Stop?, leaveAlert: Boolean = false ) { diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt index bd61194560e6a232a5754bbf33fa60b3ed03dc6f..21dbd04eefa862f93ad0f5ad95a0fc1ef48e80db 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt @@ -20,14 +20,14 @@ import xyz.apiote.bimba.czwek.repo.FeedInfo import xyz.apiote.bimba.czwek.repo.OfflineRepository import xyz.apiote.bimba.czwek.repo.OnlineRepository import xyz.apiote.bimba.czwek.repo.QrLocation -import xyz.apiote.bimba.czwek.repo.StopDepartures +import xyz.apiote.bimba.czwek.repo.StopEvents import xyz.apiote.bimba.czwek.repo.TrafficResponseException import java.time.LocalDate import java.time.LocalTime class DeparturesViewModel : ViewModel() { - private val _departures = MutableLiveData<StopDepartures>() - val departures: LiveData<StopDepartures> = _departures + private val _departures = MutableLiveData<StopEvents>() + val departures: LiveData<StopEvents> = _departures private val _error = MutableLiveData<Error>() val error: LiveData<Error> = _error var requestedItemsNumber = 12 @@ -64,7 +64,7 @@ context, requestedItemsNumber ) stopDepartures?.let { - if (stopDepartures.departures.isEmpty()) { + if (stopDepartures.events.isEmpty()) { val (string, image) = mapHttpError(44) throw TrafficResponseException(44, "", Error(44, string, image)) } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt deleted file mode 100644 index 38a48a946c2048ad2fa19b5eb4fa169769287adc..0000000000000000000000000000000000000000 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-FileCopyrightText: Adam Evyčędo -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package xyz.apiote.bimba.czwek.repo - -import android.content.Context -import android.text.format.DateUtils -import xyz.apiote.bimba.czwek.R -import xyz.apiote.bimba.czwek.api.AlertCauseV1 -import xyz.apiote.bimba.czwek.api.AlertEffectV1 -import xyz.apiote.bimba.czwek.api.AlertV1 -import xyz.apiote.bimba.czwek.api.DepartureV1 -import xyz.apiote.bimba.czwek.api.DepartureV2 -import xyz.apiote.bimba.czwek.api.DepartureV3 -import xyz.apiote.bimba.czwek.api.DepartureV4 -import xyz.apiote.bimba.czwek.api.DepartureV5 -import xyz.apiote.bimba.czwek.api.Time -import xyz.apiote.bimba.czwek.api.UnknownResourceVersionException -import xyz.apiote.bimba.czwek.units.Second -import xyz.apiote.bimba.czwek.units.TGM -import xyz.apiote.bimba.czwek.units.UnitSystem -import java.time.Instant -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit - - -class DepartureItem { - private constructor(d: Departure?, a: List<Alert>) { - departure = d - alert = a - } - - constructor(d: Departure) : this(d, emptyList()) - constructor(a: List<Alert>) : this(null, a) - - val departure: Departure? - val alert: List<Alert> -} - -enum class AlertCause { - UNKNOWN, OTHER, TECHNICAL_PROBLEM, STRIKE, DEMONSTRATION, ACCIDENT, HOLIDAY, WEATHER, MAINTENANCE, - CONSTRUCTION, POLICE_ACTIVITY, MEDICAL_EMERGENCY; - - companion object { - fun of(type: AlertCauseV1): AlertCause { - return when (type) { - AlertCauseV1.UNKNOWN -> valueOf("UNKNOWN") - AlertCauseV1.OTHER -> valueOf("OTHER") - AlertCauseV1.TECHNICAL_PROBLEM -> valueOf("TECHNICAL_PROBLEM") - AlertCauseV1.STRIKE -> valueOf("STRIKE") - AlertCauseV1.DEMONSTRATION -> valueOf("DEMONSTRATION") - AlertCauseV1.ACCIDENT -> valueOf("ACCIDENT") - AlertCauseV1.HOLIDAY -> valueOf("HOLIDAY") - AlertCauseV1.WEATHER -> valueOf("WEATHER") - AlertCauseV1.MAINTENANCE -> valueOf("MAINTENANCE") - AlertCauseV1.CONSTRUCTION -> valueOf("CONSTRUCTION") - AlertCauseV1.POLICE_ACTIVITY -> valueOf("POLICE_ACTIVITY") - AlertCauseV1.MEDICAL_EMERGENCY -> valueOf("MEDICAL_EMERGENCY") - } - } - } -} - -enum class AlertEffect { - UNKNOWN, OTHER, NO_SERVICE, REDUCED_SERVICE, SIGNIFICANT_DELAYS, DETOUR, ADDITIONAL_SERVICE, - MODIFIED_SERVICE, STOP_MOVED, NONE, ACCESSIBILITY_ISSUE; - - companion object { - fun of(type: AlertEffectV1): AlertEffect { - return when (type) { - AlertEffectV1.UNKNOWN -> valueOf("UNKNOWN") - AlertEffectV1.OTHER -> valueOf("OTHER") - AlertEffectV1.NO_SERVICE -> valueOf("NO_SERVICE") - AlertEffectV1.REDUCED_SERVICE -> valueOf("REDUCED_SERVICE") - AlertEffectV1.SIGNIFICANT_DELAYS -> valueOf("SIGNIFICANT_DELAYS") - AlertEffectV1.DETOUR -> valueOf("DETOUR") - AlertEffectV1.ADDITIONAL_SERVICE -> valueOf("ADDITIONAL_SERVICE") - AlertEffectV1.MODIFIED_SERVICE -> valueOf("MODIFIED_SERVICE") - AlertEffectV1.STOP_MOVED -> valueOf("STOP_MOVED") - AlertEffectV1.NONE -> valueOf("NONE") - AlertEffectV1.ACCESSIBILITY_ISSUE -> valueOf("ACCESSIBILITY_ISSUE") - } - } - } -} - -data class Alert( - val header: String, - val description: String, - val url: String, - val cause: AlertCause, - val effect: AlertEffect -) { - constructor(a: AlertV1) : this( - a.header, - a.Description, - a.Url, - AlertCause.of(a.Cause), - AlertEffect.of(a.Effect) - ) -} - -data class StopDepartures( - val departures: List<Departure>, - val stop: Stop, - val alerts: List<Alert> -) - -data class Departure( - val ID: String, - val time: Time, - val status: ULong, - val isRealtime: Boolean, - val vehicle: Vehicle, - val boarding: UByte, - val alerts: List<Alert>, - val exact: Boolean, - val terminusArrival: Boolean -) { - - constructor(d: DepartureV1) : this( - d.ID, - d.time, - d.status, - d.isRealtime, - Vehicle(d.vehicle), - d.boarding, - emptyList(), - true, - false - ) - - constructor(d: DepartureV2) : this( - d.ID, - d.time, - d.status, - d.isRealtime, - Vehicle(d.vehicle), - d.boarding, - emptyList(), - true, - false - ) - - constructor(d: DepartureV3) : this( - d.ID, - d.time, - d.status.ordinal.toULong(), // TODO VehicleStatus - d.isRealtime, - Vehicle(d.vehicle), - d.boarding, - emptyList(), - true, - false - ) - - constructor(d: DepartureV4) : this( - d.ID, - d.time, - d.status.ordinal.toULong(), // TODO VehicleStatus - d.isRealtime, - Vehicle(d.vehicle), - d.boarding, - d.alerts.map { Alert(it) }, - true, - false - ) - - constructor(d: DepartureV5) : this( - d.ID, - d.time, - d.status.ordinal.toULong(), // TODO VehicleStatus - d.isRealtime, - Vehicle(d.vehicle), - d.boarding, - d.alerts.map { Alert(it) }, - d.exact, - d.terminusArrival - ) - - fun statusText(context: Context?, showAsTime: Boolean, at: ZonedDateTime? = null): String { - val now = at ?: Instant.now().atZone(ZoneId.systemDefault()) - val departureTime = ZonedDateTime.of( - now.year, now.monthValue, now.dayOfMonth, - time.Hour.toInt(), time.Minute.toInt(), time.Second.toInt(), 0, ZoneId.of(time.Zone) - ).plus(time.DayOffset.toLong(), ChronoUnit.DAYS) - if (showAsTime) { - return departureTime.format(DateTimeFormatter.ofPattern("HH:mm")) - } - var r = status.toUInt() - if (departureTime.isBefore(now) && r < 3u) { - r = 0u - } - return when (r) { - 0u -> if (context != null && UnitSystem.getSelected(context) is TGM) { - val us = UnitSystem.getSelected(context) - us.toString( - context, - us.timeUnit(Second((departureTime.toEpochSecond() - now.toEpochSecond()).toInt())) - ) - } else { - DateUtils.getRelativeTimeSpanString( - departureTime.toEpochSecond() * 1000, - now.toEpochSecond() * 1000, - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE - ).toString() - } - - 1u -> context?.getString(R.string.departure_momentarily) ?: "momentarily" - 2u -> context?.getString(R.string.departure_now) ?: "now" - 3u -> context?.getString(R.string.departure_departed) ?: "departed" - else -> throw UnknownResourceVersionException("VehicleStatus/$r", 1u) - } - } - - fun timeString(context: Context): String { - return when { - isRealtime -> context.getString( - R.string.at_time_realtime, time.Hour.toInt(), time.Minute.toInt(), time.Second.toInt() - ) - - exact -> context.getString(R.string.at_time, time.Hour.toInt(), time.Minute.toInt()) - else -> context.getString(R.string.about_time, time.Hour.toInt(), time.Minute.toInt()) - } - } - - fun boardingText(context: Context): String { - // todo [3.x] probably should take into account (on|off)-boarding only, on demand - return when { - boarding == (0b0000_0000).toUByte() -> context.getString(R.string.no_boarding) - boarding == (0b1111_1111).toUByte() -> "" // unknown - boarding.and(0b0011_0011u) == (0b0000_0001).toUByte() -> context.getString(R.string.on_boarding) - boarding.and(0b0011_0011u) == (0b0001_0000).toUByte() -> context.getString(R.string.off_boarding) - boarding.and(0b0011_0011u) == (0b0001_0001).toUByte() -> context.getString(R.string.boarding) - else -> context.getString(R.string.on_demand) - } - } -} diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Event.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Event.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e70e8f0e8435eec878d87e43e5edcbde64ba262 --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Event.kt @@ -0,0 +1,302 @@ +// SPDX-FileCopyrightText: Adam Evyčędo +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package xyz.apiote.bimba.czwek.repo + +import android.content.Context +import android.text.format.DateUtils +import xyz.apiote.bimba.czwek.R +import xyz.apiote.bimba.czwek.api.AlertCauseV1 +import xyz.apiote.bimba.czwek.api.AlertEffectV1 +import xyz.apiote.bimba.czwek.api.AlertV1 +import xyz.apiote.bimba.czwek.api.DepartureV1 +import xyz.apiote.bimba.czwek.api.DepartureV2 +import xyz.apiote.bimba.czwek.api.DepartureV3 +import xyz.apiote.bimba.czwek.api.DepartureV4 +import xyz.apiote.bimba.czwek.api.DepartureV5 +import xyz.apiote.bimba.czwek.api.Time +import xyz.apiote.bimba.czwek.api.UnknownResourceVersionException +import xyz.apiote.bimba.czwek.units.Second +import xyz.apiote.bimba.czwek.units.TGM +import xyz.apiote.bimba.czwek.units.UnitSystem +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit + + +class EventItem { + private constructor(d: Event?, a: List<Alert>) { + event = d + alert = a + } + + constructor(d: Event) : this(d, emptyList()) + constructor(a: List<Alert>) : this(null, a) + + val event: Event? + val alert: List<Alert> +} + +enum class AlertCause { + UNKNOWN, OTHER, TECHNICAL_PROBLEM, STRIKE, DEMONSTRATION, ACCIDENT, HOLIDAY, WEATHER, MAINTENANCE, + CONSTRUCTION, POLICE_ACTIVITY, MEDICAL_EMERGENCY; + + companion object { + fun of(type: AlertCauseV1): AlertCause { + return when (type) { + AlertCauseV1.UNKNOWN -> valueOf("UNKNOWN") + AlertCauseV1.OTHER -> valueOf("OTHER") + AlertCauseV1.TECHNICAL_PROBLEM -> valueOf("TECHNICAL_PROBLEM") + AlertCauseV1.STRIKE -> valueOf("STRIKE") + AlertCauseV1.DEMONSTRATION -> valueOf("DEMONSTRATION") + AlertCauseV1.ACCIDENT -> valueOf("ACCIDENT") + AlertCauseV1.HOLIDAY -> valueOf("HOLIDAY") + AlertCauseV1.WEATHER -> valueOf("WEATHER") + AlertCauseV1.MAINTENANCE -> valueOf("MAINTENANCE") + AlertCauseV1.CONSTRUCTION -> valueOf("CONSTRUCTION") + AlertCauseV1.POLICE_ACTIVITY -> valueOf("POLICE_ACTIVITY") + AlertCauseV1.MEDICAL_EMERGENCY -> valueOf("MEDICAL_EMERGENCY") + } + } + } +} + +enum class AlertEffect { + UNKNOWN, OTHER, NO_SERVICE, REDUCED_SERVICE, SIGNIFICANT_DELAYS, DETOUR, ADDITIONAL_SERVICE, + MODIFIED_SERVICE, STOP_MOVED, NONE, ACCESSIBILITY_ISSUE; + + companion object { + fun of(type: AlertEffectV1): AlertEffect { + return when (type) { + AlertEffectV1.UNKNOWN -> valueOf("UNKNOWN") + AlertEffectV1.OTHER -> valueOf("OTHER") + AlertEffectV1.NO_SERVICE -> valueOf("NO_SERVICE") + AlertEffectV1.REDUCED_SERVICE -> valueOf("REDUCED_SERVICE") + AlertEffectV1.SIGNIFICANT_DELAYS -> valueOf("SIGNIFICANT_DELAYS") + AlertEffectV1.DETOUR -> valueOf("DETOUR") + AlertEffectV1.ADDITIONAL_SERVICE -> valueOf("ADDITIONAL_SERVICE") + AlertEffectV1.MODIFIED_SERVICE -> valueOf("MODIFIED_SERVICE") + AlertEffectV1.STOP_MOVED -> valueOf("STOP_MOVED") + AlertEffectV1.NONE -> valueOf("NONE") + AlertEffectV1.ACCESSIBILITY_ISSUE -> valueOf("ACCESSIBILITY_ISSUE") + } + } + } +} + +data class Alert( + val header: String, + val description: String, + val url: String, + val cause: AlertCause, + val effect: AlertEffect +) { + constructor(a: AlertV1) : this( + a.header, + a.Description, + a.Url, + AlertCause.of(a.Cause), + AlertEffect.of(a.Effect) + ) +} + +data class StopEvents( + val events: List<Event>, + val stop: Stop, + val alerts: List<Alert> +) + +data class Event( + val id: String, + val arrivalTime: Time?, + val departureTime: Time?, + val status: ULong, + val isRealtime: Boolean, + val vehicle: Vehicle, + val boarding: UByte, + val alerts: List<Alert>, + val exact: Boolean, + val terminusArrival: Boolean // TODO origin, middle, terminus; if origin -> only departure, if terminus -> only arrival +) { + + constructor(d: DepartureV1) : this( + d.ID, + d.time, + d.time, + d.status, + d.isRealtime, + Vehicle(d.vehicle), + d.boarding, + emptyList(), + true, + false + ) + + constructor(d: DepartureV2) : this( + d.ID, + d.time, + d.time, + d.status, + d.isRealtime, + Vehicle(d.vehicle), + d.boarding, + emptyList(), + true, + false + ) + + constructor(d: DepartureV3) : this( + d.ID, + d.time, + d.time, + d.status.ordinal.toULong(), // TODO VehicleStatus + d.isRealtime, + Vehicle(d.vehicle), + d.boarding, + emptyList(), + true, + false + ) + + constructor(d: DepartureV4) : this( + d.ID, + d.time, + d.time, + d.status.ordinal.toULong(), // TODO VehicleStatus + d.isRealtime, + Vehicle(d.vehicle), + d.boarding, + d.alerts.map { Alert(it) }, + true, + false + ) + + constructor(d: DepartureV5) : this( + d.ID, + d.time, + d.time, + d.status.ordinal.toULong(), // TODO VehicleStatus + d.isRealtime, + Vehicle(d.vehicle), + d.boarding, + d.alerts.map { Alert(it) }, + d.exact, + d.terminusArrival + ) + + fun timeZone() = (arrivalTime ?: departureTime)!!.Zone + + fun filterTime() = (arrivalTime ?: departureTime)!! + + fun statusText( + context: Context?, + showAsTime: Boolean, + at: ZonedDateTime? = null + ): Pair<String?, String?> { + val now = at ?: Instant.now().atZone(ZoneId.systemDefault()) + return Pair( + statusText(context, showAsTime, now, arrivalTime, R.string.departure_arrived), + statusText(context, showAsTime, now, departureTime, R.string.departure_departed) + ) + } + + private fun statusText( + context: Context?, + showAsTime: Boolean, + now: ZonedDateTime, + time: Time?, + pastString: Int + ): String? { + val r = status.toUInt() + return time?.let { + ZonedDateTime.of( + now.year, + now.monthValue, + now.dayOfMonth, + it.Hour.toInt(), + it.Minute.toInt(), + it.Second.toInt(), + 0, + ZoneId.of( + it.Zone + ) + ) + .plus(it.DayOffset.toLong(), ChronoUnit.DAYS) + .let { + if (showAsTime) { + it.format(DateTimeFormatter.ofPattern("HH:mm")) + } else { + when { + // TODO why this condition + r == 0u || (it.isBefore(now) && r < 3u) -> if (context != null && UnitSystem.getSelected( + context + ) is TGM + ) { + val us = UnitSystem.getSelected(context) + us.toString( + context, + us.timeUnit(Second((it.toEpochSecond() - now.toEpochSecond()).toInt())) + ) + } else { + DateUtils.getRelativeTimeSpanString( + it.toEpochSecond() * 1000, + now.toEpochSecond() * 1000, + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE + ).toString() + } + + r == 1u -> context?.getString(R.string.departure_momentarily) ?: "momentarily" + r == 2u -> context?.getString(R.string.departure_now) ?: "now" + r == 3u -> context?.getString(pastString) ?: "passed" + else -> throw UnknownResourceVersionException("VehicleStatus/$r", 1u) + } + } + } + } + } + + fun departureTimeString(context: Context): String? = timeString(context, departureTime) + + fun arrivalTimeString(context: Context): String? = timeString(context, arrivalTime) + + private fun timeString(context: Context, time: Time?): String? { + return when { + time == null -> null + isRealtime -> context.getString( + R.string.at_time_realtime, + time.Hour.toInt(), + time.Minute.toInt(), + time.Second.toInt() + ) + + exact -> context.getString( + R.string.at_time, + time.Hour.toInt(), + time.Minute.toInt() + ) + + else -> context.getString( + R.string.about_time, + time.Hour.toInt(), + time.Minute.toInt() + ) + } + + } + + fun boardingText(context: Context): String { + // todo [3.x] probably should take into account (on|off)-boarding only, on demand + return when { + boarding == (0b0000_0000).toUByte() -> context.getString(R.string.no_boarding) + boarding == (0b1111_1111).toUByte() -> "" // unknown + boarding.and(0b0011_0011u) == (0b0000_0001).toUByte() -> context.getString(R.string.on_boarding) + boarding.and(0b0011_0011u) == (0b0001_0000).toUByte() -> context.getString(R.string.off_boarding) + boarding.and(0b0011_0011u) == (0b0001_0001).toUByte() -> context.getString(R.string.boarding) + else -> context.getString(R.string.on_demand) + } + } +} diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt index a337b0178c144f066ffea44ebd3898c475d850c0..e612503b10f270a49961d1482475da55fb3f47db 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt @@ -36,7 +36,7 @@ stop: String, date: LocalDate?, context: Context, limit: Int? - ): StopDepartures? + ): StopEvents? suspend fun getLocatablesIn( context: Context, diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt index b11a5dfcccc9048e0390e0dfe9ad530e875a7668..309d6ec0f00248d543db51e92389dfddb1457a3d 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt @@ -200,7 +200,7 @@ stop: String, date: LocalDate?, context: Context, limit: Int? - ): StopDepartures? { + ): StopEvents? { TODO("Not yet implemented") } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt index 89f7f8d5ccc84cde130f7c6a92ded75a16e30170..0b47623ff0b1f191b90a9e19f308f9a50878117f 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt @@ -114,7 +114,7 @@ stop: String, date: LocalDate?, context: Context, limit: Int? - ): StopDepartures? { + ): StopEvents? { return if (feedID == "transitous") { getTransitousDepartures(context, stop, date, limit) } else { @@ -137,28 +137,28 @@ } } else { when (val response = withContext(Dispatchers.IO) { DeparturesResponse.unmarshal(result.stream!!) }) { - is DeparturesResponseDev -> StopDepartures( - response.departures.map { Departure(it) }, + is DeparturesResponseDev -> StopEvents( + response.departures.map { Event(it) }, Stop(response.stop), response.alerts.map { Alert(it) }) - is DeparturesResponseV4 -> StopDepartures( - response.departures.map { Departure(it) }, + is DeparturesResponseV4 -> StopEvents( + response.departures.map { Event(it) }, Stop(response.stop), response.alerts.map { Alert(it) }) - is DeparturesResponseV3 -> StopDepartures( - response.departures.map { Departure(it) }, + is DeparturesResponseV3 -> StopEvents( + response.departures.map { Event(it) }, Stop(response.stop), response.alerts.map { Alert(it) }) - is DeparturesResponseV2 -> StopDepartures( - response.departures.map { Departure(it) }, + is DeparturesResponseV2 -> StopEvents( + response.departures.map { Event(it) }, Stop(response.stop), response.alerts.map { Alert(it) }) - is DeparturesResponseV1 -> StopDepartures( - response.departures.map { Departure(it) }, + is DeparturesResponseV1 -> StopEvents( + response.departures.map { Event(it) }, Stop(response.stop), response.alerts.map { Alert(it) }) diff --git a/app/src/main/res/drawable/radar.xml b/app/src/main/res/drawable/radar.xml index f68d13754fc956d4f9ac08166860576029d28b09..376de31d4cfec3e89591559016392ec6588e29ca 100644 --- a/app/src/main/res/drawable/radar.xml +++ b/app/src/main/res/drawable/radar.xml @@ -4,8 +4,15 @@ SPDX-License-Identifier: Apache-2.0 --> -<vector android:height="24dp" android:tint="?attr/colorOnSurface" - android:viewportHeight="24" android:viewportWidth="24" - android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M19.74,18.33C21.15,16.6 22,14.4 22,12c0,-5.52 -4.48,-10 -10,-10S2,6.48 2,12s4.48,10 10,10c2.4,0 4.6,-0.85 6.33,-2.26c0.27,-0.22 0.53,-0.46 0.78,-0.71c0.03,-0.03 0.05,-0.06 0.07,-0.08C19.38,18.75 19.57,18.54 19.74,18.33zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8s8,3.59 8,8c0,1.85 -0.63,3.54 -1.69,4.9l-1.43,-1.43c0.69,-0.98 1.1,-2.17 1.1,-3.46c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6s2.69,6 6,6c1.3,0 2.51,-0.42 3.49,-1.13l1.42,1.42C15.54,19.37 13.85,20 12,20zM13.92,12.51c0.17,-0.66 0.02,-1.38 -0.49,-1.9l-0.02,-0.02c-0.77,-0.77 -2,-0.78 -2.78,-0.04c-0.01,0.01 -0.03,0.02 -0.05,0.04c-0.78,0.78 -0.78,2.05 0,2.83l0.02,0.02c0.52,0.51 1.25,0.67 1.91,0.49l1.51,1.51c-0.6,0.36 -1.29,0.58 -2.04,0.58c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4s4,1.79 4,4c0,0.73 -0.21,1.41 -0.56,2L13.92,12.51z" /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorOnSurface" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="@android:color/white" + android:pathData="M15.44,0.59l-3.18,3.18c-0.78,0.78 -0.78,2.05 0,2.83l1.24,1.24l-0.71,0.71L11.55,7.3c-0.78,-0.78 -2.05,-0.78 -2.83,0L7.3,8.72c-0.78,0.78 -0.78,2.05 0,2.83l1.24,1.24l-0.71,0.71L6.6,12.25c-0.78,-0.78 -2.05,-0.78 -2.83,0l-3.18,3.18c-0.78,0.78 -0.78,2.05 0,2.83l3.54,3.54c0.78,0.78 2.05,0.78 2.83,0l3.18,-3.18c0.78,-0.78 0.78,-2.05 0,-2.83l-1.24,-1.24l0.71,-0.71l1.24,1.24c0.78,0.78 2.05,0.78 2.83,0l1.41,-1.41c0.78,-0.78 0.78,-2.05 0,-2.83L13.84,9.6l0.71,-0.71l1.24,1.24c0.78,0.78 2.05,0.78 2.83,0l3.18,-3.18c0.78,-0.78 0.78,-2.05 0,-2.83l-3.54,-3.54C17.48,-0.2 16.22,-0.2 15.44,0.59zM6.6,19.32l-1.06,1.06L2,16.85l1.06,-1.06L6.6,19.32zM8.72,17.2l-1.06,1.06l-3.54,-3.54l1.06,-1.06L8.72,17.2zM18.26,7.66L17.2,8.72l-3.54,-3.54l1.06,-1.06L18.26,7.66zM20.38,5.54L19.32,6.6l-3.54,-3.54L16.85,2L20.38,5.54zM14,21l0,2c4.97,0 9,-4.03 9,-9l-2,0C21,17.87 17.87,21 14,21zM14,17l0,2c2.76,0 5,-2.24 5,-5l-2,0C17,15.66 15.66,17 14,17z" /> + </vector> diff --git a/app/src/main/res/layout/departure.xml b/app/src/main/res/layout/departure.xml index b2514262349aef0d448fed775a6116b4042ece6d..c3d8f14918808281ed2d6ce28a9ac3855f866213 100644 --- a/app/src/main/res/layout/departure.xml +++ b/app/src/main/res/layout/departure.xml @@ -7,63 +7,100 @@ SPDX-License-Identifier: GPL-3.0-or-later --> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tool="http://schemas.android.com/tools" - android:id="@+id/departure" - android:layout_width="match_parent" - android:layout_height="wrap_content"> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tool="http://schemas.android.com/tools" + android:id="@+id/departure" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/line_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="8dp" + app:layout_constraintBottom_toBottomOf="@+id/departure_line" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/departure_line" + tool:ignore="ContentDescription" + tool:srcCompat="@drawable/bus_black" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.LabelSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + tool:text="approx. arr." /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/arrival_status" + tool:text="1hr" /> - <ImageView - android:id="@+id/line_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="8dp" - app:layout_constraintBottom_toTopOf="@+id/departure_headsign" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/departure_time" - tool:srcCompat="@drawable/bus_black" - tool:ignore="ContentDescription" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.LabelSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/arrival_time" + tool:text="approx. dep." /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/departure_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - tool:text="1hr" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginBottom="8dp" + android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/departure_status" + tool:text="1hr" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/departure_line" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" - app:layout_constraintStart_toEndOf="@+id/line_icon" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintEnd_toStartOf="@id/departure_time" - tool:text="Circle" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_line" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + app:layout_constraintEnd_toStartOf="@id/arrival_time" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toEndOf="@+id/line_icon" + app:layout_constraintTop_toTopOf="parent" + tool:text="Circle" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/departure_headsign" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.Material3.BodySmall" - app:layout_constraintStart_toStartOf="@+id/departure_line" - app:layout_constraintTop_toBottomOf="@+id/departure_line" - tool:text="» Tower Hill" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_headsign" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Material3.BodySmall" + app:layout_constraintStart_toStartOf="@+id/departure_line" + app:layout_constraintTop_toBottomOf="@+id/departure_line" + tool:text="» Tower Hill" /> - <ImageView - android:id="@+id/time_status" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_marginEnd="8dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/departure_time" - tool:ignore="ContentDescription" - tool:srcCompat="@drawable/inexact" /> + <ImageView + android:id="@+id/event_status" + android:layout_width="15dp" + android:layout_height="11dp" + android:layout_marginStart="8dp" + android:layout_marginBottom="8dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/departure_headsign" + app:layout_constraintVertical_bias="0.0" + tool:ignore="ContentDescription" + tool:srcCompat="@drawable/radar" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/departure_bottom_sheet.xml b/app/src/main/res/layout/departure_bottom_sheet.xml index 3b6e92fa314f2d9967ae05c9200666b9989f8bbb..f2bb7dc23101df9ee440b0a8997d2ebbfbe753ce 100644 --- a/app/src/main/res/layout/departure_bottom_sheet.xml +++ b/app/src/main/res/layout/departure_bottom_sheet.xml @@ -7,282 +7,316 @@ SPDX-License-Identifier: GPL-3.0-or-later --> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tool="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="16dp"> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tool="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="16dp"> - <com.google.android.material.bottomsheet.BottomSheetDragHandleView - android:id="@+id/drag_handle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - app:layout_constraintTop_toTopOf="parent" /> + <com.google.android.material.bottomsheet.BottomSheetDragHandleView + android:id="@+id/drag_handle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="0dp" - android:textAppearance="@style/TextAppearance.Material3.DisplaySmall" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/drag_handle" - tool:text="at 12:10:30" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + android:textAppearance="@style/TextAppearance.Material3.BodySmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/drag_handle" + tool:text="approx. arr." /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/local_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:layout_constraintTop_toBottomOf="@+id/time" - app:layout_constraintEnd_toEndOf="@+id/time" - android:text="@string/local_time" - android:visibility="gone" - app:layout_constraintStart_toStartOf="@+id/time" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + android:textAppearance="@style/TextAppearance.Material3.DisplaySmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/arrival_status" + tool:text="at 12:10:30" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/offset" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:textAppearance="@style/TextAppearance.Material3.BodyLarge" - android:visibility="gone" - app:layout_constraintBaseline_toBaselineOf="@+id/time" - app:layout_constraintStart_toEndOf="@+id/time" - tool:text="(+2 min)" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.Material3.BodySmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/arrival_time" + tool:text="approx. dep." /> - <ImageView - android:id="@+id/rt_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="16dp" - android:contentDescription="@string/realtime_content_description" - app:layout_constraintBottom_toBottomOf="@+id/time" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/time" - app:srcCompat="@drawable/radar" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.Material3.DisplaySmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/departure_status" + tool:text="at 12:10:30" /> - <ImageView - android:id="@+id/wheelchair_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="8dp" - android:contentDescription="@string/wheelchair_content_description" - app:layout_constraintStart_toEndOf="@id/rt_icon" - app:layout_constraintTop_toTopOf="@+id/rt_icon" - app:srcCompat="@drawable/wheelchair" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/local_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/local_time" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="@+id/departure_time" + app:layout_constraintStart_toStartOf="@+id/departure_time" + app:layout_constraintTop_toBottomOf="@+id/departure_time" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/line" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" - android:textAlignment="center" - android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/local_time" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/offset" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + android:visibility="gone" + app:layout_constraintBaseline_toBaselineOf="@+id/arrival_time" + app:layout_constraintStart_toEndOf="@+id/arrival_time" + tool:text="(+2 min)" /> - <androidx.constraintlayout.helper.widget.Flow - android:id="@+id/info" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="48dp" - android:layout_marginTop="48dp" - android:layout_marginEnd="48dp" - app:constraint_referenced_ids="boarding,speed,congestion,occupancy" - app:flow_horizontalGap="4dp" - app:flow_horizontalStyle="spread_inside" - app:flow_verticalGap="4dp" - app:flow_wrapMode="chain" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/line" /> + <ImageView + android:id="@+id/rt_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="16dp" + android:contentDescription="@string/realtime_content_description" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/drag_handle" + app:srcCompat="@drawable/radar" /> - <LinearLayout - android:id="@+id/boarding" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical"> + <ImageView + android:id="@+id/wheelchair_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="8dp" + android:contentDescription="@string/wheelchair_content_description" + app:layout_constraintStart_toEndOf="@id/rt_icon" + app:layout_constraintTop_toTopOf="@+id/rt_icon" + app:srcCompat="@drawable/wheelchair" /> - <ImageView - android:id="@+id/boarding_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_marginEnd="8dp" - android:importantForAccessibility="no" - app:srcCompat="@drawable/transfer" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/line" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAlignment="center" + android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/local_time" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/boarding_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.BodyLarge" - tool:text="on demand" /> - </LinearLayout> + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/info" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="48dp" + android:layout_marginTop="48dp" + android:layout_marginEnd="48dp" + app:constraint_referenced_ids="boarding,speed,congestion,occupancy" + app:flow_horizontalGap="4dp" + app:flow_horizontalStyle="spread_inside" + app:flow_verticalGap="4dp" + app:flow_wrapMode="chain" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/line" /> - <LinearLayout - android:id="@+id/speed" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical"> + <LinearLayout + android:id="@+id/boarding" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical"> - <ImageView - android:id="@+id/speed_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_marginEnd="8dp" - android:importantForAccessibility="no" - app:srcCompat="@drawable/speed" /> + <ImageView + android:id="@+id/boarding_icon" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="8dp" + android:importantForAccessibility="no" + app:srcCompat="@drawable/transfer" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/speed_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.BodyLarge" - tool:text="10 Vl" /> - </LinearLayout> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/boarding_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + tool:text="on demand" /> + </LinearLayout> - <LinearLayout - android:id="@+id/congestion" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical"> + <LinearLayout + android:id="@+id/speed" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical"> + + <ImageView + android:id="@+id/speed_icon" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="8dp" + android:importantForAccessibility="no" + app:srcCompat="@drawable/speed" /> - <ImageView - android:id="@+id/congestion_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_marginEnd="8dp" - android:importantForAccessibility="no" - app:srcCompat="@drawable/traffic" /> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/speed_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + tool:text="10 Vl" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/congestion" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical"> + + <ImageView + android:id="@+id/congestion_icon" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="8dp" + android:importantForAccessibility="no" + app:srcCompat="@drawable/traffic" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/congestion_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.BodyLarge" - tool:text="smooth traffic" /> - </LinearLayout> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/congestion_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + tool:text="smooth traffic" /> + </LinearLayout> - <LinearLayout - android:id="@+id/occupancy" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical"> + <LinearLayout + android:id="@+id/occupancy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical"> - <ImageView - android:id="@+id/occupancy_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_marginEnd="8dp" - android:importantForAccessibility="no" - app:srcCompat="@drawable/crowd" /> + <ImageView + android:id="@+id/occupancy_icon" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="8dp" + android:importantForAccessibility="no" + app:srcCompat="@drawable/crowd" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/occupancy_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textAppearance="@style/TextAppearance.Material3.BodyLarge" - tool:text="empty vehicle" /> - </LinearLayout> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/occupancy_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + tool:text="empty vehicle" /> + </LinearLayout> - <androidx.constraintlayout.helper.widget.Flow - android:id="@+id/capabilities" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginTop="16dp" - android:layout_marginEnd="8dp" - app:constraint_referenced_ids="ac,bike,voice,ticket,usb" - app:flow_horizontalGap="4dp" - app:flow_horizontalStyle="packed" - app:flow_verticalGap="4dp" - app:flow_wrapMode="chain" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/info" /> + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/capabilities" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="8dp" + app:constraint_referenced_ids="ac,bike,voice,ticket,usb" + app:flow_horizontalGap="4dp" + app:flow_horizontalStyle="packed" + app:flow_verticalGap="4dp" + app:flow_wrapMode="chain" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/info" /> - <ImageView - android:id="@+id/ac" - android:layout_width="24dp" - android:layout_height="24dp" - android:contentDescription="@string/air_condition_content_description" - app:srcCompat="@drawable/ac" - tool:ignore="MissingConstraints" /> + <ImageView + android:id="@+id/ac" + android:layout_width="24dp" + android:layout_height="24dp" + android:contentDescription="@string/air_condition_content_description" + app:srcCompat="@drawable/ac" + tool:ignore="MissingConstraints" /> - <ImageView - android:id="@+id/bike" - android:layout_width="24dp" - android:layout_height="24dp" - android:contentDescription="@string/bicycles_allowed_content_description" - app:srcCompat="@drawable/bike" - tool:ignore="MissingConstraints" /> + <ImageView + android:id="@+id/bike" + android:layout_width="24dp" + android:layout_height="24dp" + android:contentDescription="@string/bicycles_allowed_content_description" + app:srcCompat="@drawable/bike" + tool:ignore="MissingConstraints" /> - <ImageView - android:id="@+id/voice" - android:layout_width="24dp" - android:layout_height="24dp" - android:contentDescription="@string/voice_announcements_content_description" - app:srcCompat="@drawable/voice" - tool:ignore="MissingConstraints" /> + <ImageView + android:id="@+id/voice" + android:layout_width="24dp" + android:layout_height="24dp" + android:contentDescription="@string/voice_announcements_content_description" + app:srcCompat="@drawable/voice" + tool:ignore="MissingConstraints" /> - <ImageView - android:id="@+id/ticket" - android:layout_width="24dp" - android:layout_height="24dp" - android:contentDescription="@string/tickets_sold_content_description" - app:srcCompat="@drawable/ticket" - tool:ignore="MissingConstraints" /> + <ImageView + android:id="@+id/ticket" + android:layout_width="24dp" + android:layout_height="24dp" + android:contentDescription="@string/tickets_sold_content_description" + app:srcCompat="@drawable/ticket" + tool:ignore="MissingConstraints" /> - <ImageView - android:id="@+id/usb" - android:layout_width="24dp" - android:layout_height="24dp" - android:contentDescription="@string/usb_charging_content_description" - app:srcCompat="@drawable/usb" - tool:ignore="MissingConstraints" /> + <ImageView + android:id="@+id/usb" + android:layout_width="24dp" + android:layout_height="24dp" + android:contentDescription="@string/usb_charging_content_description" + app:srcCompat="@drawable/usb" + tool:ignore="MissingConstraints" /> - <LinearLayout - android:layout_marginTop="8dp" - android:id="@+id/alerts" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:visibility="gone" - android:background="@color/safety" - app:layout_constraintTop_toBottomOf="@+id/capabilities"> + <LinearLayout + android:id="@+id/alerts" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:background="@color/safety" + android:gravity="center_vertical" + android:visibility="gone" + app:layout_constraintTop_toBottomOf="@+id/capabilities"> - <ImageView - android:layout_marginStart="8dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:tint="@color/black" - android:importantForAccessibility="no" - android:src="@drawable/warning" /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:importantForAccessibility="no" + android:src="@drawable/warning" + app:tint="@color/black" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/alerts_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:padding="8dp" - android:textColor="@color/black" - tool:text="Severe stops on Metropolitan line towards Tower Hill through Victoria" /> - </LinearLayout> + <com.google.android.material.textview.MaterialTextView + android:id="@+id/alerts_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:padding="8dp" + android:textColor="@color/black" + tool:text="Severe stops on Metropolitan line towards Tower Hill through Victoria" /> + </LinearLayout> - <org.osmdroid.views.MapView - android:id="@+id/map" - android:layout_width="match_parent" - android:layout_height="250dp" - android:layout_margin="16dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/alerts" /> + <org.osmdroid.views.MapView + android:id="@+id/map" + android:layout_width="match_parent" + android:layout_height="250dp" + android:layout_margin="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/alerts" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/favourite.xml b/app/src/main/res/layout/favourite.xml index 7d47ac03dc672895ee185d4c471ffb19cf4847b7..c4175c5cfe973887027f17ba67804556823382f4 100644 --- a/app/src/main/res/layout/favourite.xml +++ b/app/src/main/res/layout/favourite.xml @@ -7,10 +7,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tool="http://schemas.android.com/tools" android:id="@+id/favourite" - android:layout_marginTop="8dp" - android:layout_marginBottom="8dp" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" @@ -45,18 +45,60 @@ android:layout_height="24dp" android:layout_marginStart="8dp" app:layout_constraintBottom_toTopOf="@+id/departure_headsign" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/departure_time" + app:layout_constraintTop_toTopOf="@+id/departure_line" tool:ignore="ContentDescription" tool:srcCompat="@drawable/bus_black" /> <com.google.android.material.textview.MaterialTextView - android:id="@+id/departure_time" + android:id="@+id/arrival_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.LabelSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + tool:text="approx. arr." /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/arrival_status" + tool:text="1hr" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/arrival_full_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textAppearance="@style/TextAppearance.Material3.LabelSmall" + app:layout_constraintEnd_toEndOf="@+id/arrival_time" + app:layout_constraintTop_toBottomOf="@+id/arrival_time" + tool:text="18:55" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.LabelSmall" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/departure_line" + tool:text="approx. dep." /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/departure_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/departure_status" tool:text="1hr" /> <com.google.android.material.textview.MaterialTextView diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8c8b70d098ddbdba4dd709f21c267dd8a8458e0..d7eabb6448290335cfad6b1b52b8943562ede311 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -149,9 +149,7 @@ Server <string name="bimba_server_token_hint">Token</string> <string name="realtime_content_description">departure is realtime</string> <!-- cf timepoint field in https://gtfs.org/schedule/reference/#stop_timestxt --> - <string name="exact_content_description">departure time is exact from schedule</string> - <!-- cf timepoint field in https://gtfs.org/schedule/reference/#stop_timestxt --> - <string name="inexact_content_description">departure time is approximate from schedule</string> + <string name="schedule_content_description">departure time is from schedule</string> <string name="wheelchair_content_description">vehicle is wheelchair accessible</string> <string name="air_condition_content_description">air conditioning</string> <string name="bicycles_allowed_content_description">bicycles allowed</string> @@ -287,5 +285,11 @@link to Matrix channel <string name="email_button_description">link to email</string> <string name="transitous_description">A community-run provider-neutral international public transport routing service. Coverage is available at https://transitous.org/sources/</string> <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="local_time">local time</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> + <string name="departure">departure</string> + <string name="approximately">approximately</string> </resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 042048b10e8ca72c5956032013ac943358cfae06..1f990cc3b25e064ea38ef5e14bc97f87aa2297e5 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -58,8 +58,8 @@- 9sp
</style> <style name="Theme.Bimba.Splash" parent="Theme.SplashScreen"> - <item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher_foreground</item> - <item name="windowSplashScreenIconBackgroundColor">@color/ic_launcher_background</item> + <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item> + <item name="windowSplashScreenBackground">?attr/colorSurface</item> <item name="postSplashScreenTheme">@style/Theme.Bimba</item> </style> diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index ed15c05948b2e42e983638a24f3c885597f30c96..bdc076567a58b017c5a152746e2ba04f6e14a2b6 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -147,9 +147,7 @@Server <string name="bimba_server_token_hint">Token</string> <string name="realtime_content_description">departure is realtime</string> <!-- cf timepoint field in https://gtfs.org/schedule/reference/#stop_timestxt --> - <string name="exact_content_description">departure time is exact from schedule</string> - <!-- cf timepoint field in https://gtfs.org/schedule/reference/#stop_timestxt --> - <string name="inexact_content_description">departure time is approximate from schedule</string> + <string name="schedule_content_description">departure time is from schedule</string> <string name="wheelchair_content_description">vehicle is wheelchair accessible</string> <string name="air_condition_content_description">air conditioning</string> <string name="bicycles_allowed_content_description">bicycles allowed</string> @@ -283,4 +281,13 @@show <string name="terminus_arrival_showing">Terminus arrivals</string> <string name="matrix_button_description">link to Matrix channel</string> <string name="email_button_description">link to email</string> + <string name="transitous_description">A community-run provider-neutral international public transport routing service. Coverage is available at https://transitous.org/sources/</string> + <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="departure_arrived">arrived</string> + <string name="arrival_approximate">approx. arr.</string> + <string name="arrival">arrival</string> + <string name="departure_approximate">approx. dep.</string> + <string name="departure">departure</string> + <string name="approximately">approximately</string> </resources> diff --git a/app/src/main/res/values-en-rUS/strings.xml b/app/src/main/res/values-en-rUS/strings.xml index 1f2aa2c99cc598244676fb0917da77ac392dfb6f..075852ab7b5dd2b4fc0fca4e326a543a1ea2f949 100644 --- a/app/src/main/res/values-en-rUS/strings.xml +++ b/app/src/main/res/values-en-rUS/strings.xml @@ -103,8 +103,7 @@Results for ‘%1$s’ <string name="bimba_server_address_hint">Server</string> <string name="bimba_server_token_hint">Token</string> <string name="realtime_content_description">departure is realtime</string> - <string name="exact_content_description">departure time is exact from schedule</string> - <string name="inexact_content_description">departure time is approximate</string> + <string name="schedule_content_description">departure time is from schedule</string> <string name="wheelchair_content_description">vehicle is wheelchair accessible</string> <string name="air_condition_content_description">air conditioning</string> <string name="bicycles_allowed_content_description">bicycles allowed</string> @@ -280,4 +279,13 @@Terminus arrivals <string name="matrix_button_description">link to Matrix channel</string> <string name="email_button_description">link to email</string> <string name="no_email_app">No email app installed</string> + <string name="transitous_description">A community-run provider-neutral international public transport routing service. Coverage is available at https://transitous.org/sources/</string> + <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="departure_arrived">arrived</string> + <string name="arrival_approximate">approx. arr.</string> + <string name="arrival">arrival</string> + <string name="departure_approximate">approx. dep.</string> + <string name="departure">departure</string> + <string name="approximately">approximately</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index ea505321830dc85be8e4569db6f02aed29708d58..0480a5fed0a434e65ffaebf0d90a926db77025fe 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -31,7 +31,7 @@- %1$s dunatim
<item quantity="other">%1$s dunatimmi</item> </plurals> <string name="speed_in_m_per_s">%1$s m/s</string> - <string name="time_in_tm">%1$s %2$s tm</string> + <string name="time_in_tm">%1$s %2$sTm</string> <string name="speed_in_km_per_h">%1$s km/h</string> <plurals name="speed_in_vl_cd"> <item quantity="one">%1$d vlos</item> @@ -70,10 +70,8 @@%1$s «» %2$s <string name="results_for">Otsingutulemused: „%1$s“</string> <string name="bimba_server_address_hint">Server</string> <string name="server_rate_limited_question">See server kasutab päringumahu piiranguid ning ligipääsutunnus on sisestamata. Kas sa soovid jätkata?</string> - <string name="inexact_content_description">umbkaudne väljumise aeg sõiduplaani järgi</string> - <string name="server_private_question">See on privaatne server ning ligipääsutunnus on sisestamata</string> + <string name="server_private_question">See on privaatne server ning ligipääsutunnus on sisestamata</string> <string name="realtime_content_description">väljumine on reaalajas</string> - <string name="exact_content_description">väljumise aeg on sõiduplaani järgi</string> <string name="voice_announcements_content_description">kasutusel on häälteated</string> <string name="tickets_sold_content_description">transpordivahendis müüakse sõidupileteid</string> <string name="usb_charging_content_description">USB-põhised laadimispordid on olemas</string> @@ -109,7 +107,7 @@<item quantity="one">%1$d miil</item> <item quantity="other">%1$d miili</item> </plurals> - <string name="distance_in_gf">%1$s %2$s gf</string> + <string name="distance_in_gf">%1$s %2$sGf</string> <plurals name="distance_in_gf_cd"> <item quantity="one">%1$d grafut</item> <item quantity="other">%1$d grafutit</item> @@ -123,7 +121,7 @@ - %1$s grafut
<item quantity="other">%1$s grafutit</item> </plurals> <string name="time_in_s">%1$s sek</string> - <string name="time_in_tm_past">%1$s %2$s tm tagasi</string> + <string name="time_in_tm_past">%1$s %2$sTm tagasi</string> <plurals name="time_in_tm_cd"> <item quantity="one">%1$d tim</item> <item quantity="other">%1$d timmi</item> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4d562ae17b6049e59da601a2d07bf785f34e9ba3..604046478c874a3e78a2d33e137b169e1a3cbd7c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -203,8 +203,6 @@%1$s et %2$s <string name="time_in_s">%1$s s</string> <string name="map_attribution">© contributeurs d’<a href=https://www.openstreetmap.org/copyright>OpenStreetMap</a></string> <string name="no_email_app">Aucune application d’e-mail installée</string> - <string name="exact_content_description">L\'heure de départ est exacte d\'après l\'horaire planifié</string> - <string name="inexact_content_description">l\'heure de départ est approximative d\'après l\'horaire planifié</string> <string name="favourite_content_description">Enregistrer comme favori</string> <string name="unfiltered">Non filtré</string> <string name="cannot_save_favourite">Impossible d’enregistrer le favori</string> @@ -244,4 +242,4 @@La requête contient un code short plus mais il n\'y a pas de données de géocodage présentes. Téléchargez les données de géocodage ou activez leur mise à jour automatique dans les paramètres. <string name="congestion_congestion">ralentissements</string> <string name="about_time">environ %1$02d:%2$02d</string> <string name="italics">italique</string> -</resources> \ No newline at end of file +</resources> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d29d58e37fb7dea6fd60716bf2a9e18b7c6d2aee..9e162712580cf6ac01c38c1dec5c8a8337d52f9f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -48,7 +48,7 @@za moment <string name="departure_departed">odjechał</string> <string name="departure_now">teraz</string> <string name="at_time">o %1$02d:%2$02d</string> - <string name="about_time">about %1$02d:%2$02d</string> + <string name="about_time">około %1$02d:%2$02d</string> <string name="at_time_realtime">o %1$02d:%2$02d:%3$02d</string> <string name="on_demand">na żądanie</string> <string name="no_boarding">brak</string> @@ -64,8 +64,7 @@Wyniki dla „%1$s” <string name="bimba_server_address_hint">Serwer</string> <string name="bimba_server_token_hint">Żeton</string> <string name="realtime_content_description">odjazd w czasie rzeczywistym</string> - <string name="exact_content_description">czas odjazdu jest dokładny z rozkładu</string> - <string name="inexact_content_description">czas odjazdu jest przybliżony</string> + <string name="schedule_content_description">czas odjazdu jest z rozkładu</string> <string name="wheelchair_content_description">pojazd ma niską podłogę</string> <string name="air_condition_content_description">klimatyzacja</string> <string name="bicycles_allowed_content_description">przewóz rowerów dozwolony</string> @@ -301,4 +300,12 @@Przyjazdy na pętle <string name="matrix_button_description">link do kanału na Matrixie</string> <string name="email_button_description">link do e-maila</string> <string name="no_email_app">Brak aplikacji e-mail</string> + <string name="transitous_attribution">API Transitous (https://transitous.org) dostarczane przez Spline (https://routing.spline.de). Lokalizacje (https://github.com/public-transport/transitous/tree/main/feeds) utrzymywane przez społeczność.</string> + <string name="local_time">czas lokalny</string> + <string name="departure_arrived">przyjechał</string> + <string name="arrival_approximate">przyb. przyj.</string> + <string name="arrival">przyjazd</string> + <string name="departure_approximate">przyb. odj.</string> + <string name="departure">odjazd</string> + <string name="approximately">w przybliżeniu</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values-v31/themes.xml b/app/src/main/res/values-v31/themes.xml index 1b04dfb059a09665e1aa20100b1a0e4b5f19d4cb..f07ff67b177ed5e11ed481990ae1e34133dc4e3c 100644 --- a/app/src/main/res/values-v31/themes.xml +++ b/app/src/main/res/values-v31/themes.xml @@ -58,12 +58,12 @@- 9sp
</style> <style name="Theme.Bimba.Splash" parent="Theme.SplashScreen"> - <item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher_foreground</item> - <item name="windowSplashScreenIconBackgroundColor">@color/ic_launcher_background</item> + <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item> + <item name="windowSplashScreenBackground">?attr/colorSurface</item> <item name="postSplashScreenTheme">@style/Theme.Bimba</item> </style> <style name="Preference.SwitchPreferenceCompat" parent="@style/Preference.SwitchPreferenceCompat.Material" tool:ignore="ResourceCycle"> <item name="widgetLayout">@layout/preferences_switch_material</item> </style> -</resources> \ No newline at end of file +</resources> diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index 52ded369d35deb8e357f2bba5c042a0adc2de048..c590b8026cb06e551b4fc892f4f8c1065b4166e6 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -8,9 +8,11 @@ --> <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale android:name="en"/> - <locale android:name="pl"/> - <locale android:name="it"/> <locale android:name="de"/> - <locale android:name="fr"/> + <locale android:name="en-rGB"/> <locale android:name="en-rUS"/> + <locale android:name="et"/> + <locale android:name="fr"/> + <locale android:name="it"/> + <locale android:name="pl"/> </locale-config> \ No newline at end of file diff --git a/bimba.svg b/bimba.svg index 8bfa5182d6af0ad1a4cfa32f9383bcb8c1844c9a..42fb98d50eb985b418012cbfa8a92189e645c834 100644 --- a/bimba.svg +++ b/bimba.svg @@ -4,14 +4,15 @@ SPDX-FileCopyrightText: https://github.com/tebriz159 SPDX-License-Identifier: GPL-3.0-or-later --> + <svg - viewBox="0 0 91.999998 92" + viewBox="0 0 47.999999 48" id="vector" version="1.1" sodipodi:docname="bimba.svg" - width="92" - height="92" - inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" + width="48" + height="48" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" inkscape:export-filename="bimba.png" inkscape:export-xdpi="534.26086" inkscape:export-ydpi="534.26086" @@ -48,80 +49,83 @@ inkscape:window-width="1504" inkscape:window-height="1002" id="namedview172" showgrid="false" - fit-margin-top="8" - fit-margin-left="8" - fit-margin-bottom="8" - fit-margin-right="8" - inkscape:zoom="8.5108696" - inkscape:cx="46" - inkscape:cy="-26.965517" - inkscape:window-x="0" - inkscape:window-y="0" + fit-margin-top="4" + fit-margin-left="4" + fit-margin-bottom="4" + fit-margin-right="4" + inkscape:zoom="8.05" + inkscape:cx="26.459627" + inkscape:cy="19.006211" + inkscape:window-x="56" + inkscape:window-y="50" inkscape:window-maximized="0" inkscape:current-layer="vector" inkscape:showpageshadow="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" /> - <rect - id="rect178" - width="92" - height="92" - x="0" - y="0" - style="display:inline;fill:#3a3a3b;fill-opacity:1;stroke-width:0.436372" - ry="0" /> <g - id="group_1" - transform="matrix(0.16904335,0,0,0.16904335,-7.9999769,-8)" - style="display:inline"> + id="g1252" + transform="matrix(0.43478261,0,0,0.43478261,4,4)"> + <path + fill="none" + stroke="#000000" + stroke-width="0.645279" + d="M 46,92 C 66.156946,92 77.36246,92 84.681245,84.681247 92,77.362462 92,66.15695 92,46.000001 92,25.843058 92,14.637536 84.681245,7.3187613 77.36246,0 66.156946,0 46,0 25.843059,0 14.637536,0 7.3187622,7.3187613 0,14.637536 0,25.843058 0,46.000001 0,66.15695 0,77.362462 7.3187622,84.681247 14.637536,92 25.843059,92 46,92 Z" + id="path922" + style="fill:#3a3a3b;fill-opacity:1;stroke:none" /> <g - id="group" - transform="translate(173.03255,173.03255)"> - <path - id="path" - d="m 62.962,292.5 v 0 c -3.7,-0.9 -5.7,-3.8 -4.3,-6.5 l 32.3,-62.9 c 1.3,-2.6 5.5,-4 9.2,-3 v 0 c 3.7,0.9 5.7,3.8 4.3,6.5 l -32.3,62.9 c -1.3,2.6 -5.4,4 -9.2,3 z" - inkscape:connector-curvature="0" - style="fill:#54af39" /> - <path - id="path_1" - d="m 229.662,292.5 v 0 c 3.7,-0.9 5.7,-3.8 4.3,-6.5 l -32.3,-62.9 c -1.3,-2.6 -5.5,-4 -9.2,-3 v 0 c -3.7,0.9 -5.7,3.8 -4.3,6.5 l 32.3,62.9 c 1.3,2.6 5.5,4 9.2,3 z" - inkscape:connector-curvature="0" - style="fill:#54af39" /> - <path - id="path_2" - d="m 151.362,36.9 -0.6,0.2 c -1.8,0.7 -3.8,-0.3 -4.5,-2.1 l -9.6,-26.7 c -0.7,-1.8 0.3,-3.8 2.1,-4.5 l 0.6,-0.2 c 1.8,-0.7 3.8,0.3 4.5,2.1 l 9.6,26.7 c 0.7,1.9 -0.3,3.9 -2.1,4.5 z" - inkscape:connector-curvature="0" - style="fill:#54af39" /> - <path - id="path_3" - d="m 180.662,3.8 v 0 c 0,2.1 -1.7,3.8 -3.8,3.8 h -61 c -2.1,0 -3.8,-1.7 -3.8,-3.8 v 0 c 0,-2.1 1.7,-3.8 3.8,-3.8 h 61 c 2.1,0 3.8,1.7 3.8,3.8 z" - inkscape:connector-curvature="0" - style="fill:#54af39" /> - <path - id="path_4" - d="m 218.762,236.7 h -144.9 c -13.3,0 -24,-10.8 -24,-24 v -108 c 0,-41.3 33.5,-74.9 74.9,-74.9 h 43.3 c 41.3,0 74.9,33.5 74.9,74.9 v 108 c -0.1,13.2 -10.9,24 -24.2,24 z" - inkscape:connector-curvature="0" - style="fill:#54af39" /> - <path - id="path_5" - d="m 212.562,146.2 h -132.5 c -5.6,0 -10.2,-4.5 -10.2,-10.2 v -34.9 c 0,-16.9 13.7,-30.6 30.6,-30.6 h 91.7 c 16.9,0 30.6,13.7 30.6,30.6 V 136 c -0.1,5.7 -4.6,10.2 -10.2,10.2 z" - inkscape:connector-curvature="0" - style="fill:#ffffff" /> - <path - id="path_6" - d="m 161.462,55.5 h -30.3 c -3.2,0 -5.7,-2.6 -5.7,-5.7 v 0 c 0,-3.2 2.6,-5.7 5.7,-5.7 h 30.3 c 3.2,0 5.7,2.6 5.7,5.7 v 0 c 0,3.1 -2.6,5.7 -5.7,5.7 z" - inkscape:connector-curvature="0" - style="fill:#ffffff" /> - <path - id="path_7" - d="m 87.062,191.9 m -14.8,0 c 0,-3.924 1.56,-7.691 4.335,-10.465 2.774,-2.775 6.541,-4.335 10.465,-4.335 3.924,0 7.691,1.56 10.465,4.335 2.775,2.774 4.335,6.541 4.335,10.465 0,3.924 -1.56,7.691 -4.335,10.465 -2.774,2.775 -6.541,4.335 -10.465,4.335 -3.924,0 -7.691,-1.56 -10.465,-4.335 -2.775,-2.774 -4.335,-6.541 -4.335,-10.465" - inkscape:connector-curvature="0" - style="fill:#ffffff" /> - <path - id="path_8" - d="m 205.662,191.9 m -14.8,0 c 0,-3.924 1.56,-7.691 4.335,-10.465 2.774,-2.775 6.541,-4.335 10.465,-4.335 3.924,0 7.691,1.56 10.465,4.335 2.775,2.774 4.335,6.541 4.335,10.465 0,3.924 -1.56,7.691 -4.335,10.465 -2.774,2.775 -6.541,4.335 -10.465,4.335 -3.924,0 -7.691,-1.56 -10.465,-4.335 -2.775,-2.774 -4.335,-6.541 -4.335,-10.465" - inkscape:connector-curvature="0" - style="fill:#ffffff" /> + id="group_1" + transform="matrix(0.16904335,0,0,0.16904335,-7.9999769,-8)" + style="display:inline"> + <g + id="group" + transform="translate(173.03255,173.03255)"> + <path + id="path" + d="m 62.962,292.5 v 0 c -3.7,-0.9 -5.7,-3.8 -4.3,-6.5 l 32.3,-62.9 c 1.3,-2.6 5.5,-4 9.2,-3 v 0 c 3.7,0.9 5.7,3.8 4.3,6.5 l -32.3,62.9 c -1.3,2.6 -5.4,4 -9.2,3 z" + inkscape:connector-curvature="0" + style="fill:#54af39" /> + <path + id="path_1" + d="m 229.662,292.5 v 0 c 3.7,-0.9 5.7,-3.8 4.3,-6.5 l -32.3,-62.9 c -1.3,-2.6 -5.5,-4 -9.2,-3 v 0 c -3.7,0.9 -5.7,3.8 -4.3,6.5 l 32.3,62.9 c 1.3,2.6 5.5,4 9.2,3 z" + inkscape:connector-curvature="0" + style="fill:#54af39" /> + <path + id="path_2" + d="m 151.362,36.9 -0.6,0.2 c -1.8,0.7 -3.8,-0.3 -4.5,-2.1 l -9.6,-26.7 c -0.7,-1.8 0.3,-3.8 2.1,-4.5 l 0.6,-0.2 c 1.8,-0.7 3.8,0.3 4.5,2.1 l 9.6,26.7 c 0.7,1.9 -0.3,3.9 -2.1,4.5 z" + inkscape:connector-curvature="0" + style="fill:#54af39" /> + <path + id="path_3" + d="m 180.662,3.8 v 0 c 0,2.1 -1.7,3.8 -3.8,3.8 h -61 c -2.1,0 -3.8,-1.7 -3.8,-3.8 v 0 c 0,-2.1 1.7,-3.8 3.8,-3.8 h 61 c 2.1,0 3.8,1.7 3.8,3.8 z" + inkscape:connector-curvature="0" + style="fill:#54af39" /> + <path + id="path_4" + d="m 218.762,236.7 h -144.9 c -13.3,0 -24,-10.8 -24,-24 v -108 c 0,-41.3 33.5,-74.9 74.9,-74.9 h 43.3 c 41.3,0 74.9,33.5 74.9,74.9 v 108 c -0.1,13.2 -10.9,24 -24.2,24 z" + inkscape:connector-curvature="0" + style="fill:#54af39" /> + <path + id="path_5" + d="m 212.562,146.2 h -132.5 c -5.6,0 -10.2,-4.5 -10.2,-10.2 v -34.9 c 0,-16.9 13.7,-30.6 30.6,-30.6 h 91.7 c 16.9,0 30.6,13.7 30.6,30.6 V 136 c -0.1,5.7 -4.6,10.2 -10.2,10.2 z" + inkscape:connector-curvature="0" + style="fill:#ffffff" /> + <path + id="path_6" + d="m 161.462,55.5 h -30.3 c -3.2,0 -5.7,-2.6 -5.7,-5.7 v 0 c 0,-3.2 2.6,-5.7 5.7,-5.7 h 30.3 c 3.2,0 5.7,2.6 5.7,5.7 v 0 c 0,3.1 -2.6,5.7 -5.7,5.7 z" + inkscape:connector-curvature="0" + style="fill:#ffffff" /> + <path + id="path_7" + d="m 87.062,191.9 m -14.8,0 c 0,-3.924 1.56,-7.691 4.335,-10.465 2.774,-2.775 6.541,-4.335 10.465,-4.335 3.924,0 7.691,1.56 10.465,4.335 2.775,2.774 4.335,6.541 4.335,10.465 0,3.924 -1.56,7.691 -4.335,10.465 -2.774,2.775 -6.541,4.335 -10.465,4.335 -3.924,0 -7.691,-1.56 -10.465,-4.335 -2.775,-2.774 -4.335,-6.541 -4.335,-10.465" + inkscape:connector-curvature="0" + style="fill:#ffffff" /> + <path + id="path_8" + d="m 205.662,191.9 m -14.8,0 c 0,-3.924 1.56,-7.691 4.335,-10.465 2.774,-2.775 6.541,-4.335 10.465,-4.335 3.924,0 7.691,1.56 10.465,4.335 2.775,2.774 4.335,6.541 4.335,10.465 0,3.924 -1.56,7.691 -4.335,10.465 -2.774,2.775 -6.541,4.335 -10.465,4.335 -3.924,0 -7.691,-1.56 -10.465,-4.335 -2.775,-2.774 -4.335,-6.541 -4.335,-10.465" + inkscape:connector-curvature="0" + style="fill:#ffffff" /> + </g> </g> </g> <g diff --git a/build.gradle.kts b/build.gradle.kts index 7a3bc819960b47c4f4e7c762aa8cc8e828d14f11..86f0ddbf3403895e1c7ff8b91dd0849b8edda743 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,8 +4,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.7.0" apply false - id("com.android.library") version "8.7.0" apply false + id("com.android.application") version "8.7.2" apply false + id("com.android.library") version "8.7.2" apply false kotlin("android") version "2.0.10" apply false kotlin("jvm") version "1.7.20" apply false kotlin("plugin.parcelize") version "1.8.20" apply false diff --git a/metadata/en/changelogs/32.txt b/metadata/en/changelogs/32.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4add26464b804086eccfaebbaf8250a4b6d8452 --- /dev/null +++ b/metadata/en/changelogs/32.txt @@ -0,0 +1,2 @@ +* Added basic Transitous support +* Fixed phantom localities names in search results diff --git a/metadata/en/changelogs/33.txt b/metadata/en/changelogs/33.txt new file mode 100644 index 0000000000000000000000000000000000000000..2373678058a8b01c40301f6d459695a67eb52f91 --- /dev/null +++ b/metadata/en/changelogs/33.txt @@ -0,0 +1,5 @@ +* updated dependencies +* prepared for separate arrival and departure times +* improve styling +* added Estonian language (Priit Jõerüüt) +* improve translations (Lars K, Nicolas DERIVE, stormax181iq) diff --git a/metadata/en-US/changelogs/32.txt b/metadata/en-US/changelogs/32.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4add26464b804086eccfaebbaf8250a4b6d8452 --- /dev/null +++ b/metadata/en-US/changelogs/32.txt @@ -0,0 +1,2 @@ +* Added basic Transitous support +* Fixed phantom localities names in search results diff --git a/metadata/en-US/changelogs/33.txt b/metadata/en-US/changelogs/33.txt new file mode 100644 index 0000000000000000000000000000000000000000..2373678058a8b01c40301f6d459695a67eb52f91 --- /dev/null +++ b/metadata/en-US/changelogs/33.txt @@ -0,0 +1,5 @@ +* updated dependencies +* prepared for separate arrival and departure times +* improve styling +* added Estonian language (Priit Jõerüüt) +* improve translations (Lars K, Nicolas DERIVE, stormax181iq) diff --git a/metadata/pl-PL/changelogs/32.txt b/metadata/pl-PL/changelogs/32.txt new file mode 100644 index 0000000000000000000000000000000000000000..4eaba3d3347c664e684ced97d5c921f52eda0670 --- /dev/null +++ b/metadata/pl-PL/changelogs/32.txt @@ -0,0 +1,2 @@ +* Dodano podstawową obsługę Transitous +* Naprawiono fantomowe nazwy lokalizacji w wynikach wyszukiwania diff --git a/metadata/pl-PL/changelogs/33.txt b/metadata/pl-PL/changelogs/33.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b61d6517e93169f3833d5e9fa8430770d139962 --- /dev/null +++ b/metadata/pl-PL/changelogs/33.txt @@ -0,0 +1,5 @@ +* zaktualizowano zależności +* przygotowano pod oddzielne czasy przyjazdu i odjazdu +* poprawiono ostylowanie +* dodano język estoński (Priit Jõerüüt) +* poprawiono tłumaczenia (Lars K, Nicolas DERIVE, stormax181iq) diff --git a/release.sh b/release.sh index 62122ff378c7bd472d9e463b2b145ef1637b4669..7b8c6671ca16a510eb4014d883fdcc84505f215c 100755 --- a/release.sh +++ b/release.sh @@ -62,13 +62,21 @@ latestCIstarted=$(echo "$latestCI" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+\+[0-9]{2}:[0-9]{2}' | head -n1) while [ "$latestCIstatus" != 'OK' ] && [ "$retry" = "1" ] do echo "latest CI started at $latestCIstarted result is $latestCIstatus, not OK" - echo "retry? [y/N]" + echo "retry? [y/N/s]" read -r decision - if [ "$decision" != 'y' ] + if [ "$decision" = 'N' ] then retry="0" exit 1 fi + if [ "$decision" = 's' ] + then + retry="0" + break + fi + latestCI=$(curl https://ci.apiote.xyz/toys/czwek-commitly/latest 2>/dev/null) + latestCIstatus=$(echo "$latestCI" | grep '<h2' | sed 's/<h2[^>]*>//' | sed 's|</h2>||' | grep -oE '[A-Z]+') + latestCIstarted=$(echo "$latestCI" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+\+[0-9]{2}:[0-9]{2}' | head -n1) done currentVersionName=$(grep -Eo 'versionName = "[0-9\.]+"' app/build.gradle.kts | cut -d '=' -f2 | tr -d ' "') @@ -90,7 +98,7 @@ sed -i "s/versionCode = $currentVersionCode/versionCode = $newVersionCode/" app/build.gradle.kts git shortlog "v${currentVersionName}..HEAD" >> "metadata/en-US/changelogs/$newVersionCode.txt" - echo "time to update changelogs" + echo "time to update changelogs and README" elif [ $phase -eq 1 ] then newVersionName=$(grep -Eo 'versionName = "[0-9\.]+"' app/build.gradle.kts | cut -d '=' -f2 | tr -d ' "') @@ -106,6 +114,7 @@ fi fi git add app/build.gradle.kts git add metadata/ + git add README.adoc git commit -S -m "release version $newVersionName ($newVersionCode)" || true echo 'pushing …' git push