-
-
Notifications
You must be signed in to change notification settings - Fork 362
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
Feature #1850: Add Overlay mtb:scale #5726
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package de.westnordost.streetcomplete.overlays.mtb_scale | ||
|
||
import androidx.annotation.DrawableRes | ||
import androidx.annotation.StringRes | ||
import de.westnordost.streetcomplete.R | ||
|
||
import de.westnordost.streetcomplete.view.image_select.GroupableDisplayItem | ||
import de.westnordost.streetcomplete.view.image_select.Item | ||
|
||
enum class MtbScale( | ||
val osmValue: String, | ||
@DrawableRes val imageResId: Int?, | ||
@StringRes val titleResId: Int, | ||
@StringRes val descriptionResId: Int, | ||
val color:String, | ||
) { | ||
ZERO( | ||
osmValue = "0", | ||
imageResId = R.drawable.mtb_scale_0, | ||
titleResId = R.string.overlay_mtbScale_zero, | ||
descriptionResId = R.string.overlay_mtbScale_zero_description, | ||
color = "#DBECC0" | ||
), | ||
ONE( | ||
osmValue = "1", | ||
imageResId = R.drawable.mtb_scale_1, | ||
titleResId = R.string.overlay_mtbScale_one, | ||
descriptionResId = R.string.overlay_mtbScale_one_description, | ||
color = "#8CC63E" | ||
), | ||
TWO( | ||
osmValue = "2", | ||
imageResId = R.drawable.mtb_scale_2, | ||
titleResId = R.string.overlay_mtbScale_two, | ||
descriptionResId = R.string.overlay_mtbScale_two_description, | ||
color = "#00B2E6" | ||
), | ||
THREE( | ||
osmValue = "3", | ||
imageResId = R.drawable.mtb_scale_3, | ||
titleResId = R.string.overlay_mtbScale_three, | ||
descriptionResId = R.string.overlay_mtbScale_three_description, | ||
color = "#FECB1B" | ||
), | ||
FOUR( | ||
osmValue = "4", | ||
imageResId = R.drawable.mtb_scale_4, | ||
titleResId = R.string.overlay_mtbScale_four, | ||
descriptionResId = R.string.overlay_mtbScale_four_description, | ||
color = "#F47922" | ||
), | ||
FIVE( | ||
osmValue = "5", | ||
imageResId = R.drawable.mtb_scale_5, | ||
titleResId = R.string.overlay_mtbScale_five, | ||
descriptionResId = R.string.overlay_mtbScale_five_description, | ||
color = "#874D99" | ||
), | ||
SIX( | ||
osmValue = "6", | ||
imageResId = R.drawable.mtb_scale_6, | ||
titleResId = R.string.overlay_mtbScale_six, | ||
descriptionResId = R.string.overlay_mtbScale_six_description, | ||
color = "#000000" | ||
) | ||
} | ||
|
||
fun Collection<MtbScale>.toItems() = map { it.asItem() } | ||
|
||
fun MtbScale.asItem(): GroupableDisplayItem<MtbScale> { | ||
return Item( | ||
this, | ||
drawableId = imageResId, | ||
titleId = titleResId, | ||
descriptionId = descriptionResId | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package de.westnordost.streetcomplete.overlays.mtb_scale | ||
|
||
import de.westnordost.streetcomplete.R | ||
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.user.achievements.EditTypeAchievement | ||
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.* | ||
|
||
import de.westnordost.streetcomplete.overlays.AbstractOverlayForm | ||
import de.westnordost.streetcomplete.overlays.Color | ||
import de.westnordost.streetcomplete.overlays.Overlay | ||
import de.westnordost.streetcomplete.overlays.PolylineStyle | ||
import de.westnordost.streetcomplete.overlays.StrokeStyle | ||
import de.westnordost.streetcomplete.overlays.Style | ||
|
||
class MtbScaleOverlay : Overlay { | ||
|
||
override val title = R.string.overlay_mtb_scale | ||
override val icon = R.drawable.ic_overlay_mtb_scale | ||
override val changesetComment = "Specify mtb scale" | ||
override val wikiLink: String = "Key:mtb:scale" | ||
override val achievements: List<EditTypeAchievement> = | ||
listOf(BICYCLIST, OUTDOORS) | ||
|
||
override fun getStyledElements(mapData: MapDataWithGeometry): Sequence<Pair<Element, Style>> = | ||
mapData.filter( | ||
""" | ||
ways with | ||
highway ~ path|track | ||
and ( access !~ no|private or foot ~ yes|permissive|designated or bicycle ~ yes|permissive|designated) | ||
and (!lit or lit = no) | ||
and surface ~ "grass|sand|dirt|soil|fine_gravel|compacted|wood|gravel|pebblestone|rock|ground|earth|mud|woodchips|snow|ice|salt|stone" | ||
""" | ||
).map { | ||
it to getStyle(it) | ||
} | ||
|
||
override fun createForm(element: Element?): AbstractOverlayForm = MtbScaleOverlayForm() | ||
|
||
private fun getStyle(element: Element): Style { | ||
val color = MtbScale.entries.find { it.osmValue == element.tags["mtb:scale"]?.take(1) }.color | ||
|
||
return PolylineStyle(StrokeStyle(color)) | ||
} | ||
|
||
private val MtbScale?.color | ||
get() = when (this) { | ||
null -> Color.DATA_REQUESTED | ||
MtbScale.ZERO -> "#DBECC0" | ||
MtbScale.ONE -> "#8CC63E" | ||
MtbScale.TWO -> "#00B2E6" | ||
MtbScale.THREE -> "#FECB1B" | ||
MtbScale.FOUR -> "#F47922" | ||
MtbScale.FIVE -> "#874D99" | ||
MtbScale.SIX -> "#000000" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package de.westnordost.streetcomplete.overlays.mtb_scale | ||
|
||
import android.content.Context | ||
import android.os.Bundle | ||
import android.view.View | ||
import androidx.core.view.isGone | ||
import com.russhwolf.settings.ObservableSettings | ||
import de.westnordost.streetcomplete.R | ||
import de.westnordost.streetcomplete.data.osm.edits.update_tags.StringMapChangesBuilder | ||
import de.westnordost.streetcomplete.data.osm.edits.update_tags.UpdateElementTagsAction | ||
import de.westnordost.streetcomplete.databinding.FragmentOverlayMtbScaleSelectBinding | ||
import de.westnordost.streetcomplete.overlays.AbstractOverlayForm | ||
import de.westnordost.streetcomplete.util.LastPickedValuesStore | ||
import de.westnordost.streetcomplete.util.ktx.valueOfOrNull | ||
import de.westnordost.streetcomplete.util.logs.Log | ||
import de.westnordost.streetcomplete.view.setImage | ||
import org.koin.android.ext.android.inject | ||
|
||
class MtbScaleOverlayForm : AbstractOverlayForm() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A lot of code here and in that ViewController. Why can this not inherit from |
||
|
||
override val contentLayoutResId = R.layout.fragment_overlay_mtb_scale_select | ||
private val binding by contentViewBinding(FragmentOverlayMtbScaleSelectBinding::bind) | ||
|
||
private var originalMtbScale: MtbScale? = null | ||
|
||
private lateinit var mtbScaleCtrl: MtbScaleViewController | ||
private val prefs: ObservableSettings by inject() | ||
private lateinit var favs: LastPickedValuesStore<MtbScale> | ||
private val lastPickedMtbScale: MtbScale? | ||
get() = favs.get().firstOrNull() | ||
|
||
override fun hasChanges(): Boolean = mtbScaleCtrl.value != originalMtbScale | ||
|
||
override fun isFormComplete(): Boolean = mtbScaleCtrl.value != null | ||
|
||
override fun onClickOk() { | ||
val changesBuilder = StringMapChangesBuilder(element!!.tags) | ||
|
||
if (mtbScaleCtrl.value != null) { | ||
favs.add(mtbScaleCtrl.value!!) | ||
} | ||
changesBuilder["mtb:scale"] = mtbScaleCtrl.value!!.osmValue | ||
|
||
applyEdit(UpdateElementTagsAction(element!!, changesBuilder.create())) | ||
} | ||
|
||
|
||
override fun onAttach(ctx: Context) { | ||
super.onAttach(ctx) | ||
favs = LastPickedValuesStore( | ||
prefs, | ||
key = javaClass.simpleName, | ||
serialize = { it.name }, | ||
deserialize = { valueOfOrNull<MtbScale>(it) } | ||
) | ||
} | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
|
||
originalMtbScale = MtbScale.entries.find { it.osmValue == element!!.tags["mtb:scale"]?.take(1) } | ||
Log.e("TEST","originalMtbScale $originalMtbScale ${element!!.tags["mtb:scale"]}") | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
mtbScaleCtrl = MtbScaleViewController( | ||
selectButton = binding.selectButton.root, | ||
selectedCellView = binding.selectButton.selectedCellView, | ||
selectTextView = binding.selectButton.selectTextView, | ||
) | ||
mtbScaleCtrl.onInputChanged = { checkIsFormComplete() } | ||
|
||
binding.lastPickedButton.isGone = lastPickedMtbScale == null | ||
binding.lastPickedButton.setImage(lastPickedMtbScale?.asItem()?.image) | ||
binding.lastPickedButton.setOnClickListener { | ||
mtbScaleCtrl.value = lastPickedMtbScale | ||
binding.lastPickedButton.isGone = true | ||
checkIsFormComplete() | ||
} | ||
|
||
if (savedInstanceState != null) { | ||
onLoadInstanceState(savedInstanceState) | ||
} else { | ||
initStateFromTags() | ||
} | ||
checkIsFormComplete() | ||
} | ||
|
||
|
||
override fun onSaveInstanceState(outState: Bundle) { | ||
super.onSaveInstanceState(outState) | ||
outState.putString(MTB_SCALE, mtbScaleCtrl.value?.osmValue) | ||
|
||
} | ||
|
||
private fun onLoadInstanceState(inState: Bundle) { | ||
mtbScaleCtrl.value = MtbScale.entries.find { it.osmValue == inState.getString(MTB_SCALE) } | ||
} | ||
|
||
private fun initStateFromTags() { | ||
mtbScaleCtrl.value = originalMtbScale | ||
} | ||
|
||
companion object { | ||
private const val MTB_SCALE = "selected_mtb_scale" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package de.westnordost.streetcomplete.overlays.mtb_scale | ||
|
||
import android.view.LayoutInflater | ||
import android.view.ViewGroup | ||
import android.widget.TextView | ||
import androidx.core.view.children | ||
import androidx.core.view.isGone | ||
import de.westnordost.streetcomplete.R | ||
import de.westnordost.streetcomplete.view.image_select.DisplayItem | ||
import de.westnordost.streetcomplete.view.image_select.ImageListPickerDialog | ||
import de.westnordost.streetcomplete.view.image_select.ItemViewHolder | ||
|
||
class MtbScaleViewController( | ||
private val selectButton: ViewGroup, | ||
private val selectedCellView: ViewGroup, | ||
private val selectTextView: TextView, | ||
) { | ||
var value: MtbScale? | ||
set(value) { | ||
selectedMtbScaleItem = value?.asItem() | ||
} | ||
get() { | ||
return selectedMtbScaleItem?.value | ||
} | ||
|
||
private var selectedMtbScaleItem: DisplayItem<MtbScale>? = null | ||
set(value) { | ||
field = value | ||
updateSelectedCell() | ||
} | ||
|
||
private val cellLayoutId: Int = R.layout.cell_labeled_icon_select | ||
private val dialogCellLayoutId: Int = R.layout.cell_labeled_icon_select_mtb_scale | ||
private val items: List<DisplayItem<MtbScale>> = MtbScale.entries.toItems() | ||
|
||
var onInputChanged: (() -> Unit)? = null | ||
|
||
init { | ||
selectButton.setOnClickListener { | ||
collectMtbScaleData { sacScale: MtbScale -> | ||
selectedMtbScaleItem = sacScale.asItem() | ||
onInputChanged?.invoke() | ||
} | ||
} | ||
|
||
LayoutInflater.from(selectButton.context).inflate(cellLayoutId, selectedCellView, true) | ||
selectButton.children.first().background = null | ||
} | ||
|
||
private fun updateSelectedCell() { | ||
val item = selectedMtbScaleItem | ||
selectTextView.isGone = item != null | ||
selectedCellView.isGone = item == null | ||
if (item != null) { | ||
ItemViewHolder(selectedCellView).bind(item) | ||
} | ||
} | ||
|
||
private fun collectMtbScaleData(callback: (MtbScale) -> Unit) { | ||
ImageListPickerDialog(selectButton.context, items, dialogCellLayoutId, 1) { item -> | ||
callback(item.value!!) | ||
}.show() | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better images are a hard requirement. Minimum resolution is 384x384 px |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, the the title, description, image, ... should be put into extension functions in a separate file, to clearly separate the data from the resources. See e.g. SurfaceItem.kt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry, it was to improve readability and performance on my side, but I'll modify it for you without any worries.