Bimba.git

commit 96df8d0dcc5dfaaa00891e591b56ea73f9e7b4cc

Author: Adam <git@apiote.xyz>

search stops for journeys

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


diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1c63870bda8310cca111bf5cfc06ad880e1df2e9..a9704629f4fa1df98130fdae2194e41f2cdfe710 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -101,8 +101,8 @@ 	implementation("com.google.android.material:material:1.12.0")
 	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.navigation:navigation-fragment-ktx:2.8.5")
+	implementation("androidx.navigation:navigation-ui-ktx:2.8.5")
 	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")
@@ -111,21 +111,21 @@ 	implementation("org.yaml:snakeyaml:2.3")
 	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("org.jetbrains.kotlinx:kotlinx-serialization-core:1.8.0")
 	implementation("com.github.jershell:kbson:0.5.0")
 	implementation("androidx.preference:preference-ktx:1.2.1")
 	implementation("androidx.work:work-runtime-ktx:2.10.0")
 	implementation("com.github.doyaaaaaken:kotlin-csv-jvm:1.10.0")
 	implementation("commons-io:commons-io:2.18.0")
-	implementation("com.google.guava:guava:33.3.1-android")
+	implementation("com.google.guava:guava:33.4.0-android")
 	implementation(project(":fruchtfleisch"))
 	implementation("ch.acra:acra-http:5.12.0")
 	implementation("ch.acra:acra-notification:5.12.0")
 	implementation("com.squareup.okhttp3:okhttp:4.12.0")
-	implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
+	implementation("com.squareup.moshi:moshi-kotlin:1.15.2")
 	implementation("androidx.activity:activity:1.9.3")
 
-	coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.3")
+	coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
 
 	androidTestImplementation("androidx.test.ext:junit:1.2.1")
 	androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt
