commit d901cba8288986674db0acaf0474b4a1ae0403ce
parent 4623c5cb1ae3aabb6dcdb3ca04383a13526b96f7
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Wed, 5 Jun 2024 01:22:05 +0200
feat(better_location): edit coordinates
Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
Diffstat:
5 files changed, 541 insertions(+), 390 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt
@@ -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
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/BetterLocationRoot.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/BetterLocationRoot.kt
@@ -1,388 +0,0 @@
-package me.rhunk.snapenhance.ui.manager.pages
-
-import androidx.compose.foundation.clickable
-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.DeleteOutline
-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.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavBackStackEntry
-import kotlinx.coroutines.launch
-import me.rhunk.snapenhance.bridge.location.FriendLocation
-import me.rhunk.snapenhance.bridge.location.LocationCoordinates
-import me.rhunk.snapenhance.common.ui.rememberAsyncMutableStateList
-import me.rhunk.snapenhance.common.ui.rememberAsyncUpdateDispatcher
-import me.rhunk.snapenhance.common.util.snap.BitmojiSelfie
-import me.rhunk.snapenhance.storage.addOrUpdateLocationCoordinate
-import me.rhunk.snapenhance.storage.getLocationCoordinates
-import me.rhunk.snapenhance.storage.removeLocationCoordinate
-import me.rhunk.snapenhance.ui.manager.Routes
-import me.rhunk.snapenhance.ui.util.AlertDialogs
-import me.rhunk.snapenhance.ui.util.DialogProperties
-import me.rhunk.snapenhance.ui.util.coil.BitmojiImage
-import org.osmdroid.util.GeoPoint
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.Marker
-
-class BetterLocationRoot : Routes.Route() {
- private val alertDialogs by lazy { AlertDialogs(context.translation) }
-
- @Composable
- private fun FriendLocationItem(
- friendLocation: FriendLocation,
- dismiss: () -> Unit
- ) {
- ElevatedCard(onClick = {
- context.config.root.global.betterLocation.coordinates.setAny(friendLocation.latitude to friendLocation.longitude)
- dismiss()
- }, modifier = Modifier.padding(4.dp)) {
- Row(
- modifier = Modifier
- .padding(8.dp)
- .fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically
- ) {
- BitmojiImage(
- context = context,
- url = BitmojiSelfie.getBitmojiSelfie(
- friendLocation.bitmojiSelfieId,
- friendLocation.bitmojiId,
- BitmojiSelfie.BitmojiSelfieType.NEW_THREE_D
- ),
- size = 48,
- modifier = Modifier.padding(6.dp)
- )
- Column(
- modifier = Modifier.weight(1f),
- ) {
- Text(friendLocation.displayName?.let { "$it (${friendLocation.username})" }
- ?: friendLocation.username, fontSize = 16.sp, fontWeight = FontWeight.Bold)
- Text(
- text = buildString {
- append(friendLocation.localityPieces.joinToString(", "))
- append("\n")
- append("Lat: ${friendLocation.latitude.toFloat()}, Lng: ${friendLocation.longitude.toFloat()}")
- },
- fontSize = 10.sp,
- fontWeight = FontWeight.Light,
- lineHeight = 15.sp
- )
- }
- }
- }
- }
-
- @Composable
- private fun FriendLocationsDialogs(
- friendsLocation: List<FriendLocation>,
- dismiss: () -> Unit
- ) {
- var search by remember { mutableStateOf("") }
- val filteredFriendsLocation = rememberAsyncMutableStateList(defaultValue = friendsLocation, keys = arrayOf(search)) {
- search.takeIf { it.isNotBlank() }?.let {
- friendsLocation.filter {
- it.displayName?.contains(search, ignoreCase = true) == true || it.username.contains(search, ignoreCase = true)
- }
- } ?: friendsLocation
- }
-
- ElevatedCard(
- shape = MaterialTheme.shapes.large,
- modifier = Modifier.padding(top = 32.dp, bottom = 32.dp)
- ) {
- Text(
- translation["teleport_to_friend_title"],
- fontSize = 20.sp,
- fontWeight = FontWeight.Bold,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- .padding(12.dp)
- )
- OutlinedTextField(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp),
- value = search,
- onValueChange = { search = it },
- label = { Text(translation["search_bar"]) }
- )
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- ) {
- item {
- if (friendsLocation.isEmpty()) {
- Text(
- translation["no_friends_map"],
- fontSize = 16.sp,
- modifier = Modifier.padding(16.dp),
- fontWeight = FontWeight.Light
- )
- } else if (filteredFriendsLocation.isEmpty()) {
- Text(
- translation["no_friends_found"],
- fontSize = 16.sp,
- modifier = Modifier.padding(16.dp),
- fontWeight = FontWeight.Light
- )
- }
- }
- items(filteredFriendsLocation) { friendLocation ->
- FriendLocationItem(friendLocation, dismiss)
- }
- }
- }
- }
-
- override val content: @Composable (NavBackStackEntry) -> Unit = {
- val coordinatesProperty = remember {
- context.config.root.global.betterLocation.getPropertyPair("coordinates")
- }
-
- val updateDispatcher = rememberAsyncUpdateDispatcher()
- val savedCoordinates = rememberAsyncMutableStateList(
- defaultValue = listOf(),
- updateDispatcher = updateDispatcher
- ) {
- context.database.getLocationCoordinates()
- }
- var showMap by remember { mutableStateOf(false) }
- var addSavedCoordinateDialog by remember { mutableStateOf(false) }
- var showTeleportDialog by remember { mutableStateOf(false) }
-
- val marker = remember { mutableStateOf<Marker?>(null) }
- val mapView = remember { mutableStateOf<MapView?>(null) }
- var spoofedCoordinates by remember(showTeleportDialog, showMap) { mutableStateOf(coordinatesProperty.value.get() as? Pair<*, *>) }
-
- fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates) {
- context.coroutineScope.launch {
- context.database.addOrUpdateLocationCoordinate(id, locationCoordinates)
- updateDispatcher.dispatch()
- }
- }
-
- if (showTeleportDialog) {
- me.rhunk.snapenhance.ui.util.Dialog(
- properties = DialogProperties(usePlatformDefaultWidth = false),
- onDismissRequest = { showTeleportDialog = false },
- content = {
- FriendLocationsDialogs(remember { context.locationManager.friendsLocation }) {
- showTeleportDialog = false
- context.coroutineScope.launch {
- context.config.writeConfig()
- }
- }
- }
- )
- }
-
- Column(
- modifier = Modifier
- .fillMaxSize()
- ) {
- Text(
- translation.format(
- "spoofed_coordinates_title",
- "latitude" to ((spoofedCoordinates?.first as? Double)?.toFloat() ?: "0.0").toString(),
- "longitude" to ((spoofedCoordinates?.second as? Double)?.toFloat() ?: "0.0").toString()
- ),
- fontSize = 20.sp,
- fontWeight = FontWeight.Bold,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- .padding(12.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"])
- }
- }
- }
- }
- }
- )
- }
-
- if (showMap) {
- me.rhunk.snapenhance.ui.util.Dialog(
- onDismissRequest = { showMap = false },
- content = {
- alertDialogs.ChooseLocationDialog(property = coordinatesProperty, marker, mapView, saveCoordinates = {
- addSavedCoordinateDialog = true
- }) {
- showMap = false
- context.config.writeConfig()
- }
- }
- )
- }
-
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- .clipToBounds()
- ) {
- item {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- horizontalArrangement = Arrangement.SpaceEvenly,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Button(onClick = { showMap = true }) {
- Text(translation["choose_location_button"])
- }
- Button(onClick = { showTeleportDialog = true }) {
- Text(translation["teleport_to_friend_button"])
- }
- }
- }
- item {
- Row(
- modifier = Modifier.padding(16.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(text = translation["spoof_location_toggle"])
- Spacer(modifier = Modifier.weight(1f))
- var isSpoofing by remember { mutableStateOf(context.config.root.global.betterLocation.spoofLocation.get()) }
- Switch(
- checked = isSpoofing,
- onCheckedChange = {
- isSpoofing = it
- context.config.root.global.betterLocation.spoofLocation.set(it)
- }
- )
- }
- }
- item {
- Text(
- translation["saved_coordinates_title"],
- fontSize = 20.sp,
- fontWeight = FontWeight.Bold,
- modifier = Modifier.padding(start = 16.dp)
- )
- }
- item {
- if (savedCoordinates.isEmpty()) {
- Text(
- translation["no_saved_coordinates_hint"],
- fontSize = 16.sp,
- modifier = Modifier.padding(start = 20.dp),
- fontWeight = FontWeight.Light
- )
- }
- }
- items(savedCoordinates) { coordinates ->
- var showDeleteDialog by remember { mutableStateOf(false) }
-
- if (showDeleteDialog) {
- me.rhunk.snapenhance.ui.util.Dialog(
- onDismissRequest = { showDeleteDialog = false },
- content = {
- alertDialogs.ConfirmDialog(
- title = translation["delete_dialog_title"],
- message = translation["delete_dialog_message"],
- onConfirm = {
- showDeleteDialog = false
- context.coroutineScope.launch {
- context.database.removeLocationCoordinate(coordinates.id)
- updateDispatcher.dispatch()
- }
- },
- onDismiss = { showDeleteDialog = false }
- )
- }
- )
- }
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(start = 16.dp, end = 16.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(
- text = "${coordinates.name} (${coordinates.latitude.toFloat()}, ${coordinates.longitude.toFloat()})",
- fontWeight = if (spoofedCoordinates == coordinates.latitude to coordinates.longitude) FontWeight.Bold else FontWeight.Light,
- 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")
- }
- }
- }
- }
- }
- }
-}-
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt
@@ -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"])
+ }
+ }
+ }
+ }
+}+
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/BetterLocationRoot.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/BetterLocationRoot.kt
@@ -0,0 +1,445 @@
+package me.rhunk.snapenhance.ui.manager.pages.location
+
+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.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
+import me.rhunk.snapenhance.common.ui.rememberAsyncUpdateDispatcher
+import me.rhunk.snapenhance.common.util.snap.BitmojiSelfie
+import me.rhunk.snapenhance.storage.addOrUpdateLocationCoordinate
+import me.rhunk.snapenhance.storage.getLocationCoordinates
+import me.rhunk.snapenhance.storage.removeLocationCoordinate
+import me.rhunk.snapenhance.ui.manager.Routes
+import me.rhunk.snapenhance.ui.util.AlertDialogs
+import me.rhunk.snapenhance.ui.util.DialogProperties
+import me.rhunk.snapenhance.ui.util.coil.BitmojiImage
+import org.osmdroid.util.GeoPoint
+import org.osmdroid.views.MapView
+import org.osmdroid.views.overlay.Marker
+
+class BetterLocationRoot : Routes.Route() {
+ private val alertDialogs by lazy { AlertDialogs(context.translation) }
+
+ @Composable
+ private fun FriendLocationItem(
+ friendLocation: FriendLocation,
+ dismiss: () -> Unit
+ ) {
+ ElevatedCard(onClick = {
+ context.config.root.global.betterLocation.coordinates.setAny(friendLocation.latitude to friendLocation.longitude)
+ dismiss()
+ }, modifier = Modifier.padding(4.dp)) {
+ Row(
+ modifier = Modifier
+ .padding(8.dp)
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ BitmojiImage(
+ context = context,
+ url = BitmojiSelfie.getBitmojiSelfie(
+ friendLocation.bitmojiSelfieId,
+ friendLocation.bitmojiId,
+ BitmojiSelfie.BitmojiSelfieType.NEW_THREE_D
+ ),
+ size = 48,
+ modifier = Modifier.padding(6.dp)
+ )
+ Column(
+ modifier = Modifier.weight(1f),
+ ) {
+ Text(friendLocation.displayName?.let { "$it (${friendLocation.username})" }
+ ?: friendLocation.username, fontSize = 16.sp, fontWeight = FontWeight.Bold)
+ Text(
+ text = buildString {
+ append(friendLocation.localityPieces.joinToString(", "))
+ append("\n")
+ append("Lat: ${friendLocation.latitude.toFloat()}, Lng: ${friendLocation.longitude.toFloat()}")
+ },
+ fontSize = 10.sp,
+ fontWeight = FontWeight.Light,
+ lineHeight = 15.sp
+ )
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun FriendLocationsDialogs(
+ friendsLocation: List<FriendLocation>,
+ dismiss: () -> Unit
+ ) {
+ var search by remember { mutableStateOf("") }
+ val filteredFriendsLocation = rememberAsyncMutableStateList(defaultValue = friendsLocation, keys = arrayOf(search)) {
+ search.takeIf { it.isNotBlank() }?.let {
+ friendsLocation.filter {
+ it.displayName?.contains(search, ignoreCase = true) == true || it.username.contains(search, ignoreCase = true)
+ }
+ } ?: friendsLocation
+ }
+
+ ElevatedCard(
+ shape = MaterialTheme.shapes.large,
+ modifier = Modifier.padding(top = 32.dp, bottom = 32.dp)
+ ) {
+ Text(
+ translation["teleport_to_friend_title"],
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(12.dp)
+ )
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ value = search,
+ onValueChange = { search = it },
+ label = { Text(translation["search_bar"]) }
+ )
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+ item {
+ if (friendsLocation.isEmpty()) {
+ Text(
+ translation["no_friends_map"],
+ fontSize = 16.sp,
+ modifier = Modifier.padding(16.dp),
+ fontWeight = FontWeight.Light
+ )
+ } else if (filteredFriendsLocation.isEmpty()) {
+ Text(
+ translation["no_friends_found"],
+ fontSize = 16.sp,
+ modifier = Modifier.padding(16.dp),
+ fontWeight = FontWeight.Light
+ )
+ }
+ }
+ items(filteredFriendsLocation) { friendLocation ->
+ FriendLocationItem(friendLocation, dismiss)
+ }
+ }
+ }
+ }
+
+ override val content: @Composable (NavBackStackEntry) -> Unit = {
+ val coordinatesProperty = remember {
+ context.config.root.global.betterLocation.getPropertyPair("coordinates")
+ }
+
+ val updateDispatcher = rememberAsyncUpdateDispatcher()
+ val savedCoordinates = rememberAsyncMutableStateList(
+ defaultValue = listOf(),
+ updateDispatcher = updateDispatcher
+ ) {
+ context.database.getLocationCoordinates()
+ }
+ var showMap by remember { mutableStateOf(false) }
+ var addSavedCoordinateDialog by remember { mutableStateOf(false) }
+ var showTeleportDialog by remember { mutableStateOf(false) }
+
+ val marker = remember { mutableStateOf<Marker?>(null) }
+ val mapView = remember { mutableStateOf<MapView?>(null) }
+ var spoofedCoordinates by remember(showTeleportDialog, showMap) { mutableStateOf(coordinatesProperty.value.get() as? Pair<*, *>) }
+
+ fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates, onSuccess: suspend (id: Int) -> Unit = {}) {
+ context.coroutineScope.launch {
+ onSuccess(context.database.addOrUpdateLocationCoordinate(id, locationCoordinates))
+ }
+ }
+
+ if (showTeleportDialog) {
+ me.rhunk.snapenhance.ui.util.Dialog(
+ properties = DialogProperties(usePlatformDefaultWidth = false),
+ onDismissRequest = { showTeleportDialog = false },
+ content = {
+ FriendLocationsDialogs(remember { context.locationManager.friendsLocation }) {
+ showTeleportDialog = false
+ context.coroutineScope.launch {
+ context.config.writeConfig()
+ }
+ }
+ }
+ )
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+ Text(
+ translation.format(
+ "spoofed_coordinates_title",
+ "latitude" to ((spoofedCoordinates?.first as? Double)?.toFloat() ?: "0.0").toString(),
+ "longitude" to ((spoofedCoordinates?.second as? Double)?.toFloat() ?: "0.0").toString()
+ ),
+ fontSize = 18.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp)
+ )
+
+ if (addSavedCoordinateDialog) {
+ me.rhunk.snapenhance.ui.util.Dialog(
+ onDismissRequest = { addSavedCoordinateDialog = false },
+ content = {
+ 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 })
+ }
+ }
+ }
+ }
+ )
+ }
+
+ if (showMap) {
+ me.rhunk.snapenhance.ui.util.Dialog(
+ onDismissRequest = { showMap = false },
+ content = {
+ alertDialogs.ChooseLocationDialog(property = coordinatesProperty, marker, mapView, saveCoordinates = {
+ addSavedCoordinateDialog = true
+ }) {
+ showMap = false
+ context.config.writeConfig()
+ }
+ DisposableEffect(Unit) {
+ onDispose {
+ marker.value = null
+ }
+ }
+ }
+ )
+ }
+
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .clipToBounds()
+ ) {
+ item {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Button(onClick = { showMap = true }) {
+ Text(translation["choose_location_button"])
+ }
+ Button(onClick = { showTeleportDialog = true }) {
+ Text(translation["teleport_to_friend_button"])
+ }
+ }
+ }
+ item {
+ Row(
+ modifier = Modifier.padding(start = 16.dp, end = 16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(text = translation["spoof_location_toggle"])
+ Spacer(modifier = Modifier.weight(1f))
+ var isSpoofing by remember { mutableStateOf(context.config.root.global.betterLocation.spoofLocation.get()) }
+ Switch(
+ checked = isSpoofing,
+ onCheckedChange = {
+ isSpoofing = it
+ context.config.root.global.betterLocation.spoofLocation.set(it)
+ }
+ )
+ }
+ }
+ item {
+ 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()) {
+ Text(
+ translation["no_saved_coordinates_hint"],
+ fontSize = 16.sp,
+ modifier = Modifier.padding(start = 20.dp),
+ fontWeight = FontWeight.Light
+ )
+ }
+ }
+ 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(
+ onDismissRequest = { showDeleteDialog = false },
+ content = {
+ alertDialogs.ConfirmDialog(
+ title = translation["delete_dialog_title"],
+ message = translation["delete_dialog_message"],
+ onConfirm = {
+ showDeleteDialog = false
+ context.coroutineScope.launch {
+ context.database.removeLocationCoordinate(coordinates.id)
+ savedCoordinates.remove(coordinates)
+ }
+ },
+ onDismiss = { showDeleteDialog = false }
+ )
+ }
+ )
+ }
+
+ 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(5.dp),
+ ) {
+ Row(
+ modifier = Modifier
+ .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")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}+
\ No newline at end of file
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -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",