Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Commit

Permalink
Add LocationManagerUtils.getGnssStatusAsFlow
Browse files Browse the repository at this point in the history
  • Loading branch information
SanmerDev committed Dec 13, 2023
1 parent 4452adb commit bb69982
Show file tree
Hide file tree
Showing 14 changed files with 468 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Handler
import android.os.Looper
import androidx.annotation.RequiresPermission
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.ContextCompat
import androidx.core.location.GnssStatusCompat
import androidx.core.location.LocationListenerCompat
import androidx.core.location.LocationManagerCompat
import androidx.core.location.LocationRequestCompat
Expand Down Expand Up @@ -78,41 +79,30 @@ object LocationManagerUtils {
}
}

@RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])
private fun requestLocationUpdates(
context: Context,
listener:
LocationListenerCompat
) = isLocationEnabled(context) {
if (!isEnable) return@isLocationEnabled

Timber.d("requestLocationUpdates")
val locationRequest = LocationRequestCompat.Builder(1)
.setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY)
.setMinUpdateDistanceMeters(0f)
.build()

LocationManagerCompat.requestLocationUpdates(
context.locationManager,
LocationManager.GPS_PROVIDER,
locationRequest,
listener,
Looper.getMainLooper()
)
}

@SuppressLint("MissingPermission")
fun locationUpdates(context: Context) = callbackFlow {
if (!context.hasPermissions()) {
fun getLocationAsFlow(context: Context) = callbackFlow {
if (!(context.hasPermissions() && isEnable)) {
close()
}

val listener = LocationListenerCompat {
trySend(it)
}

val locationRequest = LocationRequestCompat.Builder(1)
.setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY)
.setMinUpdateDistanceMeters(0f)
.build()

runCatching {
requestLocationUpdates(context, listener)
Timber.d("requestLocationUpdates")
LocationManagerCompat.requestLocationUpdates(
context.locationManager,
LocationManager.GPS_PROVIDER,
locationRequest,
listener,
Looper.getMainLooper()
)
}.onFailure {
Timber.e(it, "locationUpdates")
close(it)
Expand All @@ -124,5 +114,33 @@ object LocationManagerUtils {
}
}.flowOn(Dispatchers.Default)

@SuppressLint("MissingPermission")
fun getGnssStatusAsFlow(context: Context) = callbackFlow {
if (!(context.hasPermissions() && isEnable)) {
close()
}

val callback = object : GnssStatusCompat.Callback() {
override fun onSatelliteStatusChanged(status: GnssStatusCompat) {
trySend(status)
}
}

runCatching {
Timber.d("registerGnssStatusCallback")
LocationManagerCompat.registerGnssStatusCallback(
context.locationManager,
callback,
Handler(Looper.getMainLooper())
)
}.onFailure {
Timber.e(it, "gnssStatusUpdates")
close(it)
}

awaitClose {
Timber.d("unregisterGnssStatusCallback")
LocationManagerCompat.unregisterGnssStatusCallback(context.locationManager, callback)
}
}
}
41 changes: 41 additions & 0 deletions app/src/main/kotlin/com/sanmer/geomag/model/gnss/GnssType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.sanmer.geomag.model.gnss

import androidx.core.location.GnssStatusCompat

enum class GnssType {
NAVSTAR,
GLONASS,
BEIDOU,
QZSS,
GALILEO,
IRNSS,
SBAS,
UNKNOWN;

companion object {
val GnssType.code: String get() {
return when (this) {
NAVSTAR -> "US"
GLONASS -> "RU"
BEIDOU -> "CN"
QZSS -> "JP"
GALILEO -> "EU"
IRNSS -> "IN"
else -> "UNKNOWN"
}
}

fun Int.toGnssType(): GnssType {
return when (this) {
GnssStatusCompat.CONSTELLATION_GPS -> NAVSTAR
GnssStatusCompat.CONSTELLATION_GLONASS -> GLONASS
GnssStatusCompat.CONSTELLATION_BEIDOU -> BEIDOU
GnssStatusCompat.CONSTELLATION_QZSS -> QZSS
GnssStatusCompat.CONSTELLATION_GALILEO -> GALILEO
GnssStatusCompat.CONSTELLATION_IRNSS -> IRNSS
GnssStatusCompat.CONSTELLATION_SBAS -> SBAS
else -> UNKNOWN
}
}
}
}
45 changes: 45 additions & 0 deletions app/src/main/kotlin/com/sanmer/geomag/model/gnss/Satellite.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.sanmer.geomag.model.gnss

import androidx.core.location.GnssStatusCompat
import com.sanmer.geomag.model.gnss.GnssType.Companion.code
import com.sanmer.geomag.model.gnss.GnssType.Companion.toGnssType
import com.sanmer.geomag.model.gnss.SbasType.Companion.code
import com.sanmer.geomag.model.gnss.SbasType.Companion.toSbasType

data class Satellite(
val id: Int,
val type: Int,
val cn0: Float,
val elevation: Float,
val azimuth: Float
) {
val gnssType = type.toGnssType()

val isSbas = gnssType == GnssType.SBAS
val sbasType = id.toSbasType()

val code get() = if (isSbas) {
sbasType.code
} else {
gnssType.code
}

val name get() = if (isSbas) {
sbasType.name
} else {
gnssType.name
}


override fun toString(): String {
return "ID: $id, C/N0: $cn0, Elev: ${elevation}º, Azim: ${azimuth}º"
}

constructor(status: GnssStatusCompat, index: Int) : this(
id = status.getSvid(index),
type = status.getConstellationType(index),
cn0 = status.getCn0DbHz(index),
elevation = status.getElevationDegrees(index),
azimuth = status.getAzimuthDegrees(index)
)
}
47 changes: 47 additions & 0 deletions app/src/main/kotlin/com/sanmer/geomag/model/gnss/SbasType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.sanmer.geomag.model.gnss

enum class SbasType {
WAAS,
EGNOS,
MSAS,
GAGAN,
SDCM,
BDSBAS,
SOUTHPAN,
UNKNOWN;

companion object {
val SbasType.code: String get() {
return when (this) {
WAAS -> "US"
EGNOS -> "EU"
MSAS -> "JP"
GAGAN -> "IN"
SDCM -> "RU"
BDSBAS -> "CN"
SOUTHPAN -> "AU"
UNKNOWN -> "UNKNOWN"
}
}

fun Int.toSbasType(): SbasType {
return if (this == 120 || this == 123 || this == 126 || this == 136) {
EGNOS
} else if (this == 125 || this == 140 || this == 141) {
SDCM
} else if (this == 130 || this == 143 || this == 144) {
BDSBAS
} else if (this == 131 || this == 133 || this == 135 || this == 138) {
WAAS
} else if (this == 127 || this == 128 || this == 139) {
GAGAN
} else if (this == 129 || this == 137) {
MSAS
} else if (this == 122) {
SOUTHPAN
} else {
UNKNOWN
}
}
}
}
28 changes: 24 additions & 4 deletions app/src/main/kotlin/com/sanmer/geomag/service/LocationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,35 @@ import android.location.LocationManager
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.core.location.GnssStatusCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.sanmer.geomag.app.utils.LocationManagerUtils
import com.sanmer.geomag.model.gnss.Satellite
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class LocationService : LifecycleService() {
override fun onCreate() {
super.onCreate()
isRunning = true
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
LocationManagerUtils.locationUpdates(this)
LocationManagerUtils.getLocationAsFlow(this)
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
location = it
}
.launchIn(lifecycleScope)

return super.onStartCommand(intent, flags, startId)
LocationManagerUtils.getGnssStatusAsFlow(this)
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach {
gnssStatusOrNull = it
}
.launchIn(lifecycleScope)
}

override fun onDestroy() {
Expand All @@ -44,6 +50,20 @@ class LocationService : LifecycleService() {
var location by mutableStateOf(Location(LocationManager.GPS_PROVIDER))
private set

var gnssStatusOrNull: GnssStatusCompat? by mutableStateOf(null)
private set
val gnssStatus get() = checkNotNull(gnssStatusOrNull)

fun getSatelliteList(): List<Satellite> {
if (gnssStatusOrNull == null) {
return emptyList()
}

return List(gnssStatus.satelliteCount) {
Satellite(gnssStatus, it)
}.toMutableStateList()
}

fun start(context: Context) {
val intent = Intent(context, LocationService::class.java)
context.startService(intent)
Expand Down
9 changes: 5 additions & 4 deletions app/src/main/kotlin/com/sanmer/geomag/ui/component/Logo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ fun Logo(
textStyle: TextStyle = MaterialTheme.typography.bodyLarge.copy(
fontFamily = FontFamily.Monospace,
fontWeight = FontWeight.Bold
)
),
single: Boolean = true
) = Surface(
modifier = modifier,
shape = shape,
Expand All @@ -70,10 +71,10 @@ fun Logo(
) {
val label by remember(text) {
derivedStateOf {
if (text.isNotEmpty()) {
text.first().toString()
if (single) {
(text.firstOrNull() ?: "").toString()
} else {
"?"
text
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ import com.sanmer.geomag.ui.animate.slideOutRightToLeft
import com.sanmer.geomag.ui.navigation.MainScreen
import com.sanmer.geomag.ui.screens.settings.SettingsScreen
import com.sanmer.geomag.ui.screens.settings.about.AboutScreen
import com.sanmer.geomag.ui.screens.settings.gnss.GnssStatusScreen

enum class SettingsScreen(val route: String) {
Home("Settings"),
About("About")
About("About"),
Gnss("Gnss")
}

private val subScreens = listOf(
SettingsScreen.About.route
SettingsScreen.About.route,
SettingsScreen.Gnss.route
)

fun NavGraphBuilder.settingsScreen(
Expand Down Expand Up @@ -60,4 +63,14 @@ fun NavGraphBuilder.settingsScreen(
navController = navController
)
}

composable(
route = SettingsScreen.Gnss.route,
enterTransition = { slideInRightToLeft() + fadeIn() },
exitTransition = { slideOutLeftToRight() + fadeOut() }
) {
GnssStatusScreen(
navController = navController
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.navigation.NavController
import com.sanmer.geomag.R
import com.sanmer.geomag.datastore.isDarkMode
import com.sanmer.geomag.ui.component.NavigateUpTopBar
import com.sanmer.geomag.ui.component.SettingNormalItem
import com.sanmer.geomag.ui.navigation.graphs.SettingsScreen
import com.sanmer.geomag.ui.providable.LocalUserPreferences
import com.sanmer.geomag.ui.screens.settings.items.AppThemeItem
Expand Down Expand Up @@ -55,7 +56,14 @@ fun SettingsScreen(
darkMode = userPreferences.darkMode,
isDarkMode = userPreferences.isDarkMode(),
onThemeColorChange = viewModel::setThemeColor,
onDarkModeChange =viewModel::setDarkTheme
onDarkModeChange = viewModel::setDarkTheme
)

SettingNormalItem(
icon = R.drawable.satellite,
title = stringResource(id = R.string.settings_satellite_status),
desc = stringResource(id = R.string.settings_satellite_status_desc),
onClick = { navController.navigateSingleTopTo(SettingsScreen.Gnss.route) }
)
}
}
Expand Down
Loading

0 comments on commit bb69982

Please sign in to comment.