mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64137473b3 | ||
|
|
c08effb445 | ||
|
|
65410defe2 | ||
|
|
ed71c8148b | ||
|
|
13a79af27e | ||
|
|
f6564c3c71 | ||
|
|
8e42f81ea3 | ||
|
|
ada5b48ba5 | ||
|
|
a6a59877a8 |
No files matched your search
@@ -22,7 +22,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v5
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '21'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v5
|
uses: gradle/actions/setup-gradle@v5
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
serviceAccountJsonPlainText: ${{secrets.SERVICE_ACCOUNT_JSON}}
|
serviceAccountJsonPlainText: ${{secrets.SERVICE_ACCOUNT_JSON}}
|
||||||
packageName: com.rtbishop.look4sat
|
packageName: com.rtbishop.look4sat
|
||||||
releaseFiles: app/build/outputs/apk/release/look4sat.aab
|
releaseFiles: app/build/outputs/apk/release/look4sat.aab
|
||||||
track: beta
|
track: production
|
||||||
whatsNewDirectory: fastlane/metadata/android/en-US/whatsnew
|
whatsNewDirectory: fastlane/metadata/android/en-US/whatsnew
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -13,8 +13,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.rtbishop.look4sat"
|
applicationId = "com.rtbishop.look4sat"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
versionCode = 400
|
versionCode = 403
|
||||||
versionName = "4.0.0"
|
versionName = "4.0.3"
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation
|
package com.rtbishop.look4sat.presentation
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -76,7 +79,12 @@ fun MainScreen(navController: NavHostController = rememberNavController()) {
|
|||||||
else -> NavigationSuiteType.ShortNavigationBarMedium
|
else -> NavigationSuiteType.ShortNavigationBarMedium
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
NavHost(navController = navController, startDestination = startDestination) {
|
NavHost(
|
||||||
|
navController = navController,
|
||||||
|
startDestination = startDestination,
|
||||||
|
enterTransition = { fadeIn(animationSpec = tween(350)) },
|
||||||
|
exitTransition = { fadeOut(animationSpec = tween(350)) }
|
||||||
|
) {
|
||||||
satellitesDestination { navController.navigateUp() }
|
satellitesDestination { navController.navigateUp() }
|
||||||
passesDestination { catNum: Int, aosTime: Long ->
|
passesDestination { catNum: Int, aosTime: Long ->
|
||||||
val radarRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
val radarRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@@ -210,8 +211,9 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier)
|
|||||||
onClick = { onClick() }, colors = ButtonDefaults.buttonColors(
|
onClick = { onClick() }, colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
), shape = MaterialTheme.shapes.small, modifier = modifier
|
), shape = MaterialTheme.shapes.small, modifier = modifier,
|
||||||
) { Text(text = text, fontSize = 17.sp) }
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp)
|
||||||
|
) { Text(text = text, fontSize = 16.sp, textAlign = TextAlign.Center) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -253,7 +255,7 @@ fun EmptyListCard(message: String) {
|
|||||||
modifier = Modifier.padding(32.dp)
|
modifier = Modifier.padding(32.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
||||||
Text(text = stringResource(R.string.empty_list_message), fontSize = 21.sp)
|
Text(text = stringResource(R.string.empty_list_message), fontSize = 21.sp, textAlign = TextAlign.Center)
|
||||||
Text(text = message, fontSize = 18.sp, textAlign = TextAlign.Center)
|
Text(text = message, fontSize = 18.sp, textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,7 +280,7 @@ fun SharedDialog(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
fontSize = 18.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier.padding(start = padding, top = padding, end = padding)
|
modifier = Modifier.padding(start = padding, top = padding, end = padding)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.keepScreenOn
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
@@ -64,6 +65,7 @@ import com.rtbishop.look4sat.domain.model.SatRadio
|
|||||||
import com.rtbishop.look4sat.domain.utility.toDegrees
|
import com.rtbishop.look4sat.domain.utility.toDegrees
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.Screen
|
import com.rtbishop.look4sat.presentation.Screen
|
||||||
|
import com.rtbishop.look4sat.presentation.common.EmptyListCard
|
||||||
import com.rtbishop.look4sat.presentation.common.IconCard
|
import com.rtbishop.look4sat.presentation.common.IconCard
|
||||||
import com.rtbishop.look4sat.presentation.common.NextPassRow
|
import com.rtbishop.look4sat.presentation.common.NextPassRow
|
||||||
import com.rtbishop.look4sat.presentation.common.TimerRow
|
import com.rtbishop.look4sat.presentation.common.TimerRow
|
||||||
@@ -96,7 +98,7 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
|||||||
}
|
}
|
||||||
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
||||||
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
|
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
|
||||||
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.layoutPadding().keepScreenOn(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
if (isVerticalLayout()) {
|
if (isVerticalLayout()) {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
||||||
@@ -115,49 +117,56 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
|||||||
if(isVerticalLayout()) {
|
if(isVerticalLayout()) {
|
||||||
ElevatedCard(modifier = Modifier.weight(1f)) {
|
ElevatedCard(modifier = Modifier.weight(1f)) {
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
RadarViewCompose(
|
if (uiState.orbitalPos == null) {
|
||||||
item = uiState.orbitalPos,
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
items = uiState.satTrack,
|
EmptyListCard(message = "")
|
||||||
azimElev = uiState.orientationValues,
|
|
||||||
shouldShowSweep = uiState.shouldShowSweep,
|
|
||||||
shouldUseCompass = uiState.shouldUseCompass,
|
|
||||||
modifier = Modifier.align(Alignment.Center)
|
|
||||||
)
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(horizontal = 6.dp, vertical = 4.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
RadarTextTop(
|
|
||||||
uiState.orbitalPos?.azimuth ?: 0.0,
|
|
||||||
stringResource(R.string.radar_az_text),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
RadarTextTop(
|
|
||||||
uiState.orbitalPos?.elevation ?: 0.0,
|
|
||||||
stringResource(R.string.radar_el_text),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Row(
|
}
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
uiState.orbitalPos?.let { position ->
|
||||||
modifier = Modifier.fillMaxWidth()
|
RadarViewCompose(
|
||||||
|
item = position,
|
||||||
|
items = uiState.satTrack,
|
||||||
|
azimElev = uiState.orientationValues,
|
||||||
|
shouldShowSweep = uiState.shouldShowSweep,
|
||||||
|
shouldUseCompass = uiState.shouldUseCompass,
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 6.dp, vertical = 4.dp)
|
||||||
) {
|
) {
|
||||||
RadarTextBottom(
|
Row(
|
||||||
uiState.orbitalPos?.altitude ?: 0.0,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
stringResource(R.string.radar_alt_text),
|
modifier = Modifier.fillMaxWidth()
|
||||||
true
|
) {
|
||||||
)
|
RadarTextTop(
|
||||||
RadarTextBottom(
|
position.azimuth,
|
||||||
uiState.orbitalPos?.distance ?: 0.0,
|
stringResource(R.string.radar_az_text),
|
||||||
stringResource(R.string.radar_dist_text),
|
true
|
||||||
false
|
)
|
||||||
)
|
RadarTextTop(
|
||||||
|
position.elevation,
|
||||||
|
stringResource(R.string.radar_el_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
RadarTextBottom(
|
||||||
|
position.altitude,
|
||||||
|
stringResource(R.string.radar_alt_text),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
RadarTextBottom(
|
||||||
|
position.distance,
|
||||||
|
stringResource(R.string.radar_dist_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +180,11 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
|||||||
modifier = Modifier.padding(32.dp)
|
modifier = Modifier.padding(32.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
||||||
Text(text = stringResource(R.string.empty_list_message), fontSize = 21.sp)
|
Text(
|
||||||
|
text = stringResource(R.string.empty_list_message),
|
||||||
|
fontSize = 21.sp,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.radar_no_data),
|
text = stringResource(R.string.radar_no_data),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
@@ -192,49 +205,56 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
|||||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
ElevatedCard(modifier = Modifier.weight(1f)) {
|
ElevatedCard(modifier = Modifier.weight(1f)) {
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
RadarViewCompose(
|
if (uiState.orbitalPos == null) {
|
||||||
item = uiState.orbitalPos,
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
items = uiState.satTrack,
|
EmptyListCard(message = "")
|
||||||
azimElev = uiState.orientationValues,
|
|
||||||
shouldShowSweep = uiState.shouldShowSweep,
|
|
||||||
shouldUseCompass = false,
|
|
||||||
modifier = Modifier.align(Alignment.Center)
|
|
||||||
)
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(horizontal = 6.dp, vertical = 4.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
RadarTextTop(
|
|
||||||
uiState.orbitalPos?.azimuth ?: 0.0,
|
|
||||||
stringResource(R.string.radar_az_text),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
RadarTextTop(
|
|
||||||
uiState.orbitalPos?.elevation ?: 0.0,
|
|
||||||
stringResource(R.string.radar_el_text),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Row(
|
}
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
uiState.orbitalPos?.let { position ->
|
||||||
modifier = Modifier.fillMaxWidth()
|
RadarViewCompose(
|
||||||
|
item = position,
|
||||||
|
items = uiState.satTrack,
|
||||||
|
azimElev = uiState.orientationValues,
|
||||||
|
shouldShowSweep = uiState.shouldShowSweep,
|
||||||
|
shouldUseCompass = false,
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 6.dp, vertical = 4.dp)
|
||||||
) {
|
) {
|
||||||
RadarTextBottom(
|
Row(
|
||||||
uiState.orbitalPos?.altitude ?: 0.0,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
stringResource(R.string.radar_alt_text),
|
modifier = Modifier.fillMaxWidth()
|
||||||
true
|
) {
|
||||||
)
|
RadarTextTop(
|
||||||
RadarTextBottom(
|
position.azimuth,
|
||||||
uiState.orbitalPos?.distance ?: 0.0,
|
stringResource(R.string.radar_az_text),
|
||||||
stringResource(R.string.radar_dist_text),
|
true
|
||||||
false
|
)
|
||||||
)
|
RadarTextTop(
|
||||||
|
position.elevation,
|
||||||
|
stringResource(R.string.radar_el_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
RadarTextBottom(
|
||||||
|
position.altitude,
|
||||||
|
stringResource(R.string.radar_alt_text),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
RadarTextBottom(
|
||||||
|
position.distance,
|
||||||
|
stringResource(R.string.radar_dist_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +268,11 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
|||||||
modifier = Modifier.padding(32.dp)
|
modifier = Modifier.padding(32.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
Text(text = """¯\_(ツ)_/¯""", fontSize = 32.sp)
|
||||||
Text(text = stringResource(R.string.empty_list_message), fontSize = 21.sp)
|
Text(
|
||||||
|
text = stringResource(R.string.empty_list_message),
|
||||||
|
fontSize = 21.sp,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.radar_no_data),
|
text = stringResource(R.string.radar_no_data),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.radar
|
package com.rtbishop.look4sat.presentation.radar
|
||||||
|
|
||||||
import android.media.SoundPool
|
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
@@ -27,7 +26,6 @@ import androidx.compose.foundation.layout.aspectRatio
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -44,8 +42,6 @@ import androidx.compose.ui.graphics.drawscope.Fill
|
|||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.graphics.drawscope.rotate
|
import androidx.compose.ui.graphics.drawscope.rotate
|
||||||
import androidx.compose.ui.graphics.drawscope.translate
|
import androidx.compose.ui.graphics.drawscope.translate
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalView
|
|
||||||
import androidx.compose.ui.text.TextMeasurer
|
import androidx.compose.ui.text.TextMeasurer
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.drawText
|
import androidx.compose.ui.text.drawText
|
||||||
@@ -59,15 +55,15 @@ import kotlin.math.sin
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RadarViewCompose(
|
fun RadarViewCompose(
|
||||||
item: OrbitalPos?,
|
item: OrbitalPos,
|
||||||
items: List<OrbitalPos>,
|
items: List<OrbitalPos>,
|
||||||
azimElev: Pair<Float, Float>,
|
azimElev: Pair<Float, Float>,
|
||||||
shouldShowSweep: Boolean,
|
shouldShowSweep: Boolean,
|
||||||
shouldUseCompass: Boolean,
|
shouldUseCompass: Boolean,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
// val context = LocalContext.current
|
||||||
val view = LocalView.current
|
// val view = LocalView.current
|
||||||
val radarColor = MaterialTheme.colorScheme.secondary
|
val radarColor = MaterialTheme.colorScheme.secondary
|
||||||
val trackColor = MaterialTheme.colorScheme.primary
|
val trackColor = MaterialTheme.colorScheme.primary
|
||||||
val aimColor = MaterialTheme.colorScheme.error
|
val aimColor = MaterialTheme.colorScheme.error
|
||||||
@@ -75,15 +71,15 @@ fun RadarViewCompose(
|
|||||||
val animTransition = rememberInfiniteTransition(label = "animScale")
|
val animTransition = rememberInfiniteTransition(label = "animScale")
|
||||||
val animSpec = infiniteRepeatable<Float>(tween(1000))
|
val animSpec = infiniteRepeatable<Float>(tween(1000))
|
||||||
val animScale = animTransition.animateFloat(16f, 64f, animSpec, label = "animScale")
|
val animScale = animTransition.animateFloat(16f, 64f, animSpec, label = "animScale")
|
||||||
val aimThreshold = 0.05f
|
// val aimThreshold = 0.05f
|
||||||
val measurer = rememberTextMeasurer()
|
val measurer = rememberTextMeasurer()
|
||||||
val sweepDegrees = remember { mutableFloatStateOf(0f) }
|
val sweepDegrees = remember { mutableFloatStateOf(0f) }
|
||||||
val trackCreated = remember { mutableStateOf(false) }
|
val trackCreated = remember { mutableStateOf(false) }
|
||||||
val trackPath = remember { mutableStateOf(Path()) }
|
val trackPath = remember { mutableStateOf(Path()) }
|
||||||
val trackEffect = remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
|
val trackEffect = remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
|
||||||
val soundPool = remember { mutableStateOf<SoundPool?>(null) }
|
// val soundPool = remember { mutableStateOf<SoundPool?>(null) }
|
||||||
val beepSoundId = remember { mutableIntStateOf(0) }
|
// val beepSoundId = remember { mutableIntStateOf(0) }
|
||||||
val aimTargetDifference = remember { mutableFloatStateOf(0f) }
|
// val aimTargetDifference = remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
// LaunchedEffect(item.azimuth, item.elevation, azimElev.first, azimElev.second) {
|
// LaunchedEffect(item.azimuth, item.elevation, azimElev.first, azimElev.second) {
|
||||||
// val aimAzimuthRadians = azimElev.first.toDouble().toRadians()
|
// val aimAzimuthRadians = azimElev.first.toDouble().toRadians()
|
||||||
@@ -95,13 +91,10 @@ fun RadarViewCompose(
|
|||||||
// val satX = sph2CartX(item.azimuth, item.elevation, radius)
|
// val satX = sph2CartX(item.azimuth, item.elevation, radius)
|
||||||
// val satY = sph2CartY(item.azimuth, item.elevation, radius)
|
// val satY = sph2CartY(item.azimuth, item.elevation, radius)
|
||||||
// aimTargetDifference.floatValue = sqrt((satX - aimX).pow(2) + (satY - aimY).pow(2))
|
// aimTargetDifference.floatValue = sqrt((satX - aimX).pow(2) + (satY - aimY).pow(2))
|
||||||
//
|
|
||||||
//
|
|
||||||
// val minPlaybackRate = 0.5f
|
// val minPlaybackRate = 0.5f
|
||||||
// val maxPlaybackRate = 2.0f
|
// val maxPlaybackRate = 2.0f
|
||||||
// val playbackRate =
|
// val playbackRate =
|
||||||
// maxPlaybackRate - (aimTargetDifference.floatValue / (maxPlaybackRate - minPlaybackRate))
|
// maxPlaybackRate - (aimTargetDifference.floatValue / (maxPlaybackRate - minPlaybackRate))
|
||||||
//
|
|
||||||
// soundPool.value?.setRate(beepSoundId.intValue, playbackRate)
|
// soundPool.value?.setRate(beepSoundId.intValue, playbackRate)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -127,20 +120,16 @@ fun RadarViewCompose(
|
|||||||
// .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
// .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
||||||
// .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
// .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||||
// .build()
|
// .build()
|
||||||
//
|
|
||||||
// soundPool.value = SoundPool.Builder()
|
// soundPool.value = SoundPool.Builder()
|
||||||
// .setMaxStreams(1)
|
// .setMaxStreams(1)
|
||||||
// .setAudioAttributes(audioAttributes)
|
// .setAudioAttributes(audioAttributes)
|
||||||
// .build()
|
// .build()
|
||||||
//
|
|
||||||
// beepSoundId.intValue = soundPool.value?.load(context, R.raw.beep, 1) ?: 0
|
// beepSoundId.intValue = soundPool.value?.load(context, R.raw.beep, 1) ?: 0
|
||||||
//
|
|
||||||
// soundPool.value?.setOnLoadCompleteListener { soundPool, _, status ->
|
// soundPool.value?.setOnLoadCompleteListener { soundPool, _, status ->
|
||||||
// if (status == 0) {
|
// if (status == 0) {
|
||||||
// soundPool.play(beepSoundId.intValue, 0.5f, 0.5f, 0, -1, 1f)
|
// soundPool.play(beepSoundId.intValue, 0.5f, 0.5f, 0, -1, 1f)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// onDispose {
|
// onDispose {
|
||||||
// soundPool.value?.release()
|
// soundPool.value?.release()
|
||||||
// }
|
// }
|
||||||
@@ -161,7 +150,7 @@ fun RadarViewCompose(
|
|||||||
drawInfo(radius, trackColor, measurer, 3)
|
drawInfo(radius, trackColor, measurer, 3)
|
||||||
translate(center.x, center.y) {
|
translate(center.x, center.y) {
|
||||||
drawTrack(trackPath.value, trackEffect.value, aimColor, trackColor)
|
drawTrack(trackPath.value, trackEffect.value, aimColor, trackColor)
|
||||||
if (item != null && item.elevation > 0) {
|
if (item.elevation > 0) {
|
||||||
drawPosition(item, radius, animScale.value, trackColor)
|
drawPosition(item, radius, animScale.value, trackColor)
|
||||||
}
|
}
|
||||||
if (shouldUseCompass) {
|
if (shouldUseCompass) {
|
||||||
|
|||||||
@@ -27,6 +27,19 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.common.SharedDialog
|
import com.rtbishop.look4sat.presentation.common.SharedDialog
|
||||||
|
import com.rtbishop.look4sat.presentation.common.CardButton
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.rtbishop.look4sat.presentation.LocalSpacing
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -41,10 +54,16 @@ fun PositionDialog(lat: Double, lon: Double, dismiss: () -> Unit, save: (Double,
|
|||||||
val titleText = stringResource(id = R.string.prefs_station_title)
|
val titleText = stringResource(id = R.string.prefs_station_title)
|
||||||
val onAccept = { saveValues(latValue.value, lonValue.value, save).also { dismiss() } }
|
val onAccept = { saveValues(latValue.value, lonValue.value, save).also { dismiss() } }
|
||||||
SharedDialog(title = titleText, onCancel = dismiss, onAccept = onAccept) {
|
SharedDialog(title = titleText, onCancel = dismiss, onAccept = onAccept) {
|
||||||
Text(text = stringResource(id = R.string.prefs_station_lat_text))
|
OutlinedTextField(
|
||||||
OutlinedTextField(value = latValue.value, onValueChange = { latValue.value = it })
|
value = latValue.value,
|
||||||
Text(text = stringResource(id = R.string.prefs_station_lon_text))
|
onValueChange = { latValue.value = it },
|
||||||
OutlinedTextField(value = lonValue.value, onValueChange = { lonValue.value = it })
|
label = { Text(text = stringResource(id = R.string.prefs_station_lat_text)) }
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = lonValue.value,
|
||||||
|
onValueChange = { lonValue.value = it },
|
||||||
|
label = { Text(text = stringResource(id = R.string.prefs_station_lon_text)) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +86,116 @@ fun LocatorDialog(qthLocator: String, dismiss: () -> Unit, save: (String) -> Uni
|
|||||||
val locator = rememberSaveable { mutableStateOf(qthLocator) }
|
val locator = rememberSaveable { mutableStateOf(qthLocator) }
|
||||||
val onAccept = { save(locator.value).also { dismiss() } }
|
val onAccept = { save(locator.value).also { dismiss() } }
|
||||||
SharedDialog(title = stringResource(R.string.prefs_locator_title), onCancel = dismiss, onAccept = onAccept) {
|
SharedDialog(title = stringResource(R.string.prefs_locator_title), onCancel = dismiss, onAccept = onAccept) {
|
||||||
Text(text = stringResource(id = R.string.prefs_locator_text))
|
OutlinedTextField(
|
||||||
OutlinedTextField(value = locator.value, onValueChange = { locator.value = it })
|
value = locator.value,
|
||||||
|
onValueChange = { locator.value = it },
|
||||||
|
label = { Text(text = stringResource(id = R.string.prefs_locator_text)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun TransceiversDialogPreview() {
|
||||||
|
MainTheme {
|
||||||
|
DataSourcesDialog(
|
||||||
|
useCustomTle = true,
|
||||||
|
useCustomTransceivers = true,
|
||||||
|
tleUrl = "https://example.com/tle.txt",
|
||||||
|
transceiversUrl = "https://example.com/tx.json",
|
||||||
|
onImportTle = {},
|
||||||
|
onImportTransceivers = {},
|
||||||
|
onDismiss = {},
|
||||||
|
onSave = { _, _, _, _ -> }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DataSourcesDialog(
|
||||||
|
useCustomTle: Boolean,
|
||||||
|
useCustomTransceivers: Boolean,
|
||||||
|
tleUrl: String,
|
||||||
|
transceiversUrl: String,
|
||||||
|
onImportTle: () -> Unit,
|
||||||
|
onImportTransceivers: () -> Unit,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onSave: (Boolean, Boolean, String, String) -> Unit
|
||||||
|
) {
|
||||||
|
val padding = LocalSpacing.current.large
|
||||||
|
val isEnabledCustomTle = rememberSaveable { mutableStateOf(useCustomTle) }
|
||||||
|
val isEnabledCustomTransceivers = rememberSaveable { mutableStateOf(useCustomTransceivers) }
|
||||||
|
val urlTle = rememberSaveable { mutableStateOf(tleUrl) }
|
||||||
|
val urlTransceivers = rememberSaveable { mutableStateOf(transceiversUrl) }
|
||||||
|
val onAccept = {
|
||||||
|
onSave(isEnabledCustomTle.value, isEnabledCustomTransceivers.value, urlTle.value, urlTransceivers.value)
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
val onCancel = { onDismiss() }
|
||||||
|
SharedDialog(
|
||||||
|
title = stringResource(id = R.string.prefs_data_sources_title),
|
||||||
|
onCancel = onCancel,
|
||||||
|
onAccept = onAccept,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(horizontal = padding)) {
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
|
CardButton(
|
||||||
|
onClick = {
|
||||||
|
onImportTle()
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
text = "TLE (3LE)\nR4UAB (.txt)",
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
CardButton(
|
||||||
|
onClick = {
|
||||||
|
onImportTransceivers()
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
text = "Transceivers\nSatNOGS (.json)",
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.prefs_data_sources_tle_switch))
|
||||||
|
Switch(
|
||||||
|
checked = isEnabledCustomTle.value,
|
||||||
|
onCheckedChange = { isEnabledCustomTle.value = it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OutlinedTextField(
|
||||||
|
value = urlTle.value,
|
||||||
|
onValueChange = { urlTle.value = it },
|
||||||
|
label = { Text(text = stringResource(id = R.string.prefs_data_sources_url_title)) },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isEnabledCustomTle.value,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.prefs_data_sources_transceivers_switch))
|
||||||
|
Switch(
|
||||||
|
checked = isEnabledCustomTransceivers.value,
|
||||||
|
onCheckedChange = { isEnabledCustomTransceivers.value = it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OutlinedTextField(
|
||||||
|
value = urlTransceivers.value,
|
||||||
|
onValueChange = { urlTransceivers.value = it },
|
||||||
|
label = { Text(text = stringResource(id = R.string.prefs_data_sources_url_title)) },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isEnabledCustomTransceivers.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,8 +112,11 @@ private fun SettingsScreen(uiState: SettingsState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val contentContract = ActivityResultContracts.GetContent()
|
val contentContract = ActivityResultContracts.GetContent()
|
||||||
val contentRequest = rememberLauncherForActivityResult(contentContract) { uri ->
|
val contentRequestForTle = rememberLauncherForActivityResult(contentContract) { uri ->
|
||||||
uri?.let { uiState.sendAction(SettingsAction.UpdateFromFile(uri.toString())) }
|
uri?.let { uiState.sendAction(SettingsAction.UpdateTLEFromFile(uri.toString())) }
|
||||||
|
}
|
||||||
|
val contentRequestForTransceivers = rememberLauncherForActivityResult(contentContract) { uri ->
|
||||||
|
uri?.let { uiState.sendAction(SettingsAction.UpdateTransceiversFromFile(uri.toString())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position settings
|
// Position settings
|
||||||
@@ -137,10 +140,39 @@ private fun SettingsScreen(uiState: SettingsState) {
|
|||||||
LocatorDialog(posSettings.stationPos.qthLocator, showLocDialog, setQthPos)
|
LocatorDialog(posSettings.stationPos.qthLocator, showLocDialog, setQthPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data sources dialog
|
||||||
|
val dataSourcesDialogState = rememberSaveable { mutableStateOf(false) }
|
||||||
|
val showDataSourcesDialog = { dataSourcesDialogState.value = true }
|
||||||
|
val dismissDataSourcesDialog = { dataSourcesDialogState.value = false }
|
||||||
|
if (dataSourcesDialogState.value) {
|
||||||
|
DataSourcesDialog(
|
||||||
|
useCustomTle = uiState.dataSourcesSettings.useCustomTLE,
|
||||||
|
useCustomTransceivers = uiState.dataSourcesSettings.useCustomTransceivers,
|
||||||
|
tleUrl = uiState.dataSourcesSettings.tleUrl,
|
||||||
|
transceiversUrl = uiState.dataSourcesSettings.transceiversUrl,
|
||||||
|
onImportTle = { contentRequestForTle.launch("*/*") },
|
||||||
|
onImportTransceivers = { contentRequestForTransceivers.launch("*/*") },
|
||||||
|
onDismiss = dismissDataSourcesDialog,
|
||||||
|
onSave = {
|
||||||
|
useCustomTle, useCustomTransceivers, tleUrl, transceiversUrl ->
|
||||||
|
if (!useCustomTle || tleUrl.isNotBlank()) {
|
||||||
|
uiState.sendDataSourcesAction(DataSourcesAction.SetUseCustomTle(useCustomTle))
|
||||||
|
uiState.sendDataSourcesAction(DataSourcesAction.SetTleUrl(tleUrl))
|
||||||
|
}
|
||||||
|
if (!useCustomTransceivers || transceiversUrl.isNotBlank()) {
|
||||||
|
uiState.sendDataSourcesAction(DataSourcesAction.SetUseCustomTransceivers(useCustomTransceivers))
|
||||||
|
uiState.sendDataSourcesAction(DataSourcesAction.SetTransceiversUrl(transceiversUrl))
|
||||||
|
}
|
||||||
|
if (useCustomTle || useCustomTransceivers) {
|
||||||
|
uiState.sendAction(SettingsAction.UpdateFromWeb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Data settings
|
// Data settings
|
||||||
val dataSettings = uiState.dataSettings
|
val dataSettings = uiState.dataSettings
|
||||||
val updateFromWeb: () -> Unit = { uiState.sendAction(SettingsAction.UpdateFromWeb) }
|
val updateFromWeb: () -> Unit = { uiState.sendAction(SettingsAction.UpdateFromWeb) }
|
||||||
val updateFromFile = { contentRequest.launch("*/*") }
|
|
||||||
val clearAllData: () -> Unit = { uiState.sendAction(SettingsAction.ClearAllData) }
|
val clearAllData: () -> Unit = { uiState.sendAction(SettingsAction.ClearAllData) }
|
||||||
|
|
||||||
// RC settings
|
// RC settings
|
||||||
@@ -209,7 +241,7 @@ private fun SettingsScreen(uiState: SettingsState) {
|
|||||||
item {
|
item {
|
||||||
LocationCard(posSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos, uiState.sendSystemAction)
|
LocationCard(posSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos, uiState.sendSystemAction)
|
||||||
}
|
}
|
||||||
item { DataCard(dataSettings, updateFromWeb, updateFromFile, clearAllData) }
|
item { DataCard(dataSettings, updateFromWeb, clearAllData, showDataSourcesDialog) }
|
||||||
item { NetworkOutputCard(rcSettings, setRotatorState, setRotatorAddress, setRotatorPort) }
|
item { NetworkOutputCard(rcSettings, setRotatorState, setRotatorAddress, setRotatorPort) }
|
||||||
item { BluetoothOutputCard(rcSettings, setBluetoothState, setBluetoothAddress, setBluetoothFormat) }
|
item { BluetoothOutputCard(rcSettings, setBluetoothState, setBluetoothAddress, setBluetoothFormat) }
|
||||||
item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
|
item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
|
||||||
@@ -290,15 +322,15 @@ private fun LocationCard(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun DataCardPreview() = MainTheme {
|
private fun DataCardPreview() = MainTheme {
|
||||||
val settings = DataSettings(true, 5000, 2500, 0L)
|
val settings = DataSettings(true, 5000, 2500, 0L)
|
||||||
DataCard(settings = settings, {}, {}) {}
|
DataCard(settings = settings, {}, {}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DataCard(
|
private fun DataCard(
|
||||||
settings: DataSettings,
|
settings: DataSettings,
|
||||||
updateFromWeb: () -> Unit,
|
updateFromWeb: () -> Unit,
|
||||||
updateFromFile: () -> Unit,
|
clearAllData: () -> Unit,
|
||||||
clearAllData: () -> Unit
|
showDataSourcesDialog: () -> Unit
|
||||||
) {
|
) {
|
||||||
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
|
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
|
||||||
Column(
|
Column(
|
||||||
@@ -320,21 +352,19 @@ private fun DataCard(
|
|||||||
Text(text = stringResource(R.string.prefs_data_radios, settings.radiosTotal))
|
Text(text = stringResource(R.string.prefs_data_radios, settings.radiosTotal))
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(1.dp))
|
Spacer(modifier = Modifier.height(1.dp))
|
||||||
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { updateFromWeb() },
|
onClick = updateFromWeb,
|
||||||
text = stringResource(id = R.string.prefs_data_update),
|
text = stringResource(id = R.string.prefs_data_update),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { updateFromFile() },
|
onClick = showDataSourcesDialog,
|
||||||
text = stringResource(id = R.string.prefs_data_import),
|
text = stringResource(id = R.string.prefs_data_import),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { clearAllData() },
|
onClick = clearAllData,
|
||||||
text = stringResource(id = R.string.prefs_data_clear),
|
text = stringResource(id = R.string.prefs_data_clear),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.rtbishop.look4sat.presentation.settings
|
|||||||
|
|
||||||
import com.rtbishop.look4sat.domain.model.OtherSettings
|
import com.rtbishop.look4sat.domain.model.OtherSettings
|
||||||
import com.rtbishop.look4sat.domain.model.RCSettings
|
import com.rtbishop.look4sat.domain.model.RCSettings
|
||||||
|
import com.rtbishop.look4sat.domain.model.DataSourcesSettings
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||||
|
|
||||||
@@ -39,9 +40,11 @@ data class SettingsState(
|
|||||||
val dataSettings: DataSettings,
|
val dataSettings: DataSettings,
|
||||||
val otherSettings: OtherSettings,
|
val otherSettings: OtherSettings,
|
||||||
val rcSettings: RCSettings,
|
val rcSettings: RCSettings,
|
||||||
|
val dataSourcesSettings: DataSourcesSettings,
|
||||||
val sendAction: (SettingsAction) -> Unit,
|
val sendAction: (SettingsAction) -> Unit,
|
||||||
val sendRCAction: (RCAction) -> Unit,
|
val sendRCAction: (RCAction) -> Unit,
|
||||||
val sendSystemAction: (SystemAction) -> Unit
|
val sendSystemAction: (SystemAction) -> Unit,
|
||||||
|
val sendDataSourcesAction: (DataSourcesAction) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class SettingsAction {
|
sealed class SettingsAction {
|
||||||
@@ -50,7 +53,8 @@ sealed class SettingsAction {
|
|||||||
data class SetQthPosition(val locator: String) : SettingsAction()
|
data class SetQthPosition(val locator: String) : SettingsAction()
|
||||||
data object DismissPosMessages : SettingsAction()
|
data object DismissPosMessages : SettingsAction()
|
||||||
data object UpdateFromWeb : SettingsAction()
|
data object UpdateFromWeb : SettingsAction()
|
||||||
data class UpdateFromFile(val uri: String) : SettingsAction()
|
data class UpdateTLEFromFile(val uri: String) : SettingsAction()
|
||||||
|
data class UpdateTransceiversFromFile(val uri: String) : SettingsAction()
|
||||||
data object ClearAllData : SettingsAction()
|
data object ClearAllData : SettingsAction()
|
||||||
data class ToggleUtc(val value: Boolean) : SettingsAction()
|
data class ToggleUtc(val value: Boolean) : SettingsAction()
|
||||||
data class ToggleUpdate(val value: Boolean) : SettingsAction()
|
data class ToggleUpdate(val value: Boolean) : SettingsAction()
|
||||||
@@ -72,3 +76,10 @@ sealed class RCAction {
|
|||||||
data class SetBluetoothName(val value: String) : RCAction()
|
data class SetBluetoothName(val value: String) : RCAction()
|
||||||
data class SetBluetoothAddress(val value: String) : RCAction()
|
data class SetBluetoothAddress(val value: String) : RCAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class DataSourcesAction {
|
||||||
|
data class SetUseCustomTle(val value: Boolean) : DataSourcesAction()
|
||||||
|
data class SetUseCustomTransceivers(val value: Boolean) : DataSourcesAction()
|
||||||
|
data class SetTleUrl(val value: String) : DataSourcesAction()
|
||||||
|
data class SetTransceiversUrl(val value: String) : DataSourcesAction()
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import androidx.lifecycle.viewmodel.initializer
|
|||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import com.rtbishop.look4sat.MainApplication
|
import com.rtbishop.look4sat.MainApplication
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
|
import com.rtbishop.look4sat.domain.model.DataSourcesSettings
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||||
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
|
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||||
@@ -51,6 +52,12 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0)
|
private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0)
|
||||||
private val defaultDataSettings = DataSettings(false, 0, 0, 0L)
|
private val defaultDataSettings = DataSettings(false, 0, 0, 0L)
|
||||||
|
private val defaultDataSourcesSettings = DataSourcesSettings(
|
||||||
|
useCustomTLE = false,
|
||||||
|
useCustomTransceivers = false,
|
||||||
|
tleUrl = settingsRepo.dataSourcesSettings.value.tleUrl,
|
||||||
|
transceiversUrl = settingsRepo.dataSourcesSettings.value.transceiversUrl
|
||||||
|
)
|
||||||
private val _uiState = MutableStateFlow(
|
private val _uiState = MutableStateFlow(
|
||||||
SettingsState(
|
SettingsState(
|
||||||
nextTime = "00:00:00",
|
nextTime = "00:00:00",
|
||||||
@@ -61,9 +68,11 @@ class SettingsViewModel(
|
|||||||
dataSettings = defaultDataSettings,
|
dataSettings = defaultDataSettings,
|
||||||
otherSettings = settingsRepo.otherSettings.value,
|
otherSettings = settingsRepo.otherSettings.value,
|
||||||
rcSettings = settingsRepo.rcSettings.value,
|
rcSettings = settingsRepo.rcSettings.value,
|
||||||
|
dataSourcesSettings = defaultDataSourcesSettings,
|
||||||
sendAction = ::handleAction,
|
sendAction = ::handleAction,
|
||||||
sendRCAction = ::handleAction,
|
sendRCAction = ::handleAction,
|
||||||
sendSystemAction = ::handleAction
|
sendSystemAction = ::handleAction,
|
||||||
|
sendDataSourcesAction = ::handleAction
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private var processing: Job? = null
|
private var processing: Job? = null
|
||||||
@@ -113,6 +122,11 @@ class SettingsViewModel(
|
|||||||
_uiState.update { it.copy(otherSettings = settings) }
|
_uiState.update { it.copy(otherSettings = settings) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
settingsRepo.dataSourcesSettings.collect { settings ->
|
||||||
|
_uiState.update { it.copy(dataSourcesSettings = settings) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAction(action: SettingsAction) {
|
private fun handleAction(action: SettingsAction) {
|
||||||
@@ -122,7 +136,8 @@ class SettingsViewModel(
|
|||||||
is SettingsAction.SetQthPosition -> setQthPosition(action.locator)
|
is SettingsAction.SetQthPosition -> setQthPosition(action.locator)
|
||||||
SettingsAction.DismissPosMessages -> dismissPosMessage()
|
SettingsAction.DismissPosMessages -> dismissPosMessage()
|
||||||
SettingsAction.UpdateFromWeb -> updateFromWeb()
|
SettingsAction.UpdateFromWeb -> updateFromWeb()
|
||||||
is SettingsAction.UpdateFromFile -> updateFromFile(action.uri)
|
is SettingsAction.UpdateTLEFromFile -> updateTLEFromFile(action.uri)
|
||||||
|
is SettingsAction.UpdateTransceiversFromFile -> updateTransceiversFromFile(action.uri)
|
||||||
SettingsAction.ClearAllData -> clearAllData()
|
SettingsAction.ClearAllData -> clearAllData()
|
||||||
is SettingsAction.ToggleUtc -> settingsRepo.setStateOfUtc(action.value)
|
is SettingsAction.ToggleUtc -> settingsRepo.setStateOfUtc(action.value)
|
||||||
is SettingsAction.ToggleUpdate -> settingsRepo.setStateOfAutoUpdate(action.value)
|
is SettingsAction.ToggleUpdate -> settingsRepo.setStateOfAutoUpdate(action.value)
|
||||||
@@ -150,6 +165,15 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleAction(action: DataSourcesAction) {
|
||||||
|
when (action) {
|
||||||
|
is DataSourcesAction.SetUseCustomTle -> settingsRepo.setUseCustomTle(action.value)
|
||||||
|
is DataSourcesAction.SetUseCustomTransceivers -> settingsRepo.setUseCustomTransceivers(action.value)
|
||||||
|
is DataSourcesAction.SetTleUrl -> settingsRepo.setTleUrl(action.value)
|
||||||
|
is DataSourcesAction.SetTransceiversUrl -> settingsRepo.setTransceiversUrl(action.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setGpsPosition() {
|
private fun setGpsPosition() {
|
||||||
if (settingsRepo.setStationPosition()) {
|
if (settingsRepo.setStationPosition()) {
|
||||||
val messageResId = R.string.prefs_loc_success
|
val messageResId = R.string.prefs_loc_success
|
||||||
@@ -217,11 +241,23 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFromFile(uri: String) = viewModelScope.launch {
|
private fun updateTLEFromFile(uri: String) = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = true)
|
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = true)
|
||||||
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
||||||
databaseRepo.updateFromFile(uri)
|
databaseRepo.updateTLEFromFile(uri)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = false)
|
||||||
|
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
||||||
|
println(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateTransceiversFromFile(uri: String) = viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = true)
|
||||||
|
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
||||||
|
databaseRepo.updateTransceiversFromFile(uri)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = false)
|
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = false)
|
||||||
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
_uiState.update { it.copy(dataSettings = newDataSettings) }
|
||||||
|
|||||||
@@ -108,6 +108,10 @@
|
|||||||
<string name="prefs_data_clear_success">Datos eliminados con éxito</string>
|
<string name="prefs_data_clear_success">Datos eliminados con éxito</string>
|
||||||
<string name="prefs_data_update_success">Actualización completada con éxito</string>
|
<string name="prefs_data_update_success">Actualización completada con éxito</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">Custom data sources</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">Salida de datos de red</string>
|
<string name="prefs_net_title">Salida de datos de red</string>
|
||||||
<string name="prefs_net_switch">Habilitar salida</string>
|
<string name="prefs_net_switch">Habilitar salida</string>
|
||||||
<string name="prefs_net_ip_hint">Dirección IP</string>
|
<string name="prefs_net_ip_hint">Dirección IP</string>
|
||||||
|
|||||||
@@ -93,10 +93,10 @@
|
|||||||
<string name="prefs_loc_qth_error">Неправильный локатор QTH</string>
|
<string name="prefs_loc_qth_error">Неправильный локатор QTH</string>
|
||||||
<string name="prefs_loc_success">Позиция успешно обновлена</string>
|
<string name="prefs_loc_success">Позиция успешно обновлена</string>
|
||||||
<string name="prefs_station_title">Настройки местоположения</string>
|
<string name="prefs_station_title">Настройки местоположения</string>
|
||||||
<string name="prefs_station_lat_text">Установите широту наземной станции</string>
|
<string name="prefs_station_lat_text">Введите широту наземной станции</string>
|
||||||
<string name="prefs_station_lon_text">Установите долготу наземной станции</string>
|
<string name="prefs_station_lon_text">Введите долготу наземной станции</string>
|
||||||
<string name="prefs_locator_title">Настройки локатора QTH</string>
|
<string name="prefs_locator_title">Настройки локатора QTH</string>
|
||||||
<string name="prefs_locator_text">Установить позицию используя локатор</string>
|
<string name="prefs_locator_text">Введите позицию через локатор</string>
|
||||||
|
|
||||||
<string name="prefs_data_title">Данные спутников</string>
|
<string name="prefs_data_title">Данные спутников</string>
|
||||||
<string name="prefs_data_entries">Спутников: %s</string>
|
<string name="prefs_data_entries">Спутников: %s</string>
|
||||||
@@ -107,6 +107,10 @@
|
|||||||
<string name="prefs_data_clear_success">Очистка прошла успешно</string>
|
<string name="prefs_data_clear_success">Очистка прошла успешно</string>
|
||||||
<string name="prefs_data_update_success">Обновление прошло успешно</string>
|
<string name="prefs_data_update_success">Обновление прошло успешно</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">Свои источники данных</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">Custom Transceivers URL</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">Вывод сетевых данных</string>
|
<string name="prefs_net_title">Вывод сетевых данных</string>
|
||||||
<string name="prefs_net_switch">Включить вывод</string>
|
<string name="prefs_net_switch">Включить вывод</string>
|
||||||
<string name="prefs_net_ip_hint">IP адрес</string>
|
<string name="prefs_net_ip_hint">IP адрес</string>
|
||||||
|
|||||||
@@ -108,6 +108,10 @@
|
|||||||
<string name="prefs_data_clear_success">දත්ත ඉවත් කිරීම සාර්ථකයි</string>
|
<string name="prefs_data_clear_success">දත්ත ඉවත් කිරීම සාර්ථකයි</string>
|
||||||
<string name="prefs_data_update_success">යාවත්කාලීනය සම්පූර්ණ කිරීම සාර්ථකයි</string>
|
<string name="prefs_data_update_success">යාවත්කාලීනය සම්පූර්ණ කිරීම සාර්ථකයි</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">Custom data sources</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">ජාල ප්රතිදානය</string>
|
<string name="prefs_net_title">ජාල ප්රතිදානය</string>
|
||||||
<string name="prefs_net_switch">ප්රතිදානය සක්රීය කරන්න</string>
|
<string name="prefs_net_switch">ප්රතිදානය සක්රීය කරන්න</string>
|
||||||
<string name="prefs_net_ip_hint">IP ලිපිනය</string>
|
<string name="prefs_net_ip_hint">IP ලිපිනය</string>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<string name="sat_type_title">Виберіть тип супутника</string>
|
<string name="sat_type_title">Виберіть тип супутника</string>
|
||||||
<string name="sat_search_hint">Id - Назва</string>
|
<string name="sat_search_hint">Id - Назва</string>
|
||||||
<string name="sat_search_clear">Очистити</string>
|
<string name="sat_search_clear">Очистити</string>
|
||||||
<string name="sat_clear_all">Очистити</string>
|
<string name="sat_clear_all">Очистити всі</string>
|
||||||
<string name="sat_select_all">Вибрати всі</string>
|
<string name="sat_select_all">Вибрати всі</string>
|
||||||
<string name="sat_empty_list_message">
|
<string name="sat_empty_list_message">
|
||||||
Переконайтеся, що ваш пошуковий запит правильний, а база даних оновлена</string>
|
Переконайтеся, що ваш пошуковий запит правильний, а база даних оновлена</string>
|
||||||
@@ -87,15 +87,15 @@
|
|||||||
|
|
||||||
<string name="prefs_loc_title">Місцезнаходження</string>
|
<string name="prefs_loc_title">Місцезнаходження</string>
|
||||||
<string name="prefs_loc_gps_title">GPS</string>
|
<string name="prefs_loc_gps_title">GPS</string>
|
||||||
<string name="prefs_loc_gps_error">Перевірте дозволи на визначення місця</string>
|
<string name="prefs_loc_gps_error">Перевірте дозвіл на використання геолокацї</string>
|
||||||
<string name="prefs_loc_input_title">Ввести</string>
|
<string name="prefs_loc_input_title">Ввести</string>
|
||||||
<string name="prefs_loc_input_error">Введені некоректні дані</string>
|
<string name="prefs_loc_input_error">Введено некоректну локацію</string>
|
||||||
<string name="prefs_loc_qth_title">QTH</string>
|
<string name="prefs_loc_qth_title">QTH</string>
|
||||||
<string name="prefs_loc_qth_error">Некоректний локатор QTH</string>
|
<string name="prefs_loc_qth_error">Некоректний локатор QTH</string>
|
||||||
<string name="prefs_loc_success">Позицію успішно оновлено</string>
|
<string name="prefs_loc_success">Позицію успішно оновлено</string>
|
||||||
<string name="prefs_station_title">Налаштування місцезнаходження станції</string>
|
<string name="prefs_station_title">Налаштування місцезнаходження станції</string>
|
||||||
<string name="prefs_station_lat_text">Задайте широту станції</string>
|
<string name="prefs_station_lat_text">Широта місцезнаходження станції</string>
|
||||||
<string name="prefs_station_lon_text">Задайте довготу станції</string>
|
<string name="prefs_station_lon_text">Довгота місцезнаходження станції</string>
|
||||||
<string name="prefs_locator_title">Налаштування QTH локатора</string>
|
<string name="prefs_locator_title">Налаштування QTH локатора</string>
|
||||||
<string name="prefs_locator_text">Задати позицію за QTH локатором</string>
|
<string name="prefs_locator_text">Задати позицію за QTH локатором</string>
|
||||||
|
|
||||||
@@ -108,6 +108,10 @@
|
|||||||
<string name="prefs_data_clear_success">Дані успішно очищено</string>
|
<string name="prefs_data_clear_success">Дані успішно очищено</string>
|
||||||
<string name="prefs_data_update_success">Оновлення успішне</string>
|
<string name="prefs_data_update_success">Оновлення успішне</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">Custom data sources</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">Вивід мережевих даних</string>
|
<string name="prefs_net_title">Вивід мережевих даних</string>
|
||||||
<string name="prefs_net_switch">Увімкнути вихід</string>
|
<string name="prefs_net_switch">Увімкнути вихід</string>
|
||||||
<string name="prefs_net_ip_hint">IP адреса</string>
|
<string name="prefs_net_ip_hint">IP адреса</string>
|
||||||
@@ -126,6 +130,6 @@
|
|||||||
<string name="prefs_other_switch_sensors">Використовувати сенсори пристрою</string>
|
<string name="prefs_other_switch_sensors">Використовувати сенсори пристрою</string>
|
||||||
|
|
||||||
<string name="prefs_outro_title">Я хотів би подякувати:</string>
|
<string name="prefs_outro_title">Я хотів би подякувати:</string>
|
||||||
<string name="prefs_outro_license">Це ПЗ поставляється без жодних гарантій.</string>
|
<string name="prefs_outro_license">Ця програма поставляється без жодних гарантій</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -100,6 +100,10 @@
|
|||||||
<string name="prefs_data_clear_success">数据清空成功</string>
|
<string name="prefs_data_clear_success">数据清空成功</string>
|
||||||
<string name="prefs_data_update_success">更新成功</string>
|
<string name="prefs_data_update_success">更新成功</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">自定义数据源</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">自定义TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">自定义收发器URL</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">网络数据输出</string>
|
<string name="prefs_net_title">网络数据输出</string>
|
||||||
<string name="prefs_net_switch">启用输出</string>
|
<string name="prefs_net_switch">启用输出</string>
|
||||||
<string name="prefs_net_ip_hint">IP地址</string>
|
<string name="prefs_net_ip_hint">IP地址</string>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<string name="sat_search_clear">Clear</string>
|
<string name="sat_search_clear">Clear</string>
|
||||||
<string name="sat_clear_all">Clear all</string>
|
<string name="sat_clear_all">Clear all</string>
|
||||||
<string name="sat_select_all">Select all</string>
|
<string name="sat_select_all">Select all</string>
|
||||||
<string name="sat_empty_list_message">" Make sure your search query is correct and the DB is updated"</string>
|
<string name="sat_empty_list_message">"Make sure your search query is correct and the DB is updated"</string>
|
||||||
<string name="sat_warning_title">Warning\!</string>
|
<string name="sat_warning_title">Warning\!</string>
|
||||||
<string name="sat_warning_message">
|
<string name="sat_warning_message">
|
||||||
There are over 9000 satellites listed in this app.
|
There are over 9000 satellites listed in this app.
|
||||||
@@ -131,6 +131,11 @@
|
|||||||
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
||||||
<string name="prefs_data_update_success">Update completed successfully</string>
|
<string name="prefs_data_update_success">Update completed successfully</string>
|
||||||
|
|
||||||
|
<string name="prefs_data_sources_title">Custom data sources</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
|
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
||||||
|
|
||||||
<string name="prefs_net_title">Network data output</string>
|
<string name="prefs_net_title">Network data output</string>
|
||||||
<string name="prefs_net_switch">Enable output</string>
|
<string name="prefs_net_switch">Enable output</string>
|
||||||
<string name="prefs_net_ip_hint">IP address</string>
|
<string name="prefs_net_ip_hint">IP address</string>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|||||||
@@ -39,23 +39,46 @@ class DatabaseRepo(
|
|||||||
private val settingsRepo: ISettingsRepo
|
private val settingsRepo: ISettingsRepo
|
||||||
) : IDatabaseRepo {
|
) : IDatabaseRepo {
|
||||||
|
|
||||||
override suspend fun updateFromFile(uri: String) = withContext(dispatcher) {
|
override suspend fun updateTLEFromFile(uri: String) = withContext(dispatcher) {
|
||||||
val importedSatellites = remoteSource.getFileStream(uri)?.let { dataParser.parseTLEStream(it) }
|
val importedSatellites = remoteSource.getFileStream(uri)?.let { dataParser.parseTLEStream(it) }
|
||||||
importedSatellites?.let { localSource.insertEntries(it) }
|
importedSatellites?.let { localSource.insertEntries(it) }
|
||||||
setUpdateSuccessful(System.currentTimeMillis())
|
setUpdateSuccessful(System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateTransceiversFromFile(uri: String) = withContext(dispatcher) {
|
||||||
|
val radios = remoteSource.getFileStream(uri)?.let { dataParser.parseJSONStream(it) }
|
||||||
|
radios?.let {
|
||||||
|
if(it.isNotEmpty()) {
|
||||||
|
localSource.deleteRadios()
|
||||||
|
localSource.insertRadios(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setUpdateSuccessful(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun updateFromRemote() = withContext(dispatcher) {
|
override suspend fun updateFromRemote() = withContext(dispatcher) {
|
||||||
val importedEntries = mutableListOf<OrbitalData>()
|
val importedEntries = mutableListOf<OrbitalData>()
|
||||||
val importedRadios = mutableListOf<SatRadio>()
|
val importedRadios = mutableListOf<SatRadio>()
|
||||||
// fetch
|
val tleUrls = Sources.satelliteDataUrls.toMutableMap().apply {
|
||||||
val jobsMap = Sources.satelliteDataUrls.mapValues { async { remoteSource.getNetworkStream(it.value) } }
|
if (settingsRepo.dataSourcesSettings.value.useCustomTLE) {
|
||||||
val jobRadios = async { remoteSource.getNetworkStream(Sources.RADIO_DATA_URL) }
|
this["Other"] = settingsRepo.dataSourcesSettings.value.tleUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jobsMap = tleUrls
|
||||||
|
.filter { (_, url) -> url.isNotEmpty() }
|
||||||
|
.mapValues { (_, url) -> async { remoteSource.getNetworkStream(url) } }
|
||||||
|
val radioUrls = buildList {
|
||||||
|
add(Sources.RADIO_DATA_URL)
|
||||||
|
if (settingsRepo.dataSourcesSettings.value.useCustomTransceivers) {
|
||||||
|
add(settingsRepo.dataSourcesSettings.value.transceiversUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jobRadios = radioUrls.associateWith { url -> async { remoteSource.getNetworkStream(url) } }
|
||||||
// parse
|
// parse
|
||||||
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
||||||
entry.value?.let { stream ->
|
entry.value?.let { stream ->
|
||||||
when (val type = entry.key) {
|
when (val type = entry.key) {
|
||||||
"Amsat", "R4UAB" -> {
|
"Amsat", "R4UAB", "Other" -> {
|
||||||
// parse tle stream
|
// parse tle stream
|
||||||
val satellites = dataParser.parseTLEStream(stream)
|
val satellites = dataParser.parseTLEStream(stream)
|
||||||
val catnums = satellites.map { it.catnum }
|
val catnums = satellites.map { it.catnum }
|
||||||
@@ -82,7 +105,11 @@ class DatabaseRepo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jobRadios.await()?.let { importedRadios.addAll(dataParser.parseJSONStream(it)) }
|
jobRadios.values.forEach { job ->
|
||||||
|
job.await()?.let {
|
||||||
|
importedRadios.addAll(dataParser.parseJSONStream(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
// insert
|
// insert
|
||||||
localSource.insertEntries(importedEntries)
|
localSource.insertEntries(importedEntries)
|
||||||
localSource.insertRadios(importedRadios)
|
localSource.insertRadios(importedRadios)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.rtbishop.look4sat.domain.model.DatabaseState
|
|||||||
import com.rtbishop.look4sat.domain.model.OtherSettings
|
import com.rtbishop.look4sat.domain.model.OtherSettings
|
||||||
import com.rtbishop.look4sat.domain.model.PassesSettings
|
import com.rtbishop.look4sat.domain.model.PassesSettings
|
||||||
import com.rtbishop.look4sat.domain.model.RCSettings
|
import com.rtbishop.look4sat.domain.model.RCSettings
|
||||||
|
import com.rtbishop.look4sat.domain.model.DataSourcesSettings
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.domain.utility.positionToQth
|
import com.rtbishop.look4sat.domain.utility.positionToQth
|
||||||
@@ -68,6 +69,10 @@ class SettingsRepo(
|
|||||||
private val keyUpdateTimestamp = "updateTimestamp"
|
private val keyUpdateTimestamp = "updateTimestamp"
|
||||||
private val keyShouldSeeWarning = "shouldSeeWarning"
|
private val keyShouldSeeWarning = "shouldSeeWarning"
|
||||||
private val keyShouldSeeWelcome = "shouldSeeWelcome"
|
private val keyShouldSeeWelcome = "shouldSeeWelcome"
|
||||||
|
private val keyUseCustomTle = "useCustomTle"
|
||||||
|
private val keyUseCustomTransceivers = "useCustomTransceivers"
|
||||||
|
private val keyTleUrl = "tleUrl"
|
||||||
|
private val keyTransceiversUrl = "transceiversUrl"
|
||||||
private val separatorComma = ","
|
private val separatorComma = ","
|
||||||
|
|
||||||
//region # Satellites selection settings
|
//region # Satellites selection settings
|
||||||
@@ -330,4 +335,36 @@ class SettingsRepo(
|
|||||||
shouldSeeWelcome = preferences.getBoolean(keyShouldSeeWelcome, true)
|
shouldSeeWelcome = preferences.getBoolean(keyShouldSeeWelcome, true)
|
||||||
)
|
)
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
//region # Data sources settings
|
||||||
|
private val _dataSourcesSettings = MutableStateFlow(getDataSourcesSettings())
|
||||||
|
override val dataSourcesSettings: StateFlow<DataSourcesSettings> = _dataSourcesSettings
|
||||||
|
|
||||||
|
override fun setUseCustomTle(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyUseCustomTle, value) }
|
||||||
|
_dataSourcesSettings.update { it.copy(useCustomTLE = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUseCustomTransceivers(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyUseCustomTransceivers, value) }
|
||||||
|
_dataSourcesSettings.update { it.copy(useCustomTransceivers = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTleUrl(value: String) {
|
||||||
|
preferences.edit { putString(keyTleUrl, value) }
|
||||||
|
_dataSourcesSettings.update { it.copy(tleUrl = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTransceiversUrl(value: String) {
|
||||||
|
preferences.edit { putString(keyTransceiversUrl, value) }
|
||||||
|
_dataSourcesSettings.update { it.copy(transceiversUrl = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDataSourcesSettings(): DataSourcesSettings = DataSourcesSettings(
|
||||||
|
useCustomTLE = preferences.getBoolean(keyUseCustomTle, false),
|
||||||
|
useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false),
|
||||||
|
tleUrl = preferences.getString(keyTleUrl, "https://example.com/tle.txt") ?: "",
|
||||||
|
transceiversUrl = preferences.getString(keyTransceiversUrl, "https://example.com/radio.json") ?: ""
|
||||||
|
)
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -48,3 +48,10 @@ data class OtherSettings(
|
|||||||
val shouldSeeWarning: Boolean,
|
val shouldSeeWarning: Boolean,
|
||||||
val shouldSeeWelcome: Boolean
|
val shouldSeeWelcome: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class DataSourcesSettings(
|
||||||
|
val useCustomTLE: Boolean,
|
||||||
|
val useCustomTransceivers: Boolean,
|
||||||
|
val tleUrl: String,
|
||||||
|
val transceiversUrl: String
|
||||||
|
)
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.domain.repository
|
package com.rtbishop.look4sat.domain.repository
|
||||||
|
|
||||||
interface IDatabaseRepo {
|
interface IDatabaseRepo {
|
||||||
suspend fun updateFromFile(uri: String)
|
suspend fun updateTLEFromFile(uri: String)
|
||||||
|
suspend fun updateTransceiversFromFile(uri: String)
|
||||||
suspend fun updateFromRemote()
|
suspend fun updateFromRemote()
|
||||||
suspend fun clearAllData()
|
suspend fun clearAllData()
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ import com.rtbishop.look4sat.domain.model.DatabaseState
|
|||||||
import com.rtbishop.look4sat.domain.model.OtherSettings
|
import com.rtbishop.look4sat.domain.model.OtherSettings
|
||||||
import com.rtbishop.look4sat.domain.model.PassesSettings
|
import com.rtbishop.look4sat.domain.model.PassesSettings
|
||||||
import com.rtbishop.look4sat.domain.model.RCSettings
|
import com.rtbishop.look4sat.domain.model.RCSettings
|
||||||
|
import com.rtbishop.look4sat.domain.model.DataSourcesSettings
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@@ -73,4 +74,12 @@ interface ISettingsRepo {
|
|||||||
fun setWarningDismissed()
|
fun setWarningDismissed()
|
||||||
fun setWelcomeDismissed()
|
fun setWelcomeDismissed()
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
//region # Transceivers settings
|
||||||
|
val dataSourcesSettings: StateFlow<DataSourcesSettings>
|
||||||
|
fun setUseCustomTle(value: Boolean)
|
||||||
|
fun setUseCustomTransceivers(value: Boolean)
|
||||||
|
fun setTleUrl(value: String)
|
||||||
|
fun setTransceiversUrl(value: String)
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,7 @@ object Sources {
|
|||||||
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
|
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
|
||||||
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
|
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
|
||||||
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv",
|
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv",
|
||||||
"Amsat" to "https://amsat.org/tle/current/nasabare.txt"
|
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
|
||||||
|
"Other" to "" // key for sats filter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,2 @@
|
|||||||
Added support for landscape layout and tablets
|
Added custom data source translations, by troilus #191
|
||||||
Added AOS/LOS info to the countdown component
|
Added keepScreenOn for RadarScreen, by Kaitlyn Kenwell #193
|
||||||
Added various minor UI fixes for readability
|
|
||||||
Cleaned up the translations and strings.xml
|
|
||||||
Moved Modes filter dialog to Passes screen
|
|
||||||
Rewrote all the screens with Jetpack Compose
|
|
||||||
Switched to using Bottom Navigation component
|
|
||||||
Switched to using unified dialogs and top bars
|
|
||||||
Updated GitHub Actions workflow deps and scripts
|
|
||||||
@@ -1,9 +1,2 @@
|
|||||||
Added support for landscape layout and tablets
|
Added custom data source translations, by troilus #191
|
||||||
Added AOS/LOS info to the countdown component
|
Added keepScreenOn for RadarScreen, by Kaitlyn Kenwell #193
|
||||||
Added various minor UI fixes for readability
|
|
||||||
Cleaned up the translations and strings.xml
|
|
||||||
Moved Modes filter dialog to Passes screen
|
|
||||||
Rewrote all the screens with Jetpack Compose
|
|
||||||
Switched to using Bottom Navigation component
|
|
||||||
Switched to using unified dialogs and top bars
|
|
||||||
Updated GitHub Actions workflow deps and scripts
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
android-gradle-plugin = "9.0.0"
|
android-gradle-plugin = "9.0.0"
|
||||||
google-ksp = "2.3.2"
|
google-ksp = "2.3.2"
|
||||||
kotlin = "2.3.0"
|
kotlin = "2.3.10"
|
||||||
|
|
||||||
androidx-core-ktx = "1.17.0"
|
androidx-core-ktx = "1.17.0"
|
||||||
androidx-core-splashscreen = "1.2.0"
|
androidx-core-splashscreen = "1.2.0"
|
||||||
@@ -34,7 +34,7 @@ compose-animation = { group = "androidx.compose.animation", name = "animation" }
|
|||||||
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
compose-material3-navigation = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
|
compose-material3-navigation = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
|
||||||
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
||||||
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
|
|
||||||
compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose-activity" }
|
compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose-activity" }
|
||||||
compose-lifecycle = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "compose-lifecycle" }
|
compose-lifecycle = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "compose-lifecycle" }
|
||||||
|
|||||||
Reference in new issue
Block a user