Skip to content

Commit

Permalink
Merge pull request #1901 from westnordost/multi-download
Browse files Browse the repository at this point in the history
Multi download
  • Loading branch information
westnordost authored Oct 29, 2020
2 parents 9529944 + d082015 commit 761f286
Show file tree
Hide file tree
Showing 227 changed files with 3,458 additions and 2,987 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/quest-suggestion.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ If you are not sure about how one condition applies to your suggestion or you ha
### Ideas for implementation
<!-- If you do not have any, just delete this part. ;) -->

<!-- If you have any idea for how elements should be selected, add it here. Possibly include an overpass-api query if you are sure what you do. -->
<!-- If you have any idea for how elements should be selected, add it here. -->
**Element selection:**

<!-- If you have any idea whether metadata per country is needed, add it here. If you even have ideas, where to get this data from, you can of course also mention it! -->
Expand Down
17 changes: 0 additions & 17 deletions ARCHITECTURE.md

This file was deleted.

15 changes: 4 additions & 11 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ configurations {
all {
exclude(group = "net.sf.kxml", module = "kxml2")
}

compile.configure {
exclude(group = "org.jetbrains", module = "annotations")
exclude(group = "com.intellij", module = "annotations")
exclude(group = "org.intellij", module = "annotations")
}
}

