Bimba.git

commit 47d75f47e5348dc513907e5d3972a5c2fd716029

Author: Adam <git@apiote.xyz>

show bottom sheet when clicking on departure

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


diff --git a/app/src/main/java/ml/adamsprogs/bimba/api/Structs.kt b/app/src/main/java/ml/adamsprogs/bimba/api/Structs.kt
index ad27e4d4a953cdfba99ba473c869a2c77c6e589d..a9da169aef88fa460e7559d3ca47ef093c6a9185 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/api/Structs.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/api/Structs.kt
@@ -140,6 +140,18 @@ 	val Speed: Float,
 	val CongestionLevel: UByte,
 	val OccupancyStatus: UByte
 ) {
+	enum class Capability(val bit: UShort) {
+		RAMP(0b0001u),
+		LOW_FLOOR(0b0010u),
+		LOW_ENTRY(0b0001_0000_0000u),
+		AC(0b0100u),
+		BIKE(0b1000u),
+		VOICE(0b0001_0000u),
+		TICKET_MACHINE(0b0010_0000u),
+		TICKET_DRIVER(0b0100_0000u),
+		USB_CHARGING(0b1000_0000u)
+	}
+
 	companion object {
 		fun unmarshal(stream: InputStream): Vehicle {
 			val reader = Reader(stream)
@@ -152,6 +164,10 @@ 				reader.readU8(),
 				reader.readU8()
 			)
 		}
+	}
+
+	fun getCapability(field: Capability): Boolean {
+		return Capabilities.and(field.bit) != (0).toUShort()
 	}
 }
 




diff --git a/app/src/main/java/ml/adamsprogs/bimba/departures/Departures.kt b/app/src/main/java/ml/adamsprogs/bimba/departures/Departures.kt
index f6452346edd002ec2e201aafc0062f73bc152162..92f5ea673169e60962325585e9166b0555aaabc8 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/departures/Departures.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/departures/Departures.kt
@@ -2,15 +2,21 @@ package ml.adamsprogs.bimba.departures
 
 import android.annotation.SuppressLint
 import android.content.Context
+import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.Button
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
 import ml.adamsprogs.bimba.R
 import ml.adamsprogs.bimba.api.Departure
+import ml.adamsprogs.bimba.api.Vehicle
+import ml.adamsprogs.bimba.dpToPixelI
 import java.util.*
 
 
@@ -89,5 +95,134 @@ 		departures = items
 		Log.v("Departures", departures.toString())
 		notifyDataSetChanged()
 	}
+}
 
