Bimba.git

commit c47403765e3fe723e11c9627d8f876a2ba1763f9

Author: Adam Evyčędo <git@apiote.xyz>

show journey detailed info

%!v(PANIC=String method: strings: negative Repeat count)


diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt
index 8a4aaacd2d2d7f8e5e24133eae1e752527e347d3..2e42c505cc370dace2a41ec273a5921782b6c697 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt
@@ -8,6 +8,7 @@ import android.content.Context
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
@@ -15,6 +16,9 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
 import com.google.android.material.card.MaterialCardView
 import xyz.apiote.bimba.czwek.R
 import xyz.apiote.bimba.czwek.repo.Journey
+import xyz.apiote.bimba.czwek.units.Metre
+import xyz.apiote.bimba.czwek.units.UnitSystem
+import java.util.zip.Inflater
 
 class JourneysViewHolder(itemView: View) : ViewHolder(itemView) {
 	val root: MaterialCardView = itemView.findViewById(R.id.journey)
@@ -28,15 +32,83 @@ 		fun bind(
 			holder: JourneysViewHolder,
 			onClickListener: (Journey) -> Unit,
 			journey: Journey,
-			context: Context
+			context: Context,
+			inflater: LayoutInflater
 		) {
-			holder.root.setOnClickListener { onClickListener(journey) }
+			holder.root.setOnClickListener {
+				if (holder.legs.visibility == View.GONE) {
+					holder.legs.visibility = View.VISIBLE
+				} else {
+					holder.legs.visibility = View.GONE
+				}
+				onClickListener(journey)
+			}
 			holder.startTime.text =
 				context.getString(R.string.time, journey.startTime.hour, journey.startTime.minute)
 			holder.endTime.text =
 				context.getString(R.string.time, journey.endTime.hour, journey.endTime.minute)
 			holder.lines.text =
 				journey.legs.map { it.start.vehicle.Line.name }.filter { it.isNotBlank() }.joinToString()
+
+			holder.legs.removeAllViews()
+			journey.legs.forEach {
+				val legView = inflater.inflate(R.layout.journey_leg, null, false)
+
+				val legOrigin = legView.findViewById<TextView>(R.id.leg_origin)
+				if (it.origin.stop.name.isBlank() || it.origin.stop.name == "START") {
+					legOrigin.visibility = View.GONE
+				} else {
+					legOrigin.apply {
+						text = it.origin.stop.name
+						visibility = View.VISIBLE
+					}
+				}
+
+				val legOriginTime = legView.findViewById<TextView>(R.id.leg_origin_time)
+				legOriginTime.text = context.getString(
+					R.string.time,
+					it.start.departureTime!!.Hour.toInt(),
+					it.start.departureTime.Minute.toInt()
+				)
+
+				val legModeImage = legView.findViewById<ImageView>(R.id.leg_mode_image)
+				legModeImage.setImageDrawable(it.start.vehicle.Line.icon(context))
+				// TODO legModeImage.contentDescription = translate "leg mode: $mode"
+
+				val legLine = legView.findViewById<TextView>(R.id.leg_line)
+				legLine.apply {
+					if (it.start.vehicle.Line.name.isBlank()) {
+						visibility = View.GONE
+					} else {
+						visibility = View.VISIBLE
+						text = it.start.vehicle.Line.name
+					}
+				}
+
+				val distance = legView.findViewById<TextView>(R.id.leg_distance)
+				distance.text = if (it.start.vehicle.Line.name.isBlank() && it.distance != null) {
+					val us = UnitSystem.getSelected(context)
+					us.toString(context, it.distance)
+				} else {
+					context.resources.getQuantityString(R.plurals.number_stops, it.intermediateStops.size+1, it.intermediateStops.size+1)
+				}
+
+				val legDestination = legView.findViewById<TextView>(R.id.leg_destination)
+				if (it.destination.stop.name.isBlank() || it.destination.stop.name == "END") {
+					legDestination.visibility = View.GONE
+				} else {
+					legDestination.apply {
+						text = it.destination.stop.name
+						visibility = View.VISIBLE
+					}
+				}
+
+				val legDestinationTime = legView.findViewById<TextView>(R.id.leg_destination_time)
+				legDestinationTime.text =
+					context.getString(R.string.time, it.end.arrivalTime!!.Hour.toInt(), it.end.arrivalTime.Minute.toInt())
+
+				holder.legs.addView(legView)
+			}
 		}
 	}
 }
