Skip to content

Commit

Permalink
Merge pull request #5612 from streetcomplete/info-button
Browse files Browse the repository at this point in the history
Info button and hints
  • Loading branch information
westnordost committed May 5, 2024
2 parents 5ac6201 + e69fc8e commit bd94440
Show file tree
Hide file tree
Showing 102 changed files with 385 additions and 620 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import de.westnordost.streetcomplete.quests.AbstractQuestForm
* Most QuestType inherit from [OsmElementQuestType][de.westnordost.streetcomplete.data.osm.osmquests.OsmElementQuestType] */
interface QuestType : EditType {

/** Hint text to be shown when the user taps on the ℹ️ button */
val hint: Int? get() = null

/** Hint pictures to be shown when the user taps on the ℹ️ button */
val hintImages: List<Int> get() = emptyList()

/** the string resource id that explains why this quest is disabled by default or zero if it is
* not disabled by default.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package de.westnordost.streetcomplete.quests

import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.annotation.AnyThread
import androidx.core.os.bundleOf
import androidx.core.view.isGone
Expand All @@ -30,7 +32,6 @@ import de.westnordost.streetcomplete.view.CharSequenceText
import de.westnordost.streetcomplete.view.ResText
import de.westnordost.streetcomplete.view.Text
import de.westnordost.streetcomplete.view.setText
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.koin.android.ext.android.inject
Expand Down Expand Up @@ -59,7 +60,6 @@ abstract class AbstractQuestForm :
override val bottomSheetTitle get() = binding.speechBubbleTitleContainer
override val bottomSheetContent get() = binding.speechbubbleContentContainer
override val floatingBottomView get() = binding.okButtonContainer
override val backButton get() = binding.closeButton
protected val scrollView: NestedScrollView get() = binding.scrollView

private var startedOnce = false
Expand Down Expand Up @@ -90,6 +90,8 @@ abstract class AbstractQuestForm :
private var initialMapRotation = 0f
private var initialMapTilt = 0f

private var infoIsExpanded: Boolean = false

// overridable by child classes
open val contentLayoutResId: Int? = null
open val contentPadding = true
Expand Down Expand Up @@ -117,6 +119,8 @@ abstract class AbstractQuestForm :

setTitle(resources.getString(questType.title))
setTitleHintLabel(null)
setHint(questType.hint?.let { resources.getString(it) })
setHintImages(questType.hintImages.mapNotNull { requireContext().getDrawable(it) })

binding.okButton.setOnClickListener {
if (!isFormComplete()) {
Expand All @@ -126,6 +130,10 @@ abstract class AbstractQuestForm :
}
}

infoIsExpanded = false
binding.infoButton.setOnClickListener { toggleInfoArea() }
binding.infoArea.setOnClickListener { toggleInfoArea() }

// no content? -> hide the content container
if (binding.content.childCount == 0) {
binding.content.visibility = View.GONE
Expand Down Expand Up @@ -155,6 +163,38 @@ abstract class AbstractQuestForm :
binding.titleHintLabel.text = text
}

protected fun setHint(text: CharSequence?) {
binding.infoText.isGone = text == null
binding.infoText.text = text
updateInfoButtonVisibility()
}

protected fun setHintImages(images: List<Drawable>) {
binding.infoPictures.isGone = images.isEmpty()
binding.infoPictures.removeAllViews()
for (image in images) {
val imageView = ImageView(requireContext())
imageView.setImageDrawable(image)
imageView.scaleType
binding.infoPictures.addView(imageView)
}
updateInfoButtonVisibility()
}

private fun toggleInfoArea() {
infoIsExpanded = !infoIsExpanded
binding.infoButton.setImageResource(
if (infoIsExpanded) R.drawable.ic_info_filled_24dp

Check failure on line 187 in app/src/main/java/de/westnordost/streetcomplete/quests/AbstractQuestForm.kt

View workflow job for this annotation

GitHub Actions / Kotlin

Expected a newline
else R.drawable.ic_info_outline_24dp

Check failure on line 188 in app/src/main/java/de/westnordost/streetcomplete/quests/AbstractQuestForm.kt

View workflow job for this annotation

GitHub Actions / Kotlin

Expected a newline
)
binding.infoButton.isActivated = infoIsExpanded
binding.infoArea.isGone = !infoIsExpanded
}

private fun updateInfoButtonVisibility() {
binding.infoButton.isGone = binding.infoText.isGone && binding.infoPictures.isGone
}

/** Inflate given layout resource id into the content view and return the inflated view */
protected fun setContentView(resourceId: Int): View {
if (binding.content.childCount > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class LeaveNoteInsteadFragment : AbstractCreateNoteFragment() {
override val bottomSheetTitle get() = binding.speechBubbleTitleContainer
override val bottomSheetContent get() = binding.speechbubbleContentContainer
override val floatingBottomView get() = binding.okButtonContainer
override val backButton get() = binding.closeButton
override val okButton get() = binding.okButton
override val okButtonContainer get() = binding.okButtonContainer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ class AddBikeParkingCapacity : OsmFilterQuestType<Int>() {
override val icon = R.drawable.ic_quest_bicycle_parking_capacity
override val isDeleteElementEnabled = true
override val achievements = listOf(BICYCLIST)
override val hint = R.string.quest_bikeParkingCapacity_hint

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().filter("nodes, ways with amenity = bicycle_parking")

override fun createForm() = AddBikeParkingCapacityForm.create(showClarificationText = true)
override fun createForm() = AddBikeParkingCapacityForm()

override fun applyAnswerTo(answer: Int, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags.updateWithCheckDate("capacity", answer.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package de.westnordost.streetcomplete.quests.bike_parking_capacity

import android.os.Bundle
import android.view.View
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.core.widget.doAfterTextChanged
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.QuestBikeParkingCapacityBinding
Expand All @@ -19,8 +17,6 @@ class AddBikeParkingCapacityForm : AbstractOsmQuestForm<Int>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val showClarificationText = arguments?.getBoolean(ARG_SHOW_CLARIFICATION) ?: false
binding.clarificationText.isGone = !showClarificationText
binding.capacityInput.doAfterTextChanged { checkIsFormComplete() }
}

Expand All @@ -29,14 +25,4 @@ class AddBikeParkingCapacityForm : AbstractOsmQuestForm<Int>() {
override fun onClickOk() {
applyAnswer(capacity)
}

companion object {
private const val ARG_SHOW_CLARIFICATION = "show_clarification"

fun create(showClarificationText: Boolean): AddBikeParkingCapacityForm {
val form = AddBikeParkingCapacityForm()
form.arguments = bundleOf(ARG_SHOW_CLARIFICATION to showClarificationText)
return form
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AddBikeRentalCapacity : OsmFilterQuestType<Int>() {
override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().filter("nodes, ways with amenity = bicycle_rental")

override fun createForm() = AddBikeParkingCapacityForm.create(showClarificationText = false)
override fun createForm() = AddBikeParkingCapacityForm()

override fun applyAnswerTo(answer: Int, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags.updateWithCheckDate("capacity", answer.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class AddBuildingLevels : OsmFilterQuestType<BuildingLevelsAnswer>() {
override val achievements = listOf(BUILDING)
override val defaultDisabledMessage = R.string.default_disabled_msg_difficult_and_time_consuming

override val hint = R.string.quest_buildingLevels_hint

override fun getTitle(tags: Map<String, String>) = when {
tags.containsKey("building:part") -> R.string.quest_buildingLevels_title_buildingPart2
else -> R.string.quest_buildingLevels_title2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class AddCycleway(
"US-AZ", "US-TX"
)

override val hint = R.string.quest_street_side_puzzle_tutorial

override fun getTitle(tags: Map<String, String>) = when {
parseCyclewaySides(tags, false) != null -> R.string.quest_cycleway_resurvey_title
else -> R.string.quest_cycleway_title2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package de.westnordost.streetcomplete.quests.diet_type

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.QuestDietTypeExplanationBinding
import de.westnordost.streetcomplete.quests.AbstractOsmQuestForm
import de.westnordost.streetcomplete.quests.AnswerItem
import de.westnordost.streetcomplete.quests.diet_type.DietAvailability.DIET_NO
Expand All @@ -23,35 +19,13 @@ class AddDietTypeForm : AbstractOsmQuestForm<DietAvailabilityAnswer>() {
}

override val contentLayoutResId = R.layout.quest_diet_type_explanation
private val binding by contentViewBinding(QuestDietTypeExplanationBinding::bind)

override val buttonPanelAnswers = listOf(
AnswerItem(R.string.quest_generic_hasFeature_no) { applyAnswer(DIET_NO) },
AnswerItem(R.string.quest_generic_hasFeature_yes) { applyAnswer(DIET_YES) },
AnswerItem(R.string.quest_hasFeature_only) { applyAnswer(DIET_ONLY) },
)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val resId = arguments?.getInt(ARG_DIET) ?: 0
if (resId > 0) {
binding.descriptionLabel.setText(resId)
} else {
binding.descriptionLabel.visibility = View.GONE
}
}

companion object {
private const val ARG_DIET = "diet_explanation"

fun create(dietExplanationResId: Int): AddDietTypeForm {
val form = AddDietTypeForm()
form.arguments = bundleOf(ARG_DIET to dietExplanationResId)
return form
}
}

private fun confirmNoFood() {
val ctx = context ?: return
AlertDialog.Builder(ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ class AddHalal : OsmFilterQuestType<DietAvailabilityAnswer>() {
override val achievements = listOf(CITIZEN)
override val defaultDisabledMessage = R.string.default_disabled_msg_go_inside_regional_warning

override val hint = R.string.quest_dietType_explanation_halal

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().asSequence().filter { it.isPlaceOrDisusedShop() }

override fun createForm() = AddDietTypeForm.create(R.string.quest_dietType_explanation_halal)
override fun createForm() = AddDietTypeForm()

override fun applyAnswerTo(answer: DietAvailabilityAnswer, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
when (answer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ class AddKosher : OsmFilterQuestType<DietAvailabilityAnswer>() {
override val achievements = listOf(CITIZEN)
override val defaultDisabledMessage = R.string.default_disabled_msg_go_inside_regional_warning

override val hint = R.string.quest_dietType_explanation_kosher

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().asSequence().filter { it.isPlaceOrDisusedShop() }

override fun createForm() = AddDietTypeForm.create(R.string.quest_dietType_explanation_kosher)
override fun createForm() = AddDietTypeForm()

override fun applyAnswerTo(answer: DietAvailabilityAnswer, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
when (answer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ class AddVegan : OsmFilterQuestType<DietAvailabilityAnswer>() {
override val achievements = listOf(VEG, CITIZEN)
override val defaultDisabledMessage = R.string.default_disabled_msg_go_inside

override val hint = R.string.quest_dietType_explanation_vegan

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().asSequence().filter { it.isPlaceOrDisusedShop() }

override fun createForm() = AddDietTypeForm.create(R.string.quest_dietType_explanation_vegan)
override fun createForm() = AddDietTypeForm()

override fun applyAnswerTo(answer: DietAvailabilityAnswer, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
when (answer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ class AddVegetarian : OsmFilterQuestType<DietAvailabilityAnswer>() {
override val achievements = listOf(VEG, CITIZEN)
override val defaultDisabledMessage = R.string.default_disabled_msg_go_inside

override val hint = R.string.quest_dietType_explanation_vegetarian

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

override fun getHighlightedElements(element: Element, getMapData: () -> MapDataWithGeometry) =
getMapData().asSequence().filter { it.isPlaceOrDisusedShop() }

override fun createForm() = AddDietTypeForm.create(R.string.quest_dietType_explanation_vegetarian)
override fun createForm() = AddDietTypeForm()

override fun applyAnswerTo(answer: DietAvailabilityAnswer, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
when (answer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import de.westnordost.streetcomplete.osm.ROADS_ASSUMED_TO_BE_PAVED
import de.westnordost.streetcomplete.osm.Tags
import de.westnordost.streetcomplete.osm.surface.ANYTHING_PAVED
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.HAS_SEPARATE_SIDEWALK
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.IS_LIVING_STREET
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.NO
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.YES

Expand Down Expand Up @@ -63,7 +62,6 @@ class AddProhibitedForPedestrians : OsmFilterQuestType<ProhibitedForPedestriansA
tags.remove("sidewalk:left")
tags.remove("sidewalk:right")
}
IS_LIVING_STREET -> tags["highway"] = "living_street"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,17 @@
package de.westnordost.streetcomplete.quests.foot

import androidx.appcompat.app.AlertDialog
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.DialogLivingStreetConfirmationBinding
import de.westnordost.streetcomplete.quests.AbstractOsmQuestForm
import de.westnordost.streetcomplete.quests.AnswerItem
import de.westnordost.streetcomplete.quests.AListQuestForm
import de.westnordost.streetcomplete.quests.TextItem
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.HAS_SEPARATE_SIDEWALK
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.IS_LIVING_STREET
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.NO
import de.westnordost.streetcomplete.quests.foot.ProhibitedForPedestriansAnswer.YES
import de.westnordost.streetcomplete.util.ktx.livingStreetSignDrawableResId

class AddProhibitedForPedestriansForm : AbstractOsmQuestForm<ProhibitedForPedestriansAnswer>() {
class AddProhibitedForPedestriansForm : AListQuestForm<ProhibitedForPedestriansAnswer>() {

override val contentLayoutResId = R.layout.quest_prohibited_for_pedestrians_separate_sidewalk_explanation

override val buttonPanelAnswers = listOf(
AnswerItem(R.string.quest_generic_hasFeature_no) { applyAnswer(NO) },
AnswerItem(R.string.quest_generic_hasFeature_yes) { applyAnswer(YES) },
AnswerItem(R.string.quest_sidewalk_value_yes) { applyAnswer(HAS_SEPARATE_SIDEWALK) }
override val items = listOf(
TextItem(YES, R.string.quest_accessible_for_pedestrians_prohibited),
TextItem(NO, R.string.quest_accessible_for_pedestrians_allowed),
TextItem(HAS_SEPARATE_SIDEWALK, R.string.quest_accessible_for_pedestrians_separate_sidewalk),
)

// the living street answer stuff is copied from AddMaxSpeedForm
override val otherAnswers: List<AnswerItem> get() {
val result = mutableListOf<AnswerItem>()

val highwayTag = element.tags["highway"]!!
if (countryInfo.hasLivingStreet && MAYBE_LIVING_STREET.contains(highwayTag)) {
result.add(AnswerItem(R.string.quest_maxspeed_answer_living_street) { confirmLivingStreet() })
}
return result
}

private fun confirmLivingStreet() {
val ctx = context ?: return
val dialogBinding = DialogLivingStreetConfirmationBinding.inflate(layoutInflater)
countryInfo.livingStreetSignDrawableResId?.let { dialogBinding.livingStreetImage.setImageResource(it) }
AlertDialog.Builder(ctx)
.setView(dialogBinding.root)
.setTitle(R.string.quest_maxspeed_answer_living_street_confirmation_title)
.setPositiveButton(R.string.quest_generic_confirmation_yes) { _, _ -> applyAnswer(IS_LIVING_STREET) }
.setNegativeButton(R.string.quest_generic_confirmation_no, null)
.show()
}

companion object {
private val MAYBE_LIVING_STREET = listOf("residential", "unclassified")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ package de.westnordost.streetcomplete.quests.foot
enum class ProhibitedForPedestriansAnswer {
YES,
NO,
IS_LIVING_STREET,
HAS_SEPARATE_SIDEWALK
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class AddBicycleIncline : OsmElementQuestType<BicycleInclineAnswer> {
return null
}

override val hint = R.string.quest_arrow_tutorial

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

override fun createForm() = AddBicycleInclineForm()
Expand Down

0 comments on commit bd94440

Please sign in to comment.