mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-04 11:37:22 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df358b88c | ||
|
|
7d5bca300d | ||
|
|
fb2cd85b11 |
No files matched your search
@@ -25,19 +25,17 @@ import androidx.compose.animation.core.rememberInfiniteTransition
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
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.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.statusBars
|
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -58,82 +56,107 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation3.runtime.entryProvider
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation3.runtime.rememberNavBackStack
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import androidx.navigation.compose.rememberNavController
|
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
import com.rtbishop.look4sat.core.presentation.Screen
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
||||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
import com.rtbishop.look4sat.feature.map.MapDestination
|
||||||
import com.rtbishop.look4sat.feature.map.mapDestination
|
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
||||||
import com.rtbishop.look4sat.feature.passes.passesDestination
|
import com.rtbishop.look4sat.feature.radar.RadarDestination
|
||||||
import com.rtbishop.look4sat.feature.radar.radarDestination
|
import com.rtbishop.look4sat.feature.radiocontrol.RadioControlDestination
|
||||||
import com.rtbishop.look4sat.feature.radiocontrol.radioControlDestination
|
import com.rtbishop.look4sat.feature.satellites.SatellitesDestination
|
||||||
import com.rtbishop.look4sat.feature.satellites.satellitesDestination
|
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
||||||
import com.rtbishop.look4sat.feature.settings.settingsDestination
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(navController: NavHostController = rememberNavController()) {
|
fun MainScreen() {
|
||||||
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
|
val backStack = rememberNavBackStack(Screen.Passes)
|
||||||
val currentDestination = navController.currentBackStackEntryAsState().value?.destination?.route
|
val currentKey = backStack.lastOrNull()
|
||||||
val startDestination = Screen.Passes.route
|
val navigateBack: () -> Unit = { backStack.removeAt(backStack.size - 1) }
|
||||||
|
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
||||||
|
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar(), Screen.Map, Screen.Settings)
|
||||||
|
|
||||||
// Observe radio tracking state for the status bar
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
||||||
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
NavigationSuiteScaffold(
|
NavigationSuiteScaffold(
|
||||||
navigationSuiteItems = {
|
navigationSuiteItems = {
|
||||||
items.forEach {
|
navItems.forEach { screen ->
|
||||||
|
val isSelected = when (currentKey) {
|
||||||
|
is Screen.Satellites -> screen is Screen.Satellites
|
||||||
|
is Screen.Passes -> screen is Screen.Passes
|
||||||
|
is Screen.Radar -> screen is Screen.Radar
|
||||||
|
is Screen.Map -> screen is Screen.Map
|
||||||
|
is Screen.Settings -> screen is Screen.Settings
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
item(
|
item(
|
||||||
icon = { Icon(painterResource(it.iconResId), stringResource(it.titleResId)) },
|
icon = { Icon(painterResource(screen.iconResId), stringResource(screen.titleResId)) },
|
||||||
label = { Text(stringResource(it.titleResId)) },
|
label = { Text(stringResource(screen.titleResId)) },
|
||||||
selected = currentDestination?.contains(it.route) ?: false,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (currentDestination?.contains(it.route) ?: false) return@item
|
if (isSelected) return@item
|
||||||
navController.navigate(it.route) {
|
while (backStack.size > 1) backStack.removeAt(backStack.size - 1)
|
||||||
popUpTo(startDestination) { saveState = false }
|
if (screen !is Screen.Passes) backStack.add(screen)
|
||||||
launchSingleTop = true
|
|
||||||
restoreState = false
|
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}, navigationSuiteColors = NavigationSuiteDefaults.colors(
|
},
|
||||||
|
navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||||
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
), layoutType = when {
|
),
|
||||||
|
layoutType = when {
|
||||||
!hasEnoughHeight() && hasEnoughWidth() -> NavigationSuiteType.NavigationRail
|
!hasEnoughHeight() && hasEnoughWidth() -> NavigationSuiteType.NavigationRail
|
||||||
!hasEnoughWidth() -> NavigationSuiteType.ShortNavigationBarCompact
|
!hasEnoughWidth() -> NavigationSuiteType.ShortNavigationBarCompact
|
||||||
else -> NavigationSuiteType.ShortNavigationBarMedium
|
else -> NavigationSuiteType.ShortNavigationBarMedium
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
NavHost(
|
NavDisplay(
|
||||||
navController = navController,
|
backStack = backStack,
|
||||||
startDestination = startDestination,
|
modifier = Modifier.weight(1f),
|
||||||
enterTransition = { fadeIn(animationSpec = tween(350)) },
|
onBack = navigateBack,
|
||||||
exitTransition = { fadeOut(animationSpec = tween(350)) },
|
transitionSpec = { fadeTransition },
|
||||||
modifier = Modifier.weight(1f)
|
popTransitionSpec = { fadeTransition },
|
||||||
) {
|
predictivePopTransitionSpec = { fadeTransition },
|
||||||
satellitesDestination { navController.navigateUp() }
|
entryProvider = entryProvider {
|
||||||
passesDestination { catNum: Int, aosTime: Long ->
|
entry<Screen.Satellites> {
|
||||||
val radarRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
SatellitesDestination(navigateUp = navigateBack)
|
||||||
navController.navigate(radarRoute)
|
|
||||||
}
|
}
|
||||||
radarDestination(
|
entry<Screen.Passes> {
|
||||||
navigateUp = { navController.navigateUp() },
|
PassesDestination { catNum, aosTime ->
|
||||||
|
backStack.add(Screen.Radar(catNum, aosTime))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry<Screen.Radar> { route ->
|
||||||
|
RadarDestination(
|
||||||
|
catNum = route.catNum,
|
||||||
|
aosTime = route.aosTime,
|
||||||
|
navigateUp = navigateBack,
|
||||||
navigateToRadioControl = { catNum, aosTime ->
|
navigateToRadioControl = { catNum, aosTime ->
|
||||||
val route = "${Screen.RadioControl.route}?catNum=$catNum&aosTime=$aosTime"
|
backStack.add(Screen.RadioControl(catNum, aosTime))
|
||||||
navController.navigate(route)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
radioControlDestination { navController.navigateUp() }
|
|
||||||
mapDestination()
|
|
||||||
settingsDestination()
|
|
||||||
}
|
}
|
||||||
|
entry<Screen.RadioControl> { route ->
|
||||||
// Radio tracking status banner (above bottom navigation)
|
RadioControlDestination(
|
||||||
|
catNum = route.catNum,
|
||||||
|
aosTime = route.aosTime,
|
||||||
|
navigateUp = navigateBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
entry<Screen.Map> {
|
||||||
|
MapDestination()
|
||||||
|
}
|
||||||
|
entry<Screen.Settings> {
|
||||||
|
SettingsDestination()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Radio tracking status banner
|
||||||
if (trackingState.isActive) {
|
if (trackingState.isActive) {
|
||||||
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
||||||
val alpha by infiniteTransition.animateFloat(
|
val alpha by infiniteTransition.animateFloat(
|
||||||
@@ -151,8 +174,7 @@ fun MainScreen(navController: NavHostController = rememberNavController()) {
|
|||||||
.clickable {
|
.clickable {
|
||||||
val pass = trackingState.currentPass
|
val pass = trackingState.currentPass
|
||||||
if (pass != null) {
|
if (pass != null) {
|
||||||
val route = "${Screen.RadioControl.route}?catNum=${pass.catNum}&aosTime=${pass.aosTime}"
|
backStack.add(Screen.RadioControl(pass.catNum, pass.aosTime))
|
||||||
navController.navigate(route)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(horizontal = 12.dp, vertical = 6.dp)
|
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||||
@@ -180,6 +202,6 @@ fun MainScreen(navController: NavHostController = rememberNavController()) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end Column
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
@@ -38,6 +38,8 @@ internal class ApplicationPlugin : Plugin<Project> {
|
|||||||
implementation(project(":feature:satellites"))
|
implementation(project(":feature:satellites"))
|
||||||
implementation(project(":feature:settings"))
|
implementation(project(":feature:settings"))
|
||||||
implementation(libs.androidx.core.splashscreen)
|
implementation(libs.androidx.core.splashscreen)
|
||||||
|
implementation(libs.compose.material3.adaptive)
|
||||||
|
implementation(libs.compose.navigation3)
|
||||||
androidTestImplementation(libs.bundles.androidTest)
|
androidTestImplementation(libs.bundles.androidTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ internal class CoreDataPlugin : Plugin<Project> {
|
|||||||
implementation(libs.androidx.room)
|
implementation(libs.androidx.room)
|
||||||
implementation(libs.androidx.room.runtime)
|
implementation(libs.androidx.room.runtime)
|
||||||
ksp(libs.androidx.room.compiler)
|
ksp(libs.androidx.room.compiler)
|
||||||
implementation(libs.other.coroutines)
|
implementation(libs.kotlin.coroutines)
|
||||||
implementation(libs.other.okhttp)
|
implementation(libs.other.okhttp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -25,10 +25,11 @@ import org.gradle.kotlin.dsl.dependencies
|
|||||||
internal class CoreDomainPlugin : Plugin<Project> {
|
internal class CoreDomainPlugin : Plugin<Project> {
|
||||||
override fun apply(target: Project) = with(target) {
|
override fun apply(target: Project) = with(target) {
|
||||||
applyPlugin(libs.plugins.kotlin.jvm)
|
applyPlugin(libs.plugins.kotlin.jvm)
|
||||||
|
applyPlugin(libs.plugins.kotlin.serialization)
|
||||||
setupKotlin()
|
setupKotlin()
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.other.coroutines)
|
implementation(libs.kotlin.coroutines)
|
||||||
implementation(libs.other.json)
|
implementation(libs.kotlin.serialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
@@ -24,12 +24,15 @@ import org.gradle.kotlin.dsl.dependencies
|
|||||||
@Suppress("Unused")
|
@Suppress("Unused")
|
||||||
internal class CorePresentationPlugin : Plugin<Project> {
|
internal class CorePresentationPlugin : Plugin<Project> {
|
||||||
override fun apply(target: Project) = with(target) {
|
override fun apply(target: Project) = with(target) {
|
||||||
|
applyPlugin(libs.plugins.kotlin.serialization)
|
||||||
setupAndroidLib()
|
setupAndroidLib()
|
||||||
setupCompose()
|
setupCompose()
|
||||||
setupKotlin()
|
setupKotlin()
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core:domain"))
|
implementation(project(":core:domain"))
|
||||||
implementation(libs.androidx.core.splashscreen)
|
implementation(libs.androidx.core.splashscreen)
|
||||||
|
implementation(libs.kotlin.serialization)
|
||||||
|
implementation(libs.compose.material3.adaptive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -74,7 +74,7 @@ internal fun Project.setupAndroidApp() {
|
|||||||
}
|
}
|
||||||
androidResources {
|
androidResources {
|
||||||
generateLocaleConfig = true
|
generateLocaleConfig = true
|
||||||
localeFilters.addAll(listOf("en", "es", "ru", "si", "uk", "zh"))
|
localeFilters.addAll(listOf("en", "es", "ru", "si", "tr", "uk", "zh"))
|
||||||
}
|
}
|
||||||
packaging { resources { excludes += listOf("META-INF/*") } }
|
packaging { resources { excludes += listOf("META-INF/*") } }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ plugins {
|
|||||||
alias(libs.plugins.compose.compiler) apply false
|
alias(libs.plugins.compose.compiler) apply false
|
||||||
alias(libs.plugins.google.ksp) apply false
|
alias(libs.plugins.google.ksp) apply false
|
||||||
alias(libs.plugins.kotlin.jvm) apply false
|
alias(libs.plugins.kotlin.jvm) apply false
|
||||||
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean", Delete::class.java) {
|
tasks.register("clean", Delete::class.java) {
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ data class SatRadio(
|
|||||||
@PrimaryKey val uuid: String,
|
@PrimaryKey val uuid: String,
|
||||||
val info: String,
|
val info: String,
|
||||||
val isAlive: Boolean,
|
val isAlive: Boolean,
|
||||||
var downlinkLow: Long?,
|
val downlinkLow: Long?,
|
||||||
var downlinkHigh: Long?,
|
val downlinkHigh: Long?,
|
||||||
val downlinkMode: String?,
|
val downlinkMode: String?,
|
||||||
var uplinkLow: Long?,
|
val uplinkLow: Long?,
|
||||||
var uplinkHigh: Long?,
|
val uplinkHigh: Long?,
|
||||||
val uplinkMode: String?,
|
val uplinkMode: String?,
|
||||||
val isInverted: Boolean,
|
val isInverted: Boolean,
|
||||||
val catnum: Int?
|
val catnum: Int?
|
||||||
|
|||||||
@@ -17,16 +17,20 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.core.domain.model
|
package com.rtbishop.look4sat.core.domain.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class SatRadio(
|
data class SatRadio(
|
||||||
val uuid: String,
|
@SerialName("uuid") val uuid: String,
|
||||||
val info: String,
|
@SerialName("description") val info: String,
|
||||||
val isAlive: Boolean,
|
@SerialName("alive") val isAlive: Boolean,
|
||||||
var downlinkLow: Long?,
|
@SerialName("downlink_low") val downlinkLow: Long?,
|
||||||
var downlinkHigh: Long?,
|
@SerialName("downlink_high") val downlinkHigh: Long?,
|
||||||
val downlinkMode: String?,
|
@SerialName("mode") val downlinkMode: String?,
|
||||||
var uplinkLow: Long?,
|
@SerialName("uplink_low") val uplinkLow: Long?,
|
||||||
var uplinkHigh: Long?,
|
@SerialName("uplink_high") val uplinkHigh: Long?,
|
||||||
val uplinkMode: String?,
|
@SerialName("uplink_mode") val uplinkMode: String?,
|
||||||
val isInverted: Boolean,
|
@SerialName("invert") val isInverted: Boolean,
|
||||||
val catnum: Int?
|
@SerialName("norad_cat_id") val catnum: Int?
|
||||||
)
|
)
|
||||||
@@ -21,13 +21,19 @@ import com.rtbishop.look4sat.core.domain.model.SatRadio
|
|||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalData
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalData
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.json.JSONArray
|
import kotlinx.serialization.json.Json
|
||||||
import org.json.JSONObject
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
class DataParser(private val dispatcher: CoroutineDispatcher) {
|
class DataParser(private val dispatcher: CoroutineDispatcher) {
|
||||||
|
|
||||||
|
private val json = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
coerceInputValues = true
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun parseCSVStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) {
|
suspend fun parseCSVStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) {
|
||||||
stream.bufferedReader().useLines { lines ->
|
stream.bufferedReader().useLines { lines ->
|
||||||
lines.drop(1).mapNotNull { parseCSV(it.split(",")) }.toList()
|
lines.drop(1).mapNotNull { parseCSV(it.split(",")) }.toList()
|
||||||
@@ -43,8 +49,12 @@ class DataParser(private val dispatcher: CoroutineDispatcher) {
|
|||||||
|
|
||||||
suspend fun parseJSONStream(stream: InputStream): List<SatRadio> = withContext(dispatcher) {
|
suspend fun parseJSONStream(stream: InputStream): List<SatRadio> = withContext(dispatcher) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val jsonArray = JSONArray(stream.bufferedReader().readText())
|
val root = json.parseToJsonElement(stream.bufferedReader().readText())
|
||||||
(0 until jsonArray.length()).mapNotNull { parseJSON(jsonArray.getJSONObject(it)) }
|
(root as? JsonArray)?.mapNotNull { element ->
|
||||||
|
runCatching { json.decodeFromJsonElement<SatRadio>(element) }
|
||||||
|
.onFailure { println("JSON parsing exception: $it") }
|
||||||
|
.getOrNull()
|
||||||
|
} ?: emptyList()
|
||||||
}.getOrDefault(emptyList())
|
}.getOrDefault(emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,26 +105,6 @@ class DataParser(private val dispatcher: CoroutineDispatcher) {
|
|||||||
)
|
)
|
||||||
}.onFailure { println("TLE parsing exception: $it") }.getOrNull()
|
}.onFailure { println("TLE parsing exception: $it") }.getOrNull()
|
||||||
|
|
||||||
private fun parseJSON(json: JSONObject): SatRadio? = runCatching {
|
|
||||||
SatRadio(
|
|
||||||
uuid = json.getString("uuid"),
|
|
||||||
info = json.getString("description"),
|
|
||||||
isAlive = json.getBoolean("alive"),
|
|
||||||
downlinkLow = json.optLongOrNull("downlink_low"),
|
|
||||||
downlinkHigh = json.optLongOrNull("downlink_high"),
|
|
||||||
downlinkMode = json.optStringOrNull("mode"),
|
|
||||||
uplinkLow = json.optLongOrNull("uplink_low"),
|
|
||||||
uplinkHigh = json.optLongOrNull("uplink_high"),
|
|
||||||
uplinkMode = json.optStringOrNull("uplink_mode"),
|
|
||||||
isInverted = json.getBoolean("invert"),
|
|
||||||
catnum = json.optIntOrNull("norad_cat_id")
|
|
||||||
)
|
|
||||||
}.onFailure { println("JSON parsing exception: $it") }.getOrNull()
|
|
||||||
|
|
||||||
private fun JSONObject.optStringOrNull(key: String): String? = if (isNull(key)) null else getString(key)
|
|
||||||
private fun JSONObject.optLongOrNull(key: String): Long? = if (isNull(key)) null else getLong(key)
|
|
||||||
private fun JSONObject.optIntOrNull(key: String): Int? = if (isNull(key)) null else getInt(key)
|
|
||||||
|
|
||||||
private fun getDayOfYear(year: Int, month: Int, dayOfMonth: Int): Int {
|
private fun getDayOfYear(year: Int, month: Int, dayOfMonth: Int): Int {
|
||||||
val daysInMonth = intArrayOf(31, if (isLeapYear(year)) 29 else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
val daysInMonth = intArrayOf(31, if (isLeapYear(year)) 29 else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||||
return daysInMonth.take(month - 1).sum() + dayOfMonth
|
return daysInMonth.take(month - 1).sum() + dayOfMonth
|
||||||
|
|||||||
+2
-2
@@ -2,9 +2,9 @@ package com.rtbishop.look4sat.core.domain
|
|||||||
|
|
||||||
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.core.domain.utility.TransponderMapper
|
import com.rtbishop.look4sat.core.domain.utility.TransponderMapper
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
class TransponderMapperTest {
|
class TransponderMapperTest {
|
||||||
|
|
||||||
|
|||||||
+40
-7
@@ -1,10 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2026 Arty Bishop and contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package com.rtbishop.look4sat.core.presentation
|
package com.rtbishop.look4sat.core.presentation
|
||||||
|
|
||||||
sealed class Screen(val route: String, val iconResId: Int, val titleResId: Int) {
|
import androidx.navigation3.runtime.NavKey
|
||||||
data object Satellites : Screen("satellites", R.drawable.ic_satellites, R.string.nav_sat)
|
import kotlinx.serialization.Serializable
|
||||||
data object Passes : Screen("passes", R.drawable.ic_passes, R.string.nav_pass)
|
|
||||||
data object Radar : Screen("radar", R.drawable.ic_radar, R.string.nav_radar)
|
@Serializable
|
||||||
data object Map : Screen("map", R.drawable.ic_map, R.string.nav_map)
|
sealed class Screen(val iconResId: Int, val titleResId: Int) : NavKey {
|
||||||
data object Settings : Screen("settings", R.drawable.ic_settings, R.string.nav_prefs)
|
|
||||||
data object RadioControl : Screen("radiocontrol", R.drawable.ic_radios, R.string.nav_radiocontrol)
|
@Serializable
|
||||||
|
data object Satellites : Screen(R.drawable.ic_satellites, R.string.nav_sat)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Passes : Screen(R.drawable.ic_passes, R.string.nav_pass)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Radar(val catNum: Int = 0, val aosTime: Long = 0L) : Screen(R.drawable.ic_radar, R.string.nav_radar)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RadioControl(val catNum: Int = 0, val aosTime: Long = 0L) : Screen(0, 0)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Map : Screen(R.drawable.ic_map, R.string.nav_map)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs)
|
||||||
}
|
}
|
||||||
@@ -60,15 +60,12 @@ import androidx.lifecycle.LifecycleEventObserver
|
|||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalObject
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalObject
|
||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||||
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
||||||
@@ -110,14 +107,16 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|||||||
}
|
}
|
||||||
private val iconCache = LruCache<String, Drawable>(128)
|
private val iconCache = LruCache<String, Drawable>(128)
|
||||||
|
|
||||||
fun NavGraphBuilder.mapDestination() {
|
@Composable
|
||||||
composable(Screen.Map.route) {
|
fun MapDestination() {
|
||||||
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
|
val viewModel = viewModel(
|
||||||
|
modelClass = MapViewModel::class.java,
|
||||||
|
factory = MapViewModel.Factory
|
||||||
|
)
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val mapView = rememberMapViewWithLifecycle()
|
val mapView = rememberMapViewWithLifecycle()
|
||||||
MapScreen(uiState, viewModel::onAction, mapView)
|
MapScreen(uiState, viewModel::onAction, mapView)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView: MapView) {
|
private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView: MapView) {
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import com.rtbishop.look4sat.core.domain.predict.DeepSpaceObject
|
import com.rtbishop.look4sat.core.domain.predict.DeepSpaceObject
|
||||||
import com.rtbishop.look4sat.core.domain.predict.NearEarthObject
|
import com.rtbishop.look4sat.core.domain.predict.NearEarthObject
|
||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalData
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalData
|
||||||
@@ -68,7 +66,6 @@ import com.rtbishop.look4sat.core.presentation.InfoDialog
|
|||||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.ScreenColumn
|
import com.rtbishop.look4sat.core.presentation.ScreenColumn
|
||||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
@@ -79,8 +76,8 @@ import java.util.Date
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
|
||||||
fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) {
|
@Composable
|
||||||
composable(Screen.Passes.route) {
|
fun PassesDestination(navigateToRadar: (Int, Long) -> Unit) {
|
||||||
val viewModel = viewModel(
|
val viewModel = viewModel(
|
||||||
modelClass = PassesViewModel::class.java,
|
modelClass = PassesViewModel::class.java,
|
||||||
factory = PassesViewModel.Factory
|
factory = PassesViewModel.Factory
|
||||||
@@ -88,7 +85,6 @@ fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) {
|
|||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
PassesScreen(uiState, viewModel::onAction, navigateToRadar)
|
PassesScreen(uiState, viewModel::onAction, navigateToRadar)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PassesScreen(
|
private fun PassesScreen(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ 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
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -58,9 +59,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||||
import com.rtbishop.look4sat.core.domain.utility.toDegrees
|
import com.rtbishop.look4sat.core.domain.utility.toDegrees
|
||||||
@@ -69,7 +67,6 @@ import com.rtbishop.look4sat.core.presentation.IconCard
|
|||||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
import com.rtbishop.look4sat.core.presentation.getDefaultPass
|
import com.rtbishop.look4sat.core.presentation.getDefaultPass
|
||||||
@@ -77,21 +74,21 @@ import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
|||||||
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
||||||
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
||||||
|
|
||||||
fun NavGraphBuilder.radarDestination(
|
@Composable
|
||||||
|
fun RadarDestination(
|
||||||
|
catNum: Int = 0,
|
||||||
|
aosTime: Long = 0L,
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
navigateToRadioControl: (Int, Long) -> Unit = { _, _ -> }
|
navigateToRadioControl: (Int, Long) -> Unit = { _, _ -> }
|
||||||
) {
|
) {
|
||||||
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
val viewModel = viewModel(
|
||||||
val radarArgs = listOf(
|
modelClass = RadarViewModel::class.java,
|
||||||
navArgument("catNum") { defaultValue = 0 },
|
key = "$catNum-$aosTime",
|
||||||
navArgument("aosTime") { defaultValue = 0L }
|
factory = RadarViewModel.factory(catNum, aosTime)
|
||||||
)
|
)
|
||||||
composable(radarRoute, radarArgs) {
|
|
||||||
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
RadarScreen(uiState, viewModel::onAction, navigateUp, navigateToRadioControl)
|
RadarScreen(uiState, viewModel::onAction, navigateUp, navigateToRadioControl)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RadarScreen(
|
private fun RadarScreen(
|
||||||
@@ -101,7 +98,7 @@ private fun RadarScreen(
|
|||||||
navigateToRadioControl: (Int, Long) -> Unit
|
navigateToRadioControl: (Int, Long) -> Unit
|
||||||
) {
|
) {
|
||||||
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
||||||
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
|
LaunchedEffect(uiState.isLos) { if (uiState.isLos) navigateUp() }
|
||||||
|
|
||||||
val addToCalendar: () -> Unit = {
|
val addToCalendar: () -> Unit = {
|
||||||
uiState.currentPass?.let { pass ->
|
uiState.currentPass?.let { pass ->
|
||||||
@@ -123,14 +120,14 @@ private fun RadarScreen(
|
|||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isTimeAos)
|
||||||
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
||||||
}
|
}
|
||||||
TopBar { NextPassRow(pass = upcomingPass, isUtc = uiState.isUtc) }
|
TopBar { NextPassRow(pass = upcomingPass, isUtc = uiState.isUtc) }
|
||||||
} else {
|
} else {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isTimeAos)
|
||||||
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc)
|
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc)
|
||||||
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
||||||
}
|
}
|
||||||
@@ -149,8 +146,8 @@ private fun RadarScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RadarCard(uiState: RadarState, modifier: Modifier = Modifier) {
|
private fun RadarCard(uiState: RadarState, modifier: Modifier = Modifier) {
|
||||||
val isEclipsed = uiState.orbitalPos?.eclipsed == true
|
val satellitePos = uiState.orbitalPos
|
||||||
val borderModifier = if (isEclipsed) {
|
val borderModifier = if (satellitePos?.aboveHorizon == true && satellitePos.eclipsed) {
|
||||||
val infiniteTransition = rememberInfiniteTransition(label = "eclipsedBorder")
|
val infiniteTransition = rememberInfiniteTransition(label = "eclipsedBorder")
|
||||||
val borderAlpha by infiniteTransition.animateFloat(
|
val borderAlpha by infiniteTransition.animateFloat(
|
||||||
initialValue = 1.0f,
|
initialValue = 1.0f,
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
|||||||
data class RadarState(
|
data class RadarState(
|
||||||
val currentPass: OrbitalPass? = null,
|
val currentPass: OrbitalPass? = null,
|
||||||
val currentTime: String = "00:00:00",
|
val currentTime: String = "00:00:00",
|
||||||
val isCurrentTimeAos: Boolean = true,
|
val isTimeAos: Boolean = true,
|
||||||
|
val isLos: Boolean = false,
|
||||||
val isUtc: Boolean = false,
|
val isUtc: Boolean = false,
|
||||||
val orientationValues: Pair<Float, Float> = 0f to 0f,
|
val orientationValues: Pair<Float, Float> = 0f to 0f,
|
||||||
val orbitalPos: OrbitalPos? = null,
|
val orbitalPos: OrbitalPos? = null,
|
||||||
|
|||||||
@@ -17,10 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.feature.radar
|
package com.rtbishop.look4sat.feature.radar
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.createSavedStateHandle
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
@@ -45,7 +43,8 @@ import kotlinx.coroutines.isActive
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RadarViewModel(
|
class RadarViewModel(
|
||||||
private val savedStateHandle: SavedStateHandle,
|
private val catNum: Int,
|
||||||
|
private val aosTime: Long,
|
||||||
private val bluetoothReporter: IReporter,
|
private val bluetoothReporter: IReporter,
|
||||||
private val networkReporter: IReporter,
|
private val networkReporter: IReporter,
|
||||||
private val satelliteRepo: ISatelliteRepo,
|
private val satelliteRepo: ISatelliteRepo,
|
||||||
@@ -85,8 +84,6 @@ class RadarViewModel(
|
|||||||
}
|
}
|
||||||
// Resolve which pass we're tracking and start the tick loop
|
// Resolve which pass we're tracking and start the tick loop
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
|
||||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
|
||||||
val passes = satelliteRepo.passes.value
|
val passes = satelliteRepo.passes.value
|
||||||
val currentPass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
|
val currentPass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
|
||||||
?: passes.firstOrNull()
|
?: passes.firstOrNull()
|
||||||
@@ -105,7 +102,8 @@ class RadarViewModel(
|
|||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
|
val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
|
||||||
val (time, isAos) = computeTimer(satPass.isDeepSpace, satPass.aosTime, satPass.losTime, timeNow)
|
val (time, isAos) = computeTimer(satPass.isDeepSpace, satPass.aosTime, satPass.losTime, timeNow)
|
||||||
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = isAos, orbitalPos = pos) }
|
val isLos = !satPass.isDeepSpace && timeNow > satPass.losTime
|
||||||
|
_uiState.update { it.copy(currentTime = time, isTimeAos = isAos, isLos = isLos, orbitalPos = pos) }
|
||||||
processRadios(transmitters, satPass.orbitalObject, timeNow)
|
processRadios(transmitters, satPass.orbitalObject, timeNow)
|
||||||
sendPassData(pos)
|
sendPassData(pos)
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -204,18 +202,19 @@ class RadarViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
fun factory(catNum: Int, aosTime: Long): ViewModelProvider.Factory = viewModelFactory {
|
||||||
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
||||||
initializer {
|
initializer {
|
||||||
val container = (this[applicationKey] as IContainerProvider).getMainContainer()
|
val container = (this[applicationKey] as IContainerProvider).getMainContainer()
|
||||||
RadarViewModel(
|
RadarViewModel(
|
||||||
createSavedStateHandle(),
|
catNum = catNum,
|
||||||
container.provideBluetoothReporter(),
|
aosTime = aosTime,
|
||||||
container.provideNetworkReporter(),
|
bluetoothReporter = container.provideBluetoothReporter(),
|
||||||
container.satelliteRepo,
|
networkReporter = container.provideNetworkReporter(),
|
||||||
container.settingsRepo,
|
satelliteRepo = container.satelliteRepo,
|
||||||
container.provideSensorsRepo(),
|
settingsRepo = container.settingsRepo,
|
||||||
container.provideAddToCalendar()
|
sensorsRepo = container.provideSensorsRepo(),
|
||||||
|
addToCalendar = container.provideAddToCalendar()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-12
@@ -51,15 +51,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.core.presentation.CardButton
|
import com.rtbishop.look4sat.core.presentation.CardButton
|
||||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||||
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
import com.rtbishop.look4sat.core.presentation.getDefaultPass
|
import com.rtbishop.look4sat.core.presentation.getDefaultPass
|
||||||
@@ -67,18 +63,16 @@ import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
|||||||
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
fun NavGraphBuilder.radioControlDestination(navigateUp: () -> Unit) {
|
@Composable
|
||||||
val route = "${Screen.RadioControl.route}?catNum={catNum}&aosTime={aosTime}"
|
fun RadioControlDestination(catNum: Int = 0, aosTime: Long = 0L, navigateUp: () -> Unit) {
|
||||||
val args = listOf(
|
val viewModel = viewModel(
|
||||||
navArgument("catNum") { defaultValue = 0 },
|
modelClass = RadioControlViewModel::class.java,
|
||||||
navArgument("aosTime") { defaultValue = 0L }
|
key = "$catNum-$aosTime",
|
||||||
|
factory = RadioControlViewModel.factory(catNum, aosTime)
|
||||||
)
|
)
|
||||||
composable(route, args) {
|
|
||||||
val viewModel = viewModel(RadioControlViewModel::class.java, factory = RadioControlViewModel.Factory)
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
RadioControlScreen(uiState, viewModel::onAction, navigateUp)
|
RadioControlScreen(uiState, viewModel::onAction, navigateUp)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RadioControlScreen(
|
private fun RadioControlScreen(
|
||||||
|
|||||||
+8
-11
@@ -17,10 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.feature.radiocontrol
|
package com.rtbishop.look4sat.feature.radiocontrol
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.createSavedStateHandle
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
@@ -41,7 +39,8 @@ import kotlinx.coroutines.launch
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class RadioControlViewModel(
|
class RadioControlViewModel(
|
||||||
savedStateHandle: SavedStateHandle,
|
private val catNum: Int,
|
||||||
|
private val aosTime: Long,
|
||||||
private val trackingService: IRadioTrackingService,
|
private val trackingService: IRadioTrackingService,
|
||||||
private val satelliteRepo: ISatelliteRepo,
|
private val satelliteRepo: ISatelliteRepo,
|
||||||
settingsRepo: ISettingsRepo
|
settingsRepo: ISettingsRepo
|
||||||
@@ -72,9 +71,6 @@ class RadioControlViewModel(
|
|||||||
val uiState: StateFlow<RadioControlState> = _uiState
|
val uiState: StateFlow<RadioControlState> = _uiState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
|
||||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
|
||||||
|
|
||||||
// Resolve pass and load transponders
|
// Resolve pass and load transponders
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val passes = satelliteRepo.passes.value
|
val passes = satelliteRepo.passes.value
|
||||||
@@ -190,15 +186,16 @@ class RadioControlViewModel(
|
|||||||
return String.format(Locale.ENGLISH, "%d.%03d.%03d", mhz, khz, hz)
|
return String.format(Locale.ENGLISH, "%d.%03d.%03d", mhz, khz, hz)
|
||||||
}
|
}
|
||||||
|
|
||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
fun factory(catNum: Int, aosTime: Long): ViewModelProvider.Factory = viewModelFactory {
|
||||||
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
||||||
initializer {
|
initializer {
|
||||||
val container = (this[applicationKey] as IContainerProvider).getMainContainer()
|
val container = (this[applicationKey] as IContainerProvider).getMainContainer()
|
||||||
RadioControlViewModel(
|
RadioControlViewModel(
|
||||||
createSavedStateHandle(),
|
catNum = catNum,
|
||||||
container.radioTrackingService,
|
aosTime = aosTime,
|
||||||
container.satelliteRepo,
|
trackingService = container.radioTrackingService,
|
||||||
container.settingsRepo
|
satelliteRepo = container.satelliteRepo,
|
||||||
|
settingsRepo = container.settingsRepo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-7
@@ -56,8 +56,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import com.rtbishop.look4sat.core.domain.model.SatItem
|
import com.rtbishop.look4sat.core.domain.model.SatItem
|
||||||
import com.rtbishop.look4sat.core.presentation.CardLoadingIndicator
|
import com.rtbishop.look4sat.core.presentation.CardLoadingIndicator
|
||||||
import com.rtbishop.look4sat.core.presentation.EmptyListCard
|
import com.rtbishop.look4sat.core.presentation.EmptyListCard
|
||||||
@@ -66,21 +64,20 @@ import com.rtbishop.look4sat.core.presentation.InfoDialog
|
|||||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.core.presentation.PrimaryIconCard
|
import com.rtbishop.look4sat.core.presentation.PrimaryIconCard
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
||||||
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
||||||
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
||||||
|
|
||||||
fun NavGraphBuilder.satellitesDestination(navigateUp: () -> Unit) {
|
@Composable
|
||||||
composable(Screen.Satellites.route) {
|
fun SatellitesDestination(navigateUp: () -> Unit) {
|
||||||
val viewModel = viewModel(
|
val viewModel = viewModel(
|
||||||
modelClass = SatellitesViewModel::class.java, factory = SatellitesViewModel.Factory
|
modelClass = SatellitesViewModel::class.java,
|
||||||
|
factory = SatellitesViewModel.Factory
|
||||||
)
|
)
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
SatellitesScreen(uiState, viewModel::onAction, navigateUp)
|
SatellitesScreen(uiState, viewModel::onAction, navigateUp)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SatellitesScreen(
|
private fun SatellitesScreen(
|
||||||
|
|||||||
+2
-6
@@ -60,8 +60,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import com.rtbishop.look4sat.core.domain.model.OtherSettings
|
import com.rtbishop.look4sat.core.domain.model.OtherSettings
|
||||||
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.core.presentation.CardButton
|
import com.rtbishop.look4sat.core.presentation.CardButton
|
||||||
@@ -69,7 +67,6 @@ import com.rtbishop.look4sat.core.presentation.IconCard
|
|||||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.core.presentation.PrimaryIconCard
|
import com.rtbishop.look4sat.core.presentation.PrimaryIconCard
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
|
||||||
import com.rtbishop.look4sat.core.presentation.ScreenColumn
|
import com.rtbishop.look4sat.core.presentation.ScreenColumn
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
||||||
@@ -78,8 +75,8 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
fun NavGraphBuilder.settingsDestination() {
|
@Composable
|
||||||
composable(Screen.Settings.route) {
|
fun SettingsDestination() {
|
||||||
val viewModel = viewModel(
|
val viewModel = viewModel(
|
||||||
modelClass = SettingsViewModel::class.java,
|
modelClass = SettingsViewModel::class.java,
|
||||||
factory = SettingsViewModel.Factory
|
factory = SettingsViewModel.Factory
|
||||||
@@ -87,7 +84,6 @@ fun NavGraphBuilder.settingsDestination() {
|
|||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
SettingsScreen(uiState, viewModel::onAction)
|
SettingsScreen(uiState, viewModel::onAction)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) -> Unit) {
|
private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) -> Unit) {
|
||||||
|
|||||||
+24
-25
@@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionCode = "421"
|
appVersionCode = "422"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionName = "4.2.1"
|
appVersionName = "4.2.2"
|
||||||
#noinspection GradleDependency,UnusedVersionCatalogEntry
|
#noinspection GradleDependency,UnusedVersionCatalogEntry
|
||||||
compileSdk = "36"
|
compileSdk = "36"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
@@ -12,21 +12,23 @@ jdkVersion = "17"
|
|||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
packageName = "com.rtbishop.look4sat"
|
packageName = "com.rtbishop.look4sat"
|
||||||
|
|
||||||
android-gradle-plugin = "9.1.1"
|
android-gradle-plugin = "9.2.0"
|
||||||
google-ksp = "2.3.6"
|
|
||||||
kotlin = "2.3.20"
|
|
||||||
|
|
||||||
androidx-core-ktx = "1.18.0"
|
androidx-core-ktx = "1.18.0"
|
||||||
androidx-core-splashscreen = "1.2.0"
|
androidx-core-splashscreen = "1.2.0"
|
||||||
androidx-room = "2.8.4"
|
androidx-room = "2.8.4"
|
||||||
|
|
||||||
compose-bom = "2026.03.01"
|
compose-bom = "2026.04.01"
|
||||||
compose-activity = "1.13.0"
|
compose-activity = "1.13.0"
|
||||||
compose-lifecycle = "2.10.0"
|
compose-lifecycle = "2.10.0"
|
||||||
compose-navigation = "2.9.7"
|
compose-navigation3 = "1.1.1"
|
||||||
|
|
||||||
|
google-ksp = "2.3.6"
|
||||||
|
|
||||||
|
kotlin = "2.3.20"
|
||||||
|
kotlin-coroutines = "1.10.2"
|
||||||
|
kotlin-serialization = "1.11.0"
|
||||||
|
|
||||||
other-coroutines = "1.10.2"
|
|
||||||
other-json = "20251224"
|
|
||||||
other-okhttp = "5.3.2"
|
other-okhttp = "5.3.2"
|
||||||
other-osmdroid = "6.1.20"
|
other-osmdroid = "6.1.20"
|
||||||
|
|
||||||
@@ -50,32 +52,32 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
|
|||||||
|
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
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" }
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
compose-material3-adaptive = { 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-preview" }
|
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" }
|
#noinspection UnusedVersionCatalogEntry
|
||||||
compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "compose-navigation" }
|
compose-navigation3 = { module = "androidx.navigation3:navigation3-ui", version.ref = "compose-navigation3" }
|
||||||
compose-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "compose-lifecycle" }
|
compose-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "compose-lifecycle" }
|
||||||
|
|
||||||
compose-debug-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
compose-debug-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
compose-debug-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
compose-debug-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
|
|
||||||
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
|
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization" }
|
||||||
|
|
||||||
#noinspection UnusedVersionCatalogEntry
|
|
||||||
other-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "other-coroutines" }
|
|
||||||
#noinspection UnusedVersionCatalogEntry
|
|
||||||
other-json = { module = "org.json:json", version.ref = "other-json" }
|
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
other-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "other-okhttp" }
|
other-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "other-okhttp" }
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
other-osmdroid = { module = "org.osmdroid:osmdroid-android", version.ref = "other-osmdroid" }
|
other-osmdroid = { module = "org.osmdroid:osmdroid-android", version.ref = "other-osmdroid" }
|
||||||
|
|
||||||
test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "other-coroutines" }
|
test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
|
||||||
test-junit4 = { module = "junit:junit", version.ref = "test-junit4" }
|
test-junit4 = { module = "junit:junit", version.ref = "test-junit4" }
|
||||||
|
|
||||||
androidTest-junit = { module = "androidx.test.ext:junit", version.ref = "androidTest-junit" }
|
androidTest-junit = { module = "androidx.test.ext:junit", version.ref = "androidTest-junit" }
|
||||||
@@ -87,6 +89,7 @@ android-library = { id = "com.android.library", version.ref = "android-gradle-pl
|
|||||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" }
|
google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" }
|
||||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
# plugins defined by this project
|
# plugins defined by this project
|
||||||
convention-applicationPlugin = { id = "com.rtbishop.look4sat.convention.applicationPlugin" }
|
convention-applicationPlugin = { id = "com.rtbishop.look4sat.convention.applicationPlugin" }
|
||||||
convention-coreDataPlugin = { id = "com.rtbishop.look4sat.convention.coreDataPlugin" }
|
convention-coreDataPlugin = { id = "com.rtbishop.look4sat.convention.coreDataPlugin" }
|
||||||
@@ -96,11 +99,7 @@ convention-featurePlugin = { id = "com.rtbishop.look4sat.convention.featurePlugi
|
|||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
composeAll = [
|
composeAll = ["compose-runtime", "compose-tooling", "compose-activity", "compose-material3", "compose-viewmodel"]
|
||||||
"compose-animation", "compose-runtime", "compose-tooling",
|
|
||||||
"compose-activity", "compose-lifecycle", "compose-material3",
|
|
||||||
"compose-material3-navigation", "compose-navigation", "compose-viewmodel"
|
|
||||||
]
|
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]
|
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
|||||||
Reference in new issue
Block a user