@@ -60,7 +132,7 @@ 	override fun onBindViewHolder(
 		holder: JourneysViewHolder,
 		position: Int
 	) {
-		JourneysViewHolder.bind(holder, onClickListener, items[position], context)
+		JourneysViewHolder.bind(holder, onClickListener, items[position], context, inflater)
 	}
 
 	override fun getItemCount(): Int = items.size




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt
index e807424c10d11689a915fbbbd984354d5c0474ff..058c1489442f28428b92c9a0398c7af00dfea2fe 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt
@@ -6,21 +6,33 @@ package xyz.apiote.bimba.czwek.journeys
 
 import android.content.Context
 import android.content.Intent
+import android.content.res.Configuration.UI_MODE_NIGHT_MASK
+import android.content.res.Configuration.UI_MODE_NIGHT_UNDEFINED
+import android.content.res.Configuration.UI_MODE_NIGHT_YES
 import android.os.Build
 import android.os.Bundle
+import android.util.Log
 import androidx.activity.enableEdgeToEdge
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updatePadding
 import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.LinearLayoutManager
+import org.osmdroid.tileprovider.tilesource.TileSourceFactory
 import org.osmdroid.util.BoundingBox
+import org.osmdroid.views.CustomZoomButtonsController
+import org.osmdroid.views.overlay.TilesOverlay
+import org.osmdroid.views.overlay.gestures.RotationGestureOverlay
 import xyz.apiote.bimba.czwek.databinding.ActivityJourneysBinding
