Initial pass item Compose implementation

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:32 +00:00
1 parent f13662747c
commit ac479f80ee
6 files changed
+168 -109

No files matched your search

@@ -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)))
}
@@ -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() }
}
}
@@ -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
)
}
}
@@ -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))
}
}
}
}
}