kopia lustrzana https://github.com/rt-bishop/Look4Sat
Set up more screens with portrait/landscape TopBar
rodzic
761506d2a3
commit
99a63a2a1d
|
@ -1,94 +1,75 @@
|
||||||
package com.rtbishop.look4sat.presentation
|
package com.rtbishop.look4sat.presentation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.NavigationBarItem
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
|
||||||
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||||
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
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 com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
|
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
||||||
import com.rtbishop.look4sat.presentation.map.mapDestination
|
import com.rtbishop.look4sat.presentation.map.mapDestination
|
||||||
import com.rtbishop.look4sat.presentation.passes.passesDestination
|
import com.rtbishop.look4sat.presentation.passes.passesDestination
|
||||||
import com.rtbishop.look4sat.presentation.radar.radarDestination
|
import com.rtbishop.look4sat.presentation.radar.radarDestination
|
||||||
import com.rtbishop.look4sat.presentation.satellites.satellitesDestination
|
import com.rtbishop.look4sat.presentation.satellites.satellitesDestination
|
||||||
import com.rtbishop.look4sat.presentation.settings.settingsDestination
|
import com.rtbishop.look4sat.presentation.settings.settingsDestination
|
||||||
|
|
||||||
sealed class Screen(var title: String, var icon: Int, var route: String) {
|
sealed class Screen(val title: String, val icon: Int, val route: String) {
|
||||||
data object Main : Screen("Main", R.drawable.ic_sputnik, "main")
|
|
||||||
data object Radar : Screen("Radar", R.drawable.ic_sputnik, "radar")
|
|
||||||
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
|
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
|
||||||
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
|
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
|
||||||
|
data object Radar : Screen("Radar", R.drawable.ic_satellite, "radar")
|
||||||
data object Map : Screen("Map", R.drawable.ic_map, "map")
|
data object Map : Screen("Map", R.drawable.ic_map, "map")
|
||||||
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
|
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
private val startDestination = Screen.Passes.route
|
||||||
fun MainScreen() {
|
|
||||||
val outerNavController: NavHostController = rememberNavController()
|
|
||||||
val navigateToRadar = { catNum: Int, aosTime: Long ->
|
|
||||||
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
|
||||||
outerNavController.navigate(routeWithParams)
|
|
||||||
}
|
|
||||||
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
|
|
||||||
mainDestination(navigateToRadar)
|
|
||||||
radarDestination { outerNavController.navigateUp() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun NavGraphBuilder.mainDestination(navigateToRadar: (Int, Long) -> Unit) {
|
|
||||||
composable(Screen.Main.route) { NavBarScreen(navigateToRadar) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NavBarScreen(navigateToRadar: (Int, Long) -> Unit) {
|
fun MainScreen(navController: NavHostController = rememberNavController()) {
|
||||||
val innerNavController: NavHostController = rememberNavController()
|
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
|
||||||
val navigateToPasses = { innerNavController.navigate(Screen.Passes.route) }
|
|
||||||
Scaffold(bottomBar = { MainNavBar(innerNavController) }) { innerPadding ->
|
|
||||||
NavHost(
|
|
||||||
navController = innerNavController,
|
|
||||||
startDestination = Screen.Passes.route,
|
|
||||||
modifier = Modifier.padding(innerPadding)
|
|
||||||
) {
|
|
||||||
satellitesDestination(navigateToPasses)
|
|
||||||
passesDestination(navigateToRadar)
|
|
||||||
mapDestination()
|
|
||||||
settingsDestination()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MainNavBar(navController: NavController) {
|
|
||||||
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings)
|
|
||||||
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
||||||
NavigationBar {
|
NavigationSuiteScaffold(
|
||||||
items.forEach { item ->
|
navigationSuiteItems = {
|
||||||
NavigationBarItem(
|
items.forEach {
|
||||||
icon = { Icon(painterResource(item.icon), item.title) },
|
item(
|
||||||
label = { Text(item.title) },
|
icon = { Icon(painterResource(it.icon), it.title) },
|
||||||
selected = item.route == destinationRoute,
|
label = { Text(it.title) },
|
||||||
onClick = {
|
selected = destinationRoute?.contains(it.route) ?: false,
|
||||||
if (item.route == destinationRoute) {
|
onClick = {
|
||||||
// reselecting the same tab
|
if (destinationRoute?.contains(it.route) ?: false) return@item
|
||||||
} else {
|
navController.navigate(it.route) {
|
||||||
navController.navigate(item.route) {
|
popUpTo(startDestination) { saveState = false }
|
||||||
popUpTo(Screen.Passes.route) { saveState = false }
|
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
restoreState = false
|
restoreState = false
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
)
|
}, navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||||
|
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
), layoutType = when {
|
||||||
|
isVerticalLayout() -> NavigationSuiteType.NavigationBar
|
||||||
|
else -> NavigationSuiteType.NavigationRail
|
||||||
}
|
}
|
||||||
|
) { MainNavHost(navController) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MainNavHost(navController: NavHostController) {
|
||||||
|
val navigateToRadar = { catNum: Int, aosTime: Long ->
|
||||||
|
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||||
|
navController.navigate(routeWithParams)
|
||||||
|
}
|
||||||
|
NavHost(navController = navController, startDestination = startDestination) {
|
||||||
|
satellitesDestination { navController.navigateUp() }
|
||||||
|
passesDestination(navigateToRadar)
|
||||||
|
radarDestination { navController.navigateUp() }
|
||||||
|
mapDestination()
|
||||||
|
settingsDestination()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ private val darkScheme = darkColorScheme(
|
||||||
// outlineVariant = Color(0xFF121212),
|
// outlineVariant = Color(0xFF121212),
|
||||||
scrim = Color(0xFF000000),
|
scrim = Color(0xFF000000),
|
||||||
// surfaceBright = Color(0xFF121212),
|
// surfaceBright = Color(0xFF121212),
|
||||||
surfaceContainer = Color(0xFF242424), // navBar background
|
surfaceContainer = Color(0xFF121212), // navBar background
|
||||||
// surfaceContainerHigh = Color(0xFF121212),
|
// surfaceContainerHigh = Color(0xFF121212),
|
||||||
surfaceContainerHighest = Color(0xFF242424), // filled card background
|
surfaceContainerHighest = Color(0xFF242424), // filled card background
|
||||||
surfaceContainerLow = Color(0xFF242424), // elevated card background
|
surfaceContainerLow = Color(0xFF242424), // elevated card background
|
||||||
|
|
|
@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
@ -283,3 +284,13 @@ fun isVerticalLayout(): Boolean {
|
||||||
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
||||||
return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
|
return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Modifier.layoutPadding(): Modifier {
|
||||||
|
val statusBarMod = this.statusBarsPadding()
|
||||||
|
val spacing = LocalSpacing.current.small
|
||||||
|
return when {
|
||||||
|
isVerticalLayout() -> statusBarMod.padding(horizontal = spacing)
|
||||||
|
else -> statusBarMod.padding(start = 0.dp, top = 0.dp, end = spacing, bottom = spacing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,8 @@ import org.osmdroid.views.overlay.Polyline
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.core.graphics.createBitmap
|
import androidx.core.graphics.createBitmap
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
|
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
||||||
|
import com.rtbishop.look4sat.presentation.common.layoutPadding
|
||||||
|
|
||||||
private val minLat = MapView.getTileSystem().minLatitude
|
private val minLat = MapView.getTileSystem().minLatitude
|
||||||
private val maxLat = MapView.getTileSystem().maxLatitude
|
private val maxLat = MapView.getTileSystem().maxLatitude
|
||||||
|
@ -98,13 +100,22 @@ private fun MapScreen(uiState: State<MapState>, mapView: MapView) {
|
||||||
val timeString = uiState.value.mapData?.aosTime ?: "00:00:00"
|
val timeString = uiState.value.mapData?.aosTime ?: "00:00:00"
|
||||||
val isTimeAos = uiState.value.mapData?.isTimeAos ?: true
|
val isTimeAos = uiState.value.mapData?.isTimeAos ?: true
|
||||||
val osmInfo = "© OpenStreetMap contributors"
|
val osmInfo = "© OpenStreetMap contributors"
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
TopBar {
|
if (isVerticalLayout()) {
|
||||||
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
|
TopBar {
|
||||||
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
|
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
|
||||||
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
|
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
|
||||||
|
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
|
||||||
|
}
|
||||||
|
NextPassRow(pass = uiState.value.orbitalPass)
|
||||||
|
} else {
|
||||||
|
TopBar {
|
||||||
|
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
|
||||||
|
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
|
||||||
|
NextPassRow(pass = uiState.value.orbitalPass, modifier = Modifier.weight(1f))
|
||||||
|
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NextPassRow(pass = uiState.value.orbitalPass)
|
|
||||||
ElevatedCard(modifier = Modifier.weight(1f)) {
|
ElevatedCard(modifier = Modifier.weight(1f)) {
|
||||||
Box(contentAlignment = Alignment.BottomCenter) {
|
Box(contentAlignment = Alignment.BottomCenter) {
|
||||||
LaunchedEffect(uiState.value.track) {
|
LaunchedEffect(uiState.value.track) {
|
||||||
|
|
|
@ -11,8 +11,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
@ -50,6 +51,7 @@ import com.rtbishop.look4sat.presentation.common.NextPassRow
|
||||||
import com.rtbishop.look4sat.presentation.common.TimerRow
|
import com.rtbishop.look4sat.presentation.common.TimerRow
|
||||||
import com.rtbishop.look4sat.presentation.common.TopBar
|
import com.rtbishop.look4sat.presentation.common.TopBar
|
||||||
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
||||||
|
import com.rtbishop.look4sat.presentation.common.layoutPadding
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -91,7 +93,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U
|
||||||
uiState.takeAction(PassesAction.DismissWelcome)
|
uiState.takeAction(PassesAction.DismissWelcome)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
if (isVerticalLayout()) {
|
if (isVerticalLayout()) {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
|
IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
|
||||||
|
@ -135,21 +137,16 @@ private fun PassesList(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Adaptive(320.dp),
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass ->
|
items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass ->
|
||||||
if (pass.isDeepSpace) {
|
NearEarthPass(
|
||||||
DeepSpacePass(
|
pass = pass,
|
||||||
pass = pass,
|
navigateToRadar = navigateToRadar,
|
||||||
navigateToRadar = navigateToRadar,
|
modifier = Modifier.animateItem()
|
||||||
modifier = Modifier.animateItem()
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
NearEarthPass(
|
|
||||||
pass = pass,
|
|
||||||
navigateToRadar = navigateToRadar,
|
|
||||||
modifier = Modifier.animateItem()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,114 +156,16 @@ private fun PassesList(
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun DeepSpacePassPreview() {
|
private fun DeepSpacePassPreview() {
|
||||||
val data = OrbitalData(
|
val data = OrbitalData("Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0)
|
||||||
"Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
|
|
||||||
)
|
|
||||||
val satellite = DeepSpaceObject(data)
|
val satellite = DeepSpaceObject(data)
|
||||||
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
||||||
MainTheme { DeepSpacePass(pass = pass, { _, _ -> }) }
|
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun DeepSpacePass(
|
|
||||||
pass: OrbitalPass,
|
|
||||||
navigateToRadar: (Int, Long) -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val passSatId = stringResource(id = R.string.pass_satId, pass.catNum)
|
|
||||||
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
|
||||||
Surface(modifier = Modifier
|
|
||||||
.padding(bottom = 2.dp)
|
|
||||||
.clickable { navigateToRadar(pass.catNum, pass.aosTime) }) {
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(1.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.background(color = MaterialTheme.colorScheme.surface)
|
|
||||||
.padding(horizontal = 6.dp, vertical = 4.dp)
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Text(
|
|
||||||
text = "$passSatId - ",
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = pass.name,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.padding(end = 6.dp),
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = R.drawable.ic_elevation),
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Text(
|
|
||||||
text = "${pass.maxElevation}°",
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
horizontalArrangement = Arrangement.Start,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "DeepSpace",
|
|
||||||
fontSize = 15.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = R.drawable.ic_altitude),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Text(
|
|
||||||
text = "${pass.altitude} km",
|
|
||||||
fontSize = 15.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
horizontalArrangement = Arrangement.End,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(
|
|
||||||
id = R.string.pass_aosLos,
|
|
||||||
pass.aosAzimuth.toInt(),
|
|
||||||
pass.losAzimuth.toInt()
|
|
||||||
),
|
|
||||||
fontSize = 15.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun NearEarthPassPreview() {
|
private fun NearEarthPassPreview() {
|
||||||
val data = OrbitalData(
|
val data = OrbitalData("Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0)
|
||||||
"Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
|
|
||||||
)
|
|
||||||
val satellite = NearEarthObject(data)
|
val satellite = NearEarthObject(data)
|
||||||
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
||||||
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
|
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
|
||||||
|
@ -281,7 +180,7 @@ private fun NearEarthPass(
|
||||||
val passSatId = stringResource(id = R.string.pass_satId, pass.catNum)
|
val passSatId = stringResource(id = R.string.pass_satId, pass.catNum)
|
||||||
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
||||||
Surface(modifier = Modifier
|
Surface(modifier = Modifier
|
||||||
.padding(bottom = 2.dp)
|
.padding(bottom = 2.dp, start = 1.dp, end = 1.dp)
|
||||||
.clickable { navigateToRadar(pass.catNum, pass.aosTime) }) {
|
.clickable { navigateToRadar(pass.catNum, pass.aosTime) }) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(1.dp),
|
verticalArrangement = Arrangement.spacedBy(1.dp),
|
||||||
|
@ -325,10 +224,14 @@ private fun NearEarthPass(
|
||||||
horizontalArrangement = Arrangement.Start,
|
horizontalArrangement = Arrangement.Start,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
if (pass.isDeepSpace) {
|
||||||
text = sdfDate.format(Date(pass.aosTime)),
|
Text(text = "DeepSpace", fontSize = 15.sp)
|
||||||
fontSize = 15.sp
|
} else {
|
||||||
)
|
Text(
|
||||||
|
text = sdfDate.format(Date(pass.aosTime)),
|
||||||
|
fontSize = 15.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
|
|
@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
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
|
||||||
|
@ -58,8 +57,10 @@ import com.rtbishop.look4sat.presentation.common.NextPassRow
|
||||||
import com.rtbishop.look4sat.presentation.common.TimerRow
|
import com.rtbishop.look4sat.presentation.common.TimerRow
|
||||||
import com.rtbishop.look4sat.presentation.common.TopBar
|
import com.rtbishop.look4sat.presentation.common.TopBar
|
||||||
import com.rtbishop.look4sat.presentation.common.getDefaultPass
|
import com.rtbishop.look4sat.presentation.common.getDefaultPass
|
||||||
|
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
||||||
|
import com.rtbishop.look4sat.presentation.common.layoutPadding
|
||||||
|
|
||||||
fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
|
fun NavGraphBuilder.radarDestination(navigateUp: () -> Unit) {
|
||||||
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
||||||
val radarArgs = listOf(
|
val radarArgs = listOf(
|
||||||
navArgument("catNum") { defaultValue = 0 },
|
navArgument("catNum") { defaultValue = 0 },
|
||||||
|
@ -68,31 +69,36 @@ fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
|
||||||
composable(radarRoute, radarArgs) {
|
composable(radarRoute, radarArgs) {
|
||||||
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
RadarScreen(uiState, navigateBack)
|
RadarScreen(uiState, navigateUp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
|
private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
|
||||||
// BluetoothCIV.init(LocalContext.current)
|
// BluetoothCIV.init(LocalContext.current)
|
||||||
|
|
||||||
val addToCalendar: () -> Unit = {
|
val addToCalendar: () -> Unit = {
|
||||||
uiState.currentPass?.let { pass ->
|
uiState.currentPass?.let { pass ->
|
||||||
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
|
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scaffold { innerPadding ->
|
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
||||||
val paddingMod = Modifier.padding(innerPadding)
|
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Column(
|
if (isVerticalLayout()) {
|
||||||
modifier = paddingMod.padding(6.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
|
||||||
) {
|
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(onClick = navigateBack, iconId = R.drawable.ic_back)
|
IconCard(onClick = navigateUp, iconId = R.drawable.ic_back)
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
||||||
}
|
}
|
||||||
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
|
NextPassRow(pass = upcomingPass)
|
||||||
|
} else {
|
||||||
|
TopBar {
|
||||||
|
IconCard(onClick = navigateUp, iconId = R.drawable.ic_back)
|
||||||
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
|
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f))
|
||||||
|
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isVerticalLayout()) {
|
||||||
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) {
|
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) {
|
||||||
uiState.orbitalPos?.let { position ->
|
uiState.orbitalPos?.let { position ->
|
||||||
ElevatedCard {
|
ElevatedCard {
|
||||||
|
@ -172,6 +178,88 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
|
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.weight(1f)) {
|
||||||
|
uiState.orbitalPos?.let { position ->
|
||||||
|
ElevatedCard {
|
||||||
|
RadarViewCompose(
|
||||||
|
item = position,
|
||||||
|
items = uiState.satTrack,
|
||||||
|
azimElev = uiState.orientationValues,
|
||||||
|
shouldShowSweep = uiState.shouldShowSweep,
|
||||||
|
shouldUseCompass = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 6.dp, vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
RadarTextTop(
|
||||||
|
position.azimuth,
|
||||||
|
stringResource(R.string.radar_az_text),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
RadarTextTop(
|
||||||
|
position.elevation,
|
||||||
|
stringResource(R.string.radar_el_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
RadarTextBottom(
|
||||||
|
position.altitude,
|
||||||
|
stringResource(R.string.radar_alt_text),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
RadarTextBottom(
|
||||||
|
position.distance,
|
||||||
|
stringResource(R.string.radar_dist_text),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ElevatedCard(modifier = Modifier.fillMaxSize().weight(1f)) {
|
||||||
|
if (uiState.transmitters.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_satellite),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(64.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
text = "This satellite doesn't have any known transcievers...",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
TransmittersList(transmitters = uiState.transmitters)
|
||||||
|
if (uiState.orbitalPos?.eclipsed == true) {
|
||||||
|
EclipsedIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
|
@ -46,23 +49,25 @@ import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
import com.rtbishop.look4sat.domain.model.SatItem
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.Screen
|
import com.rtbishop.look4sat.presentation.Screen
|
||||||
import com.rtbishop.look4sat.presentation.common.IconCard
|
|
||||||
import com.rtbishop.look4sat.presentation.common.CardLoadingIndicator
|
import com.rtbishop.look4sat.presentation.common.CardLoadingIndicator
|
||||||
|
import com.rtbishop.look4sat.presentation.common.IconCard
|
||||||
import com.rtbishop.look4sat.presentation.common.InfoDialog
|
import com.rtbishop.look4sat.presentation.common.InfoDialog
|
||||||
|
import com.rtbishop.look4sat.presentation.common.TopBar
|
||||||
|
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
|
||||||
|
import com.rtbishop.look4sat.presentation.common.layoutPadding
|
||||||
|
|
||||||
fun NavGraphBuilder.satellitesDestination(navigateToPasses: () -> Unit) {
|
fun NavGraphBuilder.satellitesDestination(navigateUp: () -> Unit) {
|
||||||
composable(Screen.Satellites.route) {
|
composable(Screen.Satellites.route) {
|
||||||
val viewModel = viewModel(
|
val viewModel = viewModel(
|
||||||
modelClass = SatellitesViewModel::class.java,
|
modelClass = SatellitesViewModel::class.java, factory = SatellitesViewModel.Factory
|
||||||
factory = SatellitesViewModel.Factory
|
|
||||||
)
|
)
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
SatellitesScreen(uiState, navigateToPasses)
|
SatellitesScreen(uiState, navigateUp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> Unit) {
|
private fun SatellitesScreen(uiState: SatellitesState, navigateUp: () -> Unit) {
|
||||||
val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) }
|
val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) }
|
||||||
if (uiState.isDialogShown) {
|
if (uiState.isDialogShown) {
|
||||||
MultiTypesDialog(allTypes = uiState.typesList, types = uiState.currentTypes, toggleDialog) {
|
MultiTypesDialog(allTypes = uiState.typesList, types = uiState.currentTypes, toggleDialog) {
|
||||||
|
@ -79,14 +84,28 @@ private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> U
|
||||||
}
|
}
|
||||||
val unselectAll = { uiState.takeAction(SatellitesAction.UnselectAll) }
|
val unselectAll = { uiState.takeAction(SatellitesAction.UnselectAll) }
|
||||||
val selectAll = { uiState.takeAction(SatellitesAction.SelectAll) }
|
val selectAll = { uiState.takeAction(SatellitesAction.SelectAll) }
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
val setQuery = { newQuery: String -> uiState.takeAction(SatellitesAction.SearchFor(newQuery)) }
|
||||||
TopBar(setQuery = { newQuery: String ->
|
val saveSelection = { uiState.takeAction(SatellitesAction.SaveSelection).also { navigateUp() } }
|
||||||
uiState.takeAction(SatellitesAction.SearchFor(newQuery))
|
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
}, saveSelection = {
|
if (isVerticalLayout()) {
|
||||||
uiState.takeAction(SatellitesAction.SaveSelection)
|
TopBar {
|
||||||
navigateToPasses()
|
TypeCard(types = uiState.currentTypes, toggleDialog, modifier = Modifier.weight(1f))
|
||||||
})
|
SaveButton(saveSelection = saveSelection, modifier = Modifier.height(48.dp))
|
||||||
MiddleBar(uiState.currentTypes, { toggleDialog() }, { unselectAll() }, { selectAll() })
|
}
|
||||||
|
TopBar {
|
||||||
|
SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f))
|
||||||
|
IconCard(onClick = unselectAll, iconId = R.drawable.ic_check_off)
|
||||||
|
IconCard(onClick = selectAll, iconId = R.drawable.ic_check_on)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopBar {
|
||||||
|
SaveButton(saveSelection = saveSelection, modifier = Modifier.height(48.dp))
|
||||||
|
TypeCard(types = uiState.currentTypes, toggleDialog, modifier = Modifier.weight(1f))
|
||||||
|
SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f))
|
||||||
|
IconCard(onClick = unselectAll, iconId = R.drawable.ic_check_off)
|
||||||
|
IconCard(onClick = selectAll, iconId = R.drawable.ic_check_on)
|
||||||
|
}
|
||||||
|
}
|
||||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
CardLoadingIndicator()
|
CardLoadingIndicator()
|
||||||
|
@ -99,18 +118,6 @@ private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> U
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
|
||||||
private fun TopBarPreview() = MainTheme { TopBar({}, {}) }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TopBar(setQuery: (String) -> Unit, saveSelection: () -> Unit) {
|
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) {
|
|
||||||
SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f))
|
|
||||||
SaveButton(saveSelection = { saveSelection() }, modifier = Modifier.height(48.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) {
|
private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) {
|
||||||
val currentQuery = rememberSaveable { mutableStateOf("") }
|
val currentQuery = rememberSaveable { mutableStateOf("") }
|
||||||
|
@ -168,26 +175,12 @@ private fun SaveButton(saveSelection: () -> Unit, modifier: Modifier = Modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
|
||||||
private fun MiddleBarPreview() = MainTheme { MiddleBar(listOf("Amateur"), {}, {}, {}) }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MiddleBar(
|
|
||||||
types: List<String>, navigate: () -> Unit, uncheck: () -> Unit, check: () -> Unit
|
|
||||||
) {
|
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) {
|
|
||||||
TypeCard(types = types, { navigate() }, modifier = Modifier.weight(1f))
|
|
||||||
IconCard(onClick = { uncheck() }, iconId = R.drawable.ic_check_off)
|
|
||||||
IconCard(onClick = { check() }, iconId = R.drawable.ic_check_on)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
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.spacedBy(12.dp),
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(48.dp)
|
.height(48.dp)
|
||||||
|
@ -221,7 +214,8 @@ private fun SatellitePreview() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun Satellite(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) {
|
private fun Satellite(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) {
|
||||||
val passSatId = stringResource(id = R.string.pass_satId, item.catnum)
|
val passSatId = stringResource(id = R.string.pass_satId, item.catnum)
|
||||||
Surface(color = MaterialTheme.colorScheme.background,
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background,
|
||||||
modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) {
|
modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) {
|
||||||
Surface(modifier = Modifier.padding(bottom = 1.dp)) {
|
Surface(modifier = Modifier.padding(bottom = 1.dp)) {
|
||||||
Row(
|
Row(
|
||||||
|
@ -263,7 +257,7 @@ private fun SatellitesPreview() {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
||||||
LazyColumn {
|
LazyVerticalGrid(columns = GridCells.Adaptive(320.dp)) {
|
||||||
items(items = items, key = { item -> item.catnum }) { entry ->
|
items(items = items, key = { item -> item.catnum }) { entry ->
|
||||||
Satellite(entry, onSelected, Modifier.animateItem())
|
Satellite(entry, onSelected, Modifier.animateItem())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
@ -44,6 +46,7 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.Screen
|
import com.rtbishop.look4sat.presentation.Screen
|
||||||
import com.rtbishop.look4sat.presentation.common.CardButton
|
import com.rtbishop.look4sat.presentation.common.CardButton
|
||||||
|
import com.rtbishop.look4sat.presentation.common.layoutPadding
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -130,8 +133,10 @@ private fun SettingsScreen(uiState: SettingsState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Screen setup
|
// Screen setup
|
||||||
LazyColumn(
|
LazyVerticalGrid(
|
||||||
modifier = Modifier.padding(6.dp),
|
columns = GridCells.Adaptive(320.dp),
|
||||||
|
modifier = Modifier.layoutPadding(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
) {
|
) {
|
||||||
item { CardAbout(uiState.appVersionName, uiState.sendSystemAction) }
|
item { CardAbout(uiState.appVersionName, uiState.sendSystemAction) }
|
||||||
|
@ -461,17 +466,17 @@ private fun OtherCard(
|
||||||
Text(text = stringResource(id = R.string.other_switch_sensors))
|
Text(text = stringResource(id = R.string.other_switch_sensors))
|
||||||
Switch(checked = settings.stateOfSensors, onCheckedChange = { toggleSensor(it) })
|
Switch(checked = settings.stateOfSensors, onCheckedChange = { toggleSensor(it) })
|
||||||
}
|
}
|
||||||
Row(
|
// Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
// horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
// verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth()
|
// modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
// ) {
|
||||||
Text(text = stringResource(id = R.string.other_switch_light_theme))
|
// Text(text = stringResource(id = R.string.other_switch_light_theme))
|
||||||
Switch(
|
// Switch(
|
||||||
checked = settings.stateOfLightTheme,
|
// checked = settings.stateOfLightTheme,
|
||||||
onCheckedChange = { toggleLightTheme(it) }
|
// onCheckedChange = { toggleLightTheme(it) }
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,24 +158,24 @@
|
||||||
<string name="BTremote_output_hint">Формат данных</string>
|
<string name="BTremote_output_hint">Формат данных</string>
|
||||||
<string name="BTremote_perm_error">Нет разрешения использовать bluetooth</string>
|
<string name="BTremote_perm_error">Нет разрешения использовать bluetooth</string>
|
||||||
|
|
||||||
<string name="other_switch_utc">• Показывать время по UTC</string>
|
<string name="other_switch_utc">Показывать время по UTC</string>
|
||||||
<string name="other_switch_update">• Обновлять данные автоматически</string>
|
<string name="other_switch_update">Обновлять данные автоматически</string>
|
||||||
<string name="other_switch_sweep">• Показывать анимацию радара</string>
|
<string name="other_switch_sweep">Показывать анимацию радара</string>
|
||||||
<string name="other_switch_sensors">• Использовать сенсоры устройства</string>
|
<string name="other_switch_sensors">Использовать сенсоры устройства</string>
|
||||||
<string name="other_switch_light_theme">• Использовать светлую тему</string>
|
<string name="other_switch_light_theme">Использовать светлую тему</string>
|
||||||
|
|
||||||
<string name="outro_title">Я хотел бы сказать спасибо:</string>
|
<string name="outro_title">Я хотел бы сказать спасибо:</string>
|
||||||
<string name="outro_thanks">• Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой!
|
<string name="outro_thanks">Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой!
|
||||||
\n• <a href="https://github.com/g4dpz">David A. B. Johnson</a> и
|
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a> и
|
||||||
<a href="https://github.com/davidmoten">Dave Moten</a> за работу над predict4java и доступ
|
<a href="https://github.com/davidmoten">Dave Moten</a> за работу над predict4java и доступ
|
||||||
к ней по <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>
|
к ней по <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>
|
||||||
\n• <a href="https://github.com/csete">Alexandru Csete</a> за его программу
|
\n<a href="https://github.com/csete">Alexandru Csete</a> за его программу
|
||||||
Gpredict, которая послужила для меня вдохновением.
|
Gpredict, которая послужила для меня вдохновением.
|
||||||
\n• <a href="https://celestrak.com/webmaster.php">Доктору T.S. Kelso</a> за его сайт
|
\n<a href="https://celestrak.com/webmaster.php">Доктору T.S. Kelso</a> за его сайт
|
||||||
<a href="https://celestrak.com/">Celestrak</a> и доступ к файлам орбит Two-Line Element.
|
<a href="https://celestrak.com/">Celestrak</a> и доступ к файлам орбит Two-Line Element.
|
||||||
\n• <a href="https://libre.space/">Libre Space Foundation</a> за проект
|
\n<a href="https://libre.space/">Libre Space Foundation</a> за проект
|
||||||
<a href="https://db.satnogs.org/">SatNOGS</a>, API и базу данных с информацией о спутниках.
|
<a href="https://db.satnogs.org/">SatNOGS</a>, API и базу данных с информацией о спутниках.
|
||||||
\n• <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
\n<a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
||||||
за интерес к приложению и публикацию интервью на сайте.
|
за интерес к приложению и публикацию интервью на сайте.
|
||||||
</string>
|
</string>
|
||||||
<string name="outro_license">Это ПО поставляется без гарантий.</string>
|
<string name="outro_license">Это ПО поставляется без гарантий.</string>
|
||||||
|
|
|
@ -158,22 +158,22 @@
|
||||||
<string name="BTremote_output_hint">දත්ත අකාරය</string>
|
<string name="BTremote_output_hint">දත්ත අකාරය</string>
|
||||||
<string name="BTremote_perm_error">Bluetooth අවසර පරික්ෂා ක°</string>
|
<string name="BTremote_perm_error">Bluetooth අවසර පරික්ෂා ක°</string>
|
||||||
|
|
||||||
<string name="other_switch_utc">• පසුකර වේලාවන් UTC මගින්</string>
|
<string name="other_switch_utc">පසුකර වේලාවන් UTC මගින්</string>
|
||||||
<string name="other_switch_update">• ස්වයං දත්ත යාවත්කාලීනය</string>
|
<string name="other_switch_update">ස්වයං දත්ත යාවත්කාලීනය</string>
|
||||||
<string name="other_switch_sweep">• radar sweep සජීවිකරණය සබල කරන්න</string>
|
<string name="other_switch_sweep">radar sweep සජීවිකරණය සබල කරන්න</string>
|
||||||
<string name="other_switch_sensors">• Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න </string>
|
<string name="other_switch_sensors">Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න </string>
|
||||||
<string name="other_switch_light_theme">• සැහැල්ලු තේමාව භාවිතා කරන්න</string>
|
<string name="other_switch_light_theme">සැහැල්ලු තේමාව භාවිතා කරන්න</string>
|
||||||
|
|
||||||
<string name="outro_title">මම ස්තුති කිරීමට කැමති:</string>
|
<string name="outro_title">මම ස්තුති කිරීමට කැමති:</string>
|
||||||
<string name="outro_thanks">• බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි!
|
<string name="outro_thanks">බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි!
|
||||||
\n• <a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
||||||
සහ <a href="https://github.com/davidmoten">Dave Moten</a>ඔවුනට ප්රයෝජනවත් predict4java පුස්තකාලය නිර්මාණයට.
|
සහ <a href="https://github.com/davidmoten">Dave Moten</a>ඔවුනට ප්රයෝජනවත් predict4java පුස්තකාලය නිර්මාණයට.
|
||||||
\n• සහ <a href="https://github.com/csete">Alexandru Csete</a> සඳහා Gpredict නිර්මාණයට සහ
|
\nසහ <a href="https://github.com/csete">Alexandru Csete</a> සඳහා Gpredict නිර්මාණයට සහ
|
||||||
Look4Sat නිර්මාණයට පෙළඹවීමට.
|
Look4Sat නිර්මාණයට පෙළඹවීමට.
|
||||||
\n• <a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a>ට TLE දත්ත සැපයීමට, ඔහුගේ නඩත්තුවෙන් ක්රියාත්මක WEB අඩවිට ප්රවේශය සැපයීම වෙනුවෙන්
|
\n<a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a>ට TLE දත්ත සැපයීමට, ඔහුගේ නඩත්තුවෙන් ක්රියාත්මක WEB අඩවිට ප්රවේශය සැපයීම වෙනුවෙන්
|
||||||
<a href="https://celestrak.com/"> (Celestrak)</a>.
|
<a href="https://celestrak.com/"> (Celestrak)</a>.
|
||||||
\n• <a href="https://libre.space/">Libre Space Foundation</a>ට ඔවුන්ගේ
|
\n<a href="https://libre.space/">Libre Space Foundation</a>ට ඔවුන්ගේ
|
||||||
<a href="https://db.satnogs.org/">SatNOGS</a>Web අඩවියට API හා DB විශාල දත්ත ප්රමාණයක් ලබාදීම සඳහා.
|
<a href="https://db.satnogs.org/">SatNOGS</a>Web අඩවියට API හා DB විශාල දත්ත ප්රමාණයක් ලබාදීම සඳහා.
|
||||||
\n• <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a> කණ්ඩායමට යෙදුම පිළිබඳ ඔවුන්ගේ උනන්දුව සහ කැපවීම ලැබිම සම්බන්ධයෙන්.</string>
|
\n<a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a> කණ්ඩායමට යෙදුම පිළිබඳ ඔවුන්ගේ උනන්දුව සහ කැපවීම ලැබිම සම්බන්ධයෙන්.</string>
|
||||||
<string name="outro_license">මෘදුකාංගය වගකීමක් සමග නොලැබේ</string>
|
<string name="outro_license">මෘදුකාංගය වගකීමක් සමග නොලැබේ</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -158,25 +158,25 @@
|
||||||
<string name="BTremote_output_hint">数据格式</string>
|
<string name="BTremote_output_hint">数据格式</string>
|
||||||
<string name="BTremote_perm_error">请检查蓝牙权限</string>
|
<string name="BTremote_perm_error">请检查蓝牙权限</string>
|
||||||
|
|
||||||
<string name="other_switch_utc">• 以 UTC 时间显示</string>
|
<string name="other_switch_utc">以 UTC 时间显示</string>
|
||||||
<string name="other_switch_update">• 启用卫星数据自动更新</string>
|
<string name="other_switch_update">启用卫星数据自动更新</string>
|
||||||
<string name="other_switch_sweep">• 启用雷达扫描动画</string>
|
<string name="other_switch_sweep">启用雷达扫描动画</string>
|
||||||
<string name="other_switch_sensors">• 使用传感器旋转雷达视图</string>
|
<string name="other_switch_sensors">使用传感器旋转雷达视图</string>
|
||||||
<string name="other_switch_light_theme">• 使用旧版配色方案</string>
|
<string name="other_switch_light_theme">使用旧版配色方案</string>
|
||||||
|
|
||||||
<string name="outro_title">我要感谢:</string>
|
<string name="outro_title">我要感谢:</string>
|
||||||
<string name="outro_thanks">• 感谢下载和使用 Look4Sat!愿你的天空永远晴朗!
|
<string name="outro_thanks">感谢下载和使用 Look4Sat!愿你的天空永远晴朗!
|
||||||
\n• 简体中文版翻译由 <a href="https://github.com/BA7LWN">BA7LWN</a> 于 2022.5.10 创建。
|
\n简体中文版翻译由 <a href="https://github.com/BA7LWN">BA7LWN</a> 于 2022.5.10 创建。
|
||||||
\n• <a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
||||||
and <a href="https://github.com/davidmoten">Dave Moten</a> for creating predict4java lib
|
and <a href="https://github.com/davidmoten">Dave Moten</a> for creating predict4java lib
|
||||||
under the <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>.
|
under the <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>.
|
||||||
\n• <a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
|
\n<a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
|
||||||
satellite tracker that inspired the creation of Look4Sat.
|
satellite tracker that inspired the creation of Look4Sat.
|
||||||
\n• <a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
|
\n<a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
|
||||||
<a href="https://celestrak.com/">Celestrak</a> website that provides access to the TLE data.
|
<a href="https://celestrak.com/">Celestrak</a> website that provides access to the TLE data.
|
||||||
\n• <a href="https://libre.space/">Libre Space Foundation</a> for their
|
\n<a href="https://libre.space/">Libre Space Foundation</a> for their
|
||||||
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of satellite data.
|
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of satellite data.
|
||||||
\n• <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
\n<a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
||||||
team for their interest to the app and the interview published.
|
team for their interest to the app and the interview published.
|
||||||
</string>
|
</string>
|
||||||
<string name="outro_license">该应用程序不提供任何保修.</string>
|
<string name="outro_license">该应用程序不提供任何保修.</string>
|
||||||
|
|
|
@ -173,23 +173,23 @@
|
||||||
<string name="BTremote_output_hint">Data format</string>
|
<string name="BTremote_output_hint">Data format</string>
|
||||||
<string name="BTremote_perm_error">Check your bluetooth permission</string>
|
<string name="BTremote_perm_error">Check your bluetooth permission</string>
|
||||||
|
|
||||||
<string name="other_switch_utc">• Show pass time in UTC</string>
|
<string name="other_switch_utc">Show pass time in UTC</string>
|
||||||
<string name="other_switch_update">• Enable automatic data update</string>
|
<string name="other_switch_update">Enable automatic data update</string>
|
||||||
<string name="other_switch_sweep">• Enable radar sweep animation</string>
|
<string name="other_switch_sweep">Enable radar sweep animation</string>
|
||||||
<string name="other_switch_sensors">• Use sensors to rotate radar view</string>
|
<string name="other_switch_sensors">Use sensors to rotate radar view</string>
|
||||||
<string name="other_switch_light_theme">• Use light theme</string>
|
<string name="other_switch_light_theme">Use light theme</string>
|
||||||
|
|
||||||
<string name="outro_title">I would like to say thanks to:</string>
|
<string name="outro_title">I would like to say thanks to:</string>
|
||||||
<string name="outro_thanks">• You for downloading and using Look4Sat! \nMay your sky always be clear!
|
<string name="outro_thanks">You for downloading and using Look4Sat! \nMay your sky always be clear!
|
||||||
\n• <a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
|
||||||
and <a href="https://github.com/davidmoten">Dave Moten</a> for creating a super useful predict4java library.
|
and <a href="https://github.com/davidmoten">Dave Moten</a> for creating a super useful predict4java library.
|
||||||
\n• <a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
|
\n<a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
|
||||||
tracker that inspired the creation of Look4Sat.
|
tracker that inspired the creation of Look4Sat.
|
||||||
\n• <a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
|
\n<a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
|
||||||
<a href="https://celestrak.com/">Celestrak</a> website that provides access to the TLE data.
|
<a href="https://celestrak.com/">Celestrak</a> website that provides access to the TLE data.
|
||||||
\n• <a href="https://libre.space/">Libre Space Foundation</a> for their
|
\n<a href="https://libre.space/">Libre Space Foundation</a> for their
|
||||||
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of data.
|
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of data.
|
||||||
\n• <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
\n<a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
|
||||||
team for their interest to the app and the interview published.
|
team for their interest to the app and the interview published.
|
||||||
</string>
|
</string>
|
||||||
<string name="outro_license">The app comes with no warranty.</string>
|
<string name="outro_license">The app comes with no warranty.</string>
|
||||||
|
|
|
@ -33,7 +33,7 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
|
||||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
compose-animation = { group = "androidx.compose.animation", name = "animation" }
|
compose-animation = { group = "androidx.compose.animation", name = "animation" }
|
||||||
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive" }
|
compose-material3-navigation = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
|
||||||
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
||||||
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
[bundles]
|
[bundles]
|
||||||
composeAll = [
|
composeAll = [
|
||||||
"compose-animation", "compose-runtime", "compose-tooling", "compose-activity",
|
"compose-animation", "compose-runtime", "compose-tooling", "compose-activity",
|
||||||
"compose-lifecycle", "compose-material3", "compose-material3-adaptive",
|
"compose-lifecycle", "compose-material3", "compose-material3-navigation",
|
||||||
"compose-navigation", "compose-viewmodel"
|
"compose-navigation", "compose-viewmodel"
|
||||||
]
|
]
|
||||||
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]
|
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]
|
||||||
|
|
Ładowanie…
Reference in New Issue