+import xyz.apiote.bimba.czwek.dpToPixelI
 import xyz.apiote.bimba.czwek.repo.Place
 import kotlin.math.max
 import kotlin.math.min
 
 class JourneysActivity : AppCompatActivity() {
+	private lateinit var binding: ActivityJourneysBinding
+
 	companion object {
 		const val ORIGIN_PARAM = "origin"
 		const val DESTINATION_PARAM = "destination"
@@ -49,7 +61,6 @@ 		@Suppress("DEPRECATION")
 		intent.getParcelableExtra(DESTINATION_PARAM)
 	} ?: throw Exception("Destination not given")
 
-	private lateinit var binding: ActivityJourneysBinding
 	override fun onCreate(savedInstanceState: Bundle?) {
 		enableEdgeToEdge()
 		super.onCreate(savedInstanceState)
@@ -58,31 +69,55 @@ 		setContentView(binding.root)
 		val journeysViewModel =
 			ViewModelProvider(this)[JourneysViewModel::class.java]
 
-		// TODO check insets
-		ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
-			val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
-			v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
-			insets
+		// TODO check upside-down
+		// TODO for horizontal make side sheet
+		ViewCompat.setOnApplyWindowInsetsListener(binding.journeys) { v, windowInsets ->
+			val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+			v.updatePadding(bottom = insets.bottom + dpToPixelI(16f))
+			WindowInsetsCompat.CONSUMED
 		}
 
-		val origin = getOrigin()
-		val destination = getDestination()
-		// FIXME
-		binding.map.zoomToBoundingBox(BoundingBox(
-			max(origin.latitude, destination.latitude),
-			max(origin.longitude, destination.longitude),
-			min(origin.latitude, destination.latitude),
-			min(origin.longitude, destination.longitude),
-		), false)
+		binding.map.setTileSource(TileSourceFactory.MAPNIK)
+		if (((resources?.configuration?.uiMode ?: UI_MODE_NIGHT_UNDEFINED)
+					and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES
+		) {
+			binding.map.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
+		}
+		binding.map.zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
+		binding.map.maxZoomLevel = 21.5
+		binding.map.minZoomLevel = 5.5
+		binding.map.setMultiTouchControls(true)
+		binding.map.overlays.add(RotationGestureOverlay(binding.map).apply { isEnabled = true })
 
 		binding.journeys.layoutManager = LinearLayoutManager(this)
 
+		val origin = getOrigin()
+		val destination = getDestination()
+
 		journeysViewModel.journeys.observe(this) {
+			zoomMap(100)
 			binding.journeys.adapter = JourneysAdapter(layoutInflater, this, it) {
 				// todo move map accordingly
 				// todo show shape on map
 			}
 		}
 		journeysViewModel.getJourneys(this, origin, destination)
+	}
+
+	override fun onStart() {
+		super.onStart()
+		zoomMap()
+	}
+
+	fun zoomMap(margin: Int = 0, box: BoundingBox? = null) {
+		val origin = getOrigin()
+		val destination = getDestination()
+		val bb = box?:BoundingBox(
+			max(origin.latitude, destination.latitude),
+			max(origin.longitude, destination.longitude),
+			min(origin.latitude, destination.latitude),
+			min(origin.longitude, destination.longitude),
+		)
+		binding.map.zoomToBoundingBox(bb, false, margin)
 	}
 }
\ No newline at end of file




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineAbstract.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineAbstract.kt
index 8cfbfc83fdca77fbde3ae8077f613596bc0317cf..3073620c339606475ca5c6d963cc04a23966f30c 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineAbstract.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineAbstract.kt
@@ -73,6 +73,7 @@ 			LineType.FUNICULAR -> R.drawable.funicular_black
 			LineType.MONORAIL -> R.drawable.monorail_black
 			LineType.UNKNOWN -> R.drawable.vehicle_black
 			LineType.PLANE -> R.drawable.plane_black
+			LineType.WALK -> R.drawable.walk_black
 		}
 		val icon = AppCompatResources.getDrawable(context, iconID)?.mutate()?.apply {
 			setTint(textColour(colour))




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineType.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineType.kt
index be76479081cc2763d596e2271e83e03096730d91..29612aa9cd60bebeefcd4423bc7bb84ad1164368 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineType.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineType.kt
@@ -10,7 +10,7 @@ import xyz.apiote.bimba.czwek.api.LineTypeV3
 import xyz.apiote.bimba.czwek.api.transitous.model.Mode
 
 enum class LineType {
-	UNKNOWN, TRAM, BUS, TROLLEYBUS, METRO, RAIL, FERRY, CABLE_TRAM, CABLE_CAR, FUNICULAR, MONORAIL, PLANE;
+	UNKNOWN, TRAM, BUS, TROLLEYBUS, METRO, RAIL, FERRY, CABLE_TRAM, CABLE_CAR, FUNICULAR, MONORAIL, PLANE, WALK;
 
 	companion object {
 		fun of(t: LineTypeV1): LineType {
@@ -47,7 +47,7 @@
 		fun fromTransitous2(mode: Mode): LineType {
 			return when(mode) {
 				Mode.AIRPLANE -> PLANE
-				Mode.WALK -> UNKNOWN
+				Mode.WALK -> WALK
 				Mode.BIKE -> UNKNOWN
 				Mode.CAR -> UNKNOWN
 				Mode.RENTAL -> UNKNOWN




diff --git a/app/src/main/res/drawable/walk_black.xml b/app/src/main/res/drawable/walk_black.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ae56cca4f7a58709c49b5f78ddb5306168f1ed65
--- /dev/null
+++ b/app/src/main/res/drawable/walk_black.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+SPDX-FileCopyrightText: Google
+
+SPDX-License-Identifier: Apache-2.0
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="#000000"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM9.8,8.9L7,23h2.1l1.8,-8 2.1,2v6h2v-7.5l-2.1,-2 0.6,-3C14.8,12 16.8,13 19,13v-2c-1.9,0 -3.5,-1 -4.3,-2.4l-1,-1.6c-0.4,-0.6 -1,-1 -1.7,-1 -0.3,0 -0.5,0.1 -0.8,0.1L6,8.3V13h2V9.6l1.8,-0.7" />
+</vector>




diff --git a/app/src/main/res/layout/activity_journeys.xml b/app/src/main/res/layout/activity_journeys.xml
index 245ab2b24414a10047326b0a6e871574f155987e..23884c043666b148164bde917d548dbf2229c97b 100644
--- a/app/src/main/res/layout/activity_journeys.xml
+++ b/app/src/main/res/layout/activity_journeys.xml
@@ -17,10 +17,10 @@ 		android:layout_height="match_parent" />
 
 	<androidx.constraintlayout.widget.ConstraintLayout
 		app:behavior_hideable="false"
-		android:id="@+id/standard_bottom_sheet"
+		android:id="@+id/journeys_bottom_sheet"
 		style="@style/Widget.Material3.BottomSheet"
 		android:layout_width="match_parent"
-		android:layout_height="match_parent"
+		android:layout_height="640dp"
 		app:behavior_peekHeight="256dp"
 		app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
 
@@ -36,6 +36,7 @@ 		 			android:id="@+id/journeys"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
+			android:clipToPadding="false"
 			app:layout_behavior="@string/appbar_scrolling_view_behavior"
 			app:layout_constraintTop_toBottomOf="@+id/drag_handle" />
 	</androidx.constraintlayout.widget.ConstraintLayout>




diff --git a/app/src/main/res/layout/journey.xml b/app/src/main/res/layout/journey.xml
index ecc05a3f4474b5cfd7e418ac9610bb451d3ae3e5..be5120d120ac8514a2a112c47feef904733d0a6b 100644
--- a/app/src/main/res/layout/journey.xml
+++ b/app/src/main/res/layout/journey.xml
@@ -49,6 +49,7 @@ 			tools:text="12:30" />
 
 		<LinearLayout
 			android:id="@+id/legs"
+			android:visibility="gone"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
 			android:layout_margin="8dp"




diff --git a/app/src/main/res/layout/journey_leg.xml b/app/src/main/res/layout/journey_leg.xml
index 4d09620b97d748d141e5df41708710620432c8fc..dcf3850b25ca8890463b1d5f4b7366b0a780bb66 100644
--- a/app/src/main/res/layout/journey_leg.xml
+++ b/app/src/main/res/layout/journey_leg.xml
@@ -15,7 +15,7 @@ 		android:id="@+id/leg_origin_image"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
 		android:src="@drawable/origin"
-		android:contentDescription="beginning of journey's leg"
+		android:contentDescription="@string/beginning_of_journey_s_leg"
 		app:layout_constraintStart_toStartOf="parent"
 		app:layout_constraintTop_toTopOf="parent" />
 
@@ -47,15 +47,16 @@ 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
 		android:layout_marginTop="8dp"
 		android:src="@drawable/bike"
-		android:contentDescription="mode of journey's leg: walk"
 		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@+id/leg_origin_image" />
+		app:layout_constraintTop_toBottomOf="@+id/leg_origin_image"
+        tool:ignore="ContentDescription" />
 
 	<com.google.android.material.textview.MaterialTextView
 		android:id="@+id/leg_line"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
-		android:layout_marginStart="8dp"
+		android:paddingEnd="8dp"
+		android:paddingStart="8dp"
 		android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
 		app:layout_constraintBottom_toBottomOf="@+id/leg_mode_image"
 		app:layout_constraintStart_toEndOf="@+id/leg_mode_image"
@@ -66,7 +67,7 @@ 	 		android:id="@+id/leg_distance"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
-		android:layout_marginStart="16dp"
+		android:layout_marginStart="8dp"
 		android:textAppearance="@style/TextAppearance.Material3.LabelSmall"
 		app:layout_constraintBottom_toBottomOf="@+id/leg_line"
 		app:layout_constraintStart_toEndOf="@+id/leg_line"
@@ -79,7 +80,7 @@ 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
 		android:layout_marginTop="8dp"
 		android:src="@drawable/destination"
-		android:contentDescription="end of journey's leg"
+		android:contentDescription="@string/end_of_journey_s_leg"
 		app:layout_constraintStart_toStartOf="parent"
 		app:layout_constraintTop_toBottomOf="@+id/leg_mode_image" />
 




diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 04ed881557c310fb27e0ee81e4aa3dc498b4e343..2f796af4d2f8128f2c97dedefdb33ccc04e53508 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -303,4 +303,10 @@ 	Now
 	<string name="wheelchair_accessible">Wheelchair accessible</string>
 	<string name="bike_transport">Bike transport</string>
 	<string name="go">Go</string>
+	<string name="beginning_of_journey_s_leg">beginning of journey\'s leg</string>
+	<string name="end_of_journey_s_leg">end of journey\'s leg</string>
+	<plurals name="number_stops">
+		<item quantity="one">%1$d stop</item>
+		<item quantity="other">%1$d stops</item>
+	</plurals>
 </resources>




diff --git a/build.gradle.kts b/build.gradle.kts
index 469ffc011b5a5a8007953b795633669f6779049e..c746a3ec056432afbc9711d8b48250689c0d939c 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.2" apply false
-    id("com.android.library") version "8.7.2" apply false
+    id("com.android.application") version "8.7.3" apply false
+    id("com.android.library") version "8.7.3" apply false
     id("org.openapi.generator") version "7.9.0" apply false
     id("de.undercouch.download") version "5.6.0" apply false
     kotlin("android") version "2.0.10" apply false