app/app/src/main/java/com/danilafe/fencelessgrazing/ui/activities/BoundaryEditorActivity.kt

169 lines
5.0 KiB
Kotlin

package com.danilafe.fencelessgrazing.ui.activities
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.PersistableBundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.Volley
import com.danilafe.fencelessgrazing.R
import com.danilafe.fencelessgrazing.model.CollarPos
import com.danilafe.fencelessgrazing.requests.authenticated.SetBoundaryRequest
import com.danilafe.fencelessgrazing.ui.components.GrazingPolygon
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.Polygon
/**
* Activity used to update a grazing boundary for a particular collar.
*/
class BoundaryEditorActivity : AppCompatActivity(),
Marker.OnMarkerDragListener,
Marker.OnMarkerClickListener {
/**
* The map displaying the boundary vertices and polygon.
*/
private lateinit var map: MapView
/**
* The list of markers representing the new grazing boundary's edges.
*/
private val markers: MutableList<Marker> = mutableListOf()
/**
* The polygon used to display the grazing area on the [map].
*/
private lateinit var polygon: GrazingPolygon
/**
* The center around which the [map] was originally centered,
* used also for creating new vertices.
*/
private lateinit var center: GeoPoint
/**
* The identifier of the collar whose boundary is being changed.
*/
private var identifier: Int = -1
/**
* The Volley queue used for sending API requests.
*/
private lateinit var queue: RequestQueue
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_boundary_editor)
val requestedCenter = intent.getParcelableExtra<CollarPos>("center")!!
center = requestedCenter.toGeoPoint()
identifier = intent.getIntExtra("identifier", -1)
queue = Volley.newRequestQueue(applicationContext)
map = findViewById(R.id.editorMap)
polygon = GrazingPolygon(map)
map.overlays.add(polygon)
map.controller.setZoom(9.5)
map.controller.setCenter(center)
}
/**
* Removes a marker from the [map].
*/
private fun removeMarker(m: Marker) {
markers.remove(m)
map.overlays.remove(m)
updatePolygon()
map.invalidate()
}
/**
* Sends the current list of points to the API endpoint, closing
* the activity if successful.
*/
fun sendBoundary(v: View) {
val request = SetBoundaryRequest(
getString(R.string.apiUrl),
getSharedPreferences("FencelessGrazing",0).getString("token",null)!!,
identifier, markers.map {
CollarPos(it.position.longitude.toString(), it.position.latitude.toString())
},
Response.Listener<String> {
finish()
},
Response.ErrorListener {
Toast.makeText(
this,
"Failed to update grazing boundaries",
Toast.LENGTH_SHORT).show()
}
)
queue.add(request)
}
/**
* Adds a new marker to the [map], starting it at the previously-configured
* [center].
*/
fun addMarker(v : View) {
if(markers.size >= 10) {
Toast.makeText(
this,
"Cannot add more than ten vertices to grazing boundary",
Toast.LENGTH_SHORT).show()
return
}
val newMarker = Marker(map)
newMarker.isDraggable = true
newMarker.setOnMarkerClickListener(this)
newMarker.setOnMarkerDragListener(this)
newMarker.position = center
markers.add(newMarker)
map.overlays.add(newMarker)
updatePolygon()
map.invalidate()
}
/**
* Ensures that the polygon's vertices are synchronized
* with the user-placed markers, making it so that the
* grazing area is visible to the user.
*/
private fun updatePolygon() {
polygon.points.clear()
if(markers.isEmpty()) return
val newPoints = markers.map { it.position }
polygon.points = newPoints
polygon.points.add(newPoints.first())
}
override fun onMarkerDragEnd(marker: Marker?) {
updatePolygon()
map.invalidate()
}
override fun onMarkerDragStart(marker: Marker?) {
}
override fun onMarkerDrag(marker: Marker?) {
}
override fun onMarkerClick(m: Marker, mv: MapView): Boolean {
AlertDialog.Builder(this)
.setTitle("Delete Marker")
.setMessage("Do you want to delete this marker?")
.setPositiveButton("Delete") { _, _ -> removeMarker(m) }
.setNegativeButton("Cancel") { d, _ -> d.dismiss() }
.show()
return true
}
}