Initial pass item Compose implementation

pull/122/head
Arty Bishop 2023-02-19 17:04:24 +00:00
rodzic a3fd124fed
commit e749c9ac2e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 5C71CFDC37AD73CC
6 zmienionych plików z 168 dodań i 109 usunięć

Wyświetl plik

@ -3,18 +3,28 @@ package com.rtbishop.look4sat.presentation
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.scale
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.flow.Flow
@ -30,6 +40,28 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier)
) { Text(text = text, fontSize = 17.sp) }
}
@Composable
fun RadarPing() {
val pingColor = MaterialTheme.colorScheme.primary
val pingDurationMs = 1000
val scale = remember { mutableStateOf(0f) }
val scaleAnimation = animateFloatAsState(
targetValue = scale.value,
animationSpec = infiniteRepeatable(animation = tween(durationMillis = pingDurationMs))
)
LaunchedEffect(Unit) { scale.value = 1f }
Box(
modifier = Modifier
.size(size = 48.dp)
.scale(scale = scaleAnimation.value)
.border(
width = 4.dp,
shape = CircleShape,
color = pingColor.copy(alpha = 1 - scaleAnimation.value)
)
)
}
fun gotoUrl(context: Context, url: String) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}

Wyświetl plik

@ -19,6 +19,7 @@ import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.aboutScreen.AboutScreen
import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreen
import com.rtbishop.look4sat.presentation.passesScreen.PassesScreen
@Composable
fun BottomNavBar(navController: NavController) {
@ -52,8 +53,8 @@ fun NavigationGraph(navController: NavHostController) {
NavHost(navController, startDestination = BottomNavItem.Passes.screen_route) {
composable(BottomNavItem.Satellites.screen_route) { EntriesScreen(navToPasses) }
composable(BottomNavItem.Passes.screen_route) { PassesScreen() }
composable(BottomNavItem.WorldMap.screen_route) { WorldMapScreen() }
composable(BottomNavItem.Settings.screen_route) { SettingsScreen() }
composable(BottomNavItem.WorldMap.screen_route) {}
composable(BottomNavItem.Settings.screen_route) {}
composable(BottomNavItem.About.screen_route) { AboutScreen() }
}
}

Wyświetl plik

@ -1,73 +0,0 @@
package com.rtbishop.look4sat.presentation.bottomNav
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp
import com.rtbishop.look4sat.R
@Composable
fun PassesScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.background))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Passes",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 20.sp
)
}
}
@Composable
fun WorldMapScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.background))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "World Map",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 20.sp
)
}
}
@Composable
fun SettingsScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.background))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Settings",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 20.sp
)
}
}

Wyświetl plik

@ -0,0 +1,110 @@
package com.rtbishop.look4sat.presentation.passesScreen
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.NearEarthSat
import com.rtbishop.look4sat.domain.predict.OrbitalData
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.presentation.MainTheme
@Composable
fun PassesScreen(viewModel: PassesViewModel = hiltViewModel()) {
val state = viewModel.passes.observeAsState(DataState.Loading)
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
PassesCard(state = state.value)
}
}
@Preview(showBackground = true)
@Composable
private fun PassPreview() {
val data = OrbitalData(
"Satellite", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
)
val satellite = NearEarthSat(data)
val pass = SatPass(1L, 25.0, 10L, 75.0, 850, 45.0, satellite, 150)
MainTheme { Pass(pass = pass) }
}
@Composable
private fun Pass(pass: SatPass, modifier: Modifier = Modifier) {
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
Surface(modifier = Modifier.padding(bottom = 1.dp)) {
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.padding(6.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = pass.name,
modifier = Modifier
.weight(1f)
.padding(end = 6.dp),
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = "Id:${pass.catNum}", color = MaterialTheme.colorScheme.secondary
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "AOS - 90")
Text(text = "Altitude: 1800 km")
Text(text = "180 - LOS")
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "16:02:28 - Sun")
Text(text = "Elevation: 75")
Text(text = "Sun - 16:02:28")
}
LinearProgressIndicator(progress = 0.5f, modifier.fillMaxWidth())
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun PassesCard(state: DataState<List<SatPass>>) {
ElevatedCard(modifier = Modifier.fillMaxSize()) {
when (state) {
is DataState.Success -> {
LazyColumn {
items(items = state.data, key = { item -> item.aosTime }) { pass ->
Pass(pass, Modifier.animateItemPlacement())
}
}
}
else -> {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(modifier = Modifier.size(80.dp))
}
}
}
}
}

Wyświetl plik