index 819fae616a88170d90479a661ab858448e44f5e8..b06ba0824486a44e7fa53987d5a7a55a6bc0b50d 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt
@@ -7,22 +7,39 @@
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import com.google.android.material.textfield.TextInputEditText
 import xyz.apiote.bimba.czwek.repo.Place
 
 class DashboardViewModel : ViewModel() {
+	companion object {
+		const val ORIGIN_KEY = "ORIGIN"
+		const val DEST_KEY = "DESTINATION"
 
-	private val _origin = MutableLiveData<Place>()
-	val origin: LiveData<Place> = _origin
+		val keys = arrayOf(ORIGIN_KEY, DEST_KEY)
+		val indices = mapOf(ORIGIN_KEY to 0, DEST_KEY to 1)
 
-	fun setOrigin(o: Place) {
-		_origin.value = o
+		fun otherSource(source: String): String = keys[(indices[source]!! + 1) % 2]
 	}
 
-	private val _destination = MutableLiveData<Place>()
-	val destination: LiveData<Place> = _destination
+	val mutableData = mapOf(
+		ORIGIN_KEY to MutableLiveData<Place>(),
+		DEST_KEY to MutableLiveData<Place>()
+	)
+
+	val data = mapOf<String, LiveData<Place>>(
+		ORIGIN_KEY to mutableData[ORIGIN_KEY]!!,
+		DEST_KEY to mutableData[DEST_KEY]!!
+	)
 
-	fun setDestination(d: Place) {
-		_destination.value = d
+	fun set(source: String, place: Place) {
+		mutableData[source]!!.value = place
 	}
+
+	val spans = mutableMapOf(
+		ORIGIN_KEY to "",
+		DEST_KEY to ""
+	)
+
+	val textInputs = mutableMapOf<String, TextInputEditText>()
 
 }




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt
index b9a79811cceff3ef625fb7b1554a794a9c15e77a..1f7d529564f043535e3c99c29992875981811f89 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt
@@ -7,40 +7,64 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.RectF
+import android.graphics.drawable.LayerDrawable
 import android.location.Location
 import android.location.LocationListener
 import android.location.LocationManager
+import android.os.Build
 import android.os.Bundle
+import android.text.Editable
 import android.text.Spanned
+import android.text.TextWatcher
 import android.text.style.ImageSpan
 import android.util.Log
+import android.view.KeyEvent
 import android.view.LayoutInflater
 import android.view.MotionEvent.ACTION_UP
 import android.view.View
 import android.view.ViewGroup
+import androidx.activity.result.contract.ActivityResultContracts
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updateLayoutParams
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
-import com.google.android.material.chip.Chip
 import com.google.android.material.chip.ChipDrawable
 import com.google.android.material.textfield.TextInputEditText
 import xyz.apiote.bimba.czwek.R
+import xyz.apiote.bimba.czwek.dashboard.DashboardViewModel
 import xyz.apiote.bimba.czwek.dashboard.MainActivity
 import xyz.apiote.bimba.czwek.databinding.FragmentJourneyBinding
 import xyz.apiote.bimba.czwek.dpToPixelI
 import xyz.apiote.bimba.czwek.journeys.JourneysActivity
 import xyz.apiote.bimba.czwek.repo.Place
+import xyz.apiote.bimba.czwek.search.ResultsActivity
 
 class JourneyFragment : Fragment(), LocationListener {
+	companion object {
+		const val PLACE_KEY = "PLACE"
+	}
 
 	private var _binding: FragmentJourneyBinding? = null
 	private val binding get() = _binding!!
 
 	private lateinit var dashboard: MainActivity
-	private var hereChipRequester: Chip? = null
+	private var hereChipRequester: String? = null
+	private var searchRequester: String? = null
+
+	private val activityLauncher =
+		registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+			val place = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
+				it.data?.extras?.getParcelable(PLACE_KEY, Place::class.java)
+			} else {
+				@Suppress("DEPRECATION")
+				it.data?.extras?.getParcelable(PLACE_KEY)
+			}
+			place?.let {
+				dashboard.viewModel.set(searchRequester!!, it)
+			}
+		}
 
 	override fun onCreateView(
 		inflater: LayoutInflater,
@@ -54,6 +78,9 @@ 		// TODO separate layout: two columns for horizontal
 
 		_binding = FragmentJourneyBinding.inflate(inflater, container, false)
 		val root: View = binding.root
+
+		dashboard.viewModel.textInputs[DashboardViewModel.ORIGIN_KEY] = binding.origin
+		dashboard.viewModel.textInputs[DashboardViewModel.DEST_KEY] = binding.destination
 
 		ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
 			val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
@@ -100,67 +127,161 @@
 		dashboard = activity as MainActivity
 
 		dashboard.hideBadge()
-		dashboard.viewModel.origin.let {
-			chipifyOrigin(it.value)
-		}
-
-		dashboard.viewModel.destination.let {
-			chipifyDestination(it.value)
-		}
+		chipifyOrigin(dashboard.viewModel.data[DashboardViewModel.ORIGIN_KEY]!!.value)
+		chipifyDestination(dashboard.viewModel.data[DashboardViewModel.DEST_KEY]!!.value)
 
 		binding.originChipHere.setOnClickListener {
-			setHere(it as Chip)
+			setHere(DashboardViewModel.ORIGIN_KEY)
 		}
 
 		binding.destinationChipHere.setOnClickListener {
-			setHere(it as Chip)
+			setHere(DashboardViewModel.DEST_KEY)
 		}
 
 		binding.goButton.isEnabled =
-			dashboard.viewModel.origin.value != null && dashboard.viewModel.destination.value != null
+			dashboard.viewModel.data[DashboardViewModel.ORIGIN_KEY]!!.value != null && dashboard.viewModel.data[DashboardViewModel.DEST_KEY]!!.value != null
 
 		binding.goButton.setOnClickListener {
 			val intent = JourneysActivity.getIntent(
 				requireContext(),
-				dashboard.viewModel.origin.value!!,
-				dashboard.viewModel.destination.value!!
+				dashboard.viewModel.data[DashboardViewModel.ORIGIN_KEY]!!.value!!,
+				dashboard.viewModel.data[DashboardViewModel.DEST_KEY]!!.value!!,
 			)
 			startActivity(intent)
 		}
 
-		dashboard.viewModel.origin.observe(viewLifecycleOwner) {
+		dashboard.viewModel.data[DashboardViewModel.ORIGIN_KEY]!!.observe(viewLifecycleOwner) {
 			chipifyOrigin(it)
 		}
 
-		dashboard.viewModel.destination.observe(viewLifecycleOwner) {
+		dashboard.viewModel.data[DashboardViewModel.DEST_KEY]!!.observe(viewLifecycleOwner) {
 			chipifyDestination(it)
 		}
 
-		// TODO listen to text changes
-		// TODO search by text input
+		binding.origin.setOnKeyListener { v, keyCode, event ->
+			when (keyCode) {
+				KeyEvent.KEYCODE_ENTER -> {
+					if (event.action == KeyEvent.ACTION_UP) {
+						searchText(DashboardViewModel.ORIGIN_KEY)
+						true
+					} else {
+						false
+					}
+				}
+
+				else -> false
+			}
+		}
+
+		binding.destination.setOnKeyListener { v, keyCode, event ->
+			when (keyCode) {
+				KeyEvent.KEYCODE_ENTER -> {
+					if (event.action == KeyEvent.ACTION_UP) {
+						searchText(DashboardViewModel.DEST_KEY)
+						true
+					} else {
+						false
+					}
+				}
+
+				else -> false
+			}
+		}
+
+		binding.origin.addTextChangedListener(object : TextWatcher {
+			override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+			override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
+
+			override fun afterTextChanged(s: Editable?) {
+				if (s.isNullOrBlank()) {
+					dashboard.viewModel.spans[DashboardViewModel.ORIGIN_KEY] = ""
+					binding.goButton.isEnabled = false
+					return
+				}
+				binding.goButton.isEnabled =
+					s.toString().replace(dashboard.viewModel.spans[DashboardViewModel.ORIGIN_KEY]!!, "") == "" && isDestinationClean()
+
+				// todo if searchText looks like coordinates or a plus code (short, long, compound) -> suggest it
+			}
+		})
+
+		binding.destination.addTextChangedListener(object : TextWatcher {
+			override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+			override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
+
+			override fun afterTextChanged(s: Editable?) {
+				if (s.isNullOrBlank()) {
+					dashboard.viewModel.spans[DashboardViewModel.DEST_KEY] = ""
+					binding.goButton.isEnabled = false
+					return
+				}
+				binding.goButton.isEnabled =
+					s.toString().replace(dashboard.viewModel.spans[DashboardViewModel.DEST_KEY]!!, "") == "" && isOriginClean()
+
+				// todo if searchText looks like coordinates or a plus code (short, long, compound) -> suggest it
+			}
+		})
 
 		return root
 	}
 
+	fun searchText(source: String) {
+		// TODO or coordinates intent
+		searchRequester = source
+		activityLauncher.launch(
+			ResultsActivity.getIntent(
+				requireContext(),
+				ResultsActivity.Mode.MODE_SEARCH,
+				dashboard.viewModel.textInputs[source]!!.text.toString().replace(dashboard.viewModel.spans[source]!!, ""),
+				true
+			)
+		)
+	}
+
+	private fun isOriginClean(): Boolean = binding.origin.text.toString().let {
+		it.replace(dashboard.viewModel.spans[DashboardViewModel.ORIGIN_KEY]!!, "") == "" && it != ""
+	}
+
+	private fun isDestinationClean(): Boolean = binding.destination.text.toString().let {
+		it.replace(dashboard.viewModel.spans[DashboardViewModel.DEST_KEY]!!, "") == "" && it != ""
+	}
+
 	private fun chipifyOrigin(place: Place?) {
+		val source = DashboardViewModel.ORIGIN_KEY
+		val otherSource = DashboardViewModel.otherSource(source)
 		if (place != null) {
-			chipify(place, binding.origin)
+			chipify(place, dashboard.viewModel.textInputs[source]!!)
+			dashboard.viewModel.spans[source] = place.shortString()
+			if (dashboard.viewModel.data[otherSource]!!.value != null && isDestinationClean()) {
+				binding.goButton.isEnabled = true
+			}
 		}
 	}
 
 	private fun chipifyDestination(place: Place?) {
+		val source = DashboardViewModel.DEST_KEY
+		val otherSource = DashboardViewModel.otherSource(source)
 		if (place != null) {
-			chipify(place, binding.destination)
+			chipify(place, dashboard.viewModel.textInputs[source]!!)
+			dashboard.viewModel.spans[source] = place.shortString()
+			if (dashboard.viewModel.data[otherSource]!!.value != null && isOriginClean()) {
+				binding.goButton.isEnabled = true
+			}
 		}
 	}
 
 	private fun chipify(place: Place, textView: TextInputEditText) {
-		var chip: ChipDrawable? = ChipDrawable.createFromResource(requireContext(), R.xml.journey_chip)
 		val text = place.shortString()
 		textView.setText(text)
+		var chip: ChipDrawable? = ChipDrawable.createFromResource(requireContext(), R.xml.journey_chip)
 		chip!!.text = text
-		chip.setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight)
-		val span = ImageSpan(chip)
+		val ld = LayerDrawable(arrayOf(chip)).apply {
+			setLayerInset(0, dpToPixelI(4f), 0, dpToPixelI(4f), 0)
+			setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight)
+		}
+		val span = ImageSpan(ld)
 		textView.text?.setSpan(span, 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
 		@SuppressLint("ClickableViewAccessibility")
 		textView.setOnTouchListener { v, e ->
@@ -178,7 +299,7 @@ 						// TODO popup
 						Log.i("Touch", "content")
 					}
 					true
-				} else if (e.x > textView.totalPaddingLeft + chipContentRect.right && e.x < textView.totalPaddingLeft + chipContentRect.right + chipCloseRect.right
+				} else if (e.x > textView.totalPaddingLeft + chipContentRect.right && e.x < textView.totalPaddingLeft + chipCloseRect.right
 					&& e.y > textView.totalPaddingTop && e.y < textView.totalPaddingTop + chipCloseRect.bottom
 				) {
 					if (e.action == ACTION_UP) {
@@ -195,8 +316,8 @@ 			}
 		}
 	}
 
-	private fun setHere(v: Chip) {
-		hereChipRequester = v
+	private fun setHere(source: String) {
+		hereChipRequester = source
 		if (dashboard.onGpsClicked(this)) {
 			try {
 				val locationManager =
@@ -221,13 +342,7 @@ 		_binding = null
 	}
 
 	override fun onLocationChanged(location: Location) {
-		when (hereChipRequester?.id) {
-			R.id.origin_chip_here ->
-				dashboard.viewModel.setOrigin(Place(location.latitude, location.longitude))
-
-			R.id.destination_chip_here ->
-				dashboard.viewModel.setDestination(Place(location.latitude, location.longitude))
-		}
+		hereChipRequester?.let { dashboard.viewModel.set(it, Place(location.latitude, location.longitude)) }
 		hereChipRequester = null
 	}
 }
\ No newline at end of file




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 cd4df83f11c6a696a01e19ddba00fffaedadc2da..344edca75f0285430066d9d67abb57344df07dc5 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
@@ -27,6 +27,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
 import kotlinx.coroutines.launch
 import org.osmdroid.views.MapView
 import xyz.apiote.bimba.czwek.R
+import xyz.apiote.bimba.czwek.dashboard.DashboardViewModel
 import xyz.apiote.bimba.czwek.dashboard.MainActivity
 import xyz.apiote.bimba.czwek.departures.DeparturesActivity
 import xyz.apiote.bimba.czwek.repo.CongestionLevel
@@ -75,12 +76,12 @@ 	): View {
 		val view = inflater.inflate(R.layout.place_bottom_sheet, container, false)
 		view.findViewById<TextView>(R.id.coordinates).text = "${place.latitude}, ${place.longitude}"
 		view.findViewById<Button>(R.id.use_as_origin).setOnClickListener {
-			(activity as MainActivity).viewModel.setOrigin(place)
+			(activity as MainActivity).viewModel.set(DashboardViewModel.ORIGIN_KEY, place)
 			positionUsed = true
 			dismiss()
 		}
 		view.findViewById<Button>(R.id.use_as_destination).setOnClickListener {
-			(activity as MainActivity).viewModel.setDestination(place)
+			(activity as MainActivity).viewModel.set(DashboardViewModel.DEST_KEY, place)
 			positionUsed = true
 			dismiss()
 		}
@@ -228,13 +229,13 @@ 				}
 			}
 
 			content.findViewById<Button>(R.id.use_as_origin).setOnClickListener {
-				(activity as MainActivity).viewModel.setOrigin(Place(stop, stop.location().latitude, stop.location().longitude))
+				(activity as MainActivity).viewModel.set(DashboardViewModel.ORIGIN_KEY, Place(stop, stop.location().latitude, stop.location().longitude))
 				(ctx as MainActivity).showBadge()
 				dismiss()
 			}
 
 			content.findViewById<Button>(R.id.use_as_destination).setOnClickListener {
-				(activity as MainActivity).viewModel.setDestination(Place(stop, stop.location().latitude, stop.location().longitude))
+				(activity as MainActivity).viewModel.set(DashboardViewModel.DEST_KEY, Place(stop, stop.location().latitude, stop.location().longitude))
 				(ctx as MainActivity).showBadge()
 				dismiss()
 			}




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt
index dd03572d986e0f0f4a47dcb6da195fa45bb46ee0..fe259f7d7cbf3b9822d9bc4dbb3f5575e48f9d13 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt
@@ -43,6 +43,8 @@ 			"", "", null, Position(latitude, longitude), emptyList(), null
 		), latitude, longitude
 	)
 
