Set up more screens with portrait/landscape TopBar

develop
Arty Bishop 2025-07-26 14:10:01 +01:00
rodzic 761506d2a3
commit 99a63a2a1d
13 zmienionych plików z 302 dodań i 309 usunięć

Wyświetl plik

@ -1,94 +1,75 @@
package com.rtbishop.look4sat.presentation
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.MaterialTheme
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.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
import com.rtbishop.look4sat.presentation.map.mapDestination
import com.rtbishop.look4sat.presentation.passes.passesDestination
import com.rtbishop.look4sat.presentation.radar.radarDestination
import com.rtbishop.look4sat.presentation.satellites.satellitesDestination
import com.rtbishop.look4sat.presentation.settings.settingsDestination
sealed class Screen(var title: String, var icon: Int, var route: String) {
data object Main : Screen("Main", R.drawable.ic_sputnik, "main")
data object Radar : Screen("Radar", R.drawable.ic_sputnik, "radar")
sealed class Screen(val title: String, val icon: Int, val route: String) {
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
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 Settings : Screen("Settings", R.drawable.ic_settings, "settings")
}
@Composable
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) }
}
private val startDestination = Screen.Passes.route
@Composable
private fun NavBarScreen(navigateToRadar: (Int, Long) -> Unit) {
val innerNavController: NavHostController = rememberNavController()
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)
fun MainScreen(navController: NavHostController = rememberNavController()) {
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
NavigationBar {
items.forEach { item ->
NavigationBarItem(
icon = { Icon(painterResource(item.icon), item.title) },
label = { Text(item.title) },
selected = item.route == destinationRoute,
onClick = {
if (item.route == destinationRoute) {
// reselecting the same tab
} else {
navController.navigate(item.route) {
popUpTo(Screen.Passes.route) { saveState = false }
NavigationSuiteScaffold(
navigationSuiteItems = {
items.forEach {
item(
icon = { Icon(painterResource(it.icon), it.title) },
label = { Text(it.title) },
selected = destinationRoute?.contains(it.route) ?: false,
onClick = {
if (destinationRoute?.contains(it.route) ?: false) return@item
navController.navigate(it.route) {
popUpTo(startDestination) { saveState = false }
launchSingleTop = true
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()
}
}

Wyświetl plik

@ -109,7 +109,7 @@ private val darkScheme = darkColorScheme(
// outlineVariant = Color(0xFF121212),
scrim = Color(0xFF000000),
// surfaceBright = Color(0xFF121212),
surfaceContainer = Color(0xFF242424), // navBar background
surfaceContainer = Color(0xFF121212), // navBar background
// surfaceContainerHigh = Color(0xFF121212),
surfaceContainerHighest = Color(0xFF242424), // filled card background
surfaceContainerLow = Color(0xFF242424), // elevated card background

Wyświetl plik

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
@ -283,3 +284,13 @@ fun isVerticalLayout(): Boolean {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
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)
}
}

Wyświetl plik

@ -57,6 +57,8 @@ import org.osmdroid.views.overlay.Polyline
import androidx.core.graphics.toColorInt
import androidx.core.graphics.createBitmap
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 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 isTimeAos = uiState.value.mapData?.isTimeAos ?: true
val osmInfo = "© OpenStreetMap contributors"
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TopBar {
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
TopBar {
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
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)) {
Box(contentAlignment = Alignment.BottomCenter) {
LaunchedEffect(uiState.value.track) {

Wyświetl plik

@ -11,8 +11,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
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.TopBar
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
import com.rtbishop.look4sat.presentation.common.layoutPadding
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -91,7 +93,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U
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()) {
TopBar {
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 ->
if (pass.isDeepSpace) {
DeepSpacePass(
pass = pass,
navigateToRadar = navigateToRadar,
modifier = Modifier.animateItem()
)
} else {
NearEarthPass(
pass = pass,
navigateToRadar = navigateToRadar,
modifier = Modifier.animateItem()
)
}
NearEarthPass(
pass = pass,
navigateToRadar = navigateToRadar,
modifier = Modifier.animateItem()
)
}
}
}
@ -159,114 +156,16 @@ private fun PassesList(
@Preview(showBackground = true)
@Composable
private fun DeepSpacePassPreview() {
val data = OrbitalData(
"Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
)
val data = OrbitalData("Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0)
val satellite = DeepSpaceObject(data)
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
MainTheme { DeepSpacePass(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
)
}
}
}
}
}
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
}
@Preview(showBackground = true)
@Composable
private fun NearEarthPassPreview() {
val data = OrbitalData(
"Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
)
val data = OrbitalData("Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0)
val satellite = NearEarthObject(data)
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
@ -281,7 +180,7 @@ private fun NearEarthPass(
val passSatId = stringResource(id = R.string.pass_satId, pass.catNum)
Surface(color = MaterialTheme.colorScheme.background, 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) }) {
Column(
verticalArrangement = Arrangement.spacedBy(1.dp),
@ -325,10 +224,14 @@ private fun NearEarthPass(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = sdfDate.format(Date(pass.aosTime)),
fontSize = 15.sp
)
if (pass.isDeepSpace) {
Text(text = "DeepSpace", fontSize = 15.sp)
} else {
Text(
text = sdfDate.format(Date(pass.aosTime)),
fontSize = 15.sp
)
}
}
Row(
modifier = Modifier.weight(1f),

Wyświetl plik

@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
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.TopBar
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 radarArgs = listOf(
navArgument("catNum") { defaultValue = 0 },
@ -68,31 +69,36 @@ fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
composable(radarRoute, radarArgs) {
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
RadarScreen(uiState, navigateBack)
RadarScreen(uiState, navigateUp)
}
}
@Composable
private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
// BluetoothCIV.init(LocalContext.current)
val addToCalendar: () -> Unit = {
uiState.currentPass?.let { pass ->
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
}
}
Scaffold { innerPadding ->
val paddingMod = Modifier.padding(innerPadding)
Column(
modifier = paddingMod.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
val upcomingPass = uiState.currentPass ?: getDefaultPass()
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
TopBar {
IconCard(onClick = navigateBack, iconId = R.drawable.ic_back)
IconCard(onClick = navigateUp, iconId = R.drawable.ic_back)
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
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)) {
uiState.orbitalPos?.let { position ->
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()
}
}
}
}
}
}
}
}

Wyświetl plik

@ -14,6 +14,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.text.BasicTextField
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.presentation.MainTheme
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.IconCard
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) {
val viewModel = viewModel(
modelClass = SatellitesViewModel::class.java,
factory = SatellitesViewModel.Factory
modelClass = SatellitesViewModel::class.java, factory = SatellitesViewModel.Factory
)
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
SatellitesScreen(uiState, navigateToPasses)
SatellitesScreen(uiState, navigateUp)
}
}
@Composable
private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> Unit) {
private fun SatellitesScreen(uiState: SatellitesState, navigateUp: () -> Unit) {
val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) }
if (uiState.isDialogShown) {
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 selectAll = { uiState.takeAction(SatellitesAction.SelectAll) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TopBar(setQuery = { newQuery: String ->
uiState.takeAction(SatellitesAction.SearchFor(newQuery))
}, saveSelection = {
uiState.takeAction(SatellitesAction.SaveSelection)
navigateToPasses()
})
MiddleBar(uiState.currentTypes, { toggleDialog() }, { unselectAll() }, { selectAll() })
val setQuery = { newQuery: String -> uiState.takeAction(SatellitesAction.SearchFor(newQuery)) }
val saveSelection = { uiState.takeAction(SatellitesAction.SaveSelection).also { navigateUp() } }
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
TopBar {
TypeCard(types = uiState.currentTypes, toggleDialog, modifier = Modifier.weight(1f))
SaveButton(saveSelection = saveSelection, modifier = Modifier.height(48.dp))
}
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()) {
if (uiState.isLoading) {
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
private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) {
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
private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
ElevatedCard(modifier = modifier) {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp),
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(48.dp)
@ -221,7 +214,8 @@ private fun SatellitePreview() {
@Composable
private fun Satellite(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) {
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) }) {
Surface(modifier = Modifier.padding(bottom = 1.dp)) {
Row(
@ -263,7 +257,7 @@ private fun SatellitesPreview() {
@Composable
fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
LazyColumn {
LazyVerticalGrid(columns = GridCells.Adaptive(320.dp)) {
items(items = items, key = { item -> item.catnum }) { entry ->
Satellite(entry, onSelected, Modifier.animateItem())
}

Wyświetl plik

@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.Icon
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.Screen
import com.rtbishop.look4sat.presentation.common.CardButton
import com.rtbishop.look4sat.presentation.common.layoutPadding
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -130,8 +133,10 @@ private fun SettingsScreen(uiState: SettingsState) {
}
// Screen setup
LazyColumn(
modifier = Modifier.padding(6.dp),
LazyVerticalGrid(
columns = GridCells.Adaptive(320.dp),
modifier = Modifier.layoutPadding(),
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
item { CardAbout(uiState.appVersionName, uiState.sendSystemAction) }
@ -461,17 +466,17 @@ private fun OtherCard(
Text(text = stringResource(id = R.string.other_switch_sensors))
Switch(checked = settings.stateOfSensors, onCheckedChange = { toggleSensor(it) })
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.other_switch_light_theme))
Switch(
checked = settings.stateOfLightTheme,
onCheckedChange = { toggleLightTheme(it) }
)
}
// Row(
// horizontalArrangement = Arrangement.SpaceBetween,
// verticalAlignment = Alignment.CenterVertically,
// modifier = Modifier.fillMaxWidth()
// ) {
// Text(text = stringResource(id = R.string.other_switch_light_theme))
// Switch(
// checked = settings.stateOfLightTheme,
// onCheckedChange = { toggleLightTheme(it) }
// )
// }
}
}
}

Wyświetl plik

@ -158,24 +158,24 @@
<string name="BTremote_output_hint">Формат данных</string>
<string name="BTremote_perm_error">Нет разрешения использовать bluetooth</string>
<string name="other_switch_utc">Показывать время по UTC</string>
<string name="other_switch_update">Обновлять данные автоматически</string>
<string name="other_switch_sweep">Показывать анимацию радара</string>
<string name="other_switch_sensors">Использовать сенсоры устройства</string>
<string name="other_switch_light_theme">Использовать светлую тему</string>
<string name="other_switch_utc">Показывать время по UTC</string>
<string name="other_switch_update">Обновлять данные автоматически</string>
<string name="other_switch_sweep">Показывать анимацию радара</string>
<string name="other_switch_sensors">Использовать сенсоры устройства</string>
<string name="other_switch_light_theme">Использовать светлую тему</string>
<string name="outro_title">Я хотел бы сказать спасибо:</string>
<string name="outro_thanks">Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой!
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a> и
<string name="outro_thanks">Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой!
\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://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, которая послужила для меня вдохновением.
\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.
\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 и базу данных с информацией о спутниках.
\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 name="outro_license">Это ПО поставляется без гарантий.</string>

Wyświetl plik

@ -158,22 +158,22 @@
<string name="BTremote_output_hint">දත්ත අකාරය</string>
<string name="BTremote_perm_error">Bluetooth අවසර පරික්‍ෂා ක°</string>
<string name="other_switch_utc">පසුකර වේලාවන් UTC මගින්</string>
<string name="other_switch_update">ස්වයං දත්ත යාවත්කාලීනය</string>
<string name="other_switch_sweep">radar sweep සජීවිකරණය සබල කරන්න</string>
<string name="other_switch_sensors">Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න </string>
<string name="other_switch_light_theme">සැහැල්ලු තේමාව භාවිතා කරන්න</string>
<string name="other_switch_utc">පසුකර වේලාවන් UTC මගින්</string>
<string name="other_switch_update">ස්වයං දත්ත යාවත්කාලීනය</string>
<string name="other_switch_sweep">radar sweep සජීවිකරණය සබල කරන්න</string>
<string name="other_switch_sensors">Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න </string>
<string name="other_switch_light_theme">සැහැල්ලු තේමාව භාවිතා කරන්න</string>
<string name="outro_title">මම ස්තුති කිරීමට කැමති:</string>
<string name="outro_thanks">බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි!
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
<string name="outro_thanks">බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි!
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
සහ <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 නිර්මාණය⁣ට පෙළඹවීමට.
\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>.
\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 විශාල දත්ත ප්‍රමාණයක් ලබාදීම සඳහා.
\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>
</resources>

Wyświetl plik

@ -158,25 +158,25 @@
<string name="BTremote_output_hint">数据格式</string>
<string name="BTremote_perm_error">请检查蓝牙权限</string>
<string name="other_switch_utc">以 UTC 时间显示</string>
<string name="other_switch_update">启用卫星数据自动更新</string>
<string name="other_switch_sweep">启用雷达扫描动画</string>
<string name="other_switch_sensors">使用传感器旋转雷达视图</string>
<string name="other_switch_light_theme">使用旧版配色方案</string>
<string name="other_switch_utc">以 UTC 时间显示</string>
<string name="other_switch_update">启用卫星数据自动更新</string>
<string name="other_switch_sweep">启用雷达扫描动画</string>
<string name="other_switch_sensors">使用传感器旋转雷达视图</string>
<string name="other_switch_light_theme">使用旧版配色方案</string>
<string name="outro_title">我要感谢:</string>
<string name="outro_thanks">感谢下载和使用 Look4Sat愿你的天空永远晴朗
\n简体中文版翻译由 <a href="https://github.com/BA7LWN">BA7LWN</a> 于 2022.5.10 创建。
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
<string name="outro_thanks">感谢下载和使用 Look4Sat愿你的天空永远晴朗
\n简体中文版翻译由 <a href="https://github.com/BA7LWN">BA7LWN</a> 于 2022.5.10 创建。
\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
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.
\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.
\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.
\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.
</string>
<string name="outro_license">该应用程序不提供任何保修.</string>

Wyświetl plik

@ -173,23 +173,23 @@
<string name="BTremote_output_hint">Data format</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_update">Enable automatic data update</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_light_theme">Use light theme</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_sweep">Enable radar sweep animation</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="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!
\n<a href="https://github.com/g4dpz">David A. B. Johnson</a>
<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>
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.
\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.
\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.
\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.
</string>
<string name="outro_license">The app comes with no warranty.</string>

Wyświetl plik

@ -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-animation = { group = "androidx.compose.animation", name = "animation" }
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-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
@ -68,7 +68,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
[bundles]
composeAll = [
"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"
]
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]