dependencies {
Expand Down Expand Up @@ -146,11 +140,10 @@ dependencies {
// finding a name for a feature without a name tag
implementation("de.westnordost:osmfeatures-android:1.1")
// talking with the OSM API
implementation("de.westnordost:osmapi-overpass:1.1")
implementation("de.westnordost:osmapi-map:1.2")
implementation("de.westnordost:osmapi-changesets:1.2")
implementation("de.westnordost:osmapi-notes:1.1")
implementation("de.westnordost:osmapi-user:1.1")
implementation("de.westnordost:osmapi-map:1.3")
implementation("de.westnordost:osmapi-changesets:1.3")
implementation("de.westnordost:osmapi-notes:1.2")
implementation("de.westnordost:osmapi-user:1.2")

// widgets
implementation("androidx.viewpager2:viewpager2:1.0.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.junit.Before
import org.junit.Test

import de.westnordost.streetcomplete.data.ApplicationDbTestCase
import de.westnordost.streetcomplete.ktx.containsExactlyInAnyOrder
import de.westnordost.streetcomplete.util.Tile
import de.westnordost.streetcomplete.util.TilesRect

Expand Down Expand Up @@ -76,6 +77,6 @@ class DownloadedTilesDaoTest : ApplicationDbTestCase() {
check = dao.get(r(0, 0, 6, 6), 0)
assertTrue(check.isEmpty())
}

private fun r(left: Int, top: Int, right: Int, bottom: Int) = TilesRect(left, top, right, bottom)
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,13 @@ private val TEST_QUEST_TYPE = TestQuestType()
private val TEST_QUEST_TYPE2 = TestQuestType2()

private fun create(
questType: OsmElementQuestType<*> = TEST_QUEST_TYPE,
elementType: Element.Type = Element.Type.NODE,
elementId: Long = 1,
status: QuestStatus = QuestStatus.NEW,
geometry: ElementGeometry = ElementPointGeometry(OsmLatLon(5.0, 5.0)),
changes: StringMapChanges? = null,
changesSource: String? = null
questType: OsmElementQuestType<*> = TEST_QUEST_TYPE,
elementType: Element.Type = Element.Type.NODE,
elementId: Long = 1,
status: QuestStatus = QuestStatus.NEW,
geometry: ElementGeometry = ElementPointGeometry(OsmLatLon(5.0, 5.0)),
changes: StringMapChanges? = null,
changesSource: String? = null
) = OsmQuest(
null, questType, elementType, elementId, status, changes, changesSource, Date(), geometry
)
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package de.westnordost.streetcomplete.data.osm.osmquest

import de.westnordost.osmapi.map.data.BoundingBox
import de.westnordost.osmapi.map.MapDataWithGeometry
import de.westnordost.osmapi.map.data.Element
import de.westnordost.streetcomplete.data.osm.changes.StringMapChangesBuilder
import de.westnordost.streetcomplete.data.osm.elementgeometry.ElementGeometry
import de.westnordost.streetcomplete.quests.AbstractQuestAnswerFragment

open class TestQuestType : OsmElementQuestType<String> {

override fun getTitle(tags: Map<String, String>) = 0
override fun download(bbox: BoundingBox, handler: (element: Element, geometry: ElementGeometry?) -> Unit) = false
override fun isApplicableTo(element: Element):Boolean? = null
override fun applyAnswerTo(answer: String, changes: StringMapChangesBuilder) {}
override val icon = 0
override fun createForm(): AbstractQuestAnswerFragment<String> = object : AbstractQuestAnswerFragment<String>() {}
override val commitMessage = ""
override fun getApplicableElements(mapData: MapDataWithGeometry) = emptyList<Element>()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package de.westnordost.osmapi.map

import de.westnordost.osmapi.changesets.Changeset
import de.westnordost.osmapi.map.data.*
import java.util.*

/** Same as OsmMapDataFactory only that it throws away the Changeset data included in the OSM
* response */
class LightweightOsmMapDataFactory : MapDataFactory {
override fun createNode(
id: Long, version: Int, lat: Double, lon: Double, tags: MutableMap<String, String>?,
changeset: Changeset?, dateEdited: Date?
): Node = OsmNode(id, version, lat, lon, tags, null, dateEdited)

override fun createWay(
id: Long, version: Int, nodes: MutableList<Long>, tags: MutableMap<String, String>?,
changeset: Changeset?, dateEdited: Date?
): Way = OsmWay(id, version, nodes, tags, null, dateEdited)

override fun createRelation(
id: Long, version: Int, members: MutableList<RelationMember>,
tags: MutableMap<String, String>?, changeset: Changeset?, dateEdited: Date?
): Relation = OsmRelation(id, version, members, tags, null, dateEdited)

override fun createRelationMember(
ref: Long, role: String?, type: Element.Type
): RelationMember = OsmRelationMember(ref, role, type)
}
85 changes: 85 additions & 0 deletions app/src/main/java/de/westnordost/osmapi/map/MapData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package de.westnordost.osmapi.map

import de.westnordost.osmapi.map.data.*
import de.westnordost.osmapi.map.handler.MapDataHandler
import de.westnordost.streetcomplete.data.osm.elementgeometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.elementgeometry.ElementPointGeometry
import de.westnordost.streetcomplete.util.MultiIterable

interface MapDataWithGeometry : MapData {
fun getNodeGeometry(id: Long): ElementPointGeometry?
fun getWayGeometry(id: Long): ElementGeometry?
fun getRelationGeometry(id: Long): ElementGeometry?

fun getGeometry(elementType: Element.Type, id: Long): ElementGeometry? = when(elementType) {
Element.Type.NODE -> getNodeGeometry(id)
Element.Type.WAY -> getWayGeometry(id)
Element.Type.RELATION -> getRelationGeometry(id)
}
}

interface MapData : Iterable<Element> {
val nodes: Collection<Node>
val ways: Collection<Way>
val relations: Collection<Relation>
val boundingBox: BoundingBox?

fun getNode(id: Long): Node?
fun getWay(id: Long): Way?
fun getRelation(id: Long): Relation?
}

open class MutableMapData : MapData, MapDataHandler {

protected val nodesById: MutableMap<Long, Node> = mutableMapOf()
protected val waysById: MutableMap<Long, Way> = mutableMapOf()
protected val relationsById: MutableMap<Long, Relation> = mutableMapOf()
override var boundingBox: BoundingBox? = null
protected set

override fun handle(bounds: BoundingBox) { boundingBox = bounds }
override fun handle(node: Node) { nodesById[node.id] = node }
override fun handle(way: Way) { waysById[way.id] = way }
override fun handle(relation: Relation) { relationsById[relation.id] = relation }

override val nodes get() = nodesById.values
override val ways get() = waysById.values
override val relations get() = relationsById.values

override fun getNode(id: Long) = nodesById[id]
override fun getWay(id: Long) = waysById[id]
override fun getRelation(id: Long) = relationsById[id]

fun addAll(elements: Iterable<Element>) {
for (element in elements) {
when(element) {
is Node -> nodesById[element.id] = element
is Way -> waysById[element.id] = element
is Relation -> relationsById[element.id] = element
}
}
}

override fun iterator(): Iterator<Element> {
val elements = MultiIterable<Element>()
elements.add(nodes)
elements.add(ways)
elements.add(relations)
return elements.iterator()
}
}

fun MapData.isRelationComplete(id: Long): Boolean =
getRelation(id)?.members?.all { member ->
when (member.type!!) {
Element.Type.NODE -> getNode(member.ref) != null
Element.Type.WAY -> getWay(member.ref) != null && isWayComplete(member.ref)
/* not being recursive here is deliberate. sub-relations are considered not relevant
for the element geometry in StreetComplete (and OSM API call to get a "complete"
relation also does not include sub-relations) */
Element.Type.RELATION -> getRelation(member.ref) != null
}
} ?: false

fun MapData.isWayComplete(id: Long): Boolean =
getWay(id)?.nodeIds?.all { getNode(it) != null } ?: false
22 changes: 22 additions & 0 deletions app/src/main/java/de/westnordost/osmapi/map/MapDataApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package de.westnordost.osmapi.map

import de.westnordost.osmapi.map.data.BoundingBox
import de.westnordost.streetcomplete.data.MapDataApi

fun MapDataApi.getMap(bounds: BoundingBox): MapData {
val result = MutableMapData()
getMap(bounds, result)
return result
}

fun MapDataApi.getWayComplete(id: Long): MapData {
val result = MutableMapData()
getWayComplete(id, result)
return result
}

fun MapDataApi.getRelationComplete(id: Long): MapData {
val result = MutableMapData()
getRelationComplete(id, result)
return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ interface ApplicationComponent {
fun inject(undoButtonFragment: UndoButtonFragment)
fun inject(uploadButtonFragment: UploadButtonFragment)
fun inject(answersCounterFragment: AnswersCounterFragment)
fun inject(questDownloadProgressFragment: QuestDownloadProgressFragment)
fun inject(downloadProgressFragment: DownloadProgressFragment)
fun inject(questStatisticsByCountryFragment: QuestStatisticsByCountryFragment)
fun inject(questStatisticsByQuestTypeFragment: QuestStatisticsByQuestTypeFragment)
fun inject(privacyStatementFragment: PrivacyStatementFragment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,21 @@ public class ApplicationConstants
QUESTTYPE_TAG_KEY = NAME + ":quest_type";

public final static double
MAX_DOWNLOADABLE_AREA_IN_SQKM = 20,
MIN_DOWNLOADABLE_AREA_IN_SQKM = 1;

public final static double MIN_DOWNLOADABLE_RADIUS_IN_METERS = 600;
MAX_DOWNLOADABLE_AREA_IN_SQKM = 12.0,
MIN_DOWNLOADABLE_AREA_IN_SQKM = 0.1;

public final static String DATABASE_NAME = "streetcomplete.db";

public final static int QUEST_TILE_ZOOM = 14;
public final static int QUEST_TILE_ZOOM = 16;

public final static int NOTE_MIN_ZOOM = 15;

/** How many quests to download when pressing manually on "download quests" */
public final static int MANUAL_DOWNLOAD_QUEST_TYPE_COUNT = 10;

/** a "best before" duration for quests. Quests will not be downloaded again for any tile
* before the time expired */
public static final long REFRESH_QUESTS_AFTER = 7L*24*60*60*1000; // 1 week in ms
/** the duration after which quests will be deleted from the database if unsolved */
public static final long DELETE_UNSOLVED_QUESTS_AFTER = 1L*30*24*60*60*1000; // 1 months in ms
public static final long REFRESH_QUESTS_AFTER = 3L*24*60*60*1000; // 3 days in ms
/** the duration after which quests (and quest meta data) will be deleted from the database if
* unsolved and not refreshed in the meantime */
public static final long DELETE_UNSOLVED_QUESTS_AFTER = 14*24*60*60*1000; // 14 days in ms

/** the max age of the undo history - one cannot undo changes older than X */
public static final long MAX_QUEST_UNDO_HISTORY_AGE = 24*60*60*1000; // 1 day in ms
Expand Down
23 changes: 11 additions & 12 deletions app/src/main/java/de/westnordost/streetcomplete/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
Expand All @@ -28,9 +29,6 @@
import android.widget.TextView;
import android.widget.Toast;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.inject.Inject;

import de.westnordost.osmapi.common.errors.OsmApiException;
Expand All @@ -40,12 +38,13 @@
import de.westnordost.osmapi.map.data.LatLon;
import de.westnordost.osmapi.map.data.OsmLatLon;
import de.westnordost.streetcomplete.controls.NotificationButtonFragment;
import de.westnordost.streetcomplete.data.download.DownloadItem;
import de.westnordost.streetcomplete.data.notifications.Notification;
import de.westnordost.streetcomplete.data.notifications.NotificationsSource;
import de.westnordost.streetcomplete.data.quest.Quest;
import de.westnordost.streetcomplete.data.quest.QuestAutoSyncer;
import de.westnordost.streetcomplete.data.quest.QuestController;
import de.westnordost.streetcomplete.data.download.QuestDownloadProgressListener;
import de.westnordost.streetcomplete.data.download.DownloadProgressListener;
import de.westnordost.streetcomplete.data.download.QuestDownloadController;
import de.westnordost.streetcomplete.data.quest.QuestType;
import de.westnordost.streetcomplete.data.quest.UnsyncedChangesCountSource;
Expand Down Expand Up @@ -193,7 +192,7 @@ private void handleGeoUri()
questDownloadController.setShowNotification(false);

uploadController.addUploadProgressListener(uploadProgressListener);
questDownloadController.addQuestDownloadProgressListener(downloadProgressListener);
questDownloadController.addDownloadProgressListener(downloadProgressListener);

if(!hasAskedForLocation && !prefs.getBoolean(Prefs.LAST_LOCATION_REQUEST_DENIED, false))
{
Expand Down Expand Up @@ -253,7 +252,7 @@ public boolean dispatchKeyEvent(KeyEvent event) {
questDownloadController.setShowNotification(true);

uploadController.removeUploadProgressListener(uploadProgressListener);
questDownloadController.removeQuestDownloadProgressListener(downloadProgressListener);
questDownloadController.removeDownloadProgressListener(downloadProgressListener);
}

@Override public void onConfigurationChanged(@NonNull Configuration newConfig) {
Expand Down Expand Up @@ -346,14 +345,14 @@ else if(e instanceof OsmAuthorizationException)

/* ----------------------------- Download Progress listener -------------------------------- */

private final QuestDownloadProgressListener downloadProgressListener
= new QuestDownloadProgressListener()
private final DownloadProgressListener downloadProgressListener
= new DownloadProgressListener()
{
@AnyThread @Override public void onStarted() {}

@Override public void onFinished(@NotNull QuestType<?> questType) {}
@Override public void onFinished(@NonNull DownloadItem item) {}

@Override public void onStarted(@NotNull QuestType<?> questType) {}
@Override public void onStarted(@NonNull DownloadItem item) {}

@AnyThread @Override public void onError(@NonNull final Exception e)
{
Expand Down Expand Up @@ -386,7 +385,7 @@ else if(e instanceof OsmAuthorizationException)

/* --------------------------------- NotificationButtonFragment.Listener ---------------------------------- */

@Override public void onClickShowNotification(@NotNull Notification notification)
@Override public void onClickShowNotification(@NonNull Notification notification)
{
Fragment f = getSupportFragmentManager().findFragmentById(R.id.notifications_container_fragment);
((NotificationsContainerFragment) f).showNotification(notification);
Expand All @@ -399,7 +398,7 @@ else if(e instanceof OsmAuthorizationException)
ensureLoggedIn();
}

@Override public void onCreatedNote(@NotNull Point screenPosition)
@Override public void onCreatedNote(@NonNull Point screenPosition)
{
ensureLoggedIn();
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/de/westnordost/streetcomplete/Prefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public class Prefs
KEEP_SCREEN_ON = "display.keepScreenOn",
UNGLUE_HINT_TIMES_SHOWN = "unglueHint.shown",
THEME_SELECT = "theme.select",
OVERPASS_URL = "overpass_url",
RESURVEY_INTERVALS = "quests.resurveyIntervals";


Expand All @@ -38,6 +37,7 @@ public class Prefs
LAST_PICKED_PREFIX = "imageListLastPicked.",
LAST_LOCATION_REQUEST_DENIED = "location.denied",
LAST_VERSION = "lastVersion",
LAST_VERSION_DATA = "lastVersion_data",
HAS_SHOWN_TUTORIAL = "hasShownTutorial";

public static final String HAS_SHOWN_UNDO_FUCKUP_WARNING = "alert.undo_fuckup_warning";
Expand Down
Loading

1 comment on commit 761f286

@CalliOSM
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank You! This is an exceptional upgrade! 🚀

Please sign in to comment.