+	constructor(stop: Stop) : this(stop, stop.position.latitude, stop.position.longitude)
+
 	fun planString(): String = if (stop.code == "") {
 		"${latitude},${longitude},0"
 	} else {




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/search/Results.kt b/app/src/main/java/xyz/apiote/bimba/czwek/search/Results.kt
index 2df5391da9da2b9bb1eddecfc5ba8bc2c4568ef9..36356cbe7e32da37a1451ea20fbfc507bc65292f 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/search/Results.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/search/Results.kt
@@ -215,7 +215,8 @@ 	private val context: Context?,
 	private var queryables: List<Queryable>,
 	private var position: Location?,
 	private var heading: Float?,
-	private var showArrow: Boolean
+	private var showArrow: Boolean,
+	private val returnResult: Boolean = false
 ) :
 	RecyclerView.Adapter<BimbaViewHolder>() {
 	class DiffUtilCallback(
@@ -291,15 +292,20 @@ 	var feedsSettings: FeedsSettings? = null
 	private val onClickListener: ((Queryable) -> Unit) = {
 		when (it) {
 			is Stop -> {
-				val intent = Intent(context, DeparturesActivity::class.java).apply {
-					putExtra("code", it.code)
-					putExtra("name", it.name)
-					putExtra("feedID", it.feedID)
+				if (returnResult) {
+					(context as ResultsActivity).returnResult(it)
+				} else {
+					val intent = Intent(context, DeparturesActivity::class.java).apply {
+						putExtra("code", it.code)
+						putExtra("name", it.name)
+						putExtra("feedID", it.feedID)
+					}
+					context!!.startActivity(intent)
 				}
-				context!!.startActivity(intent)
 			}
 
 			is Line -> {
+				// TODO if returnResult -> shouldn't show lines
 				val intent = Intent(context, LineGraphActivity::class.java).apply {
 					putExtra("lineName", it.name)
 					putExtra("lineID", it.id)




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/search/ResultsActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/search/ResultsActivity.kt
index 0c87cba970f5cce61d4d0d45aec18eef3015dfa7..e9200b17821fa4122cee1174b732f5d900ae75bb 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/search/ResultsActivity.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/search/ResultsActivity.kt
@@ -35,11 +35,14 @@ import kotlinx.coroutines.launch
 import org.openapitools.client.infrastructure.ServerException
 import xyz.apiote.bimba.czwek.R
 import xyz.apiote.bimba.czwek.api.Error
+import xyz.apiote.bimba.czwek.dashboard.MainActivity
 import xyz.apiote.bimba.czwek.databinding.ActivityResultsBinding
 import xyz.apiote.bimba.czwek.repo.OfflineRepository
 import xyz.apiote.bimba.czwek.repo.OnlineRepository
+import xyz.apiote.bimba.czwek.repo.Place
 import xyz.apiote.bimba.czwek.repo.Position
 import xyz.apiote.bimba.czwek.repo.Queryable
+import xyz.apiote.bimba.czwek.repo.Stop
 import xyz.apiote.bimba.czwek.repo.TrafficResponseException
 import xyz.apiote.bimba.czwek.settings.feeds.FeedsSettings
 
@@ -53,6 +56,7 @@ 		const val MODE_KEY = "mode"
 		const val QUERY_KEY = "query"
 		const val LATITUDE_KEY = "lat"
 		const val LONGITUDE_KEY = "lon"
+		const val RETURN_KEY = "ret"
 		fun getIntent(
 			context: Context,
 			mode: Mode,
@@ -73,6 +77,13 @@ 				putExtra(MODE_KEY, mode)
 				putExtra(QUERY_KEY, query)
 			}
 
+		fun getIntent(context: Context, mode: Mode, query: String, returnResult: Boolean) =
+			Intent(context, ResultsActivity::class.java).apply {
+				putExtra(MODE_KEY, mode)
+				putExtra(QUERY_KEY, query)
+				putExtra(RETURN_KEY, returnResult)
+			}
+
 	}
 
 	private var _binding: ActivityResultsBinding? = null
@@ -110,7 +121,8 @@ 			windowInsets
 		}
 
 		binding.resultsRecycler.layoutManager = LinearLayoutManager(this)
-		adapter = BimbaResultsAdapter(layoutInflater, this, listOf(), null, null, false)
+		adapter =
+			BimbaResultsAdapter(layoutInflater, this, listOf(), null, null, false, getReturnResults())
 		binding.resultsRecycler.adapter = adapter
 
 		when (getMode()) {
@@ -171,6 +183,8 @@ 			@Suppress("DEPRECATION")
 			intent.extras!!.get(MODE_KEY) as Mode
 		}
 	}
+
+	private fun getReturnResults(): Boolean = intent.extras?.getBoolean(RETURN_KEY) == true
 
 	private fun locate() {
 		val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
@@ -326,7 +340,7 @@ 					R.drawable.error_search
 				)
 			)
 		} else {
-			if (queryables.size == 1 && getMode() == Mode.MODE_SEARCH) {
+			if (queryables.size == 1 && getMode() == Mode.MODE_SEARCH && !getReturnResults()) {
 				adapter.click(0)
 			}
 			binding.resultsOverlay.visibility = View.GONE
@@ -334,5 +348,12 @@ 			binding.errorImage.visibility = View.GONE
 			binding.errorText.visibility = View.GONE
 			binding.resultsRecycler.visibility = View.VISIBLE
 		}
+	}
+
+	fun returnResult(stop: Stop) {
+		setResult(RESULT_OK, Intent(this, MainActivity::class.java).apply {
+			putExtra("PLACE", Place(stop))
+		})
+		finish()
 	}
 }




diff --git a/app/src/main/res/layout/fragment_journey.xml b/app/src/main/res/layout/fragment_journey.xml
index 6e5d145b67459d8320e9e653aefb401be76bd24f..649a53bbc3018e4aaf0ede4e62a3e1d7161495d6 100644
--- a/app/src/main/res/layout/fragment_journey.xml
+++ b/app/src/main/res/layout/fragment_journey.xml
@@ -30,30 +30,31 @@ 		 			android:id="@+id/origin"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:imeOptions="actionSearch" />
+			android:imeOptions="actionSearch"
+			android:inputType="text" />
 	</com.google.android.material.textfield.TextInputLayout>
 
 	<com.google.android.material.chip.ChipGroup
 		android:id="@+id/origin_suggestions"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
 		android:layout_marginLeft="16dp"
 		android:layout_marginTop="4dp"
 		android:layout_marginRight="16dp"
-		android:layout_width="match_parent"
 		app:layout_constraintEnd_toEndOf="parent"
 		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@id/origin_input"
-		android:layout_height="wrap_content">
+		app:layout_constraintTop_toBottomOf="@id/origin_input">
 
 		<com.google.android.material.chip.Chip
 			android:id="@+id/origin_chip_here"
 			style="@style/Widget.Material3.Chip.Suggestion"
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
+			android:checkable="false"
+			android:text="@string/here"
 			app:chipIcon="@drawable/gps_black"
 			app:chipIconTint="?attr/colorOnSurface"
-			app:chipIconVisible="true"
-			android:checkable="false"
-			android:text="@string/here" />
+			app:chipIconVisible="true" />
 
 	</com.google.android.material.chip.ChipGroup>
 
@@ -93,30 +94,31 @@ 		 			android:id="@+id/destination"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:imeOptions="actionSearch" />
+			android:imeOptions="actionSearch"
+			android:inputType="text" />
 	</com.google.android.material.textfield.TextInputLayout>
 
 	<com.google.android.material.chip.ChipGroup
 		android:id="@+id/destination_suggestions"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
 		android:layout_marginLeft="16dp"
 		android:layout_marginTop="4dp"
 		android:layout_marginRight="16dp"
-		android:layout_width="match_parent"
 		app:layout_constraintEnd_toEndOf="parent"
 		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@id/destination_input"
-		android:layout_height="wrap_content">
+		app:layout_constraintTop_toBottomOf="@id/destination_input">
 
 		<com.google.android.material.chip.Chip
 			android:id="@+id/destination_chip_here"
 			style="@style/Widget.Material3.Chip.Suggestion"
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
+			android:checkable="false"
+			android:text="@string/here"
 			app:chipIcon="@drawable/gps_black"
 			app:chipIconTint="?attr/colorOnSurface"
-			app:chipIconVisible="true"
-			android:checkable="false"
-			android:text="@string/here" />
+			app:chipIconVisible="true" />
 
 	</com.google.android.material.chip.ChipGroup>
 




diff --git a/fruchtfleisch/build.gradle.kts b/fruchtfleisch/build.gradle.kts
index 1cd1d02cb45b7590029e35f93a3e10b5468f1431..43fb515fc848c87a101c325f986d0cc4e2de095d 100644
--- a/fruchtfleisch/build.gradle.kts
+++ b/fruchtfleisch/build.gradle.kts
@@ -8,8 +8,8 @@     kotlin("jvm")
 }
 
 dependencies {
-    testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
-    testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
+    testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
+    testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
 
     //implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10")
 }