Improves the speed of Location services.

This commit is contained in:
Vitor Pamplona 2024-02-29 14:19:50 -05:00
parent c686e775bf
commit 0084c2b532
6 changed files with 103 additions and 109 deletions

View File

@ -27,6 +27,7 @@ import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.HandlerThread
import android.util.LruCache
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import kotlinx.coroutines.CancellationException
@ -92,7 +93,33 @@ class LocationUtil(context: Context) {
}
}
class ReverseGeoLocationUtil {
object CachedGeoLocations {
val locationNames = LruCache<String, String>(20)
fun cached(geoHashStr: String): String? {
return locationNames[geoHashStr]
}
suspend fun geoLocate(
geoHashStr: String,
location: Location,
context: Context,
): String? {
locationNames[geoHashStr]?.let {
return it
}
val name = ReverseGeoLocationUtil().execute(location, context)?.ifBlank { null }
if (name != null) {
locationNames.put(geoHashStr, name)
}
return name
}
}
private class ReverseGeoLocationUtil {
suspend fun execute(
location: Location,
context: Context,

View File

@ -124,7 +124,6 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import com.fonfon.kgeohash.toGeoHash
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
@ -134,7 +133,6 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.Nip96MediaServers
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.ui.components.BechLink
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.InvoiceRequest
@ -144,6 +142,7 @@ import com.vitorpamplona.amethyst.ui.components.ZapRaiserRequest
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
import com.vitorpamplona.amethyst.ui.note.CancelIcon
import com.vitorpamplona.amethyst.ui.note.CloseIcon
import com.vitorpamplona.amethyst.ui.note.LoadCityName
import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.note.PollIcon
import com.vitorpamplona.amethyst.ui.note.RegularPostIcon
@ -1172,23 +1171,12 @@ fun FowardZapTo(
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationAsHash(postViewModel: NewPostViewModel) {
val context = LocalContext.current
val locationPermissionState =
rememberPermissionState(
Manifest.permission.ACCESS_COARSE_LOCATION,
)
if (locationPermissionState.status.isGranted) {
var locationDescriptionFlow by remember(postViewModel) { mutableStateOf<Flow<String>?>(null) }
DisposableEffect(key1 = Unit) {
postViewModel.startLocation(context = context)
locationDescriptionFlow = postViewModel.location
onDispose { postViewModel.stopLocation() }
}
Column(
modifier = Modifier.fillMaxWidth(),
) {
@ -1219,7 +1207,7 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
modifier = Modifier.padding(start = 10.dp),
)
locationDescriptionFlow?.let { geoLocation -> DisplayLocationObserver(geoLocation) }
DisplayLocationObserver(postViewModel)
}
Divider()
@ -1236,41 +1224,39 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
}
@Composable
fun DisplayLocationObserver(geoLocation: Flow<String>) {
val location by geoLocation.collectAsStateWithLifecycle(null)
fun DisplayLocationObserver(postViewModel: NewPostViewModel) {
val context = LocalContext.current
var locationDescriptionFlow by remember(postViewModel) { mutableStateOf<Flow<String>?>(null) }
location?.let { DisplayLocationInTitle(geohash = it) }
DisposableEffect(key1 = context) {
postViewModel.startLocation(context = context)
locationDescriptionFlow = postViewModel.location
onDispose { postViewModel.stopLocation() }
}
locationDescriptionFlow?.let {
val location by it.collectAsStateWithLifecycle(null)
location?.let { DisplayLocationInTitle(geohash = it) }
}
}
@Composable
fun DisplayLocationInTitle(geohash: String) {
val context = LocalContext.current
var cityName by remember(geohash) { mutableStateOf<String>(geohash) }
LaunchedEffect(key1 = geohash) {
launch(Dispatchers.IO) {
val newCityName =
ReverseGeoLocationUtil().execute(geohash.toGeoHash().toLocation(), context)?.ifBlank {
null
}
if (newCityName != null && newCityName != cityName) {
cityName = newCityName
}
}
}
if (geohash != "s0000") {
LoadCityName(
geohashStr = geohash,
onLoading = {
Spacer(modifier = StdHorzSpacer)
LoadingAnimation()
},
) { cityName ->
Text(
text = cityName,
fontSize = 20.sp,
fontWeight = FontWeight.W500,
modifier = Modifier.padding(start = Size5dp),
)
} else {
Spacer(modifier = StdHorzSpacer)
LoadingAnimation()
}
}

View File

@ -74,7 +74,6 @@ import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavBackStackEntry
import coil.Coil
import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
@ -817,15 +816,12 @@ fun SimpleTextSpinner(
fun RenderOption(option: Name) {
when (option) {
is GeoHashName -> {
val geohash = runCatching { option.geoHashTag.toGeoHash() }.getOrNull()
if (geohash != null) {
LoadCityName(geohash) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth(),
) {
Text(text = "/g/$it", color = MaterialTheme.colorScheme.onSurface)
}
LoadCityName(option.geoHashTag) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth(),
) {
Text(text = "/g/$it", color = MaterialTheme.colorScheme.onSurface)
}
}
}

View File

@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.navigation
import android.os.Bundle
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@ -32,7 +31,6 @@ import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDestination
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.navArgument
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account

View File

@ -101,7 +101,6 @@ import coil.compose.AsyncImagePainter
import coil.request.SuccessResult
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fonfon.kgeohash.GeoHash
import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.BaseMediaContent
@ -115,7 +114,7 @@ import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.service.CachedGeoLocations
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
@ -2768,23 +2767,34 @@ fun LoadOts(
@Composable
fun LoadCityName(
geohash: GeoHash,
geohashStr: String,
onLoading: (@Composable () -> Unit)? = null,
content: @Composable (String) -> Unit,
) {
val context = LocalContext.current
var cityName by remember(geohash) { mutableStateOf<String>(geohash.toString()) }
var cityName by remember(geohashStr) { mutableStateOf(CachedGeoLocations.cached(geohashStr)) }
LaunchedEffect(key1 = geohash) {
launch(Dispatchers.IO) {
val newCityName =
ReverseGeoLocationUtil().execute(geohash.toLocation(), context)?.ifBlank { null }
if (newCityName != null && newCityName != cityName) {
cityName = newCityName
if (cityName == null) {
if (onLoading != null) {
onLoading()
}
val context = LocalContext.current
LaunchedEffect(key1 = geohashStr, context) {
launch(Dispatchers.IO) {
val geoHash = runCatching { geohashStr.toGeoHash() }.getOrNull()
if (geoHash != null) {
val newCityName =
CachedGeoLocations.geoLocate(geohashStr, geoHash.toLocation(), context)?.ifBlank { null }
if (newCityName != null && newCityName != cityName) {
cityName = newCityName
}
}
}
}
} else {
cityName?.let { content(it) }
}
content(cityName)
}
@Composable
@ -2792,24 +2802,21 @@ fun DisplayLocation(
geohashStr: String,
nav: (String) -> Unit,
) {
val geoHash = runCatching { geohashStr.toGeoHash() }.getOrNull()
if (geoHash != null) {
LoadCityName(geoHash) { cityName ->
ClickableText(
text = AnnotatedString(cityName),
onClick = { nav("Geohash/$geoHash") },
style =
LocalTextStyle.current.copy(
color =
MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f,
),
fontSize = Font14SP,
fontWeight = FontWeight.Bold,
),
maxLines = 1,
)
}
LoadCityName(geohashStr) { cityName ->
ClickableText(
text = AnnotatedString(cityName),
onClick = { nav("Geohash/$geohashStr") },
style =
LocalTextStyle.current.copy(
color =
MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f,
),
fontSize = Font14SP,
fontWeight = FontWeight.Bold,
),
maxLines = 1,
)
}
}

View File

@ -31,32 +31,26 @@ import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.NostrGeohashDataSource
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.ui.note.LoadCityName
import com.vitorpamplona.amethyst.ui.screen.NostrGeoHashFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.StdPadding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun GeoHashScreen(
@ -174,27 +168,13 @@ fun DislayGeoTagHeader(
geohash: String,
modifier: Modifier,
) {
val context = LocalContext.current
var cityName by remember(geohash) { mutableStateOf<String>(geohash) }
LaunchedEffect(key1 = geohash) {
launch(Dispatchers.IO) {
val newCityName =
ReverseGeoLocationUtil().execute(geohash.toGeoHash().toLocation(), context)?.ifBlank {
null
}
if (newCityName != null && newCityName != cityName) {
cityName = "$newCityName ($geohash)"
}
}
LoadCityName(geohashStr = geohash) { cityName ->
Text(
cityName,
fontWeight = FontWeight.Bold,
modifier = modifier,
)
}
Text(
cityName,
fontWeight = FontWeight.Bold,
modifier = modifier,
)
}
@Composable