Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add brewery quest #475

Merged
merged 17 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions app/src/main/assets/brewery/brewerySuggestions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
AB InBev
mcliquid marked this conversation as resolved.
Show resolved Hide resolved
Aktienbrauerei Kaufbeuren
Alhambra
Allgäuer Brauhaus
Alpirsbacher Klosterbräu
Amstel
Asahi
Astra
Augustiner Bräu München
Ayinger
Badger
Beck's
Beerlao
Bellheimer
Berg
Berliner Kindl
Bernard
Birra Moretti
Bitburger
Bosch
Brauhaus Faust
BrewDog
Budweiser
Calanda Bräu
Carlsberg
Castel
Castle Rock
Chang
Charles Wells
Corona
Dinkelacker-Schwaben Bräu
Eichbaum
Engelbräu
Erdinger
Erzquell
Farny
Feldschlösschen
Finsterwalder
Forst
Fuller's
Fürstenberg
Gambrinus
Ganter
Gold Ochsen
Greene King
Guinness
Gösser
Haacht
Hacker-Pschorr
Harvey's
Hatz
Heineken
Herrnbräu
Hirsch
Hoegaarden
Hoepfner
Hofbräu München
Häffner-Bräu
Härle
Jever
Jupiler
Kaiser
Karlsberg
Kirin
Klosterbrauerei Reutberg
Krombacher
Krušovice
Kulmbacher
König Ludwig
König Pilsner
Köstrizer
Kühbacher
Leibinger
Licher
Löwenbräu
Marton's
Meckatzer
Miller
Molson Coors
Ninkasi
Oettinger
Paulaner
Pelforth
Pilsner Urquell
Puntigamer
Radeberger
Radegast
Riegele
Robinsons
Rothaus
Ruppaner
Samuel Smith
San Miguel
Sapporo
Schloss Eggenberg
Schlossbrauerei Haimhausen
Schlösser
Schützengarten
Shepherd Neame
Spalter Bier
Spandauer
Spaten
St Austell Brewery
Starobrno
Staropramen
Stella Artois
Stiegl
Stuttgarter Hofbräu
Suntory
Suwa
Svijany
Tegernseer
Thurn und Taxis
Thwaites
Tucher
Unterbaarer
Ur-Krostitzer
Veltins
Waldhaus
Warsteiner
Weldebräu
Wildbräu
Zwiefalter
Zötler
leffe
mcliquid marked this conversation as resolved.
Show resolved Hide resolved
Сыктывкарпиво
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package de.westnordost.streetcomplete.quests

import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import androidx.core.view.doOnLayout
import androidx.core.widget.doAfterTextChanged
import androidx.preference.PreferenceManager
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.QuestMultiValueBinding
import de.westnordost.streetcomplete.quests.healthcare_speciality.AddHealthcareSpecialityForm
import de.westnordost.streetcomplete.util.LastPickedValuesStore
import de.westnordost.streetcomplete.util.ktx.dpToPx
import de.westnordost.streetcomplete.util.ktx.viewLifecycleScope
import de.westnordost.streetcomplete.util.mostCommonWithin
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/** form for adding multiple values to a single key */
abstract class AMultiValueQuestForm<T> : AbstractOsmQuestForm<T>() {

override val contentLayoutResId = R.layout.quest_multi_value
private val binding by contentViewBinding(QuestMultiValueBinding::bind)

/** convert the multi-value string answer to type T */
abstract fun stringToAnswer(answerString: String): T

/**
* provide suggestions, loaded once and stored in companion object
* shown below all other suggestions
*/
abstract fun getConstantSuggestions(): Collection<String>

/**
* provide suggestions, loaded every time the form is opened
* shown above all other suggestions
*/
open fun getVariableSuggestions(): Collection<String> = emptyList()

/** text for the addValueButton */
abstract val addAnotherValueResId: Int

open val onlyAllowSuggestions = false

private val values = mutableSetOf<String>()

private val value get() = binding.valueInput.text?.toString().orEmpty().trim()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.addValueButton.setText(addAnotherValueResId)
if (suggestions.isEmpty()) { // load suggestions if necessary
getConstantSuggestions().forEach {
if (it.isNotBlank())
suggestions.add(it.trim().intern())
}
}

binding.valueInput.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
(getVariableSuggestions() + lastPickedAnswers + suggestions).distinct()
)
)
binding.valueInput.onItemClickListener = AdapterView.OnItemClickListener { _, t, _, _ ->
val value = (t as? TextView)?.text?.toString() ?: return@OnItemClickListener
if (!values.add(value)) return@OnItemClickListener // we don't want duplicates
onAddedValue(value)
}

binding.valueInput.doAfterTextChanged { checkIsFormComplete() }
binding.valueInput.doOnLayout { binding.valueInput.dropDownWidth = binding.valueInput.width - requireContext().dpToPx(60).toInt() }

binding.addValueButton.setOnClickListener {
if (!isFormComplete() || binding.valueInput.text.isBlank()) return@setOnClickListener
values.add(value)
onAddedValue(value)
}
showSuggestions()
}

override fun onClickOk() {
values.removeAll { it.isBlank() }
if (values.isNotEmpty()) favs.add(values)
if (value.isNotBlank()) favs.add(value)
if (value.isBlank())
applyAnswer(stringToAnswer(values.joinToString(";")))
else
applyAnswer(stringToAnswer((values + listOf(value)).joinToString(";")))
}

