From fb7655603d47175882f01ffd47c2978640545138 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Tue, 7 Dec 2021 01:12:31 -0500 Subject: [PATCH 01/30] Add super simple visualization of GPX track recording Can hold click on map to start recording a GPX. Then the "stop" button can be clicked to stop. This will then create a note (next step is to write uploader and add link to the GPX path in the note). --- .../assets/map_theme/jawg/streetcomplete.yaml | 55 +++++++++++++------ .../map/LocationAwareMapFragment.kt | 24 +++++++- .../streetcomplete/map/MainFragment.kt | 31 +++++++++++ .../map/components/TracksMapComponent.kt | 18 ++++-- app/src/main/res/layout/fragment_main.xml | 10 ++++ app/src/main/res/menu/menu_map_context.xml | 4 ++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 118 insertions(+), 26 deletions(-) diff --git a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml index 9c8a236e63..7f0f48c26b 100644 --- a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml +++ b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml @@ -3,6 +3,7 @@ global: geometry_line_color: '#44D14000' geometry_point_color: '#88D14000' track_color: '#44536dfe' + track_color_record: '#44fe1616' old_track_color: '#22536dfe' textures: @@ -126,7 +127,7 @@ layers: streetcomplete_track: data: { source: streetcomplete_track } current: - filter: { old: [false] } + filter: { old: [false], record: [false] } draw: track-lines: color: global.track_color @@ -134,8 +135,17 @@ layers: collide: false join: round order: 1000 + record: + filter: { record: [true] } + draw: + track-lines: + color: global.track_color_record + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 old: - filter: { old: [true] } + filter: { old: [true], record: [false] } draw: track-lines: color: global.old_track_color @@ -146,20 +156,29 @@ layers: streetcomplete_track2: data: { source: streetcomplete_track2 } current: - filter: { old: [false] } - draw: - track-lines: - color: global.track_color - width: [[14, 6px],[18, 12px]] - collide: false - join: round - order: 1000 + filter: { old: [ false ], record: [ false ] } + draw: + track-lines: + color: global.track_color + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 + record: + filter: { record: [ true ] } + draw: + track-lines: + color: global.track_color_record + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 old: - filter: { old: [true] } - draw: - track-lines: - color: global.old_track_color - width: [[14, 6px],[18, 12px]] - collide: false - join: round - order: 1000 + filter: { old: [ true ], record: [ false ] } + draw: + track-lines: + color: global.old_track_color + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index 3559c6e8d7..a5ba36812c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -35,6 +35,9 @@ open class LocationAwareMapFragment : MapFragment() { var displayedLocation: Location? = null private set + /** If we are actively performing GPX tracking */ + var gpxTracking = false; + /** The GPS trackpoints the user has walked */ private var tracks: MutableList> @@ -130,11 +133,28 @@ open class LocationAwareMapFragment : MapFragment() { locationManager.requestUpdates(2000, 1f) } + @SuppressLint("MissingPermission") + fun startPositionTrackingGPX() { + gpxTracking = true + tracks.add(ArrayList()) + locationMapComponent?.isVisible = true + locationManager.requestUpdates(500, 1f) + tracksMapComponent?.startNewTrack(true) + } + fun stopPositionTracking() { locationMapComponent?.isVisible = false locationManager.removeUpdates() } + fun stopPositionTrackingGPX() : ArrayList { + gpxTracking = false + val recordedTracks = tracks.last() + tracks.add(ArrayList()) + tracksMapComponent?.startNewTrack(false) + return recordedTracks + } + fun clearPositionTracking() { stopPositionTracking() displayedLocation = null @@ -197,10 +217,10 @@ open class LocationAwareMapFragment : MapFragment() { val lastLocation = tracks.last().lastOrNull() // create new track if last position too old - if (lastLocation != null) { + if (lastLocation != null && !gpxTracking) { if ((displayedLocation?.time ?: 0) - lastLocation.time > MAX_TIME_BETWEEN_LOCATIONS) { tracks.add(ArrayList()) - tracksMapComponent?.startNewTrack() + tracksMapComponent?.startNewTrack(false) } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 9034a539fb..2af2f69dc6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -162,6 +162,7 @@ class MainFragment : Fragment(R.layout.fragment_main), binding.compassView.setOnClickListener { onClickCompassButton() } binding.gpsTrackingButton.setOnClickListener { onClickTrackingButton() } + binding.stopGPXButton.setOnClickListener { onClickGPXStop() } binding.zoomInButton.setOnClickListener { onClickZoomIn() } binding.zoomOutButton.setOnClickListener { onClickZoomOut() } @@ -607,6 +608,29 @@ class MainFragment : Fragment(R.layout.fragment_main), mapFragment?.updateCameraPosition(300) { zoomBy = +1f } } + private fun onClickGPXStop() { + + // hide the track information + // TODO: save as a object to be uploaded (like picture) + binding.stopGPXButton.visibility = View.INVISIBLE + val mapFragment = mapFragment ?: return + val tracks = mapFragment.stopPositionTrackingGPX() + android.util.Log.e("MAIN", "total of ${tracks.size} tracks recorded!") + android.util.Log.e("MAIN", "total of ${tracks.size} tracks recorded!") + + // show the note dialog + mapFragment.show3DBuildings = false + val location = mapFragment.displayedLocation ?: return + val pos = LatLon(location.latitude, location.longitude) + val offsetPos = mapFragment.getPositionThatCentersPosition(pos, mapOffsetWithOpenBottomSheet) + mapFragment.updateCameraPosition { position = offsetPos } + + // TODO: attach link to the note... + freezeMap() + showInBottomSheet(CreateNoteFragment()) + + } + private fun onClickCompassButton() { /* Clicking the compass button will always rotate the map back to north and remove tilt */ val mapFragment = mapFragment ?: return @@ -665,6 +689,7 @@ class MainFragment : Fragment(R.layout.fragment_main), popupMenu.setOnMenuItemClickListener { item -> when(item.itemId) { R.id.action_create_note -> onClickCreateNote(position) + R.id.action_create_gpx_track -> onClickCreateGPXTrack(position) R.id.action_open_location -> onClickOpenLocationInOtherApp(position) } true @@ -707,6 +732,12 @@ class MainFragment : Fragment(R.layout.fragment_main), showInBottomSheet(CreateNoteFragment()) } + private fun onClickCreateGPXTrack(pos: LatLon) { + val mapFragment = mapFragment ?: return + mapFragment.startPositionTrackingGPX() + binding.stopGPXButton.visibility = View.VISIBLE + } + // ---------------------------------- Location Pointer Pin --------------------------------- */ private fun updateLocationPointerPin() { diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt index 3e80bad950..fb195fada4 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt @@ -22,46 +22,52 @@ class TracksMapComponent(ctrl: KtMapController) { private var index = 0 private var tracks: MutableList> + private var tracksRecording: MutableList init { tracks = ArrayList() tracks.add(ArrayList()) + tracksRecording = ArrayList() } /** Add a point to the current track */ fun addToCurrentTrack(pos: Location) { val track = tracks.last() + val recording = tracksRecording.last() track.add(pos.toLngLat()) // every 100th trackpoint, move the index to the back if (track.size - index > 100) { putAllTracksInOldLayer() } else { - layer1.setFeatures(listOf(track.subList(index, track.size).toPolyline(false))) + layer1.setFeatures(listOf(track.subList(index, track.size).toPolyline(false, recording))) } } /** Start a new track. I.e. the points in that track will be drawn as an own polyline */ - fun startNewTrack() { + fun startNewTrack(record: Boolean) { tracks.add(ArrayList()) + tracksRecording.add(record) putAllTracksInOldLayer() } /** Set all the tracks (when re-initializing) */ fun setTracks(tracks: List>) { this.tracks = tracks.map { track -> track.map { it.toLngLat() }.toMutableList() }.toMutableList() + this.tracksRecording = tracks.map { false }.toMutableList() putAllTracksInOldLayer() } private fun putAllTracksInOldLayer() { index = max(0, tracks.last().lastIndex) layer1.clear() - layer2.setFeatures(tracks.map { it.toPolyline(true) }) + layer2.setFeatures(tracks.mapIndexed { idx, it -> it.toPolyline(true, tracksRecording[idx]) }) } fun clear() { tracks = ArrayList() - startNewTrack() + tracksRecording = ArrayList() + startNewTrack(false) } companion object { @@ -72,5 +78,5 @@ class TracksMapComponent(ctrl: KtMapController) { } } -private fun List.toPolyline(old: Boolean) = - Polyline(this, mapOf("type" to "line", "old" to old.toString())) +private fun List.toPolyline(old: Boolean, record: Boolean) = + Polyline(this, mapOf("type" to "line", "old" to old.toString(), "record" to record.toString())) diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 2d2412f32f..ae80c5a140 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -86,6 +86,16 @@ android:clipToPadding="false" tools:ignore="RtlHardcoded,RtlSymmetry"> + + + "Upload answers automatically" "Zoom out" "Zoom in" + "Stop GPX" "Menu" "<p>Ah, someone who cares about privacy, nice! I think I have only good news for you:</p> <p><b>Direct Contributions</b></p> @@ -496,6 +497,7 @@ Otherwise, you can download another keyboard in the app store. Popular keyboards What is the structure of this bridge? Also cycleway on other sideā€¦ Create new note + Create new GPX track Created note Commented note You hid it From 28496a7f8a91eaaf1b47b3d86cd95c6a81d1f1fb Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Tue, 7 Dec 2021 23:31:50 -0500 Subject: [PATCH 02/30] add insertion of track and partial upload information into database - will record partial image uploads into the database (incase the GPX upload fails) - now serializes the tracks into the database and the note when created - include the osm-track plugin (will be used to create valid GPX) --- app/build.gradle.kts | 1 + .../data/StreetCompleteSQLiteOpenHelper.kt | 4 +++ .../streetcomplete/data/osmnotes/Note.kt | 8 ++++++ .../data/osmnotes/edits/NoteEdit.kt | 11 ++++++-- .../osmnotes/edits/NoteEditsController.kt | 18 ++++++++++-- .../data/osmnotes/edits/NoteEditsDao.kt | 11 +++++++- .../data/osmnotes/edits/NoteEditsTable.kt | 4 +++ .../data/osmnotes/edits/NoteEditsUploader.kt | 28 ++++++++++++++++++- .../data/quest/QuestController.kt | 6 ++-- .../map/LocationAwareMapFragment.kt | 20 +++++++++++-- .../streetcomplete/map/MainFragment.kt | 11 ++++---- 11 files changed, 105 insertions(+), 17 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8b11f72cab..a971ffc8bd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -158,6 +158,7 @@ dependencies { implementation("de.westnordost:osmapi-map:2.0") implementation("de.westnordost:osmapi-changesets:2.0") implementation("de.westnordost:osmapi-notes:2.0") + implementation("de.westnordost:osmapi-traces:2.0") implementation("de.westnordost:osmapi-user:2.0") implementation("com.squareup.okhttp3:okhttp:3.12.13") implementation("se.akerfeldt:okhttp-signpost:1.1.0") diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt index 3b89b98e9e..4709d7531b 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt @@ -124,6 +124,10 @@ import de.westnordost.streetcomplete.quests.oneway_suspects.data.WayTrafficFlowT if (oldVersion <= 3 && newVersion > 3) { db.execSQL("DROP TABLE new_achievements") } + if (oldVersion <= 4 && newVersion > 4) { + db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.GPX_TRACKS} text DEFAULT '[]' NOT NULL") + db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.UPLOAD_DATA_MAP} text DEFAULT '{}' NOT NULL") + } } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt index e4b77213f7..35bc03bf69 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt @@ -35,3 +35,11 @@ data class NoteComment( OPENED, COMMENTED, CLOSED, REOPENED, HIDDEN } } + +@Serializable +data class NoteGPXTrack( + val position: LatLon, + val time: Long, + val horizontalDilutionOfPrecision: Float, + val elevation: Float, +) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index 5ec475e5e6..43b91ae232 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -3,6 +3,7 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import de.westnordost.streetcomplete.data.edithistory.Edit import de.westnordost.streetcomplete.data.edithistory.NoteEditKey import de.westnordost.streetcomplete.data.osm.mapdata.LatLon +import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack /** Contains all necessary information to create/comment on an OSM note. */ data class NoteEdit( @@ -31,8 +32,14 @@ data class NoteEdit( /** whether this edit has been uploaded already */ override val isSynced: Boolean, - /** Whether the images attached still need activation. Already true if imagePaths is empty */ - val imagesNeedActivation: Boolean + /** whether the images attached still need activation. Already true if imagePaths is empty */ + val imagesNeedActivation: Boolean, + + /** attached GPX location tracks */ + val tracks: List, + + /** contains partial upload responses index by type (e.g. image and GPX track urls) */ + val uploadedDataMap: Map ): Edit { override val isUndoable: Boolean get() = !isSynced override val key: NoteEditKey get() = NoteEditKey(id) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index e0938ba155..98b69fc4dd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -3,6 +3,9 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osmnotes.Note +import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import java.lang.System.currentTimeMillis import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -21,7 +24,8 @@ import javax.inject.Singleton action: NoteEditAction, position: LatLon, text: String? = null, - imagePaths: List = emptyList() + imagePaths: List = emptyList(), + tracks: List = emptyList() ) { val edit = NoteEdit( 0, @@ -32,7 +36,9 @@ import javax.inject.Singleton imagePaths, currentTimeMillis(), false, - imagePaths.isNotEmpty() + imagePaths.isNotEmpty(), + tracks, + mutableMapOf() ) synchronized(this) { editsDB.add(edit) } onAddedEdit(edit) @@ -85,6 +91,14 @@ import javax.inject.Singleton } } + fun updateData(edit: NoteEdit, name: String, data: String) { + synchronized(this) { + val tmpData = edit.uploadedDataMap.toMutableMap() + tmpData[name] = data + editsDB.updateUploadData(edit.noteId, Json.encodeToString(tmpData)) + } + } + fun markSyncFailed(edit: NoteEdit): Boolean = delete(edit) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt index da169595e6..b2d6ad99ed 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt @@ -5,6 +5,7 @@ import de.westnordost.streetcomplete.data.Database import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.CREATED_TIMESTAMP +import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.GPX_TRACKS import javax.inject.Inject import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.ID import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.IMAGES_NEED_ACTIVATION @@ -15,6 +16,7 @@ import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns. import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.NOTE_ID import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TEXT import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TYPE +import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.UPLOAD_DATA_MAP import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.NAME import de.westnordost.streetcomplete.ktx.* import kotlinx.serialization.decodeFromString @@ -105,6 +107,9 @@ class NoteEditsDao @Inject constructor(private val db: Database) { fun updateNoteId(oldNoteId: Long, newNoteId: Long): Int = db.update(NAME, listOf(NOTE_ID to newNoteId), "$NOTE_ID = $oldNoteId") + fun updateUploadData(noteId: Long, data: String): Int = + db.update(NAME, listOf(UPLOAD_DATA_MAP to data), "$NOTE_ID = $noteId") + fun getOldestNeedingImagesActivation(): NoteEdit? = db.queryOne(NAME, where = "$IS_SYNCED = 1 AND $IMAGES_NEED_ACTIVATION = 1", orderBy = CREATED_TIMESTAMP) { it.toNoteEdit() } @@ -125,6 +130,8 @@ class NoteEditsDao @Inject constructor(private val db: Database) { TEXT to text, IMAGE_PATHS to Json.encodeToString(imagePaths), IMAGES_NEED_ACTIVATION to if (imagesNeedActivation) 1 else 0, + GPX_TRACKS to Json.encodeToString(tracks), + UPLOAD_DATA_MAP to Json.encodeToString(uploadedDataMap), TYPE to action.name ) @@ -137,6 +144,8 @@ class NoteEditsDao @Inject constructor(private val db: Database) { Json.decodeFromString(getString(IMAGE_PATHS)), getLong(CREATED_TIMESTAMP), getInt(IS_SYNCED) == 1, - getInt(IMAGES_NEED_ACTIVATION) == 1 + getInt(IMAGES_NEED_ACTIVATION) == 1, + Json.decodeFromString(getString(GPX_TRACKS)), + Json.decodeFromString(getString(UPLOAD_DATA_MAP)) ) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt index 2796b2ff5e..a3c0f03dd5 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt @@ -14,6 +14,8 @@ object NoteEditsTable { const val TEXT = "text" const val IMAGE_PATHS = "image_paths" const val IMAGES_NEED_ACTIVATION = "images_need_activation" + const val GPX_TRACKS = "gpx_tracks" + const val UPLOAD_DATA_MAP = "upload_data_map" } const val CREATE = """ @@ -27,6 +29,8 @@ object NoteEditsTable { ${Columns.TEXT} text, ${Columns.IMAGE_PATHS} text NOT NULL, ${Columns.IMAGES_NEED_ACTIVATION} int NOT NULL, + ${Columns.GPX_TRACKS} text NOT NULL, + ${Columns.UPLOAD_DATA_MAP} text NOT NULL, ${Columns.TYPE} varchar(255) );""" diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 786a9c8dcb..091ff4d9b3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -55,8 +55,17 @@ class NoteEditsUploader @Inject constructor( } private fun uploadEdit(edit: NoteEdit) { - val text = edit.text.orEmpty() + uploadAndGetAttachedPhotosText(edit.imagePaths) + // try to upload the image + val imageText = edit.uploadedDataMap.getOrDefault("images", uploadAndGetAttachedPhotosText(edit.imagePaths)) + noteEditsController.updateData(edit, "images", imageText) + + // try to upload the GPX tracks + val gpxText = edit.uploadedDataMap.getOrDefault("gpx", uploadAndGetAttachedGPXText(edit.tracks)) + noteEditsController.updateData(edit, "gpx", gpxText) + + // done, try to upload the note to OSM + val text = edit.text.orEmpty() + imageText + gpxText try { val note = when(edit.action) { CREATE -> notesApi.create(edit.position, text) @@ -106,6 +115,23 @@ class NoteEditsUploader @Inject constructor( return "" } + private fun uploadAndGetAttachedGPXText(tracks: List): String { + if (tracks.isEmpty()) { + return "" + } + + // https://github.com/westnordost/osmapi/blob/396e21ef55f8f05fe69273cd67f2d084938354c2/libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTracesApi.java#L20 + throw RuntimeException("GPX Upload Has Not Been Implemented!") + + //val urls = imageUploader.upload(imagePaths) + //if (urls.isNotEmpty()) { + // return "\n\nAttached photo(s):\n" + urls.joinToString("\n") + //} + + } + + + companion object { private const val TAG = "NoteEditsUploader" private const val NOTE = "NOTE" diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt index 6ec8ce4a29..bc4091ae00 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt @@ -14,6 +14,7 @@ import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osm.mapdata.Way import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuest import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuestController +import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditAction import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsController import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuest @@ -64,10 +65,11 @@ import kotlin.collections.ArrayList suspend fun createNote( text: String, imagePaths: List, - position: LatLon + position: LatLon, + tracks: ArrayList ) = withContext(Dispatchers.IO) { val fullText = "$text\n\nvia ${ApplicationConstants.USER_AGENT}" - noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths) + noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, tracks) } /** Split a way for the given OSM Quest. diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index a5ba36812c..a9d4d54619 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -10,6 +10,7 @@ import android.view.WindowManager import androidx.core.content.edit import androidx.core.content.getSystemService import de.westnordost.streetcomplete.data.osm.mapdata.LatLon +import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.location.FineLocationManager import de.westnordost.streetcomplete.location.toLatLon @@ -19,6 +20,7 @@ import de.westnordost.streetcomplete.map.tangram.screenBottomToCenterDistance import de.westnordost.streetcomplete.util.translate import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.time.Instant import kotlin.math.PI /** Manages a map that shows the device's GPS location and orientation as markers on the map with @@ -41,6 +43,9 @@ open class LocationAwareMapFragment : MapFragment() { /** The GPS trackpoints the user has walked */ private var tracks: MutableList> + /** The GPS trackpoints the user has recorded */ + var tracksRecorded: ArrayList + /** Whether the view should automatically center on the GPS location */ var isFollowingPosition = true set(value) { @@ -76,6 +81,7 @@ open class LocationAwareMapFragment : MapFragment() { init { tracks = ArrayList() tracks.add(ArrayList()) + tracksRecorded = ArrayList() } override fun onAttach(context: Context) { @@ -147,12 +153,20 @@ open class LocationAwareMapFragment : MapFragment() { locationManager.removeUpdates() } - fun stopPositionTrackingGPX() : ArrayList { + fun stopPositionTrackingGPX() { gpxTracking = false - val recordedTracks = tracks.last() + tracksRecorded.clear() + tracks.last().forEach { + // time here is in milliseconds + // TODO: why is altitude zero in emulation? + tracksRecorded.add( + NoteGPXTrack( + LatLon(it.latitude, it.longitude), it.time, it.accuracy, it.altitude.toFloat() + ) + ) + } tracks.add(ArrayList()) tracksMapComponent?.startNewTrack(false) - return recordedTracks } fun clearPositionTracking() { diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 2af2f69dc6..a89066833d 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -493,7 +493,10 @@ class MainFragment : Fragment(R.layout.fragment_main), */ closeBottomSheet() - viewLifecycleScope.launch { questController.createNote(note, imagePaths, position) } + viewLifecycleScope.launch { + questController.createNote(note, imagePaths, position, mapFragment.tracksRecorded) + mapFragment.tracksRecorded.clear() + } listener?.onCreatedNote(screenPosition) showMarkerSolvedAnimation(R.drawable.ic_quest_create_note, PointF(screenPosition)) @@ -611,12 +614,9 @@ class MainFragment : Fragment(R.layout.fragment_main), private fun onClickGPXStop() { // hide the track information - // TODO: save as a object to be uploaded (like picture) binding.stopGPXButton.visibility = View.INVISIBLE val mapFragment = mapFragment ?: return - val tracks = mapFragment.stopPositionTrackingGPX() - android.util.Log.e("MAIN", "total of ${tracks.size} tracks recorded!") - android.util.Log.e("MAIN", "total of ${tracks.size} tracks recorded!") + mapFragment.stopPositionTrackingGPX() // show the note dialog mapFragment.show3DBuildings = false @@ -625,7 +625,6 @@ class MainFragment : Fragment(R.layout.fragment_main), val offsetPos = mapFragment.getPositionThatCentersPosition(pos, mapOffsetWithOpenBottomSheet) mapFragment.updateCameraPosition { position = offsetPos } - // TODO: attach link to the note... freezeMap() showInBottomSheet(CreateNoteFragment()) From 749f296841cc0b9eeacaf817e433bce19cb50008 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 11 Dec 2021 17:20:16 -0500 Subject: [PATCH 03/30] Add uploading of GPX traces to the OSM Api --- .../streetcomplete/data/osmnotes/NotesApi.kt | 6 ++ .../data/osmnotes/NotesApiImpl.kt | 6 ++ .../data/osmnotes/edits/NoteEditsUploader.kt | 97 ++++++++++++++----- .../streetcomplete/user/LoginFragment.kt | 4 +- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt index 75c2a62244..6b585236bd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt @@ -1,5 +1,6 @@ package de.westnordost.streetcomplete.data.osmnotes +import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.download.ConnectionException @@ -65,4 +66,9 @@ interface NotesApi { * @return the incoming notes */ fun getAll(bounds: BoundingBox, limit: Int, hideClosedNoteAfter: Int): List + + /** + * Return the GPS api endpoint + */ + fun getGPXApi() : GpsTracesApi } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt index bc16800bc7..41bf8ed587 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt @@ -9,6 +9,7 @@ import de.westnordost.osmapi.common.errors.OsmConnectionException import de.westnordost.osmapi.common.errors.OsmNotFoundException import de.westnordost.osmapi.common.errors.OsmQueryTooBigException import de.westnordost.osmapi.map.data.OsmLatLon +import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.streetcomplete.data.download.ConnectionException import de.westnordost.streetcomplete.data.download.QueryTooBigException import de.westnordost.osmapi.notes.NotesApi as OsmApiNotesApi @@ -23,6 +24,7 @@ import de.westnordost.osmapi.user.User as OsmApiUser class NotesApiImpl(osm: OsmConnection) : NotesApi { private val api: OsmApiNotesApi = OsmApiNotesApi(osm) + private val apiGps: GpsTracesApi = GpsTracesApi(osm) override fun create(pos: LatLon, text: String): Note = wrapExceptions { api.create(OsmLatLon(pos.latitude, pos.longitude), text).toNote() @@ -52,6 +54,10 @@ class NotesApiImpl(osm: OsmConnection) : NotesApi { ) notes } + + override fun getGPXApi() : GpsTracesApi { + return apiGps + } } private inline fun wrapExceptions(block: () -> T): T = diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 091ff4d9b3..52390ea8dd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -1,6 +1,12 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import android.util.Log +import de.westnordost.osmapi.map.data.LatLon +import de.westnordost.osmapi.map.data.OsmLatLon +import de.westnordost.osmapi.traces.GpsTraceDetails +import de.westnordost.osmapi.traces.GpsTrackpoint +import de.westnordost.streetcomplete.ApplicationConstants +import de.westnordost.streetcomplete.data.download.ConnectionException import de.westnordost.streetcomplete.data.osmnotes.* import de.westnordost.streetcomplete.data.upload.ConflictException import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditAction.* @@ -8,6 +14,17 @@ import de.westnordost.streetcomplete.data.upload.OnUploadedChangeListener import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URLConnection +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.time.Instant +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.* import javax.inject.Inject class NoteEditsUploader @Inject constructor( @@ -26,15 +43,17 @@ class NoteEditsUploader @Inject constructor( * Drops any edits where the upload failed because of a conflict but keeps any notes where * the upload failed because attached photos could not be uploaded (so it can try again * later). */ - suspend fun upload() = mutex.withLock { withContext(Dispatchers.IO) { - // first look if any images have not been activated yet - uploadMissedImageActivations() - // then do the usual stuff - uploadEdits() - } } + suspend fun upload() = mutex.withLock { + withContext(Dispatchers.IO) { + // first look if any images have not been activated yet + uploadMissedImageActivations() + // then do the usual stuff + uploadEdits() + } + } private suspend fun uploadMissedImageActivations() { - while(true) { + while (true) { val edit = noteEditsController.getOldestNeedingImagesActivation() ?: break /* see uploadEdits */ withContext(scope.coroutineContext) { @@ -45,7 +64,7 @@ class NoteEditsUploader @Inject constructor( } private suspend fun uploadEdits() { - while(true) { + while (true) { val edit = noteEditsController.getOldestUnsynced() ?: break /* the sync of local change -> API and its response should not be cancellable because * otherwise an inconsistency in the data would occur. F.e. a note could be uploaded @@ -57,24 +76,29 @@ class NoteEditsUploader @Inject constructor( private fun uploadEdit(edit: NoteEdit) { // try to upload the image - val imageText = edit.uploadedDataMap.getOrDefault("images", uploadAndGetAttachedPhotosText(edit.imagePaths)) + val imageText = edit.uploadedDataMap.getOrDefault( + "images", + uploadAndGetAttachedPhotosText(edit.imagePaths) + ) noteEditsController.updateData(edit, "images", imageText) // try to upload the GPX tracks - val gpxText = edit.uploadedDataMap.getOrDefault("gpx", uploadAndGetAttachedGPXText(edit.tracks)) + val gpxText = + edit.uploadedDataMap.getOrDefault("gpx", uploadAndGetAttachedGPXText(edit.tracks)) noteEditsController.updateData(edit, "gpx", gpxText) // done, try to upload the note to OSM val text = edit.text.orEmpty() + imageText + gpxText try { - val note = when(edit.action) { + val note = when (edit.action) { CREATE -> notesApi.create(edit.position, text) COMMENT -> notesApi.comment(edit.noteId, text) } - Log.d(TAG, + Log.d( + TAG, "Uploaded a ${edit.action.name} to ${note.id}" + - " at ${edit.position.latitude}, ${edit.position.longitude}" + " at ${edit.position.latitude}, ${edit.position.longitude}" ) uploadedChangeListener?.onUploaded(NOTE, edit.position) @@ -88,9 +112,10 @@ class NoteEditsUploader @Inject constructor( deleteImages(edit.imagePaths) } catch (e: ConflictException) { - Log.d(TAG, + Log.d( + TAG, "Dropped a ${edit.action.name} to ${edit.noteId}" + - " at ${edit.position.latitude}, ${edit.position.longitude}: ${e.message}" + " at ${edit.position.latitude}, ${edit.position.longitude}: ${e.message}" ) uploadedChangeListener?.onDiscarded(NOTE, edit.position) @@ -120,18 +145,44 @@ class NoteEditsUploader @Inject constructor( return "" } - // https://github.com/westnordost/osmapi/blob/396e21ef55f8f05fe69273cd67f2d084938354c2/libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTracesApi.java#L20 - throw RuntimeException("GPX Upload Has Not Been Implemented!") - - //val urls = imageUploader.upload(imagePaths) - //if (urls.isNotEmpty()) { - // return "\n\nAttached photo(s):\n" + urls.joinToString("\n") - //} + try { + // Filename is just the start of the track + // https://stackoverflow.com/a/49862573/7718197 + val name = DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS") + .withZone(ZoneOffset.UTC) + .format(Instant.ofEpochSecond(tracks[0].time)) + .replace("-","_") + .replace(":","_") + .replace(" ","_") + ".gpx" + val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE + val description = ApplicationConstants.USER_AGENT + val tags = listOf(ApplicationConstants.NAME.lowercase()) + + // Generate history of trackpoints + val trackpoints = mutableListOf() + tracks.forEachIndexed { idx, it -> + trackpoints.add( + GpsTrackpoint( + OsmLatLon(it.position.latitude, it.position.longitude), + Instant.ofEpochSecond(it.time), + idx == 0, + it.horizontalDilutionOfPrecision, + it.elevation + ) + ) + } + val traceid = + notesApi.getGPXApi().create(name, visibility, description, tags, trackpoints) + val details = notesApi.getGPXApi().get(traceid) + return "\n\nGPX Trace: \nhttps://www.openstreetmap.org/user/${details.userName}/traces/${details.id}" + } catch (e: IOException) { + throw ConnectionException("Upload failed", e) + } } - companion object { private const val TAG = "NoteEditsUploader" private const val NOTE = "NOTE" diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt index 6b00522521..1b87d638fe 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt @@ -137,7 +137,9 @@ class LoginFragment : Fragment(R.layout.fragment_login), private val REQUIRED_OSM_PERMISSIONS = listOf( Permission.READ_PREFERENCES_AND_USER_DETAILS, Permission.MODIFY_MAP, - Permission.WRITE_NOTES + Permission.WRITE_NOTES, + Permission.READ_GPS_TRACES, + Permission.WRITE_GPS_TRACES ) private const val ARG_LAUNCH_AUTH = "launch_auth" From d52f54eda2afc47b26a4bcdf24cd2077fd7fac5d Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Mon, 13 Dec 2021 22:59:02 -0500 Subject: [PATCH 04/30] Move things to osmtracks, rename GPX -> Tracks, other small review changes --- .../streetcomplete/data/OsmApiModule.kt | 4 + .../data/StreetCompleteSQLiteOpenHelper.kt | 4 +- .../streetcomplete/data/osmnotes/Note.kt | 8 -- .../streetcomplete/data/osmnotes/NotesApi.kt | 5 -- .../data/osmnotes/NotesApiImpl.kt | 6 -- .../data/osmnotes/edits/NoteEdit.kt | 8 +- .../osmnotes/edits/NoteEditsController.kt | 4 +- .../data/osmnotes/edits/NoteEditsDao.kt | 6 +- .../data/osmnotes/edits/NoteEditsTable.kt | 4 +- .../data/osmnotes/edits/NoteEditsUploader.kt | 70 +++------------- .../streetcomplete/data/osmtracks/Track.kt | 19 +++++ .../data/osmtracks/TracksApi.kt | 25 ++++++ .../data/osmtracks/TracksApiImpl.kt | 82 +++++++++++++++++++ .../data/quest/QuestController.kt | 4 +- .../map/LocationAwareMapFragment.kt | 55 ++++++------- .../streetcomplete/map/MainFragment.kt | 16 ++-- app/src/main/res/layout/fragment_main.xml | 4 +- app/src/main/res/menu/menu_map_context.xml | 4 +- app/src/main/res/values/strings.xml | 4 +- 19 files changed, 197 insertions(+), 135 deletions(-) create mode 100644 app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt create mode 100644 app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt create mode 100644 app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/OsmApiModule.kt b/app/src/main/java/de/westnordost/streetcomplete/data/OsmApiModule.kt index 183c97ab8e..2f5ea5d3b6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/OsmApiModule.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/OsmApiModule.kt @@ -9,6 +9,8 @@ import de.westnordost.streetcomplete.data.osm.mapdata.MapDataApi import de.westnordost.streetcomplete.data.osm.mapdata.MapDataApiImpl import de.westnordost.streetcomplete.data.osmnotes.NotesApi import de.westnordost.streetcomplete.data.osmnotes.NotesApiImpl +import de.westnordost.streetcomplete.data.osmtracks.TracksApi +import de.westnordost.streetcomplete.data.osmtracks.TracksApiImpl import de.westnordost.streetcomplete.data.user.OAuthStore import oauth.signpost.OAuthConsumer import javax.inject.Singleton @@ -32,5 +34,7 @@ object OsmApiModule { @Provides fun notesApi(osm: OsmConnection): NotesApi = NotesApiImpl(osm) + @Provides fun tracksApi(osm: OsmConnection): TracksApi = TracksApiImpl(osm) + @Provides fun mapDataApi(osm: OsmConnection): MapDataApi = MapDataApiImpl(osm) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt index 4709d7531b..255e9e44cd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt @@ -125,11 +125,11 @@ import de.westnordost.streetcomplete.quests.oneway_suspects.data.WayTrafficFlowT db.execSQL("DROP TABLE new_achievements") } if (oldVersion <= 4 && newVersion > 4) { - db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.GPX_TRACKS} text DEFAULT '[]' NOT NULL") + db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.TRACKS} text DEFAULT '[]' NOT NULL") db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.UPLOAD_DATA_MAP} text DEFAULT '{}' NOT NULL") } } } -private const val DB_VERSION = 4 +private const val DB_VERSION = 5 diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt index 35bc03bf69..e4b77213f7 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/Note.kt @@ -35,11 +35,3 @@ data class NoteComment( OPENED, COMMENTED, CLOSED, REOPENED, HIDDEN } } - -@Serializable -data class NoteGPXTrack( - val position: LatLon, - val time: Long, - val horizontalDilutionOfPrecision: Float, - val elevation: Float, -) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt index 6b585236bd..ed1d07cc7f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt @@ -1,6 +1,5 @@ package de.westnordost.streetcomplete.data.osmnotes -import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.download.ConnectionException @@ -67,8 +66,4 @@ interface NotesApi { */ fun getAll(bounds: BoundingBox, limit: Int, hideClosedNoteAfter: Int): List - /** - * Return the GPS api endpoint - */ - fun getGPXApi() : GpsTracesApi } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt index 41bf8ed587..bc16800bc7 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApiImpl.kt @@ -9,7 +9,6 @@ import de.westnordost.osmapi.common.errors.OsmConnectionException import de.westnordost.osmapi.common.errors.OsmNotFoundException import de.westnordost.osmapi.common.errors.OsmQueryTooBigException import de.westnordost.osmapi.map.data.OsmLatLon -import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.streetcomplete.data.download.ConnectionException import de.westnordost.streetcomplete.data.download.QueryTooBigException import de.westnordost.osmapi.notes.NotesApi as OsmApiNotesApi @@ -24,7 +23,6 @@ import de.westnordost.osmapi.user.User as OsmApiUser class NotesApiImpl(osm: OsmConnection) : NotesApi { private val api: OsmApiNotesApi = OsmApiNotesApi(osm) - private val apiGps: GpsTracesApi = GpsTracesApi(osm) override fun create(pos: LatLon, text: String): Note = wrapExceptions { api.create(OsmLatLon(pos.latitude, pos.longitude), text).toNote() @@ -54,10 +52,6 @@ class NotesApiImpl(osm: OsmConnection) : NotesApi { ) notes } - - override fun getGPXApi() : GpsTracesApi { - return apiGps - } } private inline fun wrapExceptions(block: () -> T): T = diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index 43b91ae232..6336529107 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -3,7 +3,7 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import de.westnordost.streetcomplete.data.edithistory.Edit import de.westnordost.streetcomplete.data.edithistory.NoteEditKey import de.westnordost.streetcomplete.data.osm.mapdata.LatLon -import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack +import de.westnordost.streetcomplete.data.osmtracks.Trackpoint /** Contains all necessary information to create/comment on an OSM note. */ data class NoteEdit( @@ -35,10 +35,10 @@ data class NoteEdit( /** whether the images attached still need activation. Already true if imagePaths is empty */ val imagesNeedActivation: Boolean, - /** attached GPX location tracks */ - val tracks: List, + /** attached GPS location tracks */ + val tracks: List, - /** contains partial upload responses index by type (e.g. image and GPX track urls) */ + /** contains partial upload responses index by type (e.g. image and track urls) */ val uploadedDataMap: Map ): Edit { override val isUndoable: Boolean get() = !isSynced diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index 98b69fc4dd..8635664462 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -3,7 +3,7 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osmnotes.Note -import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack +import de.westnordost.streetcomplete.data.osmtracks.Trackpoint import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.lang.System.currentTimeMillis @@ -25,7 +25,7 @@ import javax.inject.Singleton position: LatLon, text: String? = null, imagePaths: List = emptyList(), - tracks: List = emptyList() + tracks: List = emptyList() ) { val edit = NoteEdit( 0, diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt index b2d6ad99ed..9f93e19bc1 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt @@ -5,7 +5,7 @@ import de.westnordost.streetcomplete.data.Database import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.CREATED_TIMESTAMP -import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.GPX_TRACKS +import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TRACKS import javax.inject.Inject import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.ID import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.IMAGES_NEED_ACTIVATION @@ -130,7 +130,7 @@ class NoteEditsDao @Inject constructor(private val db: Database) { TEXT to text, IMAGE_PATHS to Json.encodeToString(imagePaths), IMAGES_NEED_ACTIVATION to if (imagesNeedActivation) 1 else 0, - GPX_TRACKS to Json.encodeToString(tracks), + TRACKS to Json.encodeToString(tracks), UPLOAD_DATA_MAP to Json.encodeToString(uploadedDataMap), TYPE to action.name ) @@ -145,7 +145,7 @@ class NoteEditsDao @Inject constructor(private val db: Database) { getLong(CREATED_TIMESTAMP), getInt(IS_SYNCED) == 1, getInt(IMAGES_NEED_ACTIVATION) == 1, - Json.decodeFromString(getString(GPX_TRACKS)), + Json.decodeFromString(getString(TRACKS)), Json.decodeFromString(getString(UPLOAD_DATA_MAP)) ) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt index a3c0f03dd5..24e522a77d 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt @@ -14,7 +14,7 @@ object NoteEditsTable { const val TEXT = "text" const val IMAGE_PATHS = "image_paths" const val IMAGES_NEED_ACTIVATION = "images_need_activation" - const val GPX_TRACKS = "gpx_tracks" + const val TRACKS = "tracks" const val UPLOAD_DATA_MAP = "upload_data_map" } @@ -29,7 +29,7 @@ object NoteEditsTable { ${Columns.TEXT} text, ${Columns.IMAGE_PATHS} text NOT NULL, ${Columns.IMAGES_NEED_ACTIVATION} int NOT NULL, - ${Columns.GPX_TRACKS} text NOT NULL, + ${Columns.TRACKS} text NOT NULL, ${Columns.UPLOAD_DATA_MAP} text NOT NULL, ${Columns.TYPE} varchar(255) );""" diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 52390ea8dd..627241abc9 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -1,36 +1,22 @@ package de.westnordost.streetcomplete.data.osmnotes.edits import android.util.Log -import de.westnordost.osmapi.map.data.LatLon -import de.westnordost.osmapi.map.data.OsmLatLon -import de.westnordost.osmapi.traces.GpsTraceDetails -import de.westnordost.osmapi.traces.GpsTrackpoint -import de.westnordost.streetcomplete.ApplicationConstants -import de.westnordost.streetcomplete.data.download.ConnectionException import de.westnordost.streetcomplete.data.osmnotes.* import de.westnordost.streetcomplete.data.upload.ConflictException import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditAction.* +import de.westnordost.streetcomplete.data.osmtracks.Trackpoint +import de.westnordost.streetcomplete.data.osmtracks.TracksApi import de.westnordost.streetcomplete.data.upload.OnUploadedChangeListener import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.json.JSONException -import org.json.JSONObject -import java.io.IOException -import java.net.HttpURLConnection -import java.net.URLConnection -import java.text.DateFormat -import java.text.SimpleDateFormat -import java.time.Instant -import java.time.ZoneOffset -import java.time.format.DateTimeFormatter -import java.util.* import javax.inject.Inject class NoteEditsUploader @Inject constructor( private val noteEditsController: NoteEditsController, private val noteController: NoteController, private val notesApi: NotesApi, + private val tracksApi: TracksApi, private val imageUploader: StreetCompleteImageUploader ) { var uploadedChangeListener: OnUploadedChangeListener? = null @@ -82,13 +68,13 @@ class NoteEditsUploader @Inject constructor( ) noteEditsController.updateData(edit, "images", imageText) - // try to upload the GPX tracks - val gpxText = - edit.uploadedDataMap.getOrDefault("gpx", uploadAndGetAttachedGPXText(edit.tracks)) - noteEditsController.updateData(edit, "gpx", gpxText) + // try to upload the tracks + val tracksText = + edit.uploadedDataMap.getOrDefault("tracks", uploadAndGetAttachedTracksText(edit.tracks)) + noteEditsController.updateData(edit, "tracks", tracksText) // done, try to upload the note to OSM - val text = edit.text.orEmpty() + imageText + gpxText + val text = edit.text.orEmpty() + imageText + tracksText try { val note = when (edit.action) { CREATE -> notesApi.create(edit.position, text) @@ -140,46 +126,12 @@ class NoteEditsUploader @Inject constructor( return "" } - private fun uploadAndGetAttachedGPXText(tracks: List): String { + private fun uploadAndGetAttachedTracksText(tracks: List): String { if (tracks.isEmpty()) { return "" } - - try { - - // Filename is just the start of the track - // https://stackoverflow.com/a/49862573/7718197 - val name = DateTimeFormatter - .ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS") - .withZone(ZoneOffset.UTC) - .format(Instant.ofEpochSecond(tracks[0].time)) - .replace("-","_") - .replace(":","_") - .replace(" ","_") + ".gpx" - val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE - val description = ApplicationConstants.USER_AGENT - val tags = listOf(ApplicationConstants.NAME.lowercase()) - - // Generate history of trackpoints - val trackpoints = mutableListOf() - tracks.forEachIndexed { idx, it -> - trackpoints.add( - GpsTrackpoint( - OsmLatLon(it.position.latitude, it.position.longitude), - Instant.ofEpochSecond(it.time), - idx == 0, - it.horizontalDilutionOfPrecision, - it.elevation - ) - ) - } - val traceid = - notesApi.getGPXApi().create(name, visibility, description, tags, trackpoints) - val details = notesApi.getGPXApi().get(traceid) - return "\n\nGPX Trace: \nhttps://www.openstreetmap.org/user/${details.userName}/traces/${details.id}" - } catch (e: IOException) { - throw ConnectionException("Upload failed", e) - } + val track = tracksApi.create(tracks) + return "\n\nGPS Trace: \nhttps://www.openstreetmap.org/user/${track.userName}/traces/${track.id}" } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt new file mode 100644 index 0000000000..dbc27c3e8d --- /dev/null +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt @@ -0,0 +1,19 @@ +package de.westnordost.streetcomplete.data.osmtracks + +import de.westnordost.streetcomplete.data.osm.mapdata.LatLon +import kotlinx.serialization.Serializable + + +@Serializable +data class Track( + val id: Long, + val userName: String, +) + +@Serializable +data class Trackpoint( + val position: LatLon, + val time: Long, + val horizontalDilutionOfPrecision: Float, + val elevation: Float, +) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt new file mode 100644 index 0000000000..5888997ab0 --- /dev/null +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -0,0 +1,25 @@ +package de.westnordost.streetcomplete.data.osmtracks + +import de.westnordost.streetcomplete.data.download.ConnectionException +import de.westnordost.streetcomplete.data.user.AuthorizationException + +/** + * Creates GPS / GPX trackpoint histories + * All interactions with this class require an OsmConnection with a logged in user. + */ +interface TracksApi { + + /** + * Create a new GPX track history + * + * @param tracks history of recorded trackpoints + * + * @throws AuthorizationException if this application is not authorized to write notes + * (Permission.READ_GPS_TRACES, Permission.WRITE_GPS_TRACES) + * @throws ConnectionException if a temporary network connection problem occurs + * + * @return the new track + */ + fun create(tracks: List): Track + +} diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt new file mode 100644 index 0000000000..df271cd064 --- /dev/null +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -0,0 +1,82 @@ +package de.westnordost.streetcomplete.data.osmtracks + +import de.westnordost.osmapi.OsmConnection +import de.westnordost.osmapi.common.errors.OsmApiException +import de.westnordost.osmapi.common.errors.OsmApiReadResponseException +import de.westnordost.osmapi.common.errors.OsmAuthorizationException +import de.westnordost.osmapi.common.errors.OsmConflictException +import de.westnordost.osmapi.common.errors.OsmConnectionException +import de.westnordost.osmapi.common.errors.OsmQueryTooBigException +import de.westnordost.osmapi.map.data.OsmLatLon +import de.westnordost.osmapi.traces.GpsTraceDetails +import de.westnordost.osmapi.traces.GpsTracesApi +import de.westnordost.osmapi.traces.GpsTrackpoint +import de.westnordost.streetcomplete.ApplicationConstants +import de.westnordost.streetcomplete.data.download.ConnectionException +import de.westnordost.streetcomplete.data.download.QueryTooBigException +import de.westnordost.streetcomplete.data.upload.ConflictException +import de.westnordost.streetcomplete.data.user.AuthorizationException +import java.time.Instant +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter + +class TracksApiImpl(osm: OsmConnection) : TracksApi { + private val api: GpsTracesApi = GpsTracesApi(osm) + + override fun create(tracks: List): Track = wrapExceptions { + + // Filename is just the start of the track + // https://stackoverflow.com/a/49862573/7718197 + val name = DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS") + .withZone(ZoneOffset.UTC) + .format(Instant.ofEpochSecond(tracks[0].time)) + .replace("-", "_") + .replace(":", "_") + .replace(" ", "_") + ".gpx" + val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE + val description = ApplicationConstants.USER_AGENT + val tags = listOf(ApplicationConstants.NAME.lowercase()) + + // Generate history of trackpoints + val trackpoints = mutableListOf() + tracks.forEachIndexed { idx, it -> + trackpoints.add( + GpsTrackpoint( + OsmLatLon(it.position.latitude, it.position.longitude), + Instant.ofEpochSecond(it.time), + idx == 0, + it.horizontalDilutionOfPrecision, + it.elevation + ) + ) + } + + // Finally query the API and return! + val traceId = api.create(name, visibility, description, tags, trackpoints) + val details = api.get(traceId) + Track(details.id, details.userName) + } + + +} + +private inline fun wrapExceptions(block: () -> T): T = + try { + block() + } catch (e: OsmAuthorizationException) { + throw AuthorizationException(e.message, e) + } catch (e: OsmConflictException) { + throw ConflictException(e.message, e) + } catch (e: OsmQueryTooBigException) { + throw QueryTooBigException(e.message, e) + } catch (e: OsmConnectionException) { + throw ConnectionException(e.message, e) + } catch (e: OsmApiReadResponseException) { + // probably a temporary connection error + throw ConnectionException(e.message, e) + } catch (e: OsmApiException) { + // request timeout is a temporary connection error + throw if (e.errorCode == 408) ConnectionException(e.message, e) else e + } + diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt index bc4091ae00..ee2f3a8643 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt @@ -14,11 +14,11 @@ import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osm.mapdata.Way import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuest import de.westnordost.streetcomplete.data.osm.osmquests.OsmQuestController -import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditAction import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsController import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuest import de.westnordost.streetcomplete.data.osmnotes.notequests.OsmNoteQuestController +import de.westnordost.streetcomplete.data.osmtracks.Trackpoint import de.westnordost.streetcomplete.quests.note_discussion.NoteAnswer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -66,7 +66,7 @@ import kotlin.collections.ArrayList text: String, imagePaths: List, position: LatLon, - tracks: ArrayList + tracks: ArrayList ) = withContext(Dispatchers.IO) { val fullText = "$text\n\nvia ${ApplicationConstants.USER_AGENT}" noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, tracks) diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index a9d4d54619..354f264e25 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -10,7 +10,7 @@ import android.view.WindowManager import androidx.core.content.edit import androidx.core.content.getSystemService import de.westnordost.streetcomplete.data.osm.mapdata.LatLon -import de.westnordost.streetcomplete.data.osmnotes.NoteGPXTrack +import de.westnordost.streetcomplete.data.osmtracks.Trackpoint import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.location.FineLocationManager import de.westnordost.streetcomplete.location.toLatLon @@ -20,7 +20,6 @@ import de.westnordost.streetcomplete.map.tangram.screenBottomToCenterDistance import de.westnordost.streetcomplete.util.translate import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import java.time.Instant import kotlin.math.PI /** Manages a map that shows the device's GPS location and orientation as markers on the map with @@ -37,14 +36,14 @@ open class LocationAwareMapFragment : MapFragment() { var displayedLocation: Location? = null private set - /** If we are actively performing GPX tracking */ - var gpxTracking = false; - /** The GPS trackpoints the user has walked */ private var tracks: MutableList> + /** If we are actively recording track history */ + var tracksRecording = false; + /** The GPS trackpoints the user has recorded */ - var tracksRecorded: ArrayList + var tracksRecorded: ArrayList /** Whether the view should automatically center on the GPS location */ var isFollowingPosition = true @@ -139,28 +138,39 @@ open class LocationAwareMapFragment : MapFragment() { locationManager.requestUpdates(2000, 1f) } + fun stopPositionTracking() { + locationMapComponent?.isVisible = false + locationManager.removeUpdates() + } + + fun clearPositionTracking() { + stopPositionTracking() + displayedLocation = null + isNavigationMode = false + + tracks = ArrayList() + tracks.add(ArrayList()) + + tracksMapComponent?.clear() + } + @SuppressLint("MissingPermission") - fun startPositionTrackingGPX() { - gpxTracking = true + fun startPositionTrackRecording() { + tracksRecording = true tracks.add(ArrayList()) locationMapComponent?.isVisible = true locationManager.requestUpdates(500, 1f) tracksMapComponent?.startNewTrack(true) } - fun stopPositionTracking() { - locationMapComponent?.isVisible = false - locationManager.removeUpdates() - } - - fun stopPositionTrackingGPX() { - gpxTracking = false + fun stopPositionTrackRecording() { + tracksRecording = false tracksRecorded.clear() tracks.last().forEach { // time here is in milliseconds // TODO: why is altitude zero in emulation? tracksRecorded.add( - NoteGPXTrack( + Trackpoint( LatLon(it.latitude, it.longitude), it.time, it.accuracy, it.altitude.toFloat() ) ) @@ -169,17 +179,6 @@ open class LocationAwareMapFragment : MapFragment() { tracksMapComponent?.startNewTrack(false) } - fun clearPositionTracking() { - stopPositionTracking() - displayedLocation = null - isNavigationMode = false - - tracks = ArrayList() - tracks.add(ArrayList()) - - tracksMapComponent?.clear() - } - protected open fun shouldCenterCurrentPosition(): Boolean { return isFollowingPosition } @@ -231,7 +230,7 @@ open class LocationAwareMapFragment : MapFragment() { val lastLocation = tracks.last().lastOrNull() // create new track if last position too old - if (lastLocation != null && !gpxTracking) { + if (lastLocation != null && !tracksRecording) { if ((displayedLocation?.time ?: 0) - lastLocation.time > MAX_TIME_BETWEEN_LOCATIONS) { tracks.add(ArrayList()) tracksMapComponent?.startNewTrack(false) diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index a89066833d..1c304a67cf 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -162,7 +162,7 @@ class MainFragment : Fragment(R.layout.fragment_main), binding.compassView.setOnClickListener { onClickCompassButton() } binding.gpsTrackingButton.setOnClickListener { onClickTrackingButton() } - binding.stopGPXButton.setOnClickListener { onClickGPXStop() } + binding.stopTracksButton.setOnClickListener { onClickTracksStop() } binding.zoomInButton.setOnClickListener { onClickZoomIn() } binding.zoomOutButton.setOnClickListener { onClickZoomOut() } @@ -611,12 +611,12 @@ class MainFragment : Fragment(R.layout.fragment_main), mapFragment?.updateCameraPosition(300) { zoomBy = +1f } } - private fun onClickGPXStop() { + private fun onClickTracksStop() { // hide the track information - binding.stopGPXButton.visibility = View.INVISIBLE + binding.stopTracksButton.visibility = View.INVISIBLE val mapFragment = mapFragment ?: return - mapFragment.stopPositionTrackingGPX() + mapFragment.stopPositionTrackRecording() // show the note dialog mapFragment.show3DBuildings = false @@ -688,7 +688,7 @@ class MainFragment : Fragment(R.layout.fragment_main), popupMenu.setOnMenuItemClickListener { item -> when(item.itemId) { R.id.action_create_note -> onClickCreateNote(position) - R.id.action_create_gpx_track -> onClickCreateGPXTrack(position) + R.id.action_create_track -> onClickCreateTrack(position) R.id.action_open_location -> onClickOpenLocationInOtherApp(position) } true @@ -731,10 +731,10 @@ class MainFragment : Fragment(R.layout.fragment_main), showInBottomSheet(CreateNoteFragment()) } - private fun onClickCreateGPXTrack(pos: LatLon) { + private fun onClickCreateTrack(pos: LatLon) { val mapFragment = mapFragment ?: return - mapFragment.startPositionTrackingGPX() - binding.stopGPXButton.visibility = View.VISIBLE + mapFragment.startPositionTrackRecording() + binding.stopTracksButton.visibility = View.VISIBLE } // ---------------------------------- Location Pointer Pin --------------------------------- */ diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index ae80c5a140..f3b37c2d9c 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -87,11 +87,11 @@ tools:ignore="RtlHardcoded,RtlSymmetry"> diff --git a/app/src/main/res/menu/menu_map_context.xml b/app/src/main/res/menu/menu_map_context.xml index 9b2a1686ab..7014319d9f 100644 --- a/app/src/main/res/menu/menu_map_context.xml +++ b/app/src/main/res/menu/menu_map_context.xml @@ -5,8 +5,8 @@ android:title="@string/map_btn_create_note" android:icon="@drawable/ic_create_note_24dp" android:orderInCategory="1"/> - "Upload answers automatically" "Zoom out" "Zoom in" - "Stop GPX" + "Stop Recording Track" "Menu" "<p>Ah, someone who cares about privacy, nice! I think I have only good news for you:</p> <p><b>Direct Contributions</b></p> @@ -497,7 +497,7 @@ Otherwise, you can download another keyboard in the app store. Popular keyboards What is the structure of this bridge? Also cycleway on other sideā€¦ Create new note - Create new GPX track + Create new track recording Created note Commented note You hid it From 47447504ea8cf40fff8b2cce6b6b14148a43559e Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Mon, 13 Dec 2021 23:22:46 -0500 Subject: [PATCH 05/30] Time units are in milliseconds --- .../westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index df271cd064..7eb7776ec7 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -44,7 +44,7 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { trackpoints.add( GpsTrackpoint( OsmLatLon(it.position.latitude, it.position.longitude), - Instant.ofEpochSecond(it.time), + Instant.ofEpochMilli(it.time), idx == 0, it.horizontalDilutionOfPrecision, it.elevation From 70d32df29784dd98e8deaeff15e870a986ec808b Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Mon, 13 Dec 2021 23:34:11 -0500 Subject: [PATCH 06/30] small change --- app/src/main/res/menu/menu_map_context.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/menu/menu_map_context.xml b/app/src/main/res/menu/menu_map_context.xml index 7014319d9f..c55bbeada6 100644 --- a/app/src/main/res/menu/menu_map_context.xml +++ b/app/src/main/res/menu/menu_map_context.xml @@ -7,10 +7,10 @@ android:orderInCategory="1"/> + android:icon="@drawable/ic_create_note_24dp" + android:orderInCategory="2"/> + android:orderInCategory="3" /> From 2245aa07fededb3329a3ac6be4bfd9305373720c Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 18 Dec 2021 02:20:01 -0500 Subject: [PATCH 07/30] Update app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt Co-authored-by: Tobias Zwick --- .../streetcomplete/data/osmtracks/TracksApiImpl.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 7eb7776ec7..8f3873cf39 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -28,12 +28,9 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { // Filename is just the start of the track // https://stackoverflow.com/a/49862573/7718197 val name = DateTimeFormatter - .ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS") + .ofPattern("yyyy_MM_dd'T'HH_mm_ss.SSSSSS'Z'") .withZone(ZoneOffset.UTC) - .format(Instant.ofEpochSecond(tracks[0].time)) - .replace("-", "_") - .replace(":", "_") - .replace(" ", "_") + ".gpx" + .format(Instant.ofEpochSecond(tracks[0].time)) + ".gpx" val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE val description = ApplicationConstants.USER_AGENT val tags = listOf(ApplicationConstants.NAME.lowercase()) From 37d164e4e06c181d8df1b101fc5f5b3f3f278249 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 18 Dec 2021 02:20:22 -0500 Subject: [PATCH 08/30] Update app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt Co-authored-by: Tobias Zwick --- .../data/osmtracks/TracksApiImpl.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 8f3873cf39..78c5710397 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -36,16 +36,13 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { val tags = listOf(ApplicationConstants.NAME.lowercase()) // Generate history of trackpoints - val trackpoints = mutableListOf() - tracks.forEachIndexed { idx, it -> - trackpoints.add( - GpsTrackpoint( - OsmLatLon(it.position.latitude, it.position.longitude), - Instant.ofEpochMilli(it.time), - idx == 0, - it.horizontalDilutionOfPrecision, - it.elevation - ) + val trackpoints = tracks.mapIndexed { idx, it -> + GpsTrackpoint( + OsmLatLon(it.position.latitude, it.position.longitude), + Instant.ofEpochMilli(it.time), + idx == 0, + it.horizontalDilutionOfPrecision, + it.elevation ) } From d2d11d5ebacbdba9b4ccdc6d6a0a19275ca8affa Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 18 Dec 2021 02:46:24 -0500 Subject: [PATCH 09/30] changes requested in PR --- .../data/osmnotes/edits/NoteEditsUploader.kt | 14 ++++++++++---- .../streetcomplete/data/osmtracks/TracksApi.kt | 5 +++-- .../data/osmtracks/TracksApiImpl.kt | 16 ++++++---------- .../map/LocationAwareMapFragment.kt | 5 +++-- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 627241abc9..7ee3d56cdd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -70,7 +70,10 @@ class NoteEditsUploader @Inject constructor( // try to upload the tracks val tracksText = - edit.uploadedDataMap.getOrDefault("tracks", uploadAndGetAttachedTracksText(edit.tracks)) + edit.uploadedDataMap.getOrDefault( + "tracks", + uploadAndGetAttachedTracksText(edit.tracks, edit.text) + ) noteEditsController.updateData(edit, "tracks", tracksText) // done, try to upload the note to OSM @@ -126,11 +129,14 @@ class NoteEditsUploader @Inject constructor( return "" } - private fun uploadAndGetAttachedTracksText(tracks: List): String { - if (tracks.isEmpty()) { + private fun uploadAndGetAttachedTracksText( + trackpoints: List, + noteText: String? + ): String { + if (trackpoints.isEmpty()) { return "" } - val track = tracksApi.create(tracks) + val track = tracksApi.create(trackpoints, noteText) return "\n\nGPS Trace: \nhttps://www.openstreetmap.org/user/${track.userName}/traces/${track.id}" } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt index 5888997ab0..3ebf25f95f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -12,7 +12,8 @@ interface TracksApi { /** * Create a new GPX track history * - * @param tracks history of recorded trackpoints + * @param trackpoints history of recorded trackpoints + * @param noteText optional text appended to the track * * @throws AuthorizationException if this application is not authorized to write notes * (Permission.READ_GPS_TRACES, Permission.WRITE_GPS_TRACES) @@ -20,6 +21,6 @@ interface TracksApi { * * @return the new track */ - fun create(tracks: List): Track + fun create(trackpoints: List, noteText: String?): Track } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 78c5710397..20ae2aedd9 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -23,20 +23,20 @@ import java.time.format.DateTimeFormatter class TracksApiImpl(osm: OsmConnection) : TracksApi { private val api: GpsTracesApi = GpsTracesApi(osm) - override fun create(tracks: List): Track = wrapExceptions { + override fun create(trackpoints: List, noteText: String?): Track = wrapExceptions { // Filename is just the start of the track // https://stackoverflow.com/a/49862573/7718197 val name = DateTimeFormatter .ofPattern("yyyy_MM_dd'T'HH_mm_ss.SSSSSS'Z'") .withZone(ZoneOffset.UTC) - .format(Instant.ofEpochSecond(tracks[0].time)) + ".gpx" + .format(Instant.ofEpochSecond(trackpoints[0].time)) + ".gpx" val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE - val description = ApplicationConstants.USER_AGENT + val description = noteText.orEmpty() + "(" + ApplicationConstants.USER_AGENT + ")" val tags = listOf(ApplicationConstants.NAME.lowercase()) // Generate history of trackpoints - val trackpoints = tracks.mapIndexed { idx, it -> + val history = trackpoints.mapIndexed { idx, it -> GpsTrackpoint( OsmLatLon(it.position.latitude, it.position.longitude), Instant.ofEpochMilli(it.time), @@ -47,7 +47,7 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { } // Finally query the API and return! - val traceId = api.create(name, visibility, description, tags, trackpoints) + val traceId = api.create(name, visibility, description, tags, history) val details = api.get(traceId) Track(details.id, details.userName) } @@ -59,11 +59,7 @@ private inline fun wrapExceptions(block: () -> T): T = try { block() } catch (e: OsmAuthorizationException) { - throw AuthorizationException(e.message, e) - } catch (e: OsmConflictException) { - throw ConflictException(e.message, e) - } catch (e: OsmQueryTooBigException) { - throw QueryTooBigException(e.message, e) + throw AuthorizationTracesException(e.message, e) } catch (e: OsmConnectionException) { throw ConnectionException(e.message, e) } catch (e: OsmApiReadResponseException) { diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index 354f264e25..2dda2c0be6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -167,8 +167,9 @@ open class LocationAwareMapFragment : MapFragment() { tracksRecording = false tracksRecorded.clear() tracks.last().forEach { - // time here is in milliseconds - // TODO: why is altitude zero in emulation? + // Emulator has zero altitude: + // https://stackoverflow.com/q/65325665/7718197 + // Time here is in milliseconds tracksRecorded.add( Trackpoint( LatLon(it.latitude, it.longitude), it.time, it.accuracy, it.altitude.toFloat() From 0b3e732294a4b846fab16b4d4d380b86c387ba2b Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 18 Dec 2021 03:01:55 -0500 Subject: [PATCH 10/30] custom dialog for users that need to upgrade their permissions to upload GPS traces --- .../streetcomplete/MainActivity.kt | 12 ++++++++++ .../data/osmtracks/TracksApi.kt | 4 ++-- .../data/osmtracks/TracksApiImpl.kt | 6 +---- .../data/user/AuthorizationException.kt | 3 +++ .../dialogs/RequestPermissionUpgradeDialog.kt | 24 +++++++++++++++++++ .../res/layout/dialog_permission_upgrade.xml | 23 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt create mode 100644 app/src/main/res/layout/dialog_permission_upgrade.xml diff --git a/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt b/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt index e8e4d451b9..eec48d0476 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt @@ -35,6 +35,7 @@ import de.westnordost.streetcomplete.data.upload.UploadController import de.westnordost.streetcomplete.data.upload.UploadProgressListener import de.westnordost.streetcomplete.data.upload.VersionBannedException import de.westnordost.streetcomplete.data.user.AuthorizationException +import de.westnordost.streetcomplete.data.user.AuthorizationTracesException import de.westnordost.streetcomplete.data.user.UserLoginStatusController import de.westnordost.streetcomplete.data.user.UserUpdater import de.westnordost.streetcomplete.ktx.toast @@ -48,6 +49,7 @@ import de.westnordost.streetcomplete.tutorial.TutorialFragment import de.westnordost.streetcomplete.util.CrashReportExceptionHandler import de.westnordost.streetcomplete.util.parseGeoUri import de.westnordost.streetcomplete.view.dialogs.RequestLoginDialog +import de.westnordost.streetcomplete.view.dialogs.RequestPermissionUpgradeDialog import kotlinx.coroutines.launch import javax.inject.Inject @@ -235,6 +237,16 @@ class MainActivity : BaseActivity(), Nothing we can do about it, so it does not make sense to send an error report. Just notify the user. */ toast(R.string.upload_server_error, Toast.LENGTH_LONG) + } else if (e is AuthorizationTracesException) { + // user has not upgraded their permissions to have GPS trace (or is not logged in) + // if the user is not logged in, then we should just ask them to login which will provide all permissions + val isLoggedIn = userLoginStatusController.isLoggedIn + userLoginStatusController.logOut() + if(isLoggedIn) { + RequestPermissionUpgradeDialog(this@MainActivity).show() + } else { + RequestLoginDialog(this@MainActivity).show() + } } else if (e is AuthorizationException) { // delete secret in case it failed while already having a token -> token is invalid userLoginStatusController.logOut() diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt index 3ebf25f95f..b0d0cbf9fb 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -1,7 +1,7 @@ package de.westnordost.streetcomplete.data.osmtracks import de.westnordost.streetcomplete.data.download.ConnectionException -import de.westnordost.streetcomplete.data.user.AuthorizationException +import de.westnordost.streetcomplete.data.user.AuthorizationTracesException /** * Creates GPS / GPX trackpoint histories @@ -15,7 +15,7 @@ interface TracksApi { * @param trackpoints history of recorded trackpoints * @param noteText optional text appended to the track * - * @throws AuthorizationException if this application is not authorized to write notes + * @throws AuthorizationTracesException if this application is not authorized to write notes * (Permission.READ_GPS_TRACES, Permission.WRITE_GPS_TRACES) * @throws ConnectionException if a temporary network connection problem occurs * diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 20ae2aedd9..6ff7c4c00e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -4,18 +4,14 @@ import de.westnordost.osmapi.OsmConnection import de.westnordost.osmapi.common.errors.OsmApiException import de.westnordost.osmapi.common.errors.OsmApiReadResponseException import de.westnordost.osmapi.common.errors.OsmAuthorizationException -import de.westnordost.osmapi.common.errors.OsmConflictException import de.westnordost.osmapi.common.errors.OsmConnectionException -import de.westnordost.osmapi.common.errors.OsmQueryTooBigException import de.westnordost.osmapi.map.data.OsmLatLon import de.westnordost.osmapi.traces.GpsTraceDetails import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.osmapi.traces.GpsTrackpoint import de.westnordost.streetcomplete.ApplicationConstants import de.westnordost.streetcomplete.data.download.ConnectionException -import de.westnordost.streetcomplete.data.download.QueryTooBigException -import de.westnordost.streetcomplete.data.upload.ConflictException -import de.westnordost.streetcomplete.data.user.AuthorizationException +import de.westnordost.streetcomplete.data.user.AuthorizationTracesException import java.time.Instant import java.time.ZoneOffset import java.time.format.DateTimeFormatter diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt b/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt index a06aa621ae..dfd262c097 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt @@ -2,3 +2,6 @@ package de.westnordost.streetcomplete.data.user class AuthorizationException @JvmOverloads constructor( message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) + +class AuthorizationTracesException @JvmOverloads constructor( + message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt new file mode 100644 index 0000000000..7b3344067d --- /dev/null +++ b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt @@ -0,0 +1,24 @@ +package de.westnordost.streetcomplete.view.dialogs + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.view.LayoutInflater +import androidx.appcompat.app.AlertDialog +import de.westnordost.streetcomplete.R +import de.westnordost.streetcomplete.user.UserActivity + +/** Shows a dialog that asks the user to login */ +@SuppressLint("InflateParams") +class RequestPermissionUpgradeDialog(context: Context) : AlertDialog(context, R.style.Theme_Bubble_Dialog) { + init { + val view = LayoutInflater.from(context).inflate(R.layout.dialog_permission_upgrade, null, false) + setView(view) + setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> + val intent = Intent(context, UserActivity::class.java) + intent.putExtra(UserActivity.EXTRA_LAUNCH_AUTH, true) + context.startActivity(intent) + } + setButton(BUTTON_NEGATIVE, context.getString(R.string.later)) { _, _ -> } + } +} diff --git a/app/src/main/res/layout/dialog_permission_upgrade.xml b/app/src/main/res/layout/dialog_permission_upgrade.xml new file mode 100644 index 0000000000..fe6a81c72d --- /dev/null +++ b/app/src/main/res/layout/dialog_permission_upgrade.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3dd37c5c61..6957150d7f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,6 +106,7 @@ "GNU General Public License" "You are offline" "You need to authorize with your OSM user account to publish your answers. Authorize now?" + "To upload a GPS trace, you need to authorize with your OSM user account to publish traces. Authorize now?" "Not authorized" "This version of StreetComplete is too old. Please update!" "To scan for quests around you automatically, turn on location" From 2d01383fd12366eccb170dead30c822c39b26202 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 2 Jan 2022 23:27:33 -0500 Subject: [PATCH 11/30] PR comments on TracksMapComponent datastruct and fix upload text for trace --- .../data/osmtracks/TracksApiImpl.kt | 8 +++--- .../map/components/TracksMapComponent.kt | 26 ++++++++----------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 6ff7c4c00e..73e072217e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -28,7 +28,11 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { .withZone(ZoneOffset.UTC) .format(Instant.ofEpochSecond(trackpoints[0].time)) + ".gpx" val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE - val description = noteText.orEmpty() + "(" + ApplicationConstants.USER_AGENT + ")" + val description = if (noteText != null && noteText.isNotBlank()) { + noteText + } else { + "Uploaded via " + ApplicationConstants.USER_AGENT + } val tags = listOf(ApplicationConstants.NAME.lowercase()) // Generate history of trackpoints @@ -47,8 +51,6 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { val details = api.get(traceId) Track(details.id, details.userName) } - - } private inline fun wrapExceptions(block: () -> T): T = diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt index fb195fada4..fc5f73e03e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt @@ -21,52 +21,48 @@ class TracksMapComponent(ctrl: KtMapController) { private val layer2 = ctrl.addDataLayer(LAYER2) private var index = 0 - private var tracks: MutableList> - private var tracksRecording: MutableList + private class Track(val trackpoints: MutableList, val isRecording: Boolean) + private var tracks: MutableList init { tracks = ArrayList() - tracks.add(ArrayList()) - tracksRecording = ArrayList() + tracks.add(Track(ArrayList(), false)) } /** Add a point to the current track */ fun addToCurrentTrack(pos: Location) { val track = tracks.last() - val recording = tracksRecording.last() - track.add(pos.toLngLat()) + track.trackpoints.add(pos.toLngLat()) + val trackpoints = track.trackpoints // every 100th trackpoint, move the index to the back - if (track.size - index > 100) { + if (trackpoints.size - index > 100) { putAllTracksInOldLayer() } else { - layer1.setFeatures(listOf(track.subList(index, track.size).toPolyline(false, recording))) + layer1.setFeatures(listOf(trackpoints.subList(index, trackpoints.size).toPolyline(false, track.isRecording))) } } /** Start a new track. I.e. the points in that track will be drawn as an own polyline */ fun startNewTrack(record: Boolean) { - tracks.add(ArrayList()) - tracksRecording.add(record) + tracks.add(Track(ArrayList(), record)) putAllTracksInOldLayer() } /** Set all the tracks (when re-initializing) */ fun setTracks(tracks: List>) { - this.tracks = tracks.map { track -> track.map { it.toLngLat() }.toMutableList() }.toMutableList() - this.tracksRecording = tracks.map { false }.toMutableList() + this.tracks = tracks.map { track -> Track(track.map { it.toLngLat() }.toMutableList(), false) }.toMutableList() putAllTracksInOldLayer() } private fun putAllTracksInOldLayer() { - index = max(0, tracks.last().lastIndex) + index = max(0, tracks.last().trackpoints.lastIndex) layer1.clear() - layer2.setFeatures(tracks.mapIndexed { idx, it -> it.toPolyline(true, tracksRecording[idx]) }) + layer2.setFeatures(tracks.map { it.trackpoints.toPolyline(true, it.isRecording) }) } fun clear() { tracks = ArrayList() - tracksRecording = ArrayList() startNewTrack(false) } From fe85ce5ca8b9bab732b29fef91fa0e82726a7aed Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Mon, 3 Jan 2022 23:42:52 -0500 Subject: [PATCH 12/30] More PR comment changes --- .../data/osmnotes/edits/NoteEdit.kt | 2 +- .../osmnotes/edits/NoteEditsController.kt | 4 +-- .../data/osmnotes/edits/NoteEditsDao.kt | 2 +- .../data/osmnotes/edits/NoteEditsUploader.kt | 25 ++++++++----------- .../data/osmtracks/TracksApiImpl.kt | 6 +---- .../data/quest/QuestController.kt | 2 +- .../streetcomplete/map/MainFragment.kt | 4 +-- .../map/components/TracksMapComponent.kt | 9 ++----- app/src/main/res/layout/fragment_main.xml | 2 +- 9 files changed, 21 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index 6336529107..8990d80d04 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -39,7 +39,7 @@ data class NoteEdit( val tracks: List, /** contains partial upload responses index by type (e.g. image and track urls) */ - val uploadedDataMap: Map + val uploadedDataMap: Map, ): Edit { override val isUndoable: Boolean get() = !isSynced override val key: NoteEditKey get() = NoteEditKey(id) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index 8635664462..1f7619ff9e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -25,7 +25,7 @@ import javax.inject.Singleton position: LatLon, text: String? = null, imagePaths: List = emptyList(), - tracks: List = emptyList() + tracks: List = emptyList(), ) { val edit = NoteEdit( 0, @@ -38,7 +38,7 @@ import javax.inject.Singleton false, imagePaths.isNotEmpty(), tracks, - mutableMapOf() + mutableMapOf(), ) synchronized(this) { editsDB.add(edit) } onAddedEdit(edit) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt index 9f93e19bc1..02ba5b05d5 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt @@ -146,6 +146,6 @@ class NoteEditsDao @Inject constructor(private val db: Database) { getInt(IS_SYNCED) == 1, getInt(IMAGES_NEED_ACTIVATION) == 1, Json.decodeFromString(getString(TRACKS)), - Json.decodeFromString(getString(UPLOAD_DATA_MAP)) + Json.decodeFromString(getString(UPLOAD_DATA_MAP)), ) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 7ee3d56cdd..536973b671 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -62,19 +62,18 @@ class NoteEditsUploader @Inject constructor( private fun uploadEdit(edit: NoteEdit) { // try to upload the image - val imageText = edit.uploadedDataMap.getOrDefault( - "images", - uploadAndGetAttachedPhotosText(edit.imagePaths) - ) - noteEditsController.updateData(edit, "images", imageText) + var imageText = edit.uploadedDataMap.getOrDefault("images", "") + if(imageText.isEmpty()) { + imageText = uploadAndGetAttachedPhotosText(edit.imagePaths) + noteEditsController.updateData(edit, "images", imageText) + } // try to upload the tracks - val tracksText = - edit.uploadedDataMap.getOrDefault( - "tracks", - uploadAndGetAttachedTracksText(edit.tracks, edit.text) - ) - noteEditsController.updateData(edit, "tracks", tracksText) + var tracksText = edit.uploadedDataMap.getOrDefault("tracks", "") + if(tracksText.isEmpty()) { + tracksText = uploadAndGetAttachedTracksText(edit.tracks, edit.text) + noteEditsController.updateData(edit, "tracks", tracksText) + } // done, try to upload the note to OSM val text = edit.text.orEmpty() + imageText + tracksText @@ -133,9 +132,7 @@ class NoteEditsUploader @Inject constructor( trackpoints: List, noteText: String? ): String { - if (trackpoints.isEmpty()) { - return "" - } + if (trackpoints.isEmpty()) return "" val track = tracksApi.create(trackpoints, noteText) return "\n\nGPS Trace: \nhttps://www.openstreetmap.org/user/${track.userName}/traces/${track.id}" } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 73e072217e..3a18fc33d8 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -28,11 +28,7 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { .withZone(ZoneOffset.UTC) .format(Instant.ofEpochSecond(trackpoints[0].time)) + ".gpx" val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE - val description = if (noteText != null && noteText.isNotBlank()) { - noteText - } else { - "Uploaded via " + ApplicationConstants.USER_AGENT - } + val description = noteText.orEmpty().ifBlank { "Uploaded via ${ApplicationConstants.USER_AGENT}" } val tags = listOf(ApplicationConstants.NAME.lowercase()) // Generate history of trackpoints diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt index ee2f3a8643..15ad53f6fd 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt @@ -66,7 +66,7 @@ import kotlin.collections.ArrayList text: String, imagePaths: List, position: LatLon, - tracks: ArrayList + tracks: ArrayList, ) = withContext(Dispatchers.IO) { val fullText = "$text\n\nvia ${ApplicationConstants.USER_AGENT}" noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, tracks) diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 1c304a67cf..61eed2a12f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -612,9 +612,8 @@ class MainFragment : Fragment(R.layout.fragment_main), } private fun onClickTracksStop() { - // hide the track information - binding.stopTracksButton.visibility = View.INVISIBLE + binding.stopTracksButton.visibility = View.GONE val mapFragment = mapFragment ?: return mapFragment.stopPositionTrackRecording() @@ -627,7 +626,6 @@ class MainFragment : Fragment(R.layout.fragment_main), freezeMap() showInBottomSheet(CreateNoteFragment()) - } private fun onClickCompassButton() { diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt index fc5f73e03e..d31a42f7c3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/components/TracksMapComponent.kt @@ -21,13 +21,8 @@ class TracksMapComponent(ctrl: KtMapController) { private val layer2 = ctrl.addDataLayer(LAYER2) private var index = 0 - private class Track(val trackpoints: MutableList, val isRecording: Boolean) - private var tracks: MutableList - - init { - tracks = ArrayList() - tracks.add(Track(ArrayList(), false)) - } + private data class Track(val trackpoints: MutableList, val isRecording: Boolean) + private var tracks: MutableList = arrayListOf(Track(ArrayList(), false)) /** Add a point to the current track */ fun addToCurrentTrack(pos: Location) { diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index f3b37c2d9c..8ef4fae111 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -94,7 +94,7 @@ android:contentDescription="@string/map_btn_stop_track" android:scaleType="center" android:src="@android:drawable/ic_menu_close_clear_cancel" - android:visibility="invisible" /> + android:visibility="gone" /> Date: Sat, 8 Jan 2022 20:38:28 -0500 Subject: [PATCH 13/30] pr changes --- .../data/quest/QuestController.kt | 2 +- .../map/LocationAwareMapFragment.kt | 26 ++++++++++++------- .../streetcomplete/map/MainFragment.kt | 18 ++++--------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt index 15ad53f6fd..3183584bbe 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt @@ -66,7 +66,7 @@ import kotlin.collections.ArrayList text: String, imagePaths: List, position: LatLon, - tracks: ArrayList, + tracks: List, ) = withContext(Dispatchers.IO) { val fullText = "$text\n\nvia ${ApplicationConstants.USER_AGENT}" noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, tracks) diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index 2dda2c0be6..7679505361 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -40,10 +40,13 @@ open class LocationAwareMapFragment : MapFragment() { private var tracks: MutableList> /** If we are actively recording track history */ - var tracksRecording = false; + var tracksRecording = false + private set /** The GPS trackpoints the user has recorded */ - var tracksRecorded: ArrayList + private var _tracksRecorded: ArrayList + + val tracksRecorded: List get() = _tracksRecorded /** Whether the view should automatically center on the GPS location */ var isFollowingPosition = true @@ -72,6 +75,7 @@ open class LocationAwareMapFragment : MapFragment() { /** Called after the map fragment updated its displayed location */ fun onDisplayedLocationDidChange() } + private val listener: Listener? get() = parentFragment as? Listener ?: activity as? Listener @@ -80,12 +84,13 @@ open class LocationAwareMapFragment : MapFragment() { init { tracks = ArrayList() tracks.add(ArrayList()) - tracksRecorded = ArrayList() + _tracksRecorded = ArrayList() } override fun onAttach(context: Context) { super.onAttach(context) - compass = Compass(context.getSystemService()!!, + compass = Compass( + context.getSystemService()!!, context.getSystemService()!!.defaultDisplay, this::onCompassRotationChanged ) @@ -96,7 +101,8 @@ open class LocationAwareMapFragment : MapFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) displayedLocation = savedInstanceState?.getParcelable(DISPLAYED_LOCATION) - val nullTerminatedTracks = savedInstanceState?.getParcelableArrayList(TRACKS) as ArrayList? + val nullTerminatedTracks = + savedInstanceState?.getParcelableArrayList(TRACKS) as ArrayList? if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() } @@ -165,12 +171,12 @@ open class LocationAwareMapFragment : MapFragment() { fun stopPositionTrackRecording() { tracksRecording = false - tracksRecorded.clear() + _tracksRecorded.clear() tracks.last().forEach { // Emulator has zero altitude: // https://stackoverflow.com/q/65325665/7718197 // Time here is in milliseconds - tracksRecorded.add( + _tracksRecorded.add( Trackpoint( LatLon(it.latitude, it.longitude), it.time, it.accuracy, it.altitude.toFloat() ) @@ -197,7 +203,8 @@ open class LocationAwareMapFragment : MapFragment() { behind user */ val distance = controller?.screenBottomToCenterDistance() if (distance != null) { - centerPosition = centerPosition.translate(distance * 0.4, bearing.toDouble()) + centerPosition = + centerPosition.translate(distance * 0.4, bearing.toDouble()) } } tilt = PI.toFloat() / 6f @@ -294,8 +301,7 @@ private fun List.unflattenNullTerminated(): ArrayList> { for (it in this) { if (it != null) { current.add(it) - } - else { + } else { result.add(current) current = ArrayList() } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 61eed2a12f..cc62dbcb9e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -50,6 +50,7 @@ import de.westnordost.streetcomplete.location.hasLocationPermission import de.westnordost.streetcomplete.location.isLocationEnabled import de.westnordost.streetcomplete.location.LocationRequestFragment import de.westnordost.streetcomplete.location.LocationState +import de.westnordost.streetcomplete.location.toLatLon import de.westnordost.streetcomplete.map.tangram.CameraPosition import de.westnordost.streetcomplete.osm.levelsIntersect import de.westnordost.streetcomplete.quests.* @@ -495,7 +496,6 @@ class MainFragment : Fragment(R.layout.fragment_main), viewLifecycleScope.launch { questController.createNote(note, imagePaths, position, mapFragment.tracksRecorded) - mapFragment.tracksRecorded.clear() } listener?.onCreatedNote(screenPosition) @@ -616,16 +616,8 @@ class MainFragment : Fragment(R.layout.fragment_main), binding.stopTracksButton.visibility = View.GONE val mapFragment = mapFragment ?: return mapFragment.stopPositionTrackRecording() - - // show the note dialog - mapFragment.show3DBuildings = false - val location = mapFragment.displayedLocation ?: return - val pos = LatLon(location.latitude, location.longitude) - val offsetPos = mapFragment.getPositionThatCentersPosition(pos, mapOffsetWithOpenBottomSheet) - mapFragment.updateCameraPosition { position = offsetPos } - - freezeMap() - showInBottomSheet(CreateNoteFragment()) + val pos = mapFragment.displayedLocation?.toLatLon() ?: return + composeNote(pos) } private fun onClickCompassButton() { @@ -686,7 +678,7 @@ class MainFragment : Fragment(R.layout.fragment_main), popupMenu.setOnMenuItemClickListener { item -> when(item.itemId) { R.id.action_create_note -> onClickCreateNote(position) - R.id.action_create_track -> onClickCreateTrack(position) + R.id.action_create_track -> onClickCreateTrack() R.id.action_open_location -> onClickOpenLocationInOtherApp(position) } true @@ -729,7 +721,7 @@ class MainFragment : Fragment(R.layout.fragment_main), showInBottomSheet(CreateNoteFragment()) } - private fun onClickCreateTrack(pos: LatLon) { + private fun onClickCreateTrack() { val mapFragment = mapFragment ?: return mapFragment.startPositionTrackRecording() binding.stopTracksButton.visibility = View.VISIBLE From 6988216a2e5c9cd192b31637cc39003f77e5cd8e Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Mon, 17 Jan 2022 00:53:58 -0500 Subject: [PATCH 14/30] ask the user when they create a track if they want to upgrade their permissions, remove data upload object --- .../streetcomplete/ApplicationComponent.kt | 2 ++ .../westnordost/streetcomplete/MainActivity.kt | 12 ------------ .../de/westnordost/streetcomplete/Prefs.kt | 1 + .../data/StreetCompleteSQLiteOpenHelper.kt | 1 - .../data/osmnotes/edits/NoteEdit.kt | 2 -- .../data/osmnotes/edits/NoteEditsController.kt | 9 --------- .../data/osmnotes/edits/NoteEditsDao.kt | 6 ------ .../data/osmnotes/edits/NoteEditsTable.kt | 2 -- .../data/osmnotes/edits/NoteEditsUploader.kt | 18 ++++-------------- .../streetcomplete/data/osmtracks/TracksApi.kt | 4 ++-- .../data/osmtracks/TracksApiImpl.kt | 4 ++-- .../data/user/AuthorizationException.kt | 3 --- .../data/user/UserLoginStatusController.kt | 6 ++++++ .../streetcomplete/map/MainFragment.kt | 10 ++++++++++ .../streetcomplete/user/LoginFragment.kt | 2 +- .../dialogs/RequestPermissionUpgradeDialog.kt | 13 +++++++++++-- .../res/layout/dialog_permission_upgrade.xml | 9 --------- app/src/main/res/values/strings.xml | 2 +- 18 files changed, 40 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/ApplicationComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/ApplicationComponent.kt index ea8609a850..75cc1ea156 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/ApplicationComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/ApplicationComponent.kt @@ -45,6 +45,7 @@ import de.westnordost.streetcomplete.settings.* import de.westnordost.streetcomplete.settings.questselection.QuestPresetsFragment import de.westnordost.streetcomplete.settings.questselection.QuestSelectionFragment import de.westnordost.streetcomplete.user.* +import de.westnordost.streetcomplete.view.dialogs.RequestPermissionUpgradeDialog import javax.inject.Singleton @Singleton @@ -113,4 +114,5 @@ interface ApplicationComponent { fun inject(questPresetsFragment: QuestPresetsFragment) fun inject(cleanerWorker: CleanerWorker) fun inject(addLevelForm: AddLevelForm) + fun inject(requestPermissionUpgradeDialog: RequestPermissionUpgradeDialog) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt b/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt index eec48d0476..e8e4d451b9 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/MainActivity.kt @@ -35,7 +35,6 @@ import de.westnordost.streetcomplete.data.upload.UploadController import de.westnordost.streetcomplete.data.upload.UploadProgressListener import de.westnordost.streetcomplete.data.upload.VersionBannedException import de.westnordost.streetcomplete.data.user.AuthorizationException -import de.westnordost.streetcomplete.data.user.AuthorizationTracesException import de.westnordost.streetcomplete.data.user.UserLoginStatusController import de.westnordost.streetcomplete.data.user.UserUpdater import de.westnordost.streetcomplete.ktx.toast @@ -49,7 +48,6 @@ import de.westnordost.streetcomplete.tutorial.TutorialFragment import de.westnordost.streetcomplete.util.CrashReportExceptionHandler import de.westnordost.streetcomplete.util.parseGeoUri import de.westnordost.streetcomplete.view.dialogs.RequestLoginDialog -import de.westnordost.streetcomplete.view.dialogs.RequestPermissionUpgradeDialog import kotlinx.coroutines.launch import javax.inject.Inject @@ -237,16 +235,6 @@ class MainActivity : BaseActivity(), Nothing we can do about it, so it does not make sense to send an error report. Just notify the user. */ toast(R.string.upload_server_error, Toast.LENGTH_LONG) - } else if (e is AuthorizationTracesException) { - // user has not upgraded their permissions to have GPS trace (or is not logged in) - // if the user is not logged in, then we should just ask them to login which will provide all permissions - val isLoggedIn = userLoginStatusController.isLoggedIn - userLoginStatusController.logOut() - if(isLoggedIn) { - RequestPermissionUpgradeDialog(this@MainActivity).show() - } else { - RequestLoginDialog(this@MainActivity).show() - } } else if (e is AuthorizationException) { // delete secret in case it failed while already having a token -> token is invalid userLoginStatusController.logOut() diff --git a/app/src/main/java/de/westnordost/streetcomplete/Prefs.kt b/app/src/main/java/de/westnordost/streetcomplete/Prefs.kt index 15d92a9fc0..1361731783 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/Prefs.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/Prefs.kt @@ -21,6 +21,7 @@ object Prefs { const val OSM_USER_ID = "osm.userid" const val OSM_USER_NAME = "osm.username" const val OSM_UNREAD_MESSAGES = "osm.unread_messages" + const val OSM_HAS_UPLOAD_TRACES_PERMISSION = "osm.upload_traces_permission" const val USER_DAYS_ACTIVE = "days_active" const val USER_GLOBAL_RANK = "user_global_rank" const val USER_LAST_TIMESTAMP_ACTIVE = "last_timestamp_active" diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt index 255e9e44cd..bf4c4428eb 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt @@ -126,7 +126,6 @@ import de.westnordost.streetcomplete.quests.oneway_suspects.data.WayTrafficFlowT } if (oldVersion <= 4 && newVersion > 4) { db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.TRACKS} text DEFAULT '[]' NOT NULL") - db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.UPLOAD_DATA_MAP} text DEFAULT '{}' NOT NULL") } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index 8990d80d04..d71449ec06 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -38,8 +38,6 @@ data class NoteEdit( /** attached GPS location tracks */ val tracks: List, - /** contains partial upload responses index by type (e.g. image and track urls) */ - val uploadedDataMap: Map, ): Edit { override val isUndoable: Boolean get() = !isSynced override val key: NoteEditKey get() = NoteEditKey(id) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index 1f7619ff9e..62da2d34c1 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -38,7 +38,6 @@ import javax.inject.Singleton false, imagePaths.isNotEmpty(), tracks, - mutableMapOf(), ) synchronized(this) { editsDB.add(edit) } onAddedEdit(edit) @@ -91,14 +90,6 @@ import javax.inject.Singleton } } - fun updateData(edit: NoteEdit, name: String, data: String) { - synchronized(this) { - val tmpData = edit.uploadedDataMap.toMutableMap() - tmpData[name] = data - editsDB.updateUploadData(edit.noteId, Json.encodeToString(tmpData)) - } - } - fun markSyncFailed(edit: NoteEdit): Boolean = delete(edit) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt index 02ba5b05d5..57ecb94201 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt @@ -16,7 +16,6 @@ import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns. import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.NOTE_ID import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TEXT import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TYPE -import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.UPLOAD_DATA_MAP import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.NAME import de.westnordost.streetcomplete.ktx.* import kotlinx.serialization.decodeFromString @@ -107,9 +106,6 @@ class NoteEditsDao @Inject constructor(private val db: Database) { fun updateNoteId(oldNoteId: Long, newNoteId: Long): Int = db.update(NAME, listOf(NOTE_ID to newNoteId), "$NOTE_ID = $oldNoteId") - fun updateUploadData(noteId: Long, data: String): Int = - db.update(NAME, listOf(UPLOAD_DATA_MAP to data), "$NOTE_ID = $noteId") - fun getOldestNeedingImagesActivation(): NoteEdit? = db.queryOne(NAME, where = "$IS_SYNCED = 1 AND $IMAGES_NEED_ACTIVATION = 1", orderBy = CREATED_TIMESTAMP) { it.toNoteEdit() } @@ -131,7 +127,6 @@ class NoteEditsDao @Inject constructor(private val db: Database) { IMAGE_PATHS to Json.encodeToString(imagePaths), IMAGES_NEED_ACTIVATION to if (imagesNeedActivation) 1 else 0, TRACKS to Json.encodeToString(tracks), - UPLOAD_DATA_MAP to Json.encodeToString(uploadedDataMap), TYPE to action.name ) @@ -146,6 +141,5 @@ class NoteEditsDao @Inject constructor(private val db: Database) { getInt(IS_SYNCED) == 1, getInt(IMAGES_NEED_ACTIVATION) == 1, Json.decodeFromString(getString(TRACKS)), - Json.decodeFromString(getString(UPLOAD_DATA_MAP)), ) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt index 24e522a77d..5c9a3c6691 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt @@ -15,7 +15,6 @@ object NoteEditsTable { const val IMAGE_PATHS = "image_paths" const val IMAGES_NEED_ACTIVATION = "images_need_activation" const val TRACKS = "tracks" - const val UPLOAD_DATA_MAP = "upload_data_map" } const val CREATE = """ @@ -30,7 +29,6 @@ object NoteEditsTable { ${Columns.IMAGE_PATHS} text NOT NULL, ${Columns.IMAGES_NEED_ACTIVATION} int NOT NULL, ${Columns.TRACKS} text NOT NULL, - ${Columns.UPLOAD_DATA_MAP} text NOT NULL, ${Columns.TYPE} varchar(255) );""" diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 536973b671..aa13fb8e96 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -61,22 +61,12 @@ class NoteEditsUploader @Inject constructor( private fun uploadEdit(edit: NoteEdit) { - // try to upload the image - var imageText = edit.uploadedDataMap.getOrDefault("images", "") - if(imageText.isEmpty()) { - imageText = uploadAndGetAttachedPhotosText(edit.imagePaths) - noteEditsController.updateData(edit, "images", imageText) - } - - // try to upload the tracks - var tracksText = edit.uploadedDataMap.getOrDefault("tracks", "") - if(tracksText.isEmpty()) { - tracksText = uploadAndGetAttachedTracksText(edit.tracks, edit.text) - noteEditsController.updateData(edit, "tracks", tracksText) - } + // try to upload the image and tracks if we have them + val imageText = uploadAndGetAttachedPhotosText(edit.imagePaths) + val tracksText = uploadAndGetAttachedTracksText(edit.tracks, edit.text) + val text = edit.text.orEmpty() + imageText + tracksText // done, try to upload the note to OSM - val text = edit.text.orEmpty() + imageText + tracksText try { val note = when (edit.action) { CREATE -> notesApi.create(edit.position, text) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt index b0d0cbf9fb..3ebf25f95f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -1,7 +1,7 @@ package de.westnordost.streetcomplete.data.osmtracks import de.westnordost.streetcomplete.data.download.ConnectionException -import de.westnordost.streetcomplete.data.user.AuthorizationTracesException +import de.westnordost.streetcomplete.data.user.AuthorizationException /** * Creates GPS / GPX trackpoint histories @@ -15,7 +15,7 @@ interface TracksApi { * @param trackpoints history of recorded trackpoints * @param noteText optional text appended to the track * - * @throws AuthorizationTracesException if this application is not authorized to write notes + * @throws AuthorizationException if this application is not authorized to write notes * (Permission.READ_GPS_TRACES, Permission.WRITE_GPS_TRACES) * @throws ConnectionException if a temporary network connection problem occurs * diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 3a18fc33d8..ded11fea0e 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -11,7 +11,7 @@ import de.westnordost.osmapi.traces.GpsTracesApi import de.westnordost.osmapi.traces.GpsTrackpoint import de.westnordost.streetcomplete.ApplicationConstants import de.westnordost.streetcomplete.data.download.ConnectionException -import de.westnordost.streetcomplete.data.user.AuthorizationTracesException +import de.westnordost.streetcomplete.data.user.AuthorizationException import java.time.Instant import java.time.ZoneOffset import java.time.format.DateTimeFormatter @@ -53,7 +53,7 @@ private inline fun wrapExceptions(block: () -> T): T = try { block() } catch (e: OsmAuthorizationException) { - throw AuthorizationTracesException(e.message, e) + throw AuthorizationException(e.message, e) } catch (e: OsmConnectionException) { throw ConnectionException(e.message, e) } catch (e: OsmApiReadResponseException) { diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt b/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt index dfd262c097..a06aa621ae 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/user/AuthorizationException.kt @@ -2,6 +2,3 @@ package de.westnordost.streetcomplete.data.user class AuthorizationException @JvmOverloads constructor( message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) - -class AuthorizationTracesException @JvmOverloads constructor( - message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt index 8e3ce1de2f..0a909b3e36 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt @@ -1,6 +1,9 @@ package de.westnordost.streetcomplete.data.user +import android.content.SharedPreferences import de.westnordost.osmapi.OsmConnection +import de.westnordost.streetcomplete.BuildConfig +import de.westnordost.streetcomplete.Prefs import oauth.signpost.OAuthConsumer import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -9,6 +12,7 @@ import javax.inject.Singleton @Singleton class UserLoginStatusController @Inject constructor( private val oAuthStore: OAuthStore, private val osmConnection: OsmConnection, + private val prefs: SharedPreferences, ): UserLoginStatusSource { private val listeners: MutableList = CopyOnWriteArrayList() @@ -18,12 +22,14 @@ import javax.inject.Singleton fun logIn(consumer: OAuthConsumer) { oAuthStore.oAuthConsumer = consumer osmConnection.oAuth = consumer + prefs.edit().putBoolean(Prefs.OSM_HAS_UPLOAD_TRACES_PERMISSION, true).apply() listeners.forEach { it.onLoggedIn() } } fun logOut() { oAuthStore.oAuthConsumer = null osmConnection.oAuth = null + prefs.edit().putBoolean(Prefs.OSM_HAS_UPLOAD_TRACES_PERMISSION, false).apply() listeners.forEach { it.onLoggedOut() } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index cc62dbcb9e..ffaed9a8ae 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -55,6 +55,7 @@ import de.westnordost.streetcomplete.map.tangram.CameraPosition import de.westnordost.streetcomplete.osm.levelsIntersect import de.westnordost.streetcomplete.quests.* import de.westnordost.streetcomplete.util.* +import de.westnordost.streetcomplete.view.dialogs.RequestPermissionUpgradeDialog import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -722,6 +723,15 @@ class MainFragment : Fragment(R.layout.fragment_main), } private fun onClickCreateTrack() { + + // Check that the user has required permission to record a track + val hasUploadPermission = prefs.getBoolean(Prefs.OSM_HAS_UPLOAD_TRACES_PERMISSION, false) + if(!hasUploadPermission) { + RequestPermissionUpgradeDialog(requireContext()).show() + return + } + + // Else we are good to start recording! val mapFragment = mapFragment ?: return mapFragment.startPositionTrackRecording() binding.stopTracksButton.visibility = View.VISIBLE diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt index 1b87d638fe..9cf73f331f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt @@ -139,7 +139,7 @@ class LoginFragment : Fragment(R.layout.fragment_login), Permission.MODIFY_MAP, Permission.WRITE_NOTES, Permission.READ_GPS_TRACES, - Permission.WRITE_GPS_TRACES + Permission.WRITE_GPS_TRACES, ) private const val ARG_LAUNCH_AUTH = "launch_auth" diff --git a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt index 7b3344067d..528d2c6aaa 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt @@ -1,20 +1,29 @@ package de.westnordost.streetcomplete.view.dialogs -import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.view.LayoutInflater import androidx.appcompat.app.AlertDialog +import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R +import de.westnordost.streetcomplete.data.user.UserLoginStatusController import de.westnordost.streetcomplete.user.UserActivity +import javax.inject.Inject /** Shows a dialog that asks the user to login */ -@SuppressLint("InflateParams") class RequestPermissionUpgradeDialog(context: Context) : AlertDialog(context, R.style.Theme_Bubble_Dialog) { + + @Inject + lateinit var userLoginStatusController: UserLoginStatusController + init { + Injector.applicationComponent.inject(this) val view = LayoutInflater.from(context).inflate(R.layout.dialog_permission_upgrade, null, false) setView(view) setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> + if(userLoginStatusController.isLoggedIn) { + userLoginStatusController.logOut() + } val intent = Intent(context, UserActivity::class.java) intent.putExtra(UserActivity.EXTRA_LAUNCH_AUTH, true) context.startActivity(intent) diff --git a/app/src/main/res/layout/dialog_permission_upgrade.xml b/app/src/main/res/layout/dialog_permission_upgrade.xml index fe6a81c72d..d681ddc176 100644 --- a/app/src/main/res/layout/dialog_permission_upgrade.xml +++ b/app/src/main/res/layout/dialog_permission_upgrade.xml @@ -11,13 +11,4 @@ android:textAppearance="@android:style/TextAppearance.Theme.Dialog" android:id="@+id/text"/> - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6957150d7f..710060b323 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,7 +106,7 @@ "GNU General Public License" "You are offline" "You need to authorize with your OSM user account to publish your answers. Authorize now?" - "To upload a GPS trace, you need to authorize with your OSM user account to publish traces. Authorize now?" + "To upload a GPS trace, you need to authorize with your OSM user account to publish traces. This will log you out and ask you to re-login with an internet connection. Authorize now?" "Not authorized" "This version of StreetComplete is too old. Please update!" "To scan for quests around you automatically, turn on location" From 40b410c09f63b1b4c6a9af97dbb63e44dee11d4c Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sat, 22 Jan 2022 16:45:54 -0500 Subject: [PATCH 15/30] fix merge issue --- .../main/java/de/westnordost/streetcomplete/map/MainFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 1d2d3ff2d5..68e03b7c4a 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -48,7 +48,6 @@ import de.westnordost.streetcomplete.edithistory.EditHistoryFragment import de.westnordost.streetcomplete.ktx.* import de.westnordost.streetcomplete.location.FineLocationManager import de.westnordost.streetcomplete.location.LocationState -import de.westnordost.streetcomplete.location.toLatLon import de.westnordost.streetcomplete.location.LocationRequester import de.westnordost.streetcomplete.location.LocationRequester.Companion.REQUEST_LOCATION_PERMISSION_RESULT import de.westnordost.streetcomplete.map.tangram.CameraPosition From 808580c1db10da93cb1b03a92cb62fc227c1fbaf Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Wed, 2 Feb 2022 20:50:18 +0100 Subject: [PATCH 16/30] Fix lint issues --- .../de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt | 1 - .../westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt | 2 +- .../streetcomplete/data/osmnotes/edits/NoteEditsController.kt | 2 -- .../java/de/westnordost/streetcomplete/data/osmtracks/Track.kt | 1 - .../de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt | 1 - .../westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt | 1 - .../streetcomplete/data/user/UserLoginStatusController.kt | 1 - .../view/dialogs/RequestPermissionUpgradeDialog.kt | 2 +- 8 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt index 86fdebd091..8d973914c1 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/NotesApi.kt @@ -65,5 +65,4 @@ interface NotesApi { * @return the incoming notes */ fun getAll(bounds: BoundingBox, limit: Int, hideClosedNoteAfter: Int): List - } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index e821cd49d4..5d22536fd1 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -37,7 +37,7 @@ data class NoteEdit( /** attached GPS location tracks */ val tracks: List, -): Edit { +) : Edit { override val isUndoable: Boolean get() = !isSynced override val key: NoteEditKey get() = NoteEditKey(id) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index 3bf1ae99f2..9c852ae8a6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -4,8 +4,6 @@ import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.data.osmnotes.Note import de.westnordost.streetcomplete.data.osmtracks.Trackpoint -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import java.lang.System.currentTimeMillis import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt index dbc27c3e8d..4d1511fa23 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/Track.kt @@ -3,7 +3,6 @@ package de.westnordost.streetcomplete.data.osmtracks import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import kotlinx.serialization.Serializable - @Serializable data class Track( val id: Long, diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt index 3ebf25f95f..54ed8ad2ce 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -22,5 +22,4 @@ interface TracksApi { * @return the new track */ fun create(trackpoints: List, noteText: String?): Track - } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index ded11fea0e..9fad1d078c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -63,4 +63,3 @@ private inline fun wrapExceptions(block: () -> T): T = // request timeout is a temporary connection error throw if (e.errorCode == 408) ConnectionException(e.message, e) else e } - diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt index f79c7ff306..7f06cae487 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserLoginStatusController.kt @@ -2,7 +2,6 @@ package de.westnordost.streetcomplete.data.user import android.content.SharedPreferences import de.westnordost.osmapi.OsmConnection -import de.westnordost.streetcomplete.BuildConfig import de.westnordost.streetcomplete.Prefs import oauth.signpost.OAuthConsumer import java.util.concurrent.CopyOnWriteArrayList diff --git a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt index 528d2c6aaa..f670c65896 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt @@ -21,7 +21,7 @@ class RequestPermissionUpgradeDialog(context: Context) : AlertDialog(context, R. val view = LayoutInflater.from(context).inflate(R.layout.dialog_permission_upgrade, null, false) setView(view) setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> - if(userLoginStatusController.isLoggedIn) { + if (userLoginStatusController.isLoggedIn) { userLoginStatusController.logOut() } val intent = Intent(context, UserActivity::class.java) From 260e32ebaaf7fa0af0ce2d58960a26d866c45c9d Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Thu, 17 Mar 2022 15:41:53 +0100 Subject: [PATCH 17/30] Fix remaining merge conflicts (Koin migration) --- .../data/osmnotes/edits/NoteEditsModule.kt | 2 +- .../streetcomplete/data/user/UserModule.kt | 2 +- .../streetcomplete/screens/main/MainFragment.kt | 3 ++- .../view/dialogs/RequestPermissionUpgradeDialog.kt | 13 +++++-------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsModule.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsModule.kt index 4f08a7a26d..df1480224d 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsModule.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsModule.kt @@ -5,7 +5,7 @@ import org.koin.dsl.module val noteEditsModule = module { factory { NoteEditsDao(get()) } - single { NoteEditsUploader(get(), get(), get(), get()) } + single { NoteEditsUploader(get(), get(), get(), get(), get()) } single { NoteEditsController(get()) } single { get() } single { NotesWithEditsSource(get(), get(), get()) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserModule.kt b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserModule.kt index 40f9141567..b164d55f0f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/user/UserModule.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/user/UserModule.kt @@ -28,7 +28,7 @@ val userModule = module { single { UserDataController(get(), get()) } single { get() } - single { UserLoginStatusController(get(), get()) } + single { UserLoginStatusController(get(), get(), get()) } single { UserUpdater(get(), get(), get(), get(), get()) } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt index 6f3c6e8066..064d592ba5 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt @@ -101,6 +101,7 @@ import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.koin.android.ext.android.get import org.koin.android.ext.android.inject import kotlin.math.PI import kotlin.math.abs @@ -764,7 +765,7 @@ class MainFragment : // Check that the user has required permission to record a track val hasUploadPermission = prefs.getBoolean(Prefs.OSM_HAS_UPLOAD_TRACES_PERMISSION, false) if (!hasUploadPermission) { - RequestPermissionUpgradeDialog(requireContext()).show() + RequestPermissionUpgradeDialog(requireContext(), get()).show() return } diff --git a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt index f670c65896..75b6c9e44b 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/view/dialogs/RequestPermissionUpgradeDialog.kt @@ -4,20 +4,17 @@ import android.content.Context import android.content.Intent import android.view.LayoutInflater import androidx.appcompat.app.AlertDialog -import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.user.UserLoginStatusController -import de.westnordost.streetcomplete.user.UserActivity -import javax.inject.Inject +import de.westnordost.streetcomplete.screens.user.UserActivity /** Shows a dialog that asks the user to login */ -class RequestPermissionUpgradeDialog(context: Context) : AlertDialog(context, R.style.Theme_Bubble_Dialog) { - - @Inject - lateinit var userLoginStatusController: UserLoginStatusController +class RequestPermissionUpgradeDialog( + context: Context, + private val userLoginStatusController: UserLoginStatusController, +) : AlertDialog(context, R.style.Theme_Bubble_Dialog) { init { - Injector.applicationComponent.inject(this) val view = LayoutInflater.from(context).inflate(R.layout.dialog_permission_upgrade, null, false) setView(view) setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> From ad1ec72672c74f4ef5e51a26f56aeb675a8f8a2f Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Thu, 17 Mar 2022 17:05:03 +0100 Subject: [PATCH 18/30] Lint --- .../data/osmnotes/edits/NoteEditsUploader.kt | 25 ++++++++----------- .../data/osmtracks/TracksApiImpl.kt | 1 - .../screens/main/MainFragment.kt | 1 - .../main/map/LocationAwareMapFragment.kt | 6 ++--- .../screens/measure/MeasureActivity.kt | 2 +- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index b2a78f0e2a..9563962ca4 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -36,14 +36,12 @@ class NoteEditsUploader( * Drops any edits where the upload failed because of a conflict but keeps any notes where * the upload failed because attached photos could not be uploaded (so it can try again * later). */ - suspend fun upload() = mutex.withLock { - withContext(Dispatchers.IO) { - // first look if any images have not been activated yet - uploadMissedImageActivations() - // then do the usual stuff - uploadEdits() - } - } + suspend fun upload() = mutex.withLock { withContext(Dispatchers.IO) { + // first look if any images have not been activated yet + uploadMissedImageActivations() + // then do the usual stuff + uploadEdits() + } } private suspend fun uploadMissedImageActivations() { while (true) { @@ -67,7 +65,6 @@ class NoteEditsUploader( } private fun uploadEdit(edit: NoteEdit) { - // try to upload the image and tracks if we have them val imageText = uploadAndGetAttachedPhotosText(edit.imagePaths) val tracksText = uploadAndGetAttachedTracksText(edit.tracks, edit.text) @@ -80,10 +77,9 @@ class NoteEditsUploader( COMMENT -> notesApi.comment(edit.noteId, text) } - Log.d( - TAG, + Log.d(TAG, "Uploaded a ${edit.action.name} to ${note.id}" + - " at ${edit.position.latitude}, ${edit.position.longitude}" + " at ${edit.position.latitude}, ${edit.position.longitude}" ) uploadedChangeListener?.onUploaded(NOTE, edit.position) @@ -96,10 +92,9 @@ class NoteEditsUploader( } deleteImages(edit.imagePaths) } catch (e: ConflictException) { - Log.d( - TAG, + Log.d(TAG, "Dropped a ${edit.action.name} to ${edit.noteId}" + - " at ${edit.position.latitude}, ${edit.position.longitude}: ${e.message}" + " at ${edit.position.latitude}, ${edit.position.longitude}: ${e.message}" ) uploadedChangeListener?.onDiscarded(NOTE, edit.position) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 9fad1d078c..7f779a48b3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -20,7 +20,6 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { private val api: GpsTracesApi = GpsTracesApi(osm) override fun create(trackpoints: List, noteText: String?): Track = wrapExceptions { - // Filename is just the start of the track // https://stackoverflow.com/a/49862573/7718197 val name = DateTimeFormatter diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt index 064d592ba5..334417b057 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt @@ -761,7 +761,6 @@ class MainFragment : } private fun onClickCreateTrack() { - // Check that the user has required permission to record a track val hasUploadPermission = prefs.getBoolean(Prefs.OSM_HAS_UPLOAD_TRACES_PERMISSION, false) if (!hasUploadPermission) { diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index 393df4def5..594d113eaf 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -100,8 +100,7 @@ open class LocationAwareMapFragment : MapFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) displayedLocation = savedInstanceState?.getParcelable(DISPLAYED_LOCATION) - val nullTerminatedTracks = - savedInstanceState?.getParcelableArrayList(TRACKS) as ArrayList? + val nullTerminatedTracks = savedInstanceState?.getParcelableArrayList(TRACKS) as ArrayList? if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() } @@ -202,8 +201,7 @@ open class LocationAwareMapFragment : MapFragment() { behind user */ val distance = controller?.screenBottomToCenterDistance() if (distance != null) { - centerPosition = - centerPosition.translate(distance * 0.4, bearing.toDouble()) + centerPosition = centerPosition.translate(distance * 0.4, bearing.toDouble()) } } tilt = PI.toFloat() / 6f diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/measure/MeasureActivity.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/measure/MeasureActivity.kt index 57aab399f6..24ad1cf015 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/measure/MeasureActivity.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/measure/MeasureActivity.kt @@ -164,7 +164,7 @@ class MeasureActivity : AppCompatActivity(), Scene.OnUpdateListener { if (frame.camera.trackingState == TRACKING) { if (measureVertical) { - if (measureState == MeasureState.MEASURING){ + if (measureState == MeasureState.MEASURING) { updateVerticalMeasuring(frame.camera.displayOrientedPose) } } else { From 7a04dea42fef72d95049d5ef6209cf3c3117d56c Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Fri, 29 Apr 2022 21:18:50 -0400 Subject: [PATCH 19/30] Store if the recording is active. - Also ensure that on re-creation, the new points are appended to the same path - Have not tested on a real device, but on emu it seems to work even if I tab out of the program --- .../screens/main/MainFragment.kt | 6 +++-- .../main/map/LocationAwareMapFragment.kt | 25 +++++++++++++++---- .../main/map/components/TracksMapComponent.kt | 14 ++++++++--- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt index 988075a591..23ccfcbd07 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt @@ -26,6 +26,7 @@ import androidx.core.graphics.toPointF import androidx.core.graphics.toRectF import androidx.core.view.isGone import androidx.core.view.isInvisible +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.commit @@ -269,6 +270,7 @@ class MainFragment : override fun onMapInitialized() { binding.gpsTrackingButton.isActivated = mapFragment?.isFollowingPosition ?: false binding.gpsTrackingButton.isNavigation = mapFragment?.isNavigationMode ?: false + binding.stopTracksButton.isVisible = mapFragment?.tracksRecording ?: false updateLocationPointerPin() listener?.onMapInitialized() } @@ -634,7 +636,7 @@ class MainFragment : private fun onClickTracksStop() { // hide the track information - binding.stopTracksButton.visibility = View.GONE + binding.stopTracksButton.isVisible = false val mapFragment = mapFragment ?: return mapFragment.stopPositionTrackRecording() val pos = mapFragment.displayedLocation?.toLatLon() ?: return @@ -755,7 +757,7 @@ class MainFragment : // Else we are good to start recording! val mapFragment = mapFragment ?: return mapFragment.startPositionTrackRecording() - binding.stopTracksButton.visibility = View.VISIBLE + binding.stopTracksButton.isVisible = true } // ---------------------------------- Location Pointer Pin --------------------------------- */ diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index 2a90c8df94..9f74e832ad 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -105,10 +105,22 @@ open class LocationAwareMapFragment : MapFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - displayedLocation = savedInstanceState?.getParcelable(DISPLAYED_LOCATION) - val nullTerminatedTracks = savedInstanceState?.getParcelableArrayList(TRACKS) as ArrayList? - if (nullTerminatedTracks != null) { - tracks = nullTerminatedTracks.unflattenNullTerminated() + // Restore value of members from saved state + if (savedInstanceState != null) { + with(savedInstanceState) { + displayedLocation = getParcelable(DISPLAYED_LOCATION) + tracksRecording = getBoolean(TRACKS_IS_RECORDING) + // It seems that the last list element will be an empty one normally + // If it is an empty one we can just remove it so we can keep on recording + val nullTerminatedTracks = + getParcelableArrayList(TRACKS) as ArrayList? + if (nullTerminatedTracks != null) { + tracks = nullTerminatedTracks.unflattenNullTerminated() + if (tracksRecording && tracks.last().isEmpty()) { + tracks.removeLastOrNull() + } + } + } } } @@ -137,7 +149,7 @@ open class LocationAwareMapFragment : MapFragment() { locationMapComponent?.location = displayedLocation tracksMapComponent = TracksMapComponent(ctrl) - tracksMapComponent?.setTracks(tracks) + tracksMapComponent?.setTracks(tracks, tracksRecording) centerCurrentPositionIfFollowing() } @@ -174,6 +186,7 @@ open class LocationAwareMapFragment : MapFragment() { @SuppressLint("MissingPermission") fun startPositionTrackRecording() { tracksRecording = true + _tracksRecorded.clear() tracks.add(ArrayList()) locationMapComponent?.isVisible = true locationManager.requestUpdates(500, 1f) @@ -299,6 +312,7 @@ open class LocationAwareMapFragment : MapFragment() { super.onSaveInstanceState(outState) outState.putParcelable(DISPLAYED_LOCATION, displayedLocation) outState.putParcelableArrayList(TRACKS, tracks.flattenToNullTerminated()) + outState.putBoolean(TRACKS_IS_RECORDING, tracksRecording) } companion object { @@ -307,6 +321,7 @@ open class LocationAwareMapFragment : MapFragment() { private const val DISPLAYED_LOCATION = "displayed_location" private const val TRACKS = "tracks" + private const val TRACKS_IS_RECORDING = "tracks_is_recording" private const val MIN_TRACK_ACCURACY = 20f private const val MAX_TIME_BETWEEN_LOCATIONS = 60L * 1000 // 1 minute diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt index 38121e1912..6cd5188f49 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt @@ -44,16 +44,22 @@ class TracksMapComponent(ctrl: KtMapController) { putAllTracksInOldLayer() } - /** Set all the tracks (when re-initializing) */ - fun setTracks(tracks: List>) { - this.tracks = tracks.map { track -> Track(track.map { it.toLngLat() }.toMutableList(), false) }.toMutableList() + /** Set all the tracks (when re-initializing), if recording the last track is the only recording */ + fun setTracks(tracks: List>, isRecording: Boolean) { + this.tracks = tracks.mapIndexed { it, track -> + var recording = false + if (isRecording && it == tracks.size - 1) { + recording = true + } + Track(track.map { it.toLngLat() }.toMutableList(), recording) + }.toMutableList() putAllTracksInOldLayer() } private fun putAllTracksInOldLayer() { index = max(0, tracks.last().trackpoints.lastIndex) layer1.clear() - layer2.setFeatures(tracks.map { it.trackpoints.toPolyline(true, it.isRecording) }) + layer2.setFeatures(tracks.map { it.trackpoints.toPolyline(it.isRecording, it.isRecording) }) } fun clear() { From 4e7fea1b55ac951fc50b5d9d99b2f27885f12618 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Fri, 29 Apr 2022 21:36:48 -0400 Subject: [PATCH 20/30] Always mark as old track --- .../screens/main/map/components/TracksMapComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt index 6cd5188f49..705fa08a43 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/components/TracksMapComponent.kt @@ -59,7 +59,7 @@ class TracksMapComponent(ctrl: KtMapController) { private fun putAllTracksInOldLayer() { index = max(0, tracks.last().trackpoints.lastIndex) layer1.clear() - layer2.setFeatures(tracks.map { it.trackpoints.toPolyline(it.isRecording, it.isRecording) }) + layer2.setFeatures(tracks.map { it.trackpoints.toPolyline(true, it.isRecording) }) } fun clear() { From 231d1780e1af51edb7fba776b95054f33e6528e3 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:16:07 -0400 Subject: [PATCH 21/30] Apply suggestions from code review Co-authored-by: Flo Edelmann Co-authored-by: rugk --- .../screens/main/map/LocationAwareMapFragment.kt | 9 +++++---- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index 9f74e832ad..c3850d75e9 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -116,6 +116,7 @@ open class LocationAwareMapFragment : MapFragment() { getParcelableArrayList(TRACKS) as ArrayList? if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() + // unflattenNullTerminated creates an empty list item at the end if (tracksRecording && tracks.last().isEmpty()) { tracks.removeLastOrNull() } @@ -197,12 +198,12 @@ open class LocationAwareMapFragment : MapFragment() { tracksRecording = false _tracksRecorded.clear() tracks.last().forEach { - // Emulator has zero altitude: - // https://stackoverflow.com/q/65325665/7718197 - // Time here is in milliseconds _tracksRecorded.add( Trackpoint( - LatLon(it.latitude, it.longitude), it.time, it.accuracy, it.altitude.toFloat() + LatLon(it.latitude, it.longitude), + it.time, // in milliseconds + it.accuracy, + it.altitude.toFloat() // always zero in emulator: https://stackoverflow.com/q/65325665 ) ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index acc814d3ba..2584ac5c99 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -231,7 +231,7 @@ If you are overwhelmed by the number of quests, you can always fine-tune which q "You need to authorize with your OSM user account to publish your answers. Authorize now?" You can also do this later on the profile screen. - "To upload a GPS trace, you need to authorize with your OSM user account to publish traces. This will log you out and ask you to re-login with an internet connection. Authorize now?" + "To upload a GPS trace, you need to authorize with your OpenStreetMap user account to publish traces. This will log you out of the app and ask you to re-login. Proceed now?" "Later" "Not authorized" From e0b4766a2adbdb0127e23c4fd14b4eed9302f1ef Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:33:40 -0400 Subject: [PATCH 22/30] tracks -> track --- .../streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt | 2 +- .../streetcomplete/data/osmnotes/edits/NoteEdit.kt | 4 ++-- .../data/osmnotes/edits/NoteEditsController.kt | 4 ++-- .../streetcomplete/data/osmnotes/edits/NoteEditsDao.kt | 6 +++--- .../streetcomplete/data/osmnotes/edits/NoteEditsTable.kt | 4 ++-- .../data/osmnotes/edits/NoteEditsUploader.kt | 8 ++++---- .../streetcomplete/data/quest/QuestController.kt | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt index 5e6e4ca75a..287c62c603 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/StreetCompleteSQLiteOpenHelper.kt @@ -172,7 +172,7 @@ class StreetCompleteSQLiteOpenHelper(context: Context, dbName: String) : db.execSQL("DROP TABLE $oldGeometryTableName;") } if (oldVersion <= 5 && newVersion > 5) { - db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.TRACKS} text DEFAULT '[]' NOT NULL") + db.execSQL("ALTER TABLE ${NoteEditsTable.NAME} ADD COLUMN ${NoteEditsTable.Columns.TRACK} text DEFAULT '[]' NOT NULL") } } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt index 5d22536fd1..0ae91f90f3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEdit.kt @@ -35,8 +35,8 @@ data class NoteEdit( /** whether the images attached still need activation. Already true if imagePaths is empty */ val imagesNeedActivation: Boolean, - /** attached GPS location tracks */ - val tracks: List, + /** attached GPS location history */ + val track: List, ) : Edit { override val isUndoable: Boolean get() = !isSynced override val key: NoteEditKey get() = NoteEditKey(id) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt index 52f8ff4b02..38beff7845 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsController.kt @@ -21,7 +21,7 @@ class NoteEditsController( position: LatLon, text: String? = null, imagePaths: List = emptyList(), - tracks: List = emptyList(), + track: List = emptyList(), ) { val edit = NoteEdit( 0, @@ -33,7 +33,7 @@ class NoteEditsController( currentTimeMillis(), false, imagePaths.isNotEmpty(), - tracks, + track, ) synchronized(this) { editsDB.add(edit) } onAddedEdit(edit) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt index ab4a781208..e85428740f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsDao.kt @@ -13,7 +13,7 @@ import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns. import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.LONGITUDE import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.NOTE_ID import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TEXT -import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TRACKS +import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TRACK import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.Columns.TYPE import de.westnordost.streetcomplete.data.osmnotes.edits.NoteEditsTable.NAME import kotlinx.serialization.decodeFromString @@ -124,7 +124,7 @@ class NoteEditsDao(private val db: Database) { TEXT to text, IMAGE_PATHS to Json.encodeToString(imagePaths), IMAGES_NEED_ACTIVATION to if (imagesNeedActivation) 1 else 0, - TRACKS to Json.encodeToString(tracks), + TRACK to Json.encodeToString(track), TYPE to action.name ) @@ -138,6 +138,6 @@ class NoteEditsDao(private val db: Database) { getLong(CREATED_TIMESTAMP), getInt(IS_SYNCED) == 1, getInt(IMAGES_NEED_ACTIVATION) == 1, - Json.decodeFromString(getString(TRACKS)), + Json.decodeFromString(getString(TRACK)), ) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt index 06edb36873..36166dabbb 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsTable.kt @@ -14,7 +14,7 @@ object NoteEditsTable { const val TEXT = "text" const val IMAGE_PATHS = "image_paths" const val IMAGES_NEED_ACTIVATION = "images_need_activation" - const val TRACKS = "tracks" + const val TRACK = "track" } const val CREATE = """ @@ -28,7 +28,7 @@ object NoteEditsTable { ${Columns.TEXT} text, ${Columns.IMAGE_PATHS} text NOT NULL, ${Columns.IMAGES_NEED_ACTIVATION} int NOT NULL, - ${Columns.TRACKS} text NOT NULL, + ${Columns.TRACK} text NOT NULL, ${Columns.TYPE} varchar(255) ); """ diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt index 9563962ca4..740bd9b49d 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmnotes/edits/NoteEditsUploader.kt @@ -65,10 +65,10 @@ class NoteEditsUploader( } private fun uploadEdit(edit: NoteEdit) { - // try to upload the image and tracks if we have them + // try to upload the image and track if we have them val imageText = uploadAndGetAttachedPhotosText(edit.imagePaths) - val tracksText = uploadAndGetAttachedTracksText(edit.tracks, edit.text) - val text = edit.text.orEmpty() + imageText + tracksText + val trackText = uploadAndGetAttachedTrackText(edit.track, edit.text) + val text = edit.text.orEmpty() + imageText + trackText // done, try to upload the note to OSM try { @@ -119,7 +119,7 @@ class NoteEditsUploader( return "" } - private fun uploadAndGetAttachedTracksText( + private fun uploadAndGetAttachedTrackText( trackpoints: List, noteText: String? ): String { diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt index 9fea8209b8..0b93232697 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestController.kt @@ -67,10 +67,10 @@ class QuestController( text: String, imagePaths: List, position: LatLon, - tracks: List, + track: List, ) = withContext(Dispatchers.IO) { val fullText = "$text\n\nvia ${ApplicationConstants.USER_AGENT}" - noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, tracks) + noteEditsController.add(0, NoteEditAction.CREATE, position, fullText, imagePaths, track) } /** Split a way for the given OSM Quest. From 754f9023f341c79d34f0c12691ee1065095d8cfa Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:33:52 -0400 Subject: [PATCH 23/30] remove unneeded check --- .../streetcomplete/screens/main/map/LocationAwareMapFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index c3850d75e9..40fc89be77 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -117,7 +117,7 @@ open class LocationAwareMapFragment : MapFragment() { if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() // unflattenNullTerminated creates an empty list item at the end - if (tracksRecording && tracks.last().isEmpty()) { + if (tracksRecording) { tracks.removeLastOrNull() } } From 10c58dbeb735347fb2c3c0b9d6ff3fe663552c0e Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:38:14 -0400 Subject: [PATCH 24/30] use 4 space in yaml --- .../assets/map_theme/jawg/streetcomplete.yaml | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml index 7f0f48c26b..fcdaca38fa 100644 --- a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml +++ b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml @@ -136,14 +136,14 @@ layers: join: round order: 1000 record: - filter: { record: [true] } - draw: - track-lines: - color: global.track_color_record - width: [ [ 14, 6px ],[ 18, 12px ] ] - collide: false - join: round - order: 1000 + filter: { record: [true] } + draw: + track-lines: + color: global.track_color_record + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 old: filter: { old: [true], record: [false] } draw: @@ -156,29 +156,29 @@ layers: streetcomplete_track2: data: { source: streetcomplete_track2 } current: - filter: { old: [ false ], record: [ false ] } - draw: - track-lines: - color: global.track_color - width: [ [ 14, 6px ],[ 18, 12px ] ] - collide: false - join: round - order: 1000 + filter: { old: [ false ], record: [ false ] } + draw: + track-lines: + color: global.track_color + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 record: - filter: { record: [ true ] } - draw: - track-lines: - color: global.track_color_record - width: [ [ 14, 6px ],[ 18, 12px ] ] - collide: false - join: round - order: 1000 + filter: { record: [ true ] } + draw: + track-lines: + color: global.track_color_record + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 old: - filter: { old: [ true ], record: [ false ] } - draw: - track-lines: - color: global.old_track_color - width: [ [ 14, 6px ],[ 18, 12px ] ] - collide: false - join: round - order: 1000 + filter: { old: [ true ], record: [ false ] } + draw: + track-lines: + color: global.old_track_color + width: [ [ 14, 6px ],[ 18, 12px ] ] + collide: false + join: round + order: 1000 From 6720ed0e4a214f47bbc2c63814dca6800ee6b7d8 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:41:28 -0400 Subject: [PATCH 25/30] bracket spacing --- .../main/assets/map_theme/jawg/streetcomplete.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml index fcdaca38fa..dc2dadab73 100644 --- a/app/src/main/assets/map_theme/jawg/streetcomplete.yaml +++ b/app/src/main/assets/map_theme/jawg/streetcomplete.yaml @@ -140,7 +140,7 @@ layers: draw: track-lines: color: global.track_color_record - width: [ [ 14, 6px ],[ 18, 12px ] ] + width: [[14, 6px],[18, 12px]] collide: false join: round order: 1000 @@ -156,29 +156,29 @@ layers: streetcomplete_track2: data: { source: streetcomplete_track2 } current: - filter: { old: [ false ], record: [ false ] } + filter: { old: [false], record: [false] } draw: track-lines: color: global.track_color - width: [ [ 14, 6px ],[ 18, 12px ] ] + width: [[14, 6px],[18, 12px]] collide: false join: round order: 1000 record: - filter: { record: [ true ] } + filter: { record: [true] } draw: track-lines: color: global.track_color_record - width: [ [ 14, 6px ],[ 18, 12px ] ] + width: [[14, 6px],[18, 12px]] collide: false join: round order: 1000 old: - filter: { old: [ true ], record: [ false ] } + filter: { old: [true], record: [false] } draw: track-lines: color: global.old_track_color - width: [ [ 14, 6px ],[ 18, 12px ] ] + width: [[14, 6px],[18, 12px]] collide: false join: round order: 1000 From 04aa8560b7d3cf6e04e2968428b12a4c07de4447 Mon Sep 17 00:00:00 2001 From: Patrick Geneva Date: Sun, 1 May 2022 13:47:10 -0400 Subject: [PATCH 26/30] variable renames --- .../screens/main/MainFragment.kt | 4 +-- .../main/map/LocationAwareMapFragment.kt | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt index 23ccfcbd07..f5b6381596 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/MainFragment.kt @@ -270,7 +270,7 @@ class MainFragment : override fun onMapInitialized() { binding.gpsTrackingButton.isActivated = mapFragment?.isFollowingPosition ?: false binding.gpsTrackingButton.isNavigation = mapFragment?.isNavigationMode ?: false - binding.stopTracksButton.isVisible = mapFragment?.tracksRecording ?: false + binding.stopTracksButton.isVisible = mapFragment?.isRecordingTracks ?: false updateLocationPointerPin() listener?.onMapInitialized() } @@ -524,7 +524,7 @@ class MainFragment : closeBottomSheet() viewLifecycleScope.launch { - questController.createNote(note, imagePaths, position, mapFragment.tracksRecorded) + questController.createNote(note, imagePaths, position, mapFragment.recordedTracks) } listener?.onCreatedNote(screenPosition) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index 40fc89be77..6de861d0f3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -46,13 +46,13 @@ open class LocationAwareMapFragment : MapFragment() { private var tracks: MutableList> /** If we are actively recording track history */ - var tracksRecording = false + var isRecordingTracks = false private set /** The GPS trackpoints the user has recorded */ - private var _tracksRecorded: ArrayList + private var _recordedTracks: ArrayList - val tracksRecorded: List get() = _tracksRecorded + val recordedTracks: List get() = _recordedTracks /** Whether the view should automatically center on the GPS location */ var isFollowingPosition = true @@ -89,7 +89,7 @@ open class LocationAwareMapFragment : MapFragment() { init { tracks = ArrayList() tracks.add(ArrayList()) - _tracksRecorded = ArrayList() + _recordedTracks = ArrayList() } override fun onAttach(context: Context) { @@ -109,7 +109,7 @@ open class LocationAwareMapFragment : MapFragment() { if (savedInstanceState != null) { with(savedInstanceState) { displayedLocation = getParcelable(DISPLAYED_LOCATION) - tracksRecording = getBoolean(TRACKS_IS_RECORDING) + isRecordingTracks = getBoolean(TRACKS_IS_RECORDING) // It seems that the last list element will be an empty one normally // If it is an empty one we can just remove it so we can keep on recording val nullTerminatedTracks = @@ -117,7 +117,7 @@ open class LocationAwareMapFragment : MapFragment() { if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() // unflattenNullTerminated creates an empty list item at the end - if (tracksRecording) { + if (isRecordingTracks) { tracks.removeLastOrNull() } } @@ -150,7 +150,7 @@ open class LocationAwareMapFragment : MapFragment() { locationMapComponent?.location = displayedLocation tracksMapComponent = TracksMapComponent(ctrl) - tracksMapComponent?.setTracks(tracks, tracksRecording) + tracksMapComponent?.setTracks(tracks, isRecordingTracks) centerCurrentPositionIfFollowing() } @@ -186,8 +186,8 @@ open class LocationAwareMapFragment : MapFragment() { @SuppressLint("MissingPermission") fun startPositionTrackRecording() { - tracksRecording = true - _tracksRecorded.clear() + isRecordingTracks = true + _recordedTracks.clear() tracks.add(ArrayList()) locationMapComponent?.isVisible = true locationManager.requestUpdates(500, 1f) @@ -195,10 +195,10 @@ open class LocationAwareMapFragment : MapFragment() { } fun stopPositionTrackRecording() { - tracksRecording = false - _tracksRecorded.clear() + isRecordingTracks = false + _recordedTracks.clear() tracks.last().forEach { - _tracksRecorded.add( + _recordedTracks.add( Trackpoint( LatLon(it.latitude, it.longitude), it.time, // in milliseconds @@ -269,7 +269,7 @@ open class LocationAwareMapFragment : MapFragment() { val lastLocation = tracks.last().lastOrNull() // create new track if last position too old - if (lastLocation != null && !tracksRecording) { + if (lastLocation != null && !isRecordingTracks) { if ((displayedLocation?.time ?: 0) - lastLocation.time > MAX_TIME_BETWEEN_LOCATIONS) { tracks.add(ArrayList()) tracksMapComponent?.startNewTrack(false) @@ -313,7 +313,7 @@ open class LocationAwareMapFragment : MapFragment() { super.onSaveInstanceState(outState) outState.putParcelable(DISPLAYED_LOCATION, displayedLocation) outState.putParcelableArrayList(TRACKS, tracks.flattenToNullTerminated()) - outState.putBoolean(TRACKS_IS_RECORDING, tracksRecording) + outState.putBoolean(TRACKS_IS_RECORDING, isRecordingTracks) } companion object { From cec6cdae40c7478f52ec13d0461e786b2014aba9 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Mon, 2 May 2022 22:35:43 +0200 Subject: [PATCH 27/30] Improve comments --- .../screens/main/map/LocationAwareMapFragment.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt index 6de861d0f3..885788afc7 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/screens/main/map/LocationAwareMapFragment.kt @@ -110,13 +110,12 @@ open class LocationAwareMapFragment : MapFragment() { with(savedInstanceState) { displayedLocation = getParcelable(DISPLAYED_LOCATION) isRecordingTracks = getBoolean(TRACKS_IS_RECORDING) - // It seems that the last list element will be an empty one normally - // If it is an empty one we can just remove it so we can keep on recording val nullTerminatedTracks = getParcelableArrayList(TRACKS) as ArrayList? if (nullTerminatedTracks != null) { tracks = nullTerminatedTracks.unflattenNullTerminated() - // unflattenNullTerminated creates an empty list item at the end + // unflattenNullTerminated creates an empty list item (i.e. a new track) at the end. + // This is fine if the track is not being recorded. if (isRecordingTracks) { tracks.removeLastOrNull() } From b684380401b55f992564e35744e4c97a2d00d8ad Mon Sep 17 00:00:00 2001 From: Tobias Zwick Date: Sun, 15 May 2022 17:17:00 +0200 Subject: [PATCH 28/30] remove untrue comment --- .../de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt index 54ed8ad2ce..c923ad641a 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApi.kt @@ -5,7 +5,6 @@ import de.westnordost.streetcomplete.data.user.AuthorizationException /** * Creates GPS / GPX trackpoint histories - * All interactions with this class require an OsmConnection with a logged in user. */ interface TracksApi { From 13d47c8bd6b3dd7f34f5d83f0c774bd0cd9329b1 Mon Sep 17 00:00:00 2001 From: Tobias Zwick Date: Sun, 15 May 2022 17:17:37 +0200 Subject: [PATCH 29/30] use elvis --- .../westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt index 7f779a48b3..6c5512ef70 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/osmtracks/TracksApiImpl.kt @@ -27,7 +27,7 @@ class TracksApiImpl(osm: OsmConnection) : TracksApi { .withZone(ZoneOffset.UTC) .format(Instant.ofEpochSecond(trackpoints[0].time)) + ".gpx" val visibility = GpsTraceDetails.Visibility.IDENTIFIABLE - val description = noteText.orEmpty().ifBlank { "Uploaded via ${ApplicationConstants.USER_AGENT}" } + val description = noteText ?: "Uploaded via ${ApplicationConstants.USER_AGENT}" val tags = listOf(ApplicationConstants.NAME.lowercase()) // Generate history of trackpoints From b8413a10f636203b0a1bc88a8f7b63bd8d9a9edc Mon Sep 17 00:00:00 2001 From: Tobias Zwick Date: Sun, 15 May 2022 17:17:53 +0200 Subject: [PATCH 30/30] shortened/cleared wording a bit --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2584ac5c99..184a1e0593 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -231,7 +231,7 @@ If you are overwhelmed by the number of quests, you can always fine-tune which q "You need to authorize with your OSM user account to publish your answers. Authorize now?" You can also do this later on the profile screen. - "To upload a GPS trace, you need to authorize with your OpenStreetMap user account to publish traces. This will log you out of the app and ask you to re-login. Proceed now?" + "You need to authorize this app to publish track recordings on OpenStreetMap. This requires to log in again. Proceed?" "Later" "Not authorized"