kopia lustrzana https://github.com/rt-bishop/Look4Sat
Added multiple fixes to RadarScreen and components
rodzic
be82b19b37
commit
9f771472c2
|
@ -16,6 +16,7 @@ import com.rtbishop.look4sat.data.repository.SensorsRepo
|
||||||
import com.rtbishop.look4sat.data.repository.SettingsRepo
|
import com.rtbishop.look4sat.data.repository.SettingsRepo
|
||||||
import com.rtbishop.look4sat.data.source.LocalSource
|
import com.rtbishop.look4sat.data.source.LocalSource
|
||||||
import com.rtbishop.look4sat.data.source.RemoteSource
|
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.IDatabaseRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
|
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.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||||
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
||||||
|
import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
|
||||||
import com.rtbishop.look4sat.domain.utility.DataParser
|
import com.rtbishop.look4sat.domain.utility.DataParser
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -41,6 +43,10 @@ class MainContainer(private val context: Context) {
|
||||||
val satelliteRepo = provideSatelliteRepo()
|
val satelliteRepo = provideSatelliteRepo()
|
||||||
val databaseRepo = provideDatabaseRepo()
|
val databaseRepo = provideDatabaseRepo()
|
||||||
|
|
||||||
|
fun provideAddToCalendar(): IAddToCalendar {
|
||||||
|
return AddToCalendar(context)
|
||||||
|
}
|
||||||
|
|
||||||
fun provideBluetoothReporter(): BluetoothReporter {
|
fun provideBluetoothReporter(): BluetoothReporter {
|
||||||
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||||
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
|
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
|
||||||
|
|
|
@ -16,7 +16,6 @@ import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.presentation.info.infoDestination
|
import com.rtbishop.look4sat.presentation.info.infoDestination
|
||||||
import com.rtbishop.look4sat.presentation.map.mapDestination
|
import com.rtbishop.look4sat.presentation.map.mapDestination
|
||||||
|
@ -42,14 +41,9 @@ fun MainScreen() {
|
||||||
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||||
outerNavController.navigate(routeWithParams)
|
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) {
|
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
|
||||||
mainDestination(navigateToRadar)
|
mainDestination(navigateToRadar)
|
||||||
radarDestination(radarRoute, radarArgs)
|
radarDestination { outerNavController.navigateUp() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,14 +80,14 @@ fun RowScope.TimerBar(timeString: String, isTimeAos: Boolean) {
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Text(text = "AOS", fontSize = 16.sp, color = aosColor)
|
Text(text = "AOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = aosColor)
|
||||||
Text(
|
Text(
|
||||||
text = timeString,
|
text = timeString,
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Text(text = "LOS", fontSize = 16.sp, color = losColor)
|
Text(text = "LOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = losColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,11 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||||
import com.rtbishop.look4sat.presentation.Screen
|
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.tileprovider.tilesource.XYTileSource
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
import org.osmdroid.views.CustomZoomButtonsController
|
import org.osmdroid.views.CustomZoomButtonsController
|
||||||
|
@ -78,6 +83,12 @@ private fun MapScreen() {
|
||||||
val footprint = viewModel.footprint.collectAsState(initial = null)
|
val footprint = viewModel.footprint.collectAsState(initial = null)
|
||||||
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
|
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
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()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
MapView(modifier = Modifier.fillMaxSize(), isLightTheme = viewModel.stateOfLightTheme) { mapView ->
|
MapView(modifier = Modifier.fillMaxSize(), isLightTheme = viewModel.stateOfLightTheme) { mapView ->
|
||||||
stationPos.value?.let { setStationPosition(it, mapView) }
|
stationPos.value?.let { setStationPosition(it, mapView) }
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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<OrbitalPos> = emptyList()
|
|
||||||
)
|
|
|
@ -1,9 +1,13 @@
|
||||||
package com.rtbishop.look4sat.presentation.radar
|
package com.rtbishop.look4sat.presentation.radar
|
||||||
|
|
||||||
|
import androidx.compose.foundation.MarqueeSpacing
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.basicMarquee
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
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.RowScope
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -16,57 +20,77 @@ import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NamedNavArgument
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.navArgument
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
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<NamedNavArgument>) {
|
fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
|
||||||
composable(radarRoute, radarArgs) { RadarScreen() }
|
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
|
@Composable
|
||||||
private fun RadarScreen() {
|
private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
|
||||||
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
val addToCalendar: () -> Unit = {
|
||||||
val currentPass = viewModel.getPass().collectAsState(null).value
|
uiState.currentPass?.let { pass ->
|
||||||
val id = currentPass?.catNum ?: 99999
|
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
|
||||||
val name = currentPass?.name ?: "Satellite"
|
}
|
||||||
|
}
|
||||||
Scaffold { innerPadding ->
|
Scaffold { innerPadding ->
|
||||||
|
val paddingMod = Modifier.padding(innerPadding)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = paddingMod.padding(6.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
) {
|
) {
|
||||||
// TimerBarNew(id, name, "88:88:88", R.drawable.ic_notifications) {}
|
TimerRow {
|
||||||
ElevatedCard(
|
CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
|
||||||
modifier = Modifier
|
TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
.fillMaxSize()
|
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
||||||
.weight(1f)
|
}
|
||||||
) {
|
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
|
||||||
viewModel.radarData.value?.let { data ->
|
ElevatedCard(modifier = Modifier.aspectRatio(1f)) {
|
||||||
|
uiState.orbitalPos?.let { item ->
|
||||||
RadarViewCompose(
|
RadarViewCompose(
|
||||||
item = data.orbitalPos,
|
item = item,
|
||||||
items = data.satTrack,
|
items = uiState.satTrack,
|
||||||
azimElev = viewModel.orientation.value
|
azimElev = uiState.orientationValues,
|
||||||
|
shouldShowSweep = uiState.shouldShowSweep,
|
||||||
|
shouldUseCompass = uiState.shouldUseCompass
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElevatedCard(
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
modifier = Modifier
|
TransmittersList(transmitters = uiState.transmitters)
|
||||||
.fillMaxSize()
|
|
||||||
.weight(1f)
|
|
||||||
) {
|
|
||||||
TransmittersList(transmitters = viewModel.transmitters.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +105,16 @@ private fun TransmittersList(transmitters: List<SatRadio>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Composable
|
||||||
private fun TransmitterItem(radio: SatRadio) {
|
private fun TransmitterItem(radio: SatRadio) {
|
||||||
Surface(color = MaterialTheme.colorScheme.background) {
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
|
@ -96,55 +130,80 @@ private fun TransmitterItem(radio: SatRadio) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_arrow),
|
painter = painterResource(id = R.drawable.ic_back),
|
||||||
contentDescription = null, modifier = Modifier.rotate(180f)
|
tint = Color.Green,
|
||||||
|
contentDescription = null, modifier = Modifier.rotate(-90f)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = radio.info,
|
text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ",
|
||||||
modifier = Modifier.weight(1f),
|
textAlign = TextAlign.Center,
|
||||||
textAlign = TextAlign.Center
|
modifier = Modifier
|
||||||
|
.basicMarquee(
|
||||||
|
iterations = Int.MAX_VALUE,
|
||||||
|
spacing = MarqueeSpacing(0.dp)
|
||||||
|
)
|
||||||
|
.weight(1f)
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_arrow),
|
painter = painterResource(id = R.drawable.ic_back),
|
||||||
contentDescription = null
|
tint = Color.Red,
|
||||||
)
|
contentDescription = null, modifier = Modifier.rotate(90f)
|
||||||
}
|
|
||||||
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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
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
|
//private val divider = 1000000f
|
||||||
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
|
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
|
||||||
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
|
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
|
||||||
|
|
|
@ -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<Float, Float>,
|
||||||
|
val orbitalPos: OrbitalPos?,
|
||||||
|
val satTrack: List<OrbitalPos>,
|
||||||
|
val shouldShowSweep: Boolean,
|
||||||
|
val shouldUseCompass: Boolean,
|
||||||
|
val transmitters: List<SatRadio>,
|
||||||
|
val sendAction: (RadarAction) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class RadarAction {
|
||||||
|
data class AddToCalendar(val name: String, val aosTime: Long, val losTime: Long) : RadarAction()
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package com.rtbishop.look4sat.presentation.radar
|
package com.rtbishop.look4sat.presentation.radar
|
||||||
|
|
||||||
import android.media.AudioAttributes
|
|
||||||
import android.media.SoundPool
|
import android.media.SoundPool
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
@ -10,8 +9,6 @@ import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.drawText
|
||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.unit.sp
|
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.OrbitalPos
|
||||||
import com.rtbishop.look4sat.domain.predict.PI_2
|
import com.rtbishop.look4sat.domain.predict.PI_2
|
||||||
import com.rtbishop.look4sat.domain.utility.toRadians
|
import com.rtbishop.look4sat.domain.utility.toRadians
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.min
|
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import kotlin.math.sqrt
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<Float, Float>) {
|
fun RadarViewCompose(
|
||||||
|
item: OrbitalPos,
|
||||||
|
items: List<OrbitalPos>,
|
||||||
|
azimElev: Pair<Float, Float>,
|
||||||
|
shouldShowSweep: Boolean,
|
||||||
|
shouldUseCompass: Boolean
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val radarColor = MaterialTheme.colorScheme.secondary
|
val radarColor = MaterialTheme.colorScheme.secondary
|
||||||
|
@ -138,8 +135,8 @@ fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<F
|
||||||
trackEffect.value = createTrackEffect(trackPath.value)
|
trackEffect.value = createTrackEffect(trackPath.value)
|
||||||
trackCreated.value = true
|
trackCreated.value = true
|
||||||
}
|
}
|
||||||
rotate(-azimElev.first) {
|
rotate(if (shouldUseCompass) -azimElev.first else 0f) {
|
||||||
drawSweep(center, sweepDegrees.floatValue, radius, trackColor)
|
if (shouldShowSweep) drawSweep(center, sweepDegrees.floatValue, radius, trackColor)
|
||||||
drawRadar(radius, radarColor, strokeWidth, 3)
|
drawRadar(radius, radarColor, strokeWidth, 3)
|
||||||
drawInfo(radius, trackColor, measurer, 3)
|
drawInfo(radius, trackColor, measurer, 3)
|
||||||
translate(center.x, center.y) {
|
translate(center.x, center.y) {
|
||||||
|
|
|
@ -17,9 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.radar
|
package com.rtbishop.look4sat.presentation.radar
|
||||||
|
|
||||||
import android.hardware.GeomagneticField
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
@ -31,17 +28,20 @@ import com.rtbishop.look4sat.MainApplication
|
||||||
import com.rtbishop.look4sat.data.framework.BluetoothReporter
|
import com.rtbishop.look4sat.data.framework.BluetoothReporter
|
||||||
import com.rtbishop.look4sat.data.framework.NetworkReporter
|
import com.rtbishop.look4sat.data.framework.NetworkReporter
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||||
|
import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
|
||||||
import com.rtbishop.look4sat.domain.utility.round
|
import com.rtbishop.look4sat.domain.utility.round
|
||||||
import com.rtbishop.look4sat.domain.utility.toDegrees
|
import com.rtbishop.look4sat.domain.utility.toDegrees
|
||||||
|
import com.rtbishop.look4sat.domain.utility.toTimerString
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ -51,22 +51,61 @@ class RadarViewModel(
|
||||||
private val networkReporter: NetworkReporter,
|
private val networkReporter: NetworkReporter,
|
||||||
private val satelliteRepo: ISatelliteRepo,
|
private val satelliteRepo: ISatelliteRepo,
|
||||||
private val settingsRepo: ISettingsRepo,
|
private val settingsRepo: ISettingsRepo,
|
||||||
private val sensorsRepo: ISensorsRepo
|
private val sensorsRepo: ISensorsRepo,
|
||||||
|
private val addToCalendar: IAddToCalendar
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _uiState = MutableStateFlow(
|
||||||
|
RadarState(
|
||||||
|
currentPass = null,
|
||||||
|
currentTime = "00:00:00",
|
||||||
|
isCurrentTimeAos = true,
|
||||||
|
orientationValues = sensorsRepo.orientation.value,
|
||||||
|
orbitalPos = null,
|
||||||
|
satTrack = emptyList(),
|
||||||
|
shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep,
|
||||||
|
shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors,
|
||||||
|
transmitters = emptyList(),
|
||||||
|
sendAction = ::handleAction,
|
||||||
|
)
|
||||||
|
)
|
||||||
private val stationPos = settingsRepo.stationPosition.value
|
private val stationPos = settingsRepo.stationPosition.value
|
||||||
private val magDeclination = getMagDeclination(stationPos)
|
private val magDeclination = sensorsRepo.getMagDeclination(stationPos)
|
||||||
val stateOfLightTheme = settingsRepo.otherSettings.value.stateOfLightTheme
|
val uiState: StateFlow<RadarState> = _uiState
|
||||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
|
||||||
val radarData = mutableStateOf<RadarData?>(null)
|
|
||||||
val orientation = mutableStateOf(sensorsRepo.orientation.value)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (settingsRepo.otherSettings.value.stateOfSensors) {
|
if (settingsRepo.otherSettings.value.stateOfSensors) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
sensorsRepo.enableSensor()
|
sensorsRepo.enableSensor()
|
||||||
sensorsRepo.orientation.collect { data ->
|
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<Int>("catNum") ?: 0
|
||||||
|
val aosTime = savedStateHandle.get<Long>("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()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPass() = flow {
|
private fun handleAction(action: RadarAction) {
|
||||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
when (action) {
|
||||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
is RadarAction.AddToCalendar -> addToCalendar(action.name, action.aosTime, action.losTime)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
|
private suspend fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
|
||||||
//
|
var track: List<OrbitalPos> = emptyList()
|
||||||
// fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
|
if (!orbitalPass.isDeepSpace) {
|
||||||
|
track = satelliteRepo.getTrack(
|
||||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
|
||||||
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<OrbitalPos> = 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)
|
|
||||||
}
|
}
|
||||||
|
_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) {
|
private fun sendPassDataBT(orbitalPos: OrbitalPos) {
|
||||||
|
@ -139,19 +151,16 @@ class RadarViewModel(
|
||||||
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt()
|
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt()
|
||||||
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
||||||
} else if (!bluetoothReporter.isConnecting()) {
|
} else if (!bluetoothReporter.isConnecting()) {
|
||||||
Log.i("BTReporter", "BTReporter: Attempting to connect...")
|
// Log.i("BTReporter", "BTReporter: Attempting to connect...")
|
||||||
bluetoothReporter.connectBTDevice(btDevice)
|
bluetoothReporter.connectBTDevice(btDevice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
private suspend fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
||||||
viewModelScope.launch {
|
val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
||||||
delay(125)
|
_uiState.update { it.copy(transmitters = transmitters) }
|
||||||
val list = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
|
||||||
transmitters.value = list
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -165,7 +174,8 @@ class RadarViewModel(
|
||||||
container.provideNetworkReporter(),
|
container.provideNetworkReporter(),
|
||||||
container.satelliteRepo,
|
container.satelliteRepo,
|
||||||
container.settingsRepo,
|
container.settingsRepo,
|
||||||
container.provideSensorsRepo()
|
container.provideSensorsRepo(),
|
||||||
|
container.provideAddToCalendar()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ private fun MiddleBar(
|
||||||
private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
|
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
|
||||||
ElevatedCard(modifier = modifier) {
|
ElevatedCard(modifier = modifier) {
|
||||||
Row(horizontalArrangement = Arrangement.Start,
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(48.dp)
|
.height(48.dp)
|
||||||
|
@ -194,9 +194,8 @@ private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifie
|
||||||
text = "Types: $typesText",
|
text = "Types: $typesText",
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
modifier = modifier
|
modifier = Modifier
|
||||||
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(0.dp))
|
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(16.dp))
|
||||||
.padding(start = 12.dp, end = 6.dp, bottom = 2.dp)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
|
||||||
|
</vector>
|
|
@ -17,15 +17,17 @@
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data.repository
|
package com.rtbishop.look4sat.data.repository
|
||||||
|
|
||||||
|
import android.hardware.GeomagneticField
|
||||||
import android.hardware.Sensor
|
import android.hardware.Sensor
|
||||||
import android.hardware.SensorEvent
|
import android.hardware.SensorEvent
|
||||||
import android.hardware.SensorEventListener
|
import android.hardware.SensorEventListener
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.RAD2DEG
|
import com.rtbishop.look4sat.domain.predict.RAD2DEG
|
||||||
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
||||||
import kotlin.math.round
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlin.math.round
|
||||||
|
|
||||||
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
||||||
SensorEventListener, ISensorsRepo {
|
SensorEventListener, ISensorsRepo {
|
||||||
|
@ -37,6 +39,12 @@ class SensorsRepo(private val sensorManager: SensorManager, private val sensor:
|
||||||
|
|
||||||
override val orientation: StateFlow<Pair<Float, Float>> = _orientation
|
override val orientation: StateFlow<Pair<Float, Float>> = _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() {
|
override fun enableSensor() {
|
||||||
sensor?.let { sensorManager.registerListener(this, it, 8000) }
|
sensor?.let { sensorManager.registerListener(this, it, 8000) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
package com.rtbishop.look4sat.domain.repository
|
package com.rtbishop.look4sat.domain.repository
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface ISensorsRepo {
|
interface ISensorsRepo {
|
||||||
val orientation: StateFlow<Pair<Float, Float>>
|
val orientation: StateFlow<Pair<Float, Float>>
|
||||||
|
fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float
|
||||||
fun enableSensor()
|
fun enableSensor()
|
||||||
fun disableSensor()
|
fun disableSensor()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.rtbishop.look4sat.domain.usecase
|
||||||
|
|
||||||
|
interface IAddToCalendar {
|
||||||
|
operator fun invoke(name: String, aosTime: Long, losTime: Long)
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.domain.utility
|
package com.rtbishop.look4sat.domain.utility
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
fun Long.toTimerString(): String {
|
fun Long.toTimerString(): String {
|
||||||
|
@ -24,7 +25,7 @@ fun Long.toTimerString(): String {
|
||||||
val hours = TimeUnit.MILLISECONDS.toHours(this)
|
val hours = TimeUnit.MILLISECONDS.toHours(this)
|
||||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60
|
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60
|
||||||
val seconds = TimeUnit.MILLISECONDS.toSeconds(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 {
|
fun Float.round(decimals: Int): Float {
|
||||||
|
|
Ładowanie…
Reference in New Issue