Skip to content
Draft
Show file tree
Hide file tree
Changes from 138 commits
Commits
Show all changes
179 commits
Select commit Hold shift + click to select a range
41735ab
ATP
matkoniecz Jun 8, 2025
f668f7e
+
matkoniecz Jun 8, 2025
00ada70
val var
matkoniecz Jun 8, 2025
7e2cb14
+
matkoniecz Jun 8, 2025
f9d1348
+
matkoniecz Jun 9, 2025
fdef9e7
+
matkoniecz Jun 9, 2025
3710366
+
matkoniecz Jun 9, 2025
71bb2d1
+
matkoniecz Jun 9, 2025
4512229
+
matkoniecz Jun 9, 2025
1d82d73
finish replacing inferior hack by superior hack
matkoniecz Jun 9, 2025
6937d74
prepare data passing
matkoniecz Jun 9, 2025
6b346d7
+
matkoniecz Jun 9, 2025
400b684
+
matkoniecz Jun 14, 2025
17739f3
DONE
matkoniecz Jun 14, 2025
4dba4bc
also done
matkoniecz Jun 14, 2025
aa29ad8
wat, wtf is going on
matkoniecz Jun 14, 2025
27eaf72
layout not needed
matkoniecz Jun 14, 2025
4be48d7
+
matkoniecz Jun 15, 2025
cdc0d87
+
matkoniecz Jun 15, 2025
d0fb475
try to generify lookup of nearby elements to display
matkoniecz Jun 15, 2025
3539d74
+
matkoniecz Jun 15, 2025
11f242f
+
matkoniecz Jun 15, 2025
07e0efe
+
matkoniecz Jun 15, 2025
6e5a947
+
matkoniecz Jun 15, 2025
ed2a5fb
+
matkoniecz Jun 15, 2025
e069b3d
+
matkoniecz Jun 15, 2025
838f175
+
matkoniecz Jun 15, 2025
8a3555d
+
matkoniecz Jun 15, 2025
5714b0a
+
matkoniecz Jun 15, 2025
4763359
+
matkoniecz Jun 15, 2025
ce5481c
+
matkoniecz Jun 15, 2025
3f507e9
+
matkoniecz Jun 15, 2025
1d87ef9
+
matkoniecz Jun 15, 2025
f27bfae
+
matkoniecz Jun 15, 2025
6a565c0
remove opening hours quest atp stuff
matkoniecz Jun 15, 2025
6093d18
drop some TODOs
matkoniecz Jun 15, 2025
6f7ea17
ech, it is fine
matkoniecz Jun 15, 2025
6c66a29
give up on fixing for now
matkoniecz Jun 15, 2025
7071390
purge TODO
matkoniecz Jun 15, 2025
6fdaab2
+
matkoniecz Jun 15, 2025
0a009ad
OH
matkoniecz Jun 15, 2025
7ec8161
OH
matkoniecz Jun 15, 2025
01ee28d
typo
matkoniecz Jun 16, 2025
222e220
+
matkoniecz Jun 16, 2025
be3801c
+
matkoniecz Jun 16, 2025
5dad557
fix some
matkoniecz Jun 16, 2025
afd43a5
+
matkoniecz Jun 16, 2025
4fd0873
update data for Kraków
matkoniecz Jun 17, 2025
a63a73d
done
matkoniecz Jun 17, 2025
a9bc507
done
matkoniecz Jun 17, 2025
f45857f
done as isThereOsmAtpMatch considers range
matkoniecz Jun 17, 2025
15f8898
+
matkoniecz Jun 17, 2025
b9da1e2
+
matkoniecz Jun 17, 2025
0854006
tests :)
matkoniecz Jun 18, 2025
d6383c2
add test and fix code
matkoniecz Jun 18, 2025
e8239b6
+
matkoniecz Jun 18, 2025
7e2ad82
implemented
matkoniecz Jun 18, 2025
b60a6bd
+
matkoniecz Jun 18, 2025
df179af
nixed effort for now
matkoniecz Jun 19, 2025
19bdb61
clarify
matkoniecz Jun 19, 2025
bac8449
reduce false positive ratio
matkoniecz Jun 19, 2025
dd0f248
more tests
matkoniecz Jun 19, 2025
1615502
better tests
matkoniecz Jun 19, 2025
8a4194b
wonderful, more TODOs
matkoniecz Jun 19, 2025
837c0d1
show hiding text
matkoniecz Jun 19, 2025
9a70110
actually add an actual download of external data instead of including…
matkoniecz Jun 21, 2025
8274b16
Merge branch 'master' into atp_renew
matkoniecz Jun 21, 2025
0fbcfd2
these can be done later, does not need to be done now
matkoniecz Jun 21, 2025
2de19bc
remove dead code
matkoniecz Jun 21, 2025
9c9f41f
better comment
matkoniecz Jun 21, 2025
6d03b25
fix test
matkoniecz Jun 22, 2025
b395bbb
not needed
matkoniecz Jun 22, 2025
24bb796
done already
matkoniecz Jun 22, 2025
09dc5d2
better name
matkoniecz Jun 22, 2025
57b37df
new TODO
matkoniecz Jun 22, 2025
df26aa4
it works
matkoniecz Jun 22, 2025
9841b80
clean unused imports
matkoniecz Jun 22, 2025
722d872
test added
matkoniecz Jun 22, 2025
56c3507
fix test
matkoniecz Jun 22, 2025
a2e49dc
done!
matkoniecz Jun 22, 2025
af74f70
more main keys
matkoniecz Jun 22, 2025
f82359b
more reasonable ones
matkoniecz Jun 22, 2025
c1b1403
clarify
matkoniecz Jun 22, 2025
00baf32
change my mind here a bit
matkoniecz Jun 22, 2025
b14cf33
support global ATP API now
matkoniecz Jun 22, 2025
7e7116c
drop not needed logging
matkoniecz Jun 22, 2025
90b1060
stop collision with MARKER_NOTE_ID
matkoniecz Jun 23, 2025
dd460e4
add grant info
matkoniecz Jun 23, 2025
e501b7d
move taginfo parser to dead project section
matkoniecz Jun 23, 2025
d22a765
more docs
matkoniecz Jun 23, 2025
75ba597
fix phrasing
matkoniecz Jun 23, 2025
91fe33e
add tests, fix parser
matkoniecz Jun 23, 2025
3facd8e
document value
matkoniecz Jun 24, 2025
e53c17d
update TODO
matkoniecz Jun 24, 2025
f49f6ef
fix comments
matkoniecz Jun 24, 2025
039fbfb
adapt test
matkoniecz Jun 24, 2025
cbaf108
ATP quests are not hidden by nearby notes
matkoniecz Jun 24, 2025
ba45e8a
attempt to get tests for AtpApiClient
matkoniecz Jun 26, 2025
1f47de3
fix naming
matkoniecz Jun 27, 2025
0c1ff38
done now
matkoniecz Jul 1, 2025
04a4899
done
matkoniecz Jul 1, 2025
70811ee
looks fine to duplicate
matkoniecz Jul 1, 2025
d72fde7
remove ability to leave notes
matkoniecz Jul 1, 2025
b375552
done already
matkoniecz Jul 1, 2025
d2fbe9b
dead and replaced code
matkoniecz Jul 1, 2025
7c35d60
process TODOs
matkoniecz Jul 1, 2025
483b91b
Merge remote-tracking branch 'upstream/master' into atp_renew
matkoniecz Jul 1, 2025
a5c6976
clarify confusion
matkoniecz Jul 1, 2025
302e4d9
progress
matkoniecz Jul 1, 2025
f9f5b4f
fix code and found bug
matkoniecz Jul 1, 2025
2614773
fix tests
matkoniecz Jul 1, 2025
b144ef3
disable for now
matkoniecz Jul 1, 2025
622d23b
more doubts
matkoniecz Jul 1, 2025
0ab37cd
now documented
matkoniecz Jul 1, 2025
0b3a097
clean ATP in Cleaner
matkoniecz Jul 1, 2025
770a988
space
matkoniecz Jul 1, 2025
42a9ee6
not trivial
matkoniecz Jul 1, 2025
2740c7d
drop not needed code
matkoniecz Jul 1, 2025
028fd0e
nope
matkoniecz Jul 1, 2025
949ca7c
reduce comment
matkoniecz Jul 1, 2025
218e74b
rename to clarify
matkoniecz Jul 1, 2025
b0a20c1
confusion is now resolved
matkoniecz Jul 1, 2025
dd2def0
commit no-brainer suggestion
westnordost Jul 2, 2025
28cfa73
commit no-brainer suggestion
westnordost Jul 2, 2025
0b00c5f
commit no-brainer suggestion
westnordost Jul 2, 2025
59261e5
we will be never putting new ATP entries into API
matkoniecz Jul 2, 2025
19f6abf
change text as suggested
matkoniecz Jul 2, 2025
8e092a0
remove tests for removed put
matkoniecz Jul 2, 2025
fc34ab9
fix test
matkoniecz Jul 2, 2025
f55f251
drop some unused imports
matkoniecz Jul 3, 2025
c207a33
class to object
matkoniecz Jul 3, 2025
15993cc
fix broken NULL handling
matkoniecz Jul 3, 2025
81e43a4
Merge remote-tracking branch 'upstream/master' into atp_renew
matkoniecz Jul 3, 2025
38673a6
TODO
matkoniecz Jul 3, 2025
118aa4c
code review comment in code comment
matkoniecz Jul 3, 2025
1a1a603
remove unused function
matkoniecz Jul 3, 2025
e48e6cc
add some tests
matkoniecz Jul 3, 2025
906533c
move general check to end to get more detailed fail message if anythi…
matkoniecz Jul 3, 2025
f6a972b
Fix typo
matkoniecz Jul 4, 2025
4a9bc74
no need to do anything here
matkoniecz Jul 7, 2025
92cf0ac
pinged
matkoniecz Jul 7, 2025
24ebc31
Revert "layout not needed"
matkoniecz Jul 9, 2025
40d4085
split question and guide
matkoniecz Jul 9, 2025
4062463
ATP data download failing should not take down entire download
matkoniecz Jul 9, 2025
36289df
deal with 180th meridian
matkoniecz Jul 9, 2025
cbef44e
not notes
matkoniecz Jul 9, 2025
f87fa5d
it was based on matching notes code, update to its upgrade
matkoniecz Jul 9, 2025
806c4b9
Merge branch 'master' into atp_renew
matkoniecz Jul 21, 2025
983fc7d
update to changes upstream
matkoniecz Jul 21, 2025
e0d8b3c
Merge branch 'master' into atp_renew
matkoniecz Jul 23, 2025
690683c
given context it will be enough, just add StreetComplete to data user…
matkoniecz Jul 25, 2025
1e8fa5d
fix typo
matkoniecz Jul 25, 2025
2e9dc1e
+
matkoniecz Jul 25, 2025
9cb3e97
fixed externally
matkoniecz Jul 25, 2025
3709c94
quests deleted already may be come listed again
matkoniecz Jul 25, 2025
b081dab
skip tagless
matkoniecz Jul 25, 2025
c5b2b07
specific filter idea
matkoniecz Jul 25, 2025
a5babf5
make grepping work
matkoniecz Jul 25, 2025
bc8ed2d
lol, I am already calling function supporting smart distance to areas
matkoniecz Jul 25, 2025
c0a6af0
mark as to be done on a final approach
matkoniecz Jul 25, 2025
574cfe0
annotate stuck ones
matkoniecz Jul 25, 2025
abe9a76
better name as multiple are passed
matkoniecz Jul 25, 2025
2ed9146
add missing test
matkoniecz Jul 25, 2025
094fa17
annotate as TODO for later
matkoniecz Jul 25, 2025
cfd2c8e
not needed, enforced by compiler
matkoniecz Jul 25, 2025
19d14b3
I see no reason to really panick if it happens
matkoniecz Jul 25, 2025
4d1a777
mark deferred TODOs
matkoniecz Jul 25, 2025
2bff5de
seems not tested in other cases
matkoniecz Jul 25, 2025
99673f9
Merge remote-tracking branch 'upstream/master' into atp_renew
matkoniecz Jul 27, 2025
a3c853f
will come up in testing if needed
matkoniecz Jul 29, 2025
6f2e970
remove wrong comment, mark TODO
matkoniecz Jul 29, 2025
73d134e
clarify interface TODO
matkoniecz Jul 29, 2025
ab87b00
Merge remote-tracking branch 'upstream/master' into atp_renew
matkoniecz Jul 29, 2025
4e2acf7
seems ready for removal
matkoniecz Jul 29, 2025
07359f1
also for later
matkoniecz Jul 29, 2025
2194f5f
need finish API first
matkoniecz Aug 1, 2025
1546e7e
Merge branch 'master' into atp_renew
matkoniecz Aug 30, 2025
5924686
Merge remote-tracking branch 'upstream/master' into atp_renew
matkoniecz Aug 30, 2025
c7da5c4
this is unfixable
matkoniecz Sep 9, 2025
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
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Materials in [`res/documentation`](res/documentation) also may be useful, it inc
* [sc-photo-service](https://github.com/streetcomplete/sc-photo-service) by [@exploide](https://github.com/exploide) allows StreetComplete to upload photos associated with OSM Notes
* [sc-statistics-service](https://github.com/streetcomplete/sc-statistics-service) by [@westnordost](https://github.com/westnordost) aggregates and provides StreetComplete-related statistics about users.
* [StreetComplete-taginfo-categorize](https://github.com/mnalis/StreetComplete-taginfo-categorize) by [@mnalis](https://github.com/mnalis) generates tags listed in [KEYS_THAT_SHOULD_BE_REMOVED_WHEN_PLACE_IS_REPLACED](https://github.com/streetcomplete/StreetComplete/blob/master/app/src/main/java/de/westnordost/streetcomplete/osm/Place.kt#L244)
* [All The Places <-> OpenStreetMap matcher](https://codeberg.org/matkoniecz/list_how_openstreetmap_can_be_improved_with_alltheplaces_data#all-the-places-openstreetmap-matcher) - for comparison between OpenStreetMap and All The Places. Produced dataset is listed at [this website](https://matkoniecz.codeberg.page/improving_openstreetmap_using_alltheplaces_dataset/) and powers quests that detect missing points of interests

You may find more projects under [the StreetComplete tag](https://github.com/topics/streetcomplete) on GitHub.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ This software is released under the terms of the [GNU General Public License](ht
## Sponsors

<a href="https://nlnet.nl/discovery/"><img src=".github/images/logo_nlnet.svg" alt="nlnet" height="100"/></a><br/>
The **NLnet foundation** sponsored development on this app in four individual grants with funds from the European Commission:<br/>
The **NLnet foundation** sponsored development on this app in five individual grants with funds from the European Commission:<br/>
- A grant from <a href="https://nlnet.nl/project/StreetComplete-multiplatform/">2025</a> will allow Tobias Zwick to finish migrating the app to a multiplatform, so that it runs also on iOS (see <a href="https://github.com/streetcomplete/StreetComplete/issues/5421">ticket</a>)
- In <a href="https://nlnet.nl/project/StreetComplete-Together/">2021</a>, a grant enabled Tobias Zwick to work about five months on the app - most notably, implement the overlays functionality and measuring with AR.
- In <a href="https://www.openstreetmap.org/user/Mateusz%20Konieczny/diary/368849">2019</a> and <a href="https://www.openstreetmap.org/user/Mateusz%20Konieczny/diary/397825">2021</a>, Mateusz Konieczny each got a grant to work on StreetComplete with a focus on improvements on UI and data collection
- In 2025, Mateusz Konieczny got a grant to work on [attempt to use All The Places dataset](https://github.com/streetcomplete/StreetComplete/pull/6302) within StreetComplete
<br/>

<a href="https://github.com/sponsors/westnordost"><picture><source media="(prefers-color-scheme: dark)" srcset=".github/images/logo_github_dark.svg"><img alt="GitHub Sponsors" width="80" src=".github/images/logo_github.svg"></picture></a> <a href="https://liberapay.com/westnordost"><img src=".github/images/logo_liberapay.svg" alt="Liberapay" width="80"/></a> <a href="https://www.patreon.com/westnordost"><picture><source media="(prefers-color-scheme: dark)" srcset=".github/images/logo_patreon_dark.svg"><img alt="Patreon" width="80" src=".github/images/logo_patreon.svg"></picture></a><br/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package de.westnordost.streetcomplete.data.atp

import de.westnordost.streetcomplete.data.ApplicationDbTestCase
import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.ElementKey
import de.westnordost.streetcomplete.data.osm.mapdata.ElementType.NODE
import de.westnordost.streetcomplete.data.osm.mapdata.ElementType.WAY
import de.westnordost.streetcomplete.data.osm.mapdata.ElementType.RELATION
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.util.ktx.containsExactlyInAnyOrder
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class AtpDaoTest : ApplicationDbTestCase() {
private lateinit var dao: AtpDao

@BeforeTest fun createDao() {
dao = AtpDao(database)
}

@Test fun putGet() {
val entry = createAtpEntry()

dao.put(entry)
val dbAtp = dao.get(entry.id)!!
assertEquals(entry, dbAtp)
}

@Test fun putAll() {
dao.putAll(listOf(createAtpEntry(1), createAtpEntry(2)))
assertNotNull(dao.get(1))
assertNotNull(dao.get(2))
}


@Test fun deleteButNothingIsThere() {
assertFalse(dao.delete(1))
}

@Test fun delete() {
val entry = createAtpEntry()
dao.put(entry)
assertTrue(dao.delete(entry.id))
assertNull(dao.get(entry.id))
assertFalse(dao.delete(entry.id))
}

@Test fun getAllPositions() {
val thisIsIn = createAtpEntry(1, LatLon(0.5, 0.5))
val thisIsOut = createAtpEntry(2, LatLon(-0.5, 0.5))
dao.putAll(listOf(thisIsIn, thisIsOut))

val positions = dao.getAllPositions(BoundingBox(0.0, 0.0, 1.0, 1.0))
assertEquals(LatLon(0.5, 0.5), positions.single())
}

@Test fun getAllByBbox() {
val thisIsIn = createAtpEntry(1, LatLon(0.5, 0.5))
val thisIsOut = createAtpEntry(2, LatLon(-0.5, 0.5))
dao.putAll(listOf(thisIsIn, thisIsOut))

val entries = dao.getAll(BoundingBox(0.0, 0.0, 1.0, 1.0))
assertEquals(thisIsIn, entries.single())
}

@Test fun getAllByIds() {
val first = createAtpEntry(1)
val second = createAtpEntry(2)
val third = createAtpEntry(3)
dao.putAll(listOf(first, second, third))

assertEquals(listOf(first, second), dao.getAll(listOf(1, 2)))
}

@Test fun deleteAllByIds() {
dao.putAll(listOf(createAtpEntry(1), createAtpEntry(2), createAtpEntry(3)))

assertEquals(2, dao.deleteAll(listOf(1, 2)))
assertNull(dao.get(1))
assertNull(dao.get(2))
assertNotNull(dao.get(3))
}

@Test fun getUnusedAndOldIds() {
dao.putAll(listOf(createAtpEntry(1), createAtpEntry(2), createAtpEntry(3)))
val unusedIds = dao.getIdsOlderThan(nowAsEpochMilliseconds() + 10)
assertTrue(unusedIds.containsExactlyInAnyOrder(listOf(1L, 2L, 3L)))
}

@Test fun getUnusedAndOldIdsButAtMostX() {
dao.putAll(listOf(createAtpEntry(1), createAtpEntry(2), createAtpEntry(3)))
val unusedIds = dao.getIdsOlderThan(nowAsEpochMilliseconds() + 10, 2)
assertEquals(2, unusedIds.size)
}

@Test fun clear() {
dao.putAll(listOf(createAtpEntry(1), createAtpEntry(2), createAtpEntry(3)))
dao.clear()
assertTrue(dao.getAll(listOf(1L, 2L, 3L)).isEmpty())
}
}

private fun createAtpEntry(
id: Long = 5,
position: LatLon = LatLon(1.0, 1.0),
osmMatch: ElementKey = ElementKey(NODE, 1),
tagsInATP: Map<String, String> = mapOf(),
tagsInOSM: Map<String, String> = mapOf(),
reportType: ReportType = ReportType.OPENING_HOURS_REPORTED_AS_OUTDATED_IN_OPENSTREETMAP,
) = AtpEntry(
position = position,
id = id,
osmMatch = osmMatch,
tagsInATP = tagsInATP,
tagsInOSM = tagsInOSM,
reportType = reportType,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package de.westnordost.streetcomplete.data.atp.atpquests

import de.westnordost.streetcomplete.data.ApplicationDbTestCase
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class AtpQuestsHiddenDaoTest : ApplicationDbTestCase() {
private lateinit var dao: AtpQuestsHiddenDao

@BeforeTest fun createDao() {
dao = AtpQuestsHiddenDao(database)
}

@Test fun addGetDelete() {
assertFalse(dao.delete(123L))
dao.add(123L)
assertNotNull(dao.getTimestamp(123L))
assertTrue(dao.delete(123L))
assertFalse(dao.delete(123L))
assertNull(dao.getTimestamp(123L))
}

@Test fun getAll() {
dao.add(1L)
dao.add(2L)
assertEquals(
setOf(1L, 2L),
dao.getAll().map { it.allThePlacesEntryId }.toSet()
)
}

@Test fun getNewerThan() = runBlocking {
dao.add(1L)
delay(200)
val time = nowAsEpochMilliseconds()
dao.add(2L)
val result = dao.getNewerThan(time - 100).single()
assertEquals(2L, result.allThePlacesEntryId)
}

@Test fun deleteAll() {
assertEquals(0, dao.deleteAll())
dao.add(1L)
dao.add(2L)
assertEquals(2, dao.deleteAll())
assertNull(dao.getTimestamp(1L))
assertNull(dao.getTimestamp(2L))
}

@Test fun countAll() {
assertEquals(0, dao.countAll())
dao.add(3L)
assertEquals(1, dao.countAll())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import de.westnordost.streetcomplete.data.CacheTrimmer
import de.westnordost.streetcomplete.data.CleanerWorker
import de.westnordost.streetcomplete.data.Preloader
import de.westnordost.streetcomplete.data.allEditTypesModule
import de.westnordost.streetcomplete.data.atp.atpEditsModule
import de.westnordost.streetcomplete.data.atp.atpModule
import de.westnordost.streetcomplete.data.atp.atpquests.atpQuestModule
import de.westnordost.streetcomplete.data.download.downloadModule
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesController
import de.westnordost.streetcomplete.data.edithistory.EditHistoryController
Expand Down Expand Up @@ -120,6 +123,9 @@ class StreetCompleteApplication : Application() {
messagesModule,
osmApiModule,
osmNoteQuestModule,
atpEditsModule,
atpModule,
atpQuestModule,
osmQuestModule,
preferencesModule,
questModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import org.koin.dsl.module

val questModule = module {
single { QuestAutoSyncer(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
single { VisibleQuestsSource(get(), get(), get(), get(), get(), get(), get()) }
single { VisibleQuestsSource(get(), get(), get(), get(), get(), get(), get(), get()) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package de.westnordost.streetcomplete.data.quest.atp

import android.location.Location
import android.os.Bundle
import android.view.View
import androidx.core.os.bundleOf
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.atp.AtpEntry
import de.westnordost.streetcomplete.data.location.SurveyChecker
import de.westnordost.streetcomplete.data.osm.edits.AddElementEditsController
import de.westnordost.streetcomplete.data.osm.edits.ElementEditAction
import de.westnordost.streetcomplete.data.osm.edits.ElementEditType
import de.westnordost.streetcomplete.data.osm.edits.ElementEditsController
import de.westnordost.streetcomplete.data.osm.edits.create.CreateNodeAction
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.data.osm.mapdata.Node
import de.westnordost.streetcomplete.data.quest.QuestKey
import de.westnordost.streetcomplete.data.visiblequests.HideQuestController
import de.westnordost.streetcomplete.data.visiblequests.QuestsHiddenController
import de.westnordost.streetcomplete.quests.AbstractQuestForm
import de.westnordost.streetcomplete.quests.AnswerItem
import de.westnordost.streetcomplete.util.getNameAndLocationSpanned
import de.westnordost.streetcomplete.util.ktx.viewLifecycleScope
import de.westnordost.streetcomplete.view.confirmIsSurvey
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import org.koin.android.ext.android.inject
import org.koin.core.qualifier.named
import kotlin.getValue

class AtpCreateForm : AbstractQuestForm() {
private val hiddenQuestsController: QuestsHiddenController by inject()
private val featureDictionaryLazy: Lazy<FeatureDictionary> by inject(named("FeatureDictionaryLazy"))
private val elementEditsController: ElementEditsController by inject()
private val surveyChecker: SurveyChecker by inject()

private lateinit var entry: AtpEntry private set
private val featureDictionary: FeatureDictionary get() = featureDictionaryLazy.value
var hideQuestController: HideQuestController = hiddenQuestsController
var selectedLocation: LatLon? = null
var addElementEditsController: AddElementEditsController = elementEditsController

override fun onClickMapAt(position: LatLon, clickAreaSizeInMeters: Double): Boolean {
selectedLocation = position
checkIsFormComplete()
return true
}

override fun onClickOk() {
if(selectedLocation == null) {
return
} else {
viewLifecycleScope.launch { // viewLifecycleScope is here via cargo cult - what it is doing and is it needed TODO
applyEdit(CreateNodeAction(selectedLocation!!, entry.tagsInATP))
}
}
}

protected fun applyEdit(answer: ElementEditAction, geometry: ElementGeometry = this.geometry) {
viewLifecycleScope.launch {
solve(answer, geometry)
}
}

private suspend fun solve(action: ElementEditAction, geometry: ElementGeometry) {
setLocked(true)
val isSurvey = surveyChecker.checkIsSurvey(geometry)
if (!isSurvey && !confirmIsSurvey(requireContext())) {
setLocked(false)
return
}

withContext(Dispatchers.IO) {
addElementEditsController.add(CreatePoiBasedOnAtp, geometry, "survey", action, isSurvey)
}
listener?.onEdited(CreatePoiBasedOnAtp, geometry)
}
override fun isFormComplete(): Boolean {
return selectedLocation != null
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val args = requireArguments()
entry = Json.decodeFromString(args.getString(ATP_ENTRY)!!)
}

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

// TODO: maybe should be more prominent?
setTitleHintLabel(getNameAndLocationSpanned(Node(
1,
position = entry.position,
tags = entry.tagsInATP,
version = 1,
timestampEdited = 1,
), resources, featureDictionary))
}

override fun onStart() {
super.onStart()
updateButtonPanel()
}

protected fun updateButtonPanel() {
// TODO: create answers to send to API, not just hide quests
val mappedAlready = AnswerItem(R.string.quest_atp_add_missing_poi_mapped_already) { /*applyAnswer(false)*/ hideQuest() }
val missing = AnswerItem(R.string.quest_atp_add_missing_poi_does_not_exist) { /*applyAnswer(true)*/ hideQuest() }
val cantSay = AnswerItem(R.string.quest_generic_answer_notApplicable) { hideQuest() /* no option to leave note */ }

setButtonPanelAnswers(listOf(mappedAlready, missing, cantSay))
}

interface Listener {
/** The GPS position at which the user is displayed at */
val displayedMapLocation: Location?

/** Called when the user successfully answered the quest */
fun onEdited(editType: ElementEditType, geometry: ElementGeometry)

/** Called when the user successfully answered the quest */
// TODO actually use that (or remove, if not needed)
fun onRejectedAtpEntry(editType: ElementEditType, geometry: ElementGeometry)

/** Called when the user chose to move the node */
fun onMoveNode(editType: ElementEditType, node: Node)

/** Called when the user chose to hide the quest instead */
fun onQuestHidden(questKey: QuestKey)
}
private val listener: Listener? get() = parentFragment as? Listener ?: activity as? Listener

protected fun hideQuest() {
viewLifecycleScope.launch {
withContext(Dispatchers.IO) { hideQuestController.hide(questKey) }
listener?.onQuestHidden(questKey)
}
}

companion object {
private const val ATP_ENTRY = "atp_entry"

fun createArguments(entry: AtpEntry) = bundleOf(
ATP_ENTRY to Json.encodeToString(entry)
)
}
}
Loading