feat(better_location): edit coordinates

Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
rhunk
2024-06-05 01:22:05 +02:00
parent 4623c5cb1a
commit d901cba828
4 changed files with 235 additions and 85 deletions

View File

@@ -11,7 +11,7 @@ import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavGraphBuilder
import me.rhunk.snapenhance.RemoteSideContext
import me.rhunk.snapenhance.ui.manager.pages.BetterLocationRoot
import me.rhunk.snapenhance.ui.manager.pages.location.BetterLocationRoot
import me.rhunk.snapenhance.ui.manager.pages.FileImportsRoot
import me.rhunk.snapenhance.ui.manager.pages.LoggerHistoryRoot
import me.rhunk.snapenhance.ui.manager.pages.TasksRootSection

View File

@@ -0,0 +1,91 @@
package me.rhunk.snapenhance.ui.manager.pages.location
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import me.rhunk.snapenhance.bridge.location.LocationCoordinates
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.ui.util.AlertDialogs
@Composable
fun AddCoordinatesDialog(
alertDialogs: AlertDialogs,
translation: LocaleWrapper,
locationCoordinates: LocationCoordinates,
confirm: (locationCoordinates: LocationCoordinates) -> Unit
) {
var savedName by remember {
mutableStateOf(
(locationCoordinates.name ?: "").let {
TextFieldValue(it, selection = TextRange(it.length))
}
)
}
var savedLatitude by remember { mutableStateOf(locationCoordinates.latitude.toFloat().toString()) }
var savedLongitude by remember { mutableStateOf(locationCoordinates.longitude.toFloat().toString()) }
alertDialogs.DefaultDialogCard {
val focusRequester = remember { FocusRequester() }
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Text(translation["save_coordinates_dialog_title"], fontSize = 20.sp, fontWeight = FontWeight.Bold)
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester),
value = savedName,
onValueChange = { savedName = it },
label = { Text(translation["saved_name_dialog_hint"]) }
)
LaunchedEffect(Unit) {
delay(200)
focusRequester.requestFocus()
}
OutlinedTextField(
value = savedLatitude,
onValueChange = { savedLatitude = it },
label = { Text(translation["latitude_dialog_hint"]) }
)
OutlinedTextField(
value = savedLongitude,
onValueChange = { savedLongitude = it },
label = { Text(translation["longitude_dialog_hint"]) }
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.End
) {
Button(
onClick = {
confirm(LocationCoordinates().apply {
this.name = savedName.text
this.latitude = savedLatitude.toDoubleOrNull() ?: 0.0
this.longitude = savedLongitude.toDoubleOrNull() ?: 0.0
})
},
enabled = savedName.text.isNotBlank() && savedLatitude.isNotBlank() && savedLongitude.isNotBlank()
) {
Text(translation["save_dialog_button"])
}
}
}
}
}

View File