+class ModalBottomSheet(private val departure: Departure) : BottomSheetDialogFragment() {
+	companion object {
+		const val TAG = "ModalBottomSheet"
+	}
+
+	override fun onCreateView(
+		inflater: LayoutInflater,
+		container: ViewGroup?,
+		savedInstanceState: Bundle?
+	): View {
+		val content = inflater.inflate(R.layout.departure_bottom_sheet, container, false)
+
+		var timeText = "at ${departure.time.Hour.toString().padStart(2, '0')}:${
+			departure.time.Minute.toString().padStart(2, '0')
+		}"
+		if (departure.isRealtime) {
+			timeText += ":${departure.time.Second.toString().padStart(2, '0')}"
+		}
+		content.apply {
+			findViewById<TextView>(R.id.time).text = timeText
+
+			findViewById<ImageView>(R.id.rt_icon).apply {
+				visibility = if (departure.isRealtime) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			}
+			findViewById<ImageView>(R.id.wheelchair_icon).apply {
+				visibility = if (departure.vehicle.let {
+						it.getCapability(Vehicle.Capability.LOW_FLOOR) || it.getCapability(Vehicle.Capability.LOW_ENTRY) || it.getCapability(
+							Vehicle.Capability.RAMP
+						)
+					}) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			}
+
+			findViewById<TextView>(R.id.line).apply {
+				contentDescription = "${departure.line.name} towards ${departure.headsign}"
+				text = "${departure.line.name} » ${departure.headsign}"
+			}
+
+			findViewById<Button>(R.id.map_button).apply {
+				setOnClickListener {
+					// todo show on map
+					// todo(szczanieckiej) vehicleID
+				}
+			}
+			findViewById<TextView>(R.id.boarding_text).apply {
+				text = if (departure.boarding.and(0b1010u) != (0b0).toUByte()) {
+					"on demand"
+				} else if (departure.boarding.and(0b0101u) != (0b0).toUByte()) {
+					"no boarding"
+				} else {
+					"can board"
+				}
+			}
+			findViewById<TextView>(R.id.speed_text).apply {
+				// todo units
+				// fixme(shchanyetskiey?)
+				val speed = departure.vehicle.Speed * 1.703
+				text = "%.3f Vl".format(speed)
+			}
+			findViewById<TextView>(R.id.congestion_text).apply {
+				text = when (departure.vehicle.CongestionLevel.toUInt()) {
+					0u -> "unknown"
+					1u -> "smooth traffic"
+					2u -> "stop and go"
+					3u -> "congestion"
+					4u -> "severe jams"
+					else -> TODO("throw invalid congestion")
+				}
+			}
+			findViewById<TextView>(R.id.occupancy_text).apply {
+				text = when (departure.vehicle.OccupancyStatus.toUInt()) {
+					0u -> "unknown"
+					1u -> "empty"
+					2u -> "many seats"
+					3u -> "few seats"
+					4u -> "standing only"
+					5u -> "crowded"
+					6u -> "full"
+					7u -> "won’t accept passengers"  // todo shorten
+					else -> TODO("throw invalid congestion")
+				}
+			}
+
+			findViewById<ImageView>(R.id.ac).visibility =
+				if (departure.vehicle.getCapability(Vehicle.Capability.AC)) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			findViewById<ImageView>(R.id.bike).visibility =
+				if (departure.vehicle.getCapability(Vehicle.Capability.BIKE)) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			findViewById<ImageView>(R.id.voice).visibility =
+				if (departure.vehicle.getCapability(Vehicle.Capability.VOICE)) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			findViewById<ImageView>(R.id.ticket).visibility =
+				if (departure.vehicle.let {
+						it.getCapability(Vehicle.Capability.TICKET_DRIVER) || it.getCapability(Vehicle.Capability.TICKET_MACHINE)
+					}) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+			findViewById<ImageView>(R.id.usb).visibility =
+				if (departure.vehicle.getCapability(Vehicle.Capability.USB_CHARGING)) {
+					View.VISIBLE
+				} else {
+					View.GONE
+				}
+		}
+
+		(dialog as BottomSheetDialog).behavior.peekHeight = dpToPixelI(150f)
+
+		return content
+	}
 }
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/departures/DeparturesActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/departures/DeparturesActivity.kt
index 762cb5ae966e261c605cb625076a9269b942d0cb..0e9e821e8aec10c6e835504ac36019f720980646 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/departures/DeparturesActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/departures/DeparturesActivity.kt
@@ -43,7 +43,9 @@
 		binding.departuresRecycler.layoutManager = LinearLayoutManager(this)
 		adapter = BimbaDeparturesAdapter(layoutInflater, this, listOf()) {
 			Log.v("Departure", "clicked: $it")
-			// todo show bottom sheet
+			val modalBottomSheet = ModalBottomSheet(it).apply {
+				show(supportFragmentManager, ModalBottomSheet.TAG)
+			}
 		}
 		binding.departuresRecycler.adapter = adapter
 		WindowCompat.setDecorFitsSystemWindows(window, false)