@ -22,9 +22,9 @@ data class SatPass(
val aosAzimuth: Double,
val losTime: Long,
val losAzimuth: Double,
val tcaTime: Long,
val tcaAzimuth: Double,
val altitude: Double,
// val tcaTime: Long,
// val tcaAzimuth: Double,
val altitude: Int,
val maxElevation: Double,
val satellite: Satellite,
var progress: Int = 0

Wyświetl plik

@ -19,6 +19,7 @@ package com.rtbishop.look4sat.domain.predict
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.utility.round
import com.rtbishop.look4sat.utility.toDegrees
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
@ -38,10 +39,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
}
override suspend fun getTrack(
sat: Satellite,
pos: GeoPos,
start: Long,
end: Long
sat: Satellite, pos: GeoPos, start: Long, end: Long
): List<SatPos> {
return withContext(defaultDispatcher) {
val positions = mutableListOf<SatPos>()
@ -55,10 +53,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
}
override suspend fun processRadios(
sat: Satellite,
pos: GeoPos,
radios: List<SatRadio>,
time: Long
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
): List<SatRadio> {
return withContext(defaultDispatcher) {
val satPos = sat.getPosition(pos, time)
@ -92,11 +87,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
}
override suspend fun calculatePasses(
satList: List<Satellite>,
pos: GeoPos,
time: Long,
hoursAhead: Int,
minElevation: Double
satList: List<Satellite>, pos: GeoPos, time: Long, hoursAhead: Int, minElevation: Double
) {
if (satList.isEmpty()) {
val newPasses = emptyList<SatPass>()
@ -142,21 +133,19 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
private fun List<SatPass>.filter(time: Long, hoursAhead: Int, minElev: Double): List<SatPass> {
val timeFuture = time + (hoursAhead * 60L * 60L * 1000L)
return this.filter { it.losTime > time }
.filter { it.aosTime < timeFuture }
.filter { it.maxElevation > minElev }
.sortedBy { it.aosTime }
return this.filter { it.losTime > time }.filter { it.aosTime < timeFuture }
.filter { it.maxElevation > minElev }.sortedBy { it.aosTime }
}
private fun getGeoPass(sat: Satellite, pos: GeoPos, time: Long): SatPass {
val satPos = sat.getPosition(pos, time)
val aos = time - 24 * 60L * 60L * 1000L
val los = time + 24 * 60L * 60L * 1000L
val tca = (aos + los) / 2
val az = satPos.azimuth.toDegrees()
val elev = satPos.elevation.toDegrees()
// val tca = (aos + los) / 2
val az = satPos.azimuth.toDegrees().round(1)
val elev = satPos.elevation.toDegrees().round(1)
val alt = satPos.altitude
return SatPass(aos, az, los, az, tca, az, alt, elev, sat)
return SatPass(aos, az, los, az, alt.toInt(), elev, sat)
}
private fun getLeoPass(sat: Satellite, pos: GeoPos, time: Long, rewind: Boolean): SatPass {
@ -165,7 +154,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
var elevation: Double
var maxElevation = 0.0
var alt = 0.0
var tcaAz = 0.0
// var tcaAz = 0.0
// rewind 1/4 of an orbit
if (rewind) calendarTimeMillis += -quarterOrbitMin * 60L * 1000L
@ -188,7 +177,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = satPos.azimuth.toDegrees()
// tcaAz = satPos.azimuth.toDegrees()
}
} while (satPos.elevation < 0.0)
@ -201,12 +190,12 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = satPos.azimuth.toDegrees()
// tcaAz = satPos.azimuth.toDegrees()
}
} while (satPos.elevation < 0.0)
val aos = satPos.time
val aosAz = satPos.azimuth.toDegrees()
val aosAz = satPos.azimuth.toDegrees().round(1)
// find when sat goes below
do {
@ -216,7 +205,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = satPos.azimuth.toDegrees()
// tcaAz = satPos.azimuth.toDegrees()
}
} while (satPos.elevation > 0.0)
@ -229,14 +218,14 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = satPos.azimuth.toDegrees()
// tcaAz = satPos.azimuth.toDegrees()
}
} while (satPos.elevation > 0.0)
val los = satPos.time
val losAz = satPos.azimuth.toDegrees()
val tca = (aos + los) / 2
val elev = maxElevation.toDegrees()
return SatPass(aos, aosAz, los, losAz, tca, tcaAz, alt, elev, sat)
val losAz = satPos.azimuth.toDegrees().round(1)
// val tca = (aos + los) / 2
val elev = maxElevation.toDegrees().round(1)
return SatPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat)
}
}