diff --git a/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt b/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt
index 787ccedd..23a972fc 100644
--- a/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt
@@ -16,6 +16,7 @@ import com.rtbishop.look4sat.data.repository.SensorsRepo
import com.rtbishop.look4sat.data.repository.SettingsRepo
import com.rtbishop.look4sat.data.source.LocalSource
import com.rtbishop.look4sat.data.source.RemoteSource
+import com.rtbishop.look4sat.data.usecase.AddToCalendar
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
@@ -23,6 +24,7 @@ import com.rtbishop.look4sat.domain.repository.ISensorsRepo
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.domain.source.ILocalSource
import com.rtbishop.look4sat.domain.source.IRemoteSource
+import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
import com.rtbishop.look4sat.domain.utility.DataParser
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
@@ -41,6 +43,10 @@ class MainContainer(private val context: Context) {
val satelliteRepo = provideSatelliteRepo()
val databaseRepo = provideDatabaseRepo()
+ fun provideAddToCalendar(): IAddToCalendar {
+ return AddToCalendar(context)
+ }
+
fun provideBluetoothReporter(): BluetoothReporter {
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt
index 873c4afa..7fe956a6 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt
@@ -16,7 +16,6 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
-import androidx.navigation.navArgument
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.info.infoDestination
import com.rtbishop.look4sat.presentation.map.mapDestination
@@ -42,14 +41,9 @@ fun MainScreen() {
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
outerNavController.navigate(routeWithParams)
}
- val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
- val radarArgs = listOf(
- navArgument("catNum") { defaultValue = 0 },
- navArgument("aosTime") { defaultValue = 0L }
- )
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
mainDestination(navigateToRadar)
- radarDestination(radarRoute, radarArgs)
+ radarDestination { outerNavController.navigateUp() }
}
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/components/Common.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/components/Common.kt
index 3cb976da..646754f9 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/components/Common.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/components/Common.kt
@@ -80,14 +80,14 @@ fun RowScope.TimerBar(timeString: String, isTimeAos: Boolean) {
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxSize()
) {
- Text(text = "AOS", fontSize = 16.sp, color = aosColor)
+ Text(text = "AOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = aosColor)
Text(
text = timeString,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
- Text(text = "LOS", fontSize = 16.sp, color = losColor)
+ Text(text = "LOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = losColor)
}
}
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt
index 1504f06f..07c5aec1 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt
@@ -34,6 +34,11 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.OrbitalObject
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.presentation.Screen
+import com.rtbishop.look4sat.presentation.components.CardIcon
+import com.rtbishop.look4sat.presentation.components.NextPassRow
+import com.rtbishop.look4sat.presentation.components.TimerBar
+import com.rtbishop.look4sat.presentation.components.TimerRow
+import com.rtbishop.look4sat.presentation.components.getDefaultPass
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
@@ -78,6 +83,12 @@ private fun MapScreen() {
val footprint = viewModel.footprint.collectAsState(initial = null)
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
+ TimerRow {
+ CardIcon(onClick = {}, iconId = R.drawable.ic_filter)
+ TimerBar(timeString = "88:88:88", isTimeAos = true)
+ CardIcon(onClick = {}, iconId = R.drawable.ic_satellite)
+ }
+ NextPassRow(pass = getDefaultPass())
ElevatedCard(modifier = Modifier.fillMaxSize()) {
MapView(modifier = Modifier.fillMaxSize(), isLightTheme = viewModel.stateOfLightTheme) { mapView ->
stationPos.value?.let { setStationPosition(it, mapView) }
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarData.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarData.kt
deleted file mode 100644
index 72ad66e3..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarData.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Look4Sat. Amateur radio satellite tracker and pass predictor.
- * Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
- *
- * 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 .
- */
-package com.rtbishop.look4sat.presentation.radar
-
-import com.rtbishop.look4sat.domain.predict.OrbitalPass
-import com.rtbishop.look4sat.domain.predict.OrbitalPos
-
-data class RadarData(
- val orbitalPass: OrbitalPass,
- val orbitalPos: OrbitalPos,
- val satTrack: List = emptyList()
-)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt
index 47d3c49f..a3fb774b 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt
@@ -1,9 +1,13 @@
package com.rtbishop.look4sat.presentation.radar
+import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.background
+import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -16,57 +20,77 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NamedNavArgument
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
+import androidx.navigation.navArgument
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.SatRadio
+import com.rtbishop.look4sat.presentation.MainTheme
+import com.rtbishop.look4sat.presentation.Screen
+import com.rtbishop.look4sat.presentation.components.CardIcon
+import com.rtbishop.look4sat.presentation.components.NextPassRow
+import com.rtbishop.look4sat.presentation.components.TimerBar
+import com.rtbishop.look4sat.presentation.components.TimerRow
+import com.rtbishop.look4sat.presentation.components.getDefaultPass
-fun NavGraphBuilder.radarDestination(radarRoute: String, radarArgs: List) {
- composable(radarRoute, radarArgs) { RadarScreen() }
+fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
+ val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
+ val radarArgs = listOf(
+ navArgument("catNum") { defaultValue = 0 },
+ navArgument("aosTime") { defaultValue = 0L }
+ )
+ composable(radarRoute, radarArgs) {
+ val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
+ val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
+ RadarScreen(uiState, navigateBack)
+ }
}
@Composable
-private fun RadarScreen() {
- val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
- val currentPass = viewModel.getPass().collectAsState(null).value
- val id = currentPass?.catNum ?: 99999
- val name = currentPass?.name ?: "Satellite"
+private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
+ val addToCalendar: () -> Unit = {
+ uiState.currentPass?.let { pass ->
+ uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
+ }
+ }
Scaffold { innerPadding ->
+ val paddingMod = Modifier.padding(innerPadding)
Column(
- modifier = Modifier.padding(innerPadding),
+ modifier = paddingMod.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
-// TimerBarNew(id, name, "88:88:88", R.drawable.ic_notifications) {}
- ElevatedCard(
- modifier = Modifier
- .fillMaxSize()
- .weight(1f)
- ) {
- viewModel.radarData.value?.let { data ->
+ TimerRow {
+ CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
+ TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
+ CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
+ }
+ NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
+ ElevatedCard(modifier = Modifier.aspectRatio(1f)) {
+ uiState.orbitalPos?.let { item ->
RadarViewCompose(
- item = data.orbitalPos,
- items = data.satTrack,
- azimElev = viewModel.orientation.value
+ item = item,
+ items = uiState.satTrack,
+ azimElev = uiState.orientationValues,
+ shouldShowSweep = uiState.shouldShowSweep,
+ shouldUseCompass = uiState.shouldUseCompass
)
}
}
- ElevatedCard(
- modifier = Modifier
- .fillMaxSize()
- .weight(1f)
- ) {
- TransmittersList(transmitters = viewModel.transmitters.value)
+ ElevatedCard(modifier = Modifier.fillMaxSize()) {
+ TransmittersList(transmitters = uiState.transmitters)
}
}
}
@@ -81,6 +105,16 @@ private fun TransmittersList(transmitters: List) {
}
}
+@Preview
+@Composable
+private fun TransmitterItemPreview() {
+ val transmitter = SatRadio(
+ "", "Extremely powerful transmitter", true, 10000000000L, 10000000000L,
+ null, 10000000000L, 10000000000L, "FSK AX.100 Mode 5", true, 0
+ )
+ MainTheme { TransmitterItem(transmitter) }
+}
+
@Composable
private fun TransmitterItem(radio: SatRadio) {
Surface(color = MaterialTheme.colorScheme.background) {
@@ -96,55 +130,80 @@ private fun TransmitterItem(radio: SatRadio) {
modifier = Modifier.fillMaxWidth()
) {
Icon(
- painter = painterResource(id = R.drawable.ic_arrow),
- contentDescription = null, modifier = Modifier.rotate(180f)
+ painter = painterResource(id = R.drawable.ic_back),
+ tint = Color.Green,
+ contentDescription = null, modifier = Modifier.rotate(-90f)
)
Text(
- text = radio.info,
- modifier = Modifier.weight(1f),
- textAlign = TextAlign.Center
+ text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ",
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .basicMarquee(
+ iterations = Int.MAX_VALUE,
+ spacing = MarqueeSpacing(0.dp)
+ )
+ .weight(1f)
)
Icon(
- painter = painterResource(id = R.drawable.ic_arrow),
- contentDescription = null
- )
- }
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(
- text = stringResource(id = R.string.radio_downlink, radio.downlinkLow ?: 0L),
- textAlign = TextAlign.Center,
- fontSize = 15.sp,
- modifier = Modifier.weight(0.5f)
- )
- Text(
- text = stringResource(id = R.string.radio_uplink, radio.uplinkLow ?: 0L),
- textAlign = TextAlign.Center,
- fontSize = 15.sp,
- modifier = Modifier.weight(0.5f)
- )
- }
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(
- text = radio.downlinkMode ?: "",
- fontSize = 15.sp
- )
- Text(
- text = radio.isInverted.toString(),
- fontSize = 15.sp
+ painter = painterResource(id = R.drawable.ic_back),
+ tint = Color.Red,
+ contentDescription = null, modifier = Modifier.rotate(90f)
)
}
+ FrequencyRow(satRadio = radio)
}
}
}
}
+@Composable
+private fun FrequencyRow(satRadio: SatRadio) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
+ ModeText(satRadio.downlinkMode)
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ modifier = Modifier.weight(0.35f)
+ ) {
+ FrequencyText(satRadio.downlinkHigh)
+ FrequencyText(satRadio.downlinkLow)
+ }
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ modifier = Modifier.weight(0.35f)
+ ) {
+ FrequencyText(satRadio.uplinkHigh)
+ FrequencyText(satRadio.uplinkLow)
+ }
+ ModeText(satRadio.uplinkMode)
+ }
+}
+
+@Composable
+private fun RowScope.ModeText(mode: String?) {
+ Text(
+ text = mode ?: "- - : - -",
+ textAlign = TextAlign.Center,
+ fontSize = 15.sp,
+ modifier = Modifier.weight(0.15f)
+ )
+}
+
+@Composable
+private fun FrequencyText(frequency: Long?) {
+ val noLinkText = stringResource(R.string.radio_no_link)
+ val freqValue = frequency?.let { it / 1000000f }
+ val freqText = freqValue?.let { stringResource(id = R.string.radio_link_low, it) } ?: noLinkText
+ Text(
+ text = freqText,
+ textAlign = TextAlign.Center,
+ fontSize = 21.sp,
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.primary
+ )
+}
+
//private val divider = 1000000f
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarState.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarState.kt
new file mode 100644
index 00000000..325895ce
--- /dev/null
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarState.kt
@@ -0,0 +1,22 @@
+package com.rtbishop.look4sat.presentation.radar
+
+import com.rtbishop.look4sat.domain.model.SatRadio
+import com.rtbishop.look4sat.domain.predict.OrbitalPass
+import com.rtbishop.look4sat.domain.predict.OrbitalPos
+
+data class RadarState(
+ val currentPass: OrbitalPass?,
+ val currentTime: String,
+ val isCurrentTimeAos: Boolean,
+ val orientationValues: Pair,
+ val orbitalPos: OrbitalPos?,
+ val satTrack: List,
+ val shouldShowSweep: Boolean,
+ val shouldUseCompass: Boolean,
+ val transmitters: List,
+ val sendAction: (RadarAction) -> Unit
+)
+
+sealed class RadarAction {
+ data class AddToCalendar(val name: String, val aosTime: Long, val losTime: Long) : RadarAction()
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt
index 8be34fd6..cfdd72ee 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarView.kt
@@ -1,6 +1,5 @@
package com.rtbishop.look4sat.presentation.radar
-import android.media.AudioAttributes
import android.media.SoundPool
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
@@ -10,8 +9,6 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@@ -37,20 +34,20 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.sp
-import androidx.core.view.HapticFeedbackConstantsCompat
-import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.domain.predict.PI_2
import com.rtbishop.look4sat.domain.utility.toRadians
-import kotlin.math.abs
import kotlin.math.cos
-import kotlin.math.min
-import kotlin.math.pow
import kotlin.math.sin
-import kotlin.math.sqrt
@Composable
-fun RadarViewCompose(item: OrbitalPos, items: List, azimElev: Pair) {
+fun RadarViewCompose(
+ item: OrbitalPos,
+ items: List,
+ azimElev: Pair,
+ shouldShowSweep: Boolean,
+ shouldUseCompass: Boolean
+) {
val context = LocalContext.current
val view = LocalView.current
val radarColor = MaterialTheme.colorScheme.secondary
@@ -138,8 +135,8 @@ fun RadarViewCompose(item: OrbitalPos, items: List, azimElev: Pair>(emptyList())
- val radarData = mutableStateOf(null)
- val orientation = mutableStateOf(sensorsRepo.orientation.value)
+ private val magDeclination = sensorsRepo.getMagDeclination(stationPos)
+ val uiState: StateFlow = _uiState
init {
if (settingsRepo.otherSettings.value.stateOfSensors) {
viewModelScope.launch {
sensorsRepo.enableSensor()
sensorsRepo.orientation.collect { data ->
- orientation.value = Pair(data.first + magDeclination, data.second)
+ val orientationValues = Pair(data.first + magDeclination, data.second)
+ _uiState.update { it.copy(orientationValues = orientationValues) }
+ }
+ }
+ }
+ viewModelScope.launch {
+ val catNum = savedStateHandle.get("catNum") ?: 0
+ val aosTime = savedStateHandle.get("aosTime") ?: 0L
+ val passes = satelliteRepo.passes.value
+ val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
+ val currentPass = pass ?: passes.firstOrNull()
+ currentPass?.let { satPass ->
+ _uiState.update { it.copy(currentPass = satPass) }
+ val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
+ while (isActive) {
+ val timeNow = System.currentTimeMillis()
+ val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
+ if (satPass.aosTime > timeNow) {
+ val time = satPass.aosTime.minus(timeNow).toTimerString()
+ _uiState.update { it.copy(currentTime = time, isCurrentTimeAos = true) }
+ } else {
+ val time = satPass.losTime.minus(timeNow).toTimerString()
+ _uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false) }
+ }
+ sendPassData(satPass, pos, satPass.orbitalObject)
+ sendPassDataBT(pos)
+ processRadios(transmitters, satPass.orbitalObject, timeNow)
+ delay(1000)
}
}
}
@@ -77,56 +116,29 @@ class RadarViewModel(
super.onCleared()
}
- fun getPass() = flow {
- val catNum = savedStateHandle.get("catNum") ?: 0
- val aosTime = savedStateHandle.get("aosTime") ?: 0L
- val passes = satelliteRepo.passes.value
- val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
- val currentPass = pass ?: passes.firstOrNull()
- currentPass?.let { satPass ->
- emit(satPass)
- val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
- viewModelScope.launch {
- while (isActive) {
- val timeNow = System.currentTimeMillis()
- val satPos =
- satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
- sendPassData(satPass, satPos, satPass.orbitalObject)
- sendPassDataBT(satPos)
- processRadios(transmitters, satPass.orbitalObject, timeNow)
- delay(1000)
- }
- }
+ private fun handleAction(action: RadarAction) {
+ when (action) {
+ is RadarAction.AddToCalendar -> addToCalendar(action.name, action.aosTime, action.losTime)
}
}
-// fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
-//
-// fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
-
- private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
- val latitude = geoPos.latitude.toFloat()
- val longitude = geoPos.longitude.toFloat()
- return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
- }
-
- private fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
- viewModelScope.launch {
- var track: List = emptyList()
- if (!orbitalPass.isDeepSpace) {
- track = satelliteRepo.getTrack(
- orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
- )
- }
- if (settingsRepo.getRotatorState()) {
- val server = settingsRepo.getRotatorAddress()
- val port = settingsRepo.getRotatorPort().toInt()
- val azimuth = orbitalPos.azimuth.toDegrees().round(2)
- val elevation = orbitalPos.elevation.toDegrees().round(2)
- networkReporter.reportRotation(server, port, azimuth, elevation)
- }
- radarData.value = RadarData(orbitalPass, orbitalPos, track)
+ private suspend fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
+ var track: List = emptyList()
+ if (!orbitalPass.isDeepSpace) {
+ track = satelliteRepo.getTrack(
+ orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
+ )
}
+ _uiState.update { it.copy(orbitalPos = orbitalPos, satTrack = track) }
+// viewModelScope.launch {
+// if (settingsRepo.getRotatorState()) {
+// val server = settingsRepo.getRotatorAddress()
+// val port = settingsRepo.getRotatorPort().toInt()
+// val azimuth = orbitalPos.azimuth.toDegrees().round(2)
+// val elevation = orbitalPos.elevation.toDegrees().round(2)
+// networkReporter.reportRotation(server, port, azimuth, elevation)
+// }
+// }
}
private fun sendPassDataBT(orbitalPos: OrbitalPos) {
@@ -139,19 +151,16 @@ class RadarViewModel(
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt()
bluetoothReporter.reportRotation(format, azimuth, elevation)
} else if (!bluetoothReporter.isConnecting()) {
- Log.i("BTReporter", "BTReporter: Attempting to connect...")
+// Log.i("BTReporter", "BTReporter: Attempting to connect...")
bluetoothReporter.connectBTDevice(btDevice)
}
}
}
}
- private fun processRadios(radios: List, orbitalObject: OrbitalObject, time: Long) {
- viewModelScope.launch {
- delay(125)
- val list = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
- transmitters.value = list
- }
+ private suspend fun processRadios(radios: List, orbitalObject: OrbitalObject, time: Long) {
+ val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
+ _uiState.update { it.copy(transmitters = transmitters) }
}
companion object {
@@ -165,7 +174,8 @@ class RadarViewModel(
container.provideNetworkReporter(),
container.satelliteRepo,
container.settingsRepo,
- container.provideSensorsRepo()
+ container.provideSensorsRepo(),
+ container.provideAddToCalendar()
)
}
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt
index 44903f9f..7775e781 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt
@@ -178,7 +178,7 @@ private fun MiddleBar(
private fun TypeCard(types: List, onClick: () -> Unit, modifier: Modifier = Modifier) {
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
ElevatedCard(modifier = modifier) {
- Row(horizontalArrangement = Arrangement.Start,
+ Row(horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(48.dp)
@@ -194,9 +194,8 @@ private fun TypeCard(types: List, onClick: () -> Unit, modifier: Modifie
text = "Types: $typesText",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
- modifier = modifier
- .basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(0.dp))
- .padding(start = 12.dp, end = 6.dp, bottom = 2.dp)
+ modifier = Modifier
+ .basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(16.dp))
)
}
}
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 00000000..805a9e6a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/repository/SensorsRepo.kt b/data/src/main/java/com/rtbishop/look4sat/data/repository/SensorsRepo.kt
index df56eb82..74d2eed3 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/repository/SensorsRepo.kt
+++ b/data/src/main/java/com/rtbishop/look4sat/data/repository/SensorsRepo.kt
@@ -17,15 +17,17 @@
*/
package com.rtbishop.look4sat.data.repository
+import android.hardware.GeomagneticField
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
+import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.RAD2DEG
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
-import kotlin.math.round
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlin.math.round
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
SensorEventListener, ISensorsRepo {
@@ -37,6 +39,12 @@ class SensorsRepo(private val sensorManager: SensorManager, private val sensor:
override val orientation: StateFlow> = _orientation
+ override fun getMagDeclination(geoPos: GeoPos, time: Long): Float {
+ val latitude = geoPos.latitude.toFloat()
+ val longitude = geoPos.longitude.toFloat()
+ return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
+ }
+
override fun enableSensor() {
sensor?.let { sensorManager.registerListener(this, it, 8000) }
}
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/usecase/AddToCalendar.kt b/data/src/main/java/com/rtbishop/look4sat/data/usecase/AddToCalendar.kt
new file mode 100644
index 00000000..1a05fdb7
--- /dev/null
+++ b/data/src/main/java/com/rtbishop/look4sat/data/usecase/AddToCalendar.kt
@@ -0,0 +1,24 @@
+package com.rtbishop.look4sat.data.usecase
+
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.provider.CalendarContract
+import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
+
+class AddToCalendar(private val context: Context) : IAddToCalendar {
+ override fun invoke(name: String, aosTime: Long, losTime: Long) {
+ val intent = Intent(Intent.ACTION_INSERT).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ setData(CalendarContract.Events.CONTENT_URI)
+ putExtra(CalendarContract.Events.TITLE, name)
+ putExtra(CalendarContract.Events.DESCRIPTION, "Look4Sat Pass")
+ putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, aosTime)
+ putExtra(CalendarContract.EXTRA_EVENT_END_TIME, losTime)
+ }
+ try {
+ context.startActivity(intent)
+ } catch (_: ActivityNotFoundException) {
+ }
+ }
+}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISensorsRepo.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISensorsRepo.kt
index 5167630f..aaff74a6 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISensorsRepo.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISensorsRepo.kt
@@ -1,9 +1,11 @@
package com.rtbishop.look4sat.domain.repository
+import com.rtbishop.look4sat.domain.predict.GeoPos
import kotlinx.coroutines.flow.StateFlow
interface ISensorsRepo {
val orientation: StateFlow>
+ fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float
fun enableSensor()
fun disableSensor()
}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/usecase/IAddToCalendar.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/usecase/IAddToCalendar.kt
new file mode 100644
index 00000000..a73c391d
--- /dev/null
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/usecase/IAddToCalendar.kt
@@ -0,0 +1,5 @@
+package com.rtbishop.look4sat.domain.usecase
+
+interface IAddToCalendar {
+ operator fun invoke(name: String, aosTime: Long, losTime: Long)
+}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt
index 0bd63eac..f68cadbd 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt
@@ -17,6 +17,7 @@
*/
package com.rtbishop.look4sat.domain.utility
+import java.util.Locale
import java.util.concurrent.TimeUnit
fun Long.toTimerString(): String {
@@ -24,7 +25,7 @@ fun Long.toTimerString(): String {
val hours = TimeUnit.MILLISECONDS.toHours(this)
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60
val seconds = TimeUnit.MILLISECONDS.toSeconds(this) % 60
- return String.format(format, hours, minutes, seconds)
+ return String.format(Locale.ENGLISH, format, hours, minutes, seconds)
}
fun Float.round(decimals: Int): Float {