@@ -1,25 +1,27 @@
package me.rhunk.snapenhance.ui.manager.pages
package me.rhunk.snapenhance.ui.manager.pages.location
import androidx.compose.foundation.clickable
import android.os.Parcel
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.DeleteOutline
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavBackStackEntry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.rhunk.snapenhance.bridge.location.FriendLocation
import me.rhunk.snapenhance.bridge.location.LocationCoordinates
import me.rhunk.snapenhance.common.ui.rememberAsyncMutableStateList
@@ -167,10 +169,9 @@ class BetterLocationRoot : Routes.Route() {
val mapView = remember { mutableStateOf<MapView?>(null) }
var spoofedCoordinates by remember(showTeleportDialog, showMap) { mutableStateOf(coordinatesProperty.value.get() as? Pair<*, *>) }
fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates) {
fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates, onSuccess: suspend (id: Int) -> Unit = {}) {
context.coroutineScope.launch {
context.database.addOrUpdateLocationCoordinate(id, locationCoordinates)
updateDispatcher.dispatch()
onSuccess(context.database.addOrUpdateLocationCoordinate(id, locationCoordinates))
}
}
@@ -199,53 +200,30 @@ class BetterLocationRoot : Routes.Route() {
"latitude" to ((spoofedCoordinates?.first as? Double)?.toFloat() ?: "0.0").toString(),
"longitude" to ((spoofedCoordinates?.second as? Double)?.toFloat() ?: "0.0").toString()
),
fontSize = 20.sp,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.padding(8.dp)
)
if (addSavedCoordinateDialog) {
var savedName by remember { mutableStateOf("") }
me.rhunk.snapenhance.ui.util.Dialog(
onDismissRequest = { addSavedCoordinateDialog = false },
content = {
alertDialogs.DefaultDialogCard {
val focusRequester = remember { FocusRequester() }
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(translation["save_coordinates_dialog_title"], fontSize = 20.sp, fontWeight = FontWeight.Bold)
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
focusRequester.requestFocus()
},
value = savedName,
onValueChange = { savedName = it },
label = { Text(translation["saved_name_dialog_hint"]) }
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Button(
onClick = {
addSavedCoordinateDialog = false
addSavedCoordinate(null, LocationCoordinates().apply {
this.name = savedName
this.latitude = marker.value?.position?.latitude as Double
this.longitude = marker.value?.position?.longitude as Double
})
},
enabled = savedName.isNotBlank()
) {
Text(translation["save_dialog_button"])
}
AddCoordinatesDialog(
alertDialogs,
translation,
LocationCoordinates().apply {
this.latitude = marker.value?.position?.latitude ?: 0.0
this.longitude = marker.value?.position?.longitude ?: 0.0
},
) { coordinates ->
addSavedCoordinateDialog = false
addSavedCoordinate(null, coordinates) {
withContext(Dispatchers.Main) {
savedCoordinates.add(0, coordinates.apply { id = it })
}
}
}
@@ -263,6 +241,11 @@ class BetterLocationRoot : Routes.Route() {
showMap = false
context.config.writeConfig()
}
DisposableEffect(Unit) {
onDispose {
marker.value = null
}
}
}
)
}
@@ -276,7 +259,7 @@ class BetterLocationRoot : Routes.Route() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
@@ -290,7 +273,7 @@ class BetterLocationRoot : Routes.Route() {
}
item {
Row(
modifier = Modifier.padding(16.dp),
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = translation["spoof_location_toggle"])
@@ -306,12 +289,27 @@ class BetterLocationRoot : Routes.Route() {
}
}
item {
Text(
translation["saved_coordinates_title"],
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 16.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
translation["saved_coordinates_title"],
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f),
lineHeight = 20.sp
)
IconButton(
onClick = {
addSavedCoordinateDialog = true
}
) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
}
item {
if (savedCoordinates.isEmpty()) {
@@ -323,8 +321,19 @@ class BetterLocationRoot : Routes.Route() {
)
}
}
items(savedCoordinates) { coordinates ->
items(savedCoordinates, key = { it.id }) { coordinates ->
var mutableCoordinates by remember { mutableStateOf(coordinates) }
val isSelected = spoofedCoordinates == mutableCoordinates.latitude to mutableCoordinates.longitude
var showDeleteDialog by remember { mutableStateOf(false) }
var showEditDialog by remember { mutableStateOf(false) }
fun setSpoofedCoordinates() {
spoofedCoordinates = mutableCoordinates.latitude to mutableCoordinates.longitude
coordinatesProperty.value.setAny(spoofedCoordinates)
context.coroutineScope.launch {
context.config.writeConfig()
}
}
if (showDeleteDialog) {
me.rhunk.snapenhance.ui.util.Dialog(
@@ -337,7 +346,7 @@ class BetterLocationRoot : Routes.Route() {
showDeleteDialog = false
context.coroutineScope.launch {
context.database.removeLocationCoordinate(coordinates.id)
updateDispatcher.dispatch()
savedCoordinates.remove(coordinates)
}
},
onDismiss = { showDeleteDialog = false }
@@ -346,39 +355,87 @@ class BetterLocationRoot : Routes.Route() {
)
}
Row(
if (showEditDialog) {
me.rhunk.snapenhance.ui.util.Dialog(
onDismissRequest = { showEditDialog = false },
content = {
AddCoordinatesDialog(
alertDialogs,
translation,
mutableCoordinates
) {
val itemId = coordinates.id
context.coroutineScope.launch {
addSavedCoordinate(itemId, it)
}
Parcel.obtain().apply {
it.writeToParcel(this, 0)
setDataPosition(0)
coordinates.readFromParcel(this)
coordinates.id = itemId
recycle()
}
mutableCoordinates = it
if (isSelected) setSpoofedCoordinates()
showEditDialog = false
}
}
)
}
ElevatedCard(
onClick = {
mutableCoordinates = coordinates
setSpoofedCoordinates()
GeoPoint(coordinates.latitude, coordinates.longitude).also {
marker.value?.position = it
mapView.value?.controller?.apply {
animateTo(it)
setZoom(16.0)
}
}
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
.padding(5.dp),
) {
Text(
text = "${coordinates.name} (${coordinates.latitude.toFloat()}, ${coordinates.longitude.toFloat()})",
fontWeight = if (spoofedCoordinates == coordinates.latitude to coordinates.longitude) FontWeight.Bold else FontWeight.Light,
Row(
modifier = Modifier
.padding(8.dp)
.weight(1f)
.clickable {
spoofedCoordinates =
coordinates.latitude to coordinates.longitude
coordinatesProperty.value.setAny(spoofedCoordinates)
context.coroutineScope.launch {
context.config.writeConfig()
}
GeoPoint(coordinates.latitude, coordinates.longitude).also {
marker.value?.position = it
mapView.value?.controller?.apply {
animateTo(it)
setZoom(16.0)
}
}
},
fontSize = 16.sp
)
FilledIconButton(onClick = {
showDeleteDialog = true
}) {
Icon(Icons.Default.DeleteOutline, contentDescription = "Delete")
.fillMaxWidth()
.padding(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(2.dp)
.weight(1f)
) {
Text(
text = remember(mutableCoordinates) { mutableCoordinates.name },
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Light,
fontSize = 16.sp,
lineHeight = 20.sp,
overflow = TextOverflow.Ellipsis
)
Text(
text = remember(mutableCoordinates) { "(${mutableCoordinates.latitude.toFloat()}, ${mutableCoordinates.longitude.toFloat()})" },
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Light,
fontSize = 12.sp,
lineHeight = 15.sp,
overflow = TextOverflow.Ellipsis
)
}
FilledIconButton(onClick = {
showEditDialog = true
}) {
Icon(Icons.Default.Edit, contentDescription = "Delete")
}
Spacer(modifier = Modifier.width(4.dp))
FilledIconButton(onClick = {
showDeleteDialog = true
}) {
Icon(Icons.Default.DeleteOutline, contentDescription = "Delete")
}
}
}
}

View File

@@ -152,6 +152,8 @@
"spoofed_coordinates_title": "Spoofed Coordinates\nLat {latitude}, Lng {longitude}",
"save_coordinates_dialog_title": "Save Coordinates",
"saved_name_dialog_hint": "Saved Name",
"latitude_dialog_hint": "Latitude",
"longitude_dialog_hint": "Longitude",
"save_dialog_button": "Save",
"choose_location_button": "Choose Location",
"teleport_to_friend_button": "Teleport to Friend",