diff --git a/app/src/main/res/drawable/ic_ac_black_24dp.xml b/app/src/main/res/drawable/ic_ac_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0b4c72a0cb1aa0371cbb33f87e9eb0355249af9e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_ac_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M22,11h-4.17l3.24,-3.24 -1.41,-1.42L15,11h-2V9l4.66,-4.66 -1.42,-1.41L13,6.17V2h-2v4.17L7.76,2.93 6.34,4.34 11,9v2H9L4.34,6.34 2.93,7.76 6.17,11H2v2h4.17l-3.24,3.24 1.41,1.42L9,13h2v2l-4.66,4.66 1.42,1.41L11,17.83V22h2v-4.17l3.24,3.24 1.42,-1.41L13,15v-2h2l4.66,4.66 1.41,-1.42L17.83,13H22z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_bike_black_24dp.xml b/app/src/main/res/drawable/ic_bike_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6c449259d5df2a29eb3a6c460aa4f9cc13163d34
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bike_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M18.18,10l-1.7,-4.68C16.19,4.53 15.44,4 14.6,4H12v2h2.6l1.46,4h-4.81l-0.36,-1H12V7H7v2h1.75l1.82,5H9.9c-0.44,-2.23 -2.31,-3.88 -4.65,-3.99C2.45,9.87 0,12.2 0,15c0,2.8 2.2,5 5,5c2.46,0 4.45,-1.69 4.9,-4h4.2c0.44,2.23 2.31,3.88 4.65,3.99c2.8,0.13 5.25,-2.19 5.25,-5c0,-2.8 -2.2,-5 -5,-5H18.18zM7.82,16c-0.4,1.17 -1.49,2 -2.82,2c-1.68,0 -3,-1.32 -3,-3s1.32,-3 3,-3c1.33,0 2.42,0.83 2.82,2H5v2H7.82zM14.1,14h-1.4l-0.73,-2H15C14.56,12.58 14.24,13.25 14.1,14zM19,18c-1.68,0 -3,-1.32 -3,-3c0,-0.93 0.41,-1.73 1.05,-2.28l0.96,2.64l1.88,-0.68l-0.97,-2.67c0.03,0 0.06,-0.01 0.09,-0.01c1.68,0 3,1.32 3,3S20.68,18 19,18z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_crowd_black_24dp.xml b/app/src/main/res/drawable/ic_crowd_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..211e9db40fe9762c961537c4ec38c3baeee72ba9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_crowd_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M12,12.75c1.63,0 3.07,0.39 4.24,0.9c1.08,0.48 1.76,1.56 1.76,2.73L18,18H6l0,-1.61c0,-1.18 0.68,-2.26 1.76,-2.73C8.93,13.14 10.37,12.75 12,12.75zM4,13c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2C2,12.1 2.9,13 4,13zM5.13,14.1C4.76,14.04 4.39,14 4,14c-0.99,0 -1.93,0.21 -2.78,0.58C0.48,14.9 0,15.62 0,16.43V18l4.5,0v-1.61C4.5,15.56 4.73,14.78 5.13,14.1zM20,13c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2C18,12.1 18.9,13 20,13zM24,16.43c0,-0.81 -0.48,-1.53 -1.22,-1.85C21.93,14.21 20.99,14 20,14c-0.39,0 -0.76,0.04 -1.13,0.1c0.4,0.68 0.63,1.46 0.63,2.29V18l4.5,0V16.43zM12,6c1.66,0 3,1.34 3,3c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3C9,7.34 10.34,6 12,6z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_radar_black_24dp.xml b/app/src/main/res/drawable/ic_radar_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a15b4cdfec5219c8f31da1a5f9db2ce56180360e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_radar_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      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"
+      android:fillColor="#000000"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_speed_black_24dp.xml b/app/src/main/res/drawable/ic_speed_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..99fe648585a8d2da8b66de2be5a82dbca837c54d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_speed_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M20.38,8.57l-1.23,1.85a8,8 0,0 1,-0.22 7.58L5.07,18A8,8 0,0 1,15.58 6.85l1.85,-1.23A10,10 0,0 0,3.35 19a2,2 0,0 0,1.72 1h13.85a2,2 0,0 0,1.74 -1,10 10,0 0,0 -0.27,-10.44zM10.59,15.41a2,2 0,0 0,2.83 0l5.66,-8.49 -8.49,5.66a2,2 0,0 0,0 2.83z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_ticket_black_24dp.xml b/app/src/main/res/drawable/ic_ticket_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b7c4363d4c9ee91f2005dc92275385a56f795049
--- /dev/null
+++ b/app/src/main/res/drawable/ic_ticket_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M22,10V6c0,-1.11 -0.9,-2 -2,-2H4C2.9,4 2.01,4.89 2.01,6v4C3.11,10 4,10.9 4,12s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2S20.9,10 22,10zM13,17.5h-2v-2h2V17.5zM13,13h-2v-2h2V13zM13,8.5h-2v-2h2V8.5z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_traffic_black_24dp.xml b/app/src/main/res/drawable/ic_traffic_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b087ad91bf87a51a958e5f81137414b61060a098
--- /dev/null
+++ b/app/src/main/res/drawable/ic_traffic_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M20,10h-3L17,8.86c1.72,-0.45 3,-2 3,-3.86h-3L17,4c0,-0.55 -0.45,-1 -1,-1L8,3c-0.55,0 -1,0.45 -1,1v1L4,5c0,1.86 1.28,3.41 3,3.86L7,10L4,10c0,1.86 1.28,3.41 3,3.86L7,15L4,15c0,1.86 1.28,3.41 3,3.86L7,20c0,0.55 0.45,1 1,1h8c0.55,0 1,-0.45 1,-1v-1.14c1.72,-0.45 3,-2 3,-3.86h-3v-1.14c1.72,-0.45 3,-2 3,-3.86zM12,19c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zM12,14c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zM12,9c-1.11,0 -2,-0.9 -2,-2 0,-1.11 0.89,-2 2,-2 1.1,0 2,0.89 2,2 0,1.1 -0.89,2 -2,2z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_transfer_black_24dp.xml b/app/src/main/res/drawable/ic_transfer_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0d410e80f6d0d96892bd55a47b43a20e2cc75d31
--- /dev/null
+++ b/app/src/main/res/drawable/ic_transfer_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M16.49,15.5v-1.75L14,16.25l2.49,2.5L16.49,17L22,17v-1.5zM19.51,19.75L14,19.75v1.5h5.51L19.51,23L22,20.5 19.51,18zM9.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM5.75,8.9L3,23h2.1l1.75,-8L9,17v6h2v-7.55L8.95,13.4l0.6,-3C10.85,12 12.8,13 15,13v-2c-1.85,0 -3.45,-1 -4.35,-2.45l-0.95,-1.6C9.35,6.35 8.7,6 8,6c-0.25,0 -0.5,0.05 -0.75,0.15L2,8.3L2,13h2L4,9.65l1.75,-0.75"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_usb_black_24dp.xml b/app/src/main/res/drawable/ic_usb_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d4e91d959c5cd6c0f5e68d2e79a2883abd8fb6a4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_usb_black_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M15,7v4h1v2h-3V5h2l-3,-4 -3,4h2v8H8v-2.07c0.7,-0.37 1.2,-1.08 1.2,-1.93 0,-1.21 -0.99,-2.2 -2.2,-2.2 -1.21,0 -2.2,0.99 -2.2,2.2 0,0.85 0.5,1.56 1.2,1.93V13c0,1.11 0.89,2 2,2h3v3.05c-0.71,0.37 -1.2,1.1 -1.2,1.95 0,1.22 0.99,2.2 2.2,2.2 1.21,0 2.2,-0.98 2.2,-2.2 0,-0.85 -0.49,-1.58 -1.2,-1.95V15h3c1.11,0 2,-0.89 2,-2v-2h1V7h-4z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_voice_black_24dp.xml b/app/src/main/res/drawable/ic_voice_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8d70cbee2230841b41009250f1a99518e11a2d58
--- /dev/null
+++ b/app/src/main/res/drawable/ic_voice_black_24dp.xml
@@ -0,0 +1,8 @@
+<vector android:height="24dp" android:tint="#000000"
+    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="M10,9m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"/>
+    <path android:fillColor="@android:color/white" android:pathData="M16.39,15.56C14.71,14.7 12.53,14 10,14c-2.53,0 -4.71,0.7 -6.39,1.56C2.61,16.07 2,17.1 2,18.22V21h16v-2.78C18,17.1 17.39,16.07 16.39,15.56z"/>
+    <path android:fillColor="@android:color/white" android:pathData="M16,1h-2c0,4.97 4.03,9 9,9V8C19.14,8 16,4.86 16,1z"/>
+    <path android:fillColor="@android:color/white" android:pathData="M20,1h-2c0,2.76 2.24,5 5,5V4C21.35,4 20,2.65 20,1z"/>
+</vector>