override fun isFormComplete() = (value.isNotBlank() || values.isNotEmpty()) && !value.contains(";")
&& !values.contains(value)
&& (!onlyAllowSuggestions || values.all { suggestions.contains(it) })

override fun onAttach(ctx: Context) {
super.onAttach(ctx)
favs = LastPickedValuesStore(
PreferenceManager.getDefaultSharedPreferences(ctx.applicationContext),
key = javaClass.simpleName,
serialize = { it },
deserialize = { it },
)
}

private fun onAddedValue(value: String) {
binding.currentValues.text = values.joinToString(";")
binding.valueInput.text.clear()
(binding.valueInput.adapter as ArrayAdapter<String>).remove(value)
showSuggestions()
}

private lateinit var favs: LastPickedValuesStore<String>

private val lastPickedAnswers by lazy {
favs.get()
.mostCommonWithin(target = 20, historyCount = 50, first = 1)
.toList()
}

private fun showSuggestions() {
viewLifecycleScope.launch {
delay(30) // delay, because otherwise it sometimes doesn't work properly
binding.valueInput.showDropDown()
}
}

companion object {
private val suggestions = mutableListOf<String>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import de.westnordost.streetcomplete.quests.bike_shop.AddSecondHandBicycleAvaila
import de.westnordost.streetcomplete.quests.board_type.AddBoardType
import de.westnordost.streetcomplete.quests.bollard_type.AddBollardType
import de.westnordost.streetcomplete.quests.bridge_structure.AddBridgeStructure
import de.westnordost.streetcomplete.quests.brewery.AddBrewery
import de.westnordost.streetcomplete.quests.building_colour.AddBuildingColour
import de.westnordost.streetcomplete.quests.building_entrance.AddEntrance
import de.westnordost.streetcomplete.quests.building_entrance_reference.AddEntranceReference
Expand Down Expand Up @@ -579,6 +580,7 @@ fun getQuestTypeList(
EE_QUEST_OFFSET + 1 to AddContactPhone(),
EE_QUEST_OFFSET + 2 to AddContactWebsite(),
EE_QUEST_OFFSET + 4 to AddCuisine(),
EE_QUEST_OFFSET + 32 to AddBrewery(),
EE_QUEST_OFFSET + 5 to AddHealthcareSpeciality(),
EE_QUEST_OFFSET + 6 to AddServiceBuildingType(),
EE_QUEST_OFFSET + 7 to AddServiceBuildingOperator(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package de.westnordost.streetcomplete.quests.brewery

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataWithGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.filter
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.osm.IS_SHOP_OR_DISUSED_SHOP_EXPRESSION
import de.westnordost.streetcomplete.osm.Tags

class AddBrewery : OsmFilterQuestType<BreweryAnswer>() {

override val elementFilter = """
nodes, ways with
amenity ~ bar|biergarten|pub|restaurant|nightclub
and (drink:beer != no)
and (
(brewery ~ yes|no)
or !brewery
)
"""
override val changesetComment = "Add brewery"
override val wikiLink = "Key:brewery"
override val icon = R.drawable.ic_quest_brewery
override val isReplaceShopEnabled = true
override val defaultDisabledMessage = R.string.default_disabled_msg_go_inside

override fun getTitle(tags: Map<String, String>) = R.string.quest_brewery_title

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().filter(IS_SHOP_OR_DISUSED_SHOP_EXPRESSION)

override fun createForm() = AddBreweryForm()

override fun applyAnswerTo(answer: BreweryAnswer, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
when (answer) {
is NoBeerAnswer -> tags["drink:beer"] = "no"
is ManyBeerAnswer -> tags["brewery"] = "various"
is BreweryStringAnswer -> tags["brewery"] = answer.brewery
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package de.westnordost.streetcomplete.quests.brewery

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.osm.edits.MapDataWithEditsSource
import de.westnordost.streetcomplete.data.osm.mapdata.filter
import de.westnordost.streetcomplete.quests.AMultiValueQuestForm
import de.westnordost.streetcomplete.quests.AnswerItem
import de.westnordost.streetcomplete.util.math.enlargedBy
import org.koin.android.ext.android.inject

class AddBreweryForm : AMultiValueQuestForm<BreweryAnswer>() {

private val mapDataSource: MapDataWithEditsSource by inject()

override fun stringToAnswer(answerString: String) = BreweryStringAnswer(answerString)

override fun getConstantSuggestions() =
requireContext().assets.open("brewery/brewerySuggestions.txt").bufferedReader().readLines()

override val addAnotherValueResId = R.string.quest_brewery_add_more

override fun getVariableSuggestions(): Collection<String> {
val data = mapDataSource.getMapDataWithGeometry(geometry.getBounds().enlargedBy(100.0))
val suggestions = hashSetOf<String>()
data.filter("nodes, ways with brewery").forEach {
it.tags["brewery"]?.let { suggestions.addAll(it.split(";")) }
}
suggestions.remove("yes")
suggestions.remove("various")
suggestions.remove("no")
return suggestions
}

override val otherAnswers = listOf(
AnswerItem(R.string.quest_brewery_is_not_available) { applyAnswer(NoBeerAnswer) },
AnswerItem(R.string.quest_brewery_is_various) { applyAnswer(ManyBeerAnswer) }
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.westnordost.streetcomplete.quests.brewery

sealed interface BreweryAnswer

data class BreweryStringAnswer(val brewery: String) : BreweryAnswer
object ManyBeerAnswer : BreweryAnswer
object NoBeerAnswer : BreweryAnswer
Loading