diff --git a/app/src/main/res/drawable/ic_wheelchair_black_24dp.xml b/app/src/main/res/drawable/ic_wheelchair_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..48109d461e87bd70a9ddc026ae7ee079b9be6c49
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wheelchair_black_24dp.xml
@@ -0,0 +1,6 @@
+<vector android:autoMirrored="true" android:height="24dp"
+    android:tint="#000000" 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="M12,4m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+    <path android:fillColor="@android:color/white" android:pathData="M19,13v-2c-1.54,0.02 -3.09,-0.75 -4.07,-1.83l-1.29,-1.43c-0.17,-0.19 -0.38,-0.34 -0.61,-0.45 -0.01,0 -0.01,-0.01 -0.02,-0.01L13,7.28c-0.35,-0.2 -0.75,-0.3 -1.19,-0.26C10.76,7.11 10,8.04 10,9.09L10,15c0,1.1 0.9,2 2,2h5v5h2v-5.5c0,-1.1 -0.9,-2 -2,-2h-3v-3.45c1.29,1.07 3.25,1.94 5,1.95zM12.83,18c-0.41,1.16 -1.52,2 -2.83,2 -1.66,0 -3,-1.34 -3,-3 0,-1.31 0.84,-2.41 2,-2.83L9,12.1c-2.28,0.46 -4,2.48 -4,4.9 0,2.76 2.24,5 5,5 2.42,0 4.44,-1.72 4.9,-4h-2.07z"/>
+</vector>




diff --git a/app/src/main/res/layout/departure_bottom_sheet.xml b/app/src/main/res/layout/departure_bottom_sheet.xml
new file mode 100644
index 0000000000000000000000000000000000000000..02439c5cdd0599326b322c811f0e336cfa2adbec
--- /dev/null
+++ b/app/src/main/res/layout/departure_bottom_sheet.xml
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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">
+
+	<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="48dp"
+		android:textAppearance="@style/TextAppearance.Material3.DisplaySmall"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="parent"
+		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 mins)" />
+
+	<!-- todo dark theme -->
+	<ImageView
+		android:id="@+id/rt_icon"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:layout_marginStart="16dp"
+		android:contentDescription="departure is realtime"
+		app:layout_constraintBottom_toBottomOf="@+id/time"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="@+id/time"
+		app:srcCompat="@drawable/ic_radar_black_24dp" />
+
+	<!-- todo dark theme -->
+	<ImageView
+		android:id="@+id/wheelchair_icon"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:layout_marginStart="8dp"
+		android:contentDescription="vehicle is wheelchair accessible"
+		app:layout_constraintStart_toEndOf="@id/rt_icon"
+		app:layout_constraintTop_toTopOf="@+id/rt_icon"
+		app:srcCompat="@drawable/ic_wheelchair_black_24dp" />
+
+	<!-- todo center text -->
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/line"
+		android:layout_width="wrap_content"
+		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_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@id/time"
+		tool:text="Metropolitan » Tower Hill" />
+
+	<Button
+		android:id="@+id/map_button"
+		style="@style/Widget.Material3.Button.TextButton.Icon"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_margin="8dp"
+		android:text="Show on map"
+		app:icon="@drawable/ic_map_black_24dp"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@+id/line" />
+
+	<!--suppress AndroidUnknownAttribute -->
+	<ImageView
+		android:id="@+id/boarding_icon"
+		android:layout_width="16dp"
+		android:layout_height="16dp"
+		android:layout_marginEnd="8dp"
+		android:importantForAccessibility="no"
+		app:layout_constraintBottom_toBottomOf="@+id/boarding_text"
+		app:layout_constraintEnd_toStartOf="@+id/boarding_text"
+		app:layout_constraintTop_toTopOf="@+id/boarding_text"
+		app:srcCompat="@drawable/ic_transfer_black_24dp" />
+
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/boarding_text"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginStart="8dp"
+		android:layout_marginTop="8dp"
+		android:layout_marginEnd="8dp"
+		android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+		app:layout_constraintEnd_toEndOf="@id/middle"
+		app:layout_constraintTop_toBottomOf="@id/map_button"
+		tool:text="on demand" />
+
+	<!--suppress AndroidUnknownAttribute -->
+	<ImageView
+		android:id="@+id/speed_icon"
+		android:layout_width="16dp"
+		android:layout_height="16dp"
+		android:layout_marginEnd="8dp"
+		android:importantForAccessibility="no"
+		app:layout_constraintBottom_toBottomOf="@+id/speed_text"
+		app:layout_constraintEnd_toStartOf="@+id/speed_text"
+		app:layout_constraintTop_toTopOf="@+id/speed_text"
+		app:srcCompat="@drawable/ic_speed_black_24dp" />
+
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/speed_text"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginTop="8dp"
+		android:layout_marginEnd="8dp"
+		android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+		app:layout_constraintEnd_toStartOf="@+id/middle"
+		app:layout_constraintTop_toBottomOf="@id/boarding_text"
+		tool:text="10 Vl" />
+
+	<!--suppress AndroidUnknownAttribute -->
+	<ImageView
+		android:id="@+id/congestion_icon"
+		android:layout_width="16dp"
+		android:layout_height="16dp"
+		android:layout_marginStart="8dp"
+		android:layout_marginEnd="8dp"
+		android:importantForAccessibility="no"
+		app:layout_constraintBottom_toBottomOf="@+id/congestion_text"
+		app:layout_constraintStart_toStartOf="@+id/middle"
+		app:layout_constraintTop_toTopOf="@+id/congestion_text"
+		app:srcCompat="@drawable/ic_traffic_black_24dp" />
+
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/congestion_text"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginStart="8dp"
+		android:layout_marginTop="8dp"
+		android:layout_marginEnd="8dp"
+		android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+		app:layout_constraintStart_toEndOf="@id/congestion_icon"
+		app:layout_constraintTop_toBottomOf="@id/map_button"
+		tool:text="smooth traffic" />
+
+	<!--suppress AndroidUnknownAttribute -->
+	<ImageView
+		android:id="@+id/occupancy_icon"
+		android:layout_width="16dp"
+		android:layout_height="16dp"
+		android:layout_marginStart="8dp"
+		android:layout_marginEnd="8dp"
+		android:importantForAccessibility="no"
+		app:layout_constraintBottom_toBottomOf="@+id/occupancy_text"
+		app:layout_constraintStart_toStartOf="@+id/middle"
+		app:layout_constraintTop_toTopOf="@+id/occupancy_text"
+		app:srcCompat="@drawable/ic_crowd_black_24dp" />
+
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/occupancy_text"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginStart="8dp"
+		android:layout_marginTop="8dp"
+		android:layout_marginEnd="8dp"
+		android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+		app:layout_constraintStart_toEndOf="@id/occupancy_icon"
+		app:layout_constraintTop_toBottomOf="@id/congestion_text"
+		tool:text="empty vehicle" />
+
+	<androidx.constraintlayout.widget.Guideline
+		android:id="@+id/middle"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:orientation="vertical"
+		app:layout_constraintGuide_percent=".5" />
+
+	<androidx.constraintlayout.helper.widget.Flow
+		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/occupancy_text" />
+
+	<ImageView
+		android:id="@+id/ac"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:contentDescription="air condition"
+		app:srcCompat="@drawable/ic_ac_black_24dp"
+		tool:ignore="MissingConstraints" />
+
+	<ImageView
+		android:id="@+id/bike"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:contentDescription="bicycles allowed"
+		app:srcCompat="@drawable/ic_bike_black_24dp"
+		tool:ignore="MissingConstraints" />
+
+	<ImageView
+		android:id="@+id/voice"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:contentDescription="voice announcements"
+		app:srcCompat="@drawable/ic_voice_black_24dp"
+		tool:ignore="MissingConstraints" />
+
+	<ImageView
+		android:id="@+id/ticket"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:contentDescription="tickets sold"
+		app:srcCompat="@drawable/ic_ticket_black_24dp"
+		tool:ignore="MissingConstraints" />
+
+	<ImageView
+		android:id="@+id/usb"
+		android:layout_width="24dp"
+		android:layout_height="24dp"
+		android:contentDescription="USB charging"
+		app:srcCompat="@drawable/ic_usb_black_24dp"
+		tool:ignore="MissingConstraints" />
+</androidx.constraintlayout.widget.ConstraintLayout>