kopia lustrzana https://github.com/rt-bishop/Look4Sat
Added several tweaks to Screens, Dialogs and Layouts
rodzic
674712509d
commit
1688b68c3c
|
@ -1,6 +1,5 @@
|
|||
package com.rtbishop.look4sat.presentation
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
|
@ -10,9 +9,8 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
|
@ -20,16 +18,14 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.presentation.info.InfoScreen
|
||||
import com.rtbishop.look4sat.presentation.map.MapScreen
|
||||
import com.rtbishop.look4sat.presentation.passes.PassesScreen
|
||||
import com.rtbishop.look4sat.presentation.passes.PassesViewModel
|
||||
import com.rtbishop.look4sat.presentation.radar.RadarScreen
|
||||
import com.rtbishop.look4sat.presentation.satellites.SatellitesScreen
|
||||
import com.rtbishop.look4sat.presentation.satellites.SatellitesViewModel
|
||||
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
||||
import com.rtbishop.look4sat.presentation.info.infoDestination
|
||||
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
|
||||
|
||||
private sealed class Screen(var title: String, var icon: Int, var route: String) {
|
||||
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")
|
||||
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
|
||||
|
@ -42,46 +38,40 @@ private sealed class Screen(var title: String, var icon: Int, var route: String)
|
|||
@Composable
|
||||
fun MainScreen() {
|
||||
val outerNavController: NavHostController = rememberNavController()
|
||||
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
||||
val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 },
|
||||
navArgument("aosTime") { defaultValue = 0L })
|
||||
val navToRadar = { catNum: Int, aosTime: Long ->
|
||||
val navRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||
outerNavController.navigate(navRoute)
|
||||
val navigateToRadar = { catNum: Int, aosTime: Long ->
|
||||
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||
outerNavController.navigate(routeWithParams)
|
||||
}
|
||||
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
||||
val radarArgs = listOf(
|
||||
navArgument("catNum") { defaultValue = 0 },
|
||||
navArgument("aosTime") { defaultValue = 0L }
|
||||
)
|
||||
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
|
||||
composable(Screen.Main.route) { NavBarScreen(navToRadar) }
|
||||
composable(radarRoute, radarArgs) { RadarScreen() }
|
||||
mainDestination(navigateToRadar)
|
||||
radarDestination(radarRoute, radarArgs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NavGraphBuilder.mainDestination(navigateToRadar: (Int, Long) -> Unit) {
|
||||
composable(Screen.Main.route) { NavBarScreen(navigateToRadar) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavBarScreen(navToRadar: (Int, Long) -> Unit) {
|
||||
private fun NavBarScreen(navigateToRadar: (Int, Long) -> Unit) {
|
||||
val innerNavController: NavHostController = rememberNavController()
|
||||
val navToPasses = { innerNavController.navigate(Screen.Passes.route) }
|
||||
Scaffold(bottomBar = { MainNavBar(navController = innerNavController) }) { innerPadding ->
|
||||
Box(modifier = Modifier.padding(innerPadding)) {
|
||||
NavHost(navController = innerNavController, startDestination = Screen.Passes.route) {
|
||||
composable(Screen.Satellites.route) {
|
||||
val viewModel = viewModel(
|
||||
modelClass = SatellitesViewModel::class.java,
|
||||
factory = SatellitesViewModel.Factory
|
||||
)
|
||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||
SatellitesScreen(uiState, navToPasses)
|
||||
}
|
||||
composable(Screen.Passes.route) {
|
||||
val viewModel = viewModel(
|
||||
modelClass = PassesViewModel::class.java,
|
||||
factory = PassesViewModel.Factory
|
||||
)
|
||||
val uiState = viewModel.uiState.value
|
||||
PassesScreen(uiState, navToRadar)
|
||||
}
|
||||
composable(Screen.Map.route) { MapScreen() }
|
||||
composable(Screen.Settings.route) { SettingsScreen() }
|
||||
composable(Screen.Info.route) { InfoScreen() }
|
||||
}
|
||||
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()
|
||||
infoDestination()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,14 +79,13 @@ private fun NavBarScreen(navToRadar: (Int, Long) -> Unit) {
|
|||
@Composable
|
||||
private fun MainNavBar(navController: NavController) {
|
||||
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings, Screen.Info)
|
||||
val currentBackStackEntry = navController.currentBackStackEntryAsState()
|
||||
val currentRoute = currentBackStackEntry.value?.destination?.route
|
||||
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
||||
NavigationBar {
|
||||
items.forEach { item ->
|
||||
NavigationBarItem(
|
||||
icon = { Icon(painterResource(item.icon), item.title) },
|
||||
label = { Text(item.title) },
|
||||
selected = currentRoute?.contains(item.route) ?: false,
|
||||
selected = destinationRoute?.contains(item.route) ?: false,
|
||||
onClick = {
|
||||
navController.navigate(item.route) {
|
||||
popUpTo(navController.graph.startDestinationId) { saveState = false }
|
||||
|
|
|
@ -21,16 +21,23 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.presentation.components.CardButton
|
||||
import com.rtbishop.look4sat.presentation.components.gotoUrl
|
||||
|
||||
private const val POLICY_URL = "https://sites.google.com/view/look4sat-privacy-policy/home"
|
||||
private const val LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html"
|
||||
|
||||
fun NavGraphBuilder.infoDestination() {
|
||||
composable(Screen.Info.route) { InfoScreen() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoScreen() {
|
||||
private fun InfoScreen() {
|
||||
LazyColumn(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
item { CardCredits() }
|
||||
}
|
||||
|
|
|
@ -27,10 +27,13 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import org.osmdroid.tileprovider.tilesource.XYTileSource
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
|
@ -61,8 +64,12 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|||
}
|
||||
private val labelRect = Rect()
|
||||
|
||||
fun NavGraphBuilder.mapDestination() {
|
||||
composable(Screen.Map.route) { MapScreen() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MapScreen() {
|
||||
private fun MapScreen() {
|
||||
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
|
||||
viewModel.selectDefaultSatellite(-1)
|
||||
val stationPos = viewModel.stationPosition.collectAsState(initial = null)
|
||||
|
|
|
@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
@ -32,16 +31,13 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
|||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
|
@ -55,6 +51,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.components.CardButton
|
||||
|
@ -73,80 +70,79 @@ private fun PassesDialogPreview() {
|
|||
MainTheme { PassesDialog(24, 16.0, {}) { _, _ -> } }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PassesDialog(hours: Int, elev: Double, dismiss: () -> Unit, save: (Int, Double) -> Unit) {
|
||||
val maxWidthModifier = Modifier.fillMaxWidth()
|
||||
val hoursValue = remember { mutableIntStateOf(hours) }
|
||||
val elevationValue = remember { mutableIntStateOf(elev.toInt()) }
|
||||
ModalBottomSheet(onDismissRequest = { dismiss() }) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = maxWidthModifier.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 32.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = maxWidthModifier
|
||||
Dialog(onDismissRequest = { dismiss() }) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = maxWidthModifier.padding(16.dp)
|
||||
) {
|
||||
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
||||
Text(text = "Passes", fontSize = 18.sp, color = MaterialTheme.colorScheme.primary)
|
||||
CardButton(
|
||||
onClick = {
|
||||
save(hoursValue.intValue, elevationValue.intValue.toDouble())
|
||||
dismiss()
|
||||
}, text = stringResource(id = R.string.btn_accept)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
|
||||
Text(text = "Show passes above", fontSize = 16.sp, modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_elevation),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(bottom = 4.dp)
|
||||
)
|
||||
Text(
|
||||
text = "${elevationValue.intValue}°",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
Slider(
|
||||
value = elevationValue.intValue.toFloat(),
|
||||
onValueChange = { elevationValue.intValue = it.toInt() },
|
||||
valueRange = 0f..60f
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
|
||||
Text(text = "Show passes within", fontSize = 16.sp, modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_time),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(bottom = 4.dp)
|
||||
)
|
||||
Text(
|
||||
text = "${hoursValue.intValue}h",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
Slider(
|
||||
value = hoursValue.intValue.toFloat(),
|
||||
onValueChange = { hoursValue.intValue = it.toInt() },
|
||||
valueRange = 1f..240f
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = maxWidthModifier
|
||||
) {
|
||||
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
||||
CardButton(
|
||||
onClick = {
|
||||
save(hoursValue.intValue, elevationValue.intValue.toDouble())
|
||||
dismiss()
|
||||
}, text = stringResource(id = R.string.btn_accept)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
|
||||
Text(text = "Show passes above", fontSize = 16.sp, modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_elevation),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(bottom = 4.dp)
|
||||
)
|
||||
Text(
|
||||
text = "${elevationValue.intValue}°",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
Slider(
|
||||
value = elevationValue.intValue.toFloat(),
|
||||
onValueChange = { elevationValue.intValue = it.toInt() },
|
||||
valueRange = 0f..60f,
|
||||
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
|
||||
Text(text = "Show passes within", fontSize = 16.sp, modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_time),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(bottom = 4.dp)
|
||||
)
|
||||
Text(
|
||||
text = "${hoursValue.intValue}h",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
Slider(
|
||||
value = hoursValue.intValue.toFloat(),
|
||||
onValueChange = { hoursValue.intValue = it.toInt() },
|
||||
valueRange = 1f..240f,
|
||||
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,73 +153,69 @@ private fun RadiosDialogPreview() {
|
|||
MainTheme { RadiosDialog(emptyList(), {}) { _ -> } }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RadiosDialog(modes: List<String>, dismiss: () -> Unit, save: (List<String>) -> Unit) {
|
||||
val maxWidthModifier = Modifier.fillMaxWidth(1f)
|
||||
val selected = remember { mutableStateListOf<String>().apply { addAll(modes) } }
|
||||
val select = { mode: String -> if (selected.contains(mode)) selected.remove(mode) else selected.add(mode) }
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { dismiss() },
|
||||
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||
modifier = Modifier.fillMaxHeight(0.80f)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = maxWidthModifier.padding(horizontal = 12.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = maxWidthModifier
|
||||
Dialog(onDismissRequest = { dismiss() }) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = maxWidthModifier.padding(vertical = 16.dp)
|
||||
) {
|
||||
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
||||
Text(text = "Radios", fontSize = 18.sp, color = MaterialTheme.colorScheme.primary)
|
||||
CardButton(
|
||||
onClick = {
|
||||
save(selected.toList())
|
||||
dismiss()
|
||||
}, text = stringResource(id = R.string.btn_accept)
|
||||
)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(1),
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.weight(1f),
|
||||
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
item { HorizontalDivider(thickness = 0.dp, color = MaterialTheme.colorScheme.surface) }
|
||||
itemsIndexed(allModes) { index, item ->
|
||||
Surface {
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable { select(item) }) {
|
||||
Text(
|
||||
text = "$index).",
|
||||
modifier = Modifier.padding(start = 8.dp, end = 6.dp),
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = item,
|
||||
modifier = Modifier.weight(1f),
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Checkbox(
|
||||
checked = selected.contains(item),
|
||||
onCheckedChange = null,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(1),
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.weight(1f),
|
||||
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
item { HorizontalDivider(thickness = 0.dp, color = MaterialTheme.colorScheme.surface) }
|
||||
itemsIndexed(allModes) { index, item ->
|
||||
Surface {
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable { select(item) }) {
|
||||
Text(
|
||||
text = "$index).",
|
||||
modifier = Modifier.padding(start = 8.dp, end = 6.dp),
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = item,
|
||||
modifier = Modifier.weight(1f),
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Checkbox(
|
||||
checked = selected.contains(item),
|
||||
onCheckedChange = null,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider(thickness = 24.dp, color = MaterialTheme.colorScheme.surface) }
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = maxWidthModifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
||||
CardButton(
|
||||
onClick = {
|
||||
save(selected.toList())
|
||||
dismiss()
|
||||
}, text = stringResource(id = R.string.btn_accept)
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider(thickness = 24.dp, color = MaterialTheme.colorScheme.surface) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,12 +33,16 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.predict.DeepSpaceObject
|
||||
import com.rtbishop.look4sat.domain.predict.NearEarthObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.presentation.components.CardIcon
|
||||
import com.rtbishop.look4sat.presentation.components.NextPassRow
|
||||
import com.rtbishop.look4sat.presentation.components.TimerBar
|
||||
|
@ -50,8 +54,19 @@ import java.util.Locale
|
|||
private val sdfDate = SimpleDateFormat("EEE dd MMM", Locale.ENGLISH)
|
||||
private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
|
||||
|
||||
fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) {
|
||||
composable(Screen.Passes.route) {
|
||||
val viewModel = viewModel(
|
||||
modelClass = PassesViewModel::class.java,
|
||||
factory = PassesViewModel.Factory
|
||||
)
|
||||
val uiState = viewModel.uiState.value
|
||||
PassesScreen(uiState, navigateToRadar)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
|
||||
private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> Unit) {
|
||||
val refreshPasses = { uiState.takeAction(PassesAction.RefreshPasses) }
|
||||
val showPassesDialog = { uiState.takeAction(PassesAction.TogglePassesDialog) }
|
||||
val showRadiosDialog = { uiState.takeAction(PassesAction.ToggleRadiosDialog) }
|
||||
|
@ -72,7 +87,7 @@ fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
|
|||
CardIcon(onClick = { showRadiosDialog() }, iconId = R.drawable.ic_satellite)
|
||||
}
|
||||
NextPassRow(pass = uiState.nextPass)
|
||||
PassesList(uiState.isRefreshing, uiState.itemsList, navToRadar, refreshPasses)
|
||||
PassesList(uiState.isRefreshing, uiState.itemsList, navigateToRadar, refreshPasses)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +96,7 @@ fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
|
|||
private fun PassesList(
|
||||
isRefreshing: Boolean,
|
||||
passes: List<OrbitalPass>,
|
||||
navToRadar: (Int, Long) -> Unit,
|
||||
navigateToRadar: (Int, Long) -> Unit,
|
||||
refreshPasses: () -> Unit
|
||||
) {
|
||||
val refreshState = rememberPullToRefreshState()
|
||||
|
@ -102,11 +117,19 @@ private fun PassesList(
|
|||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass ->
|
||||
PassItem(
|
||||
pass = pass,
|
||||
navToRadar = navToRadar,
|
||||
modifier = Modifier.animateItem()
|
||||
)
|
||||
if (pass.isDeepSpace) {
|
||||
DeepSpacePass(
|
||||
pass = pass,
|
||||
navigateToRadar = navigateToRadar,
|
||||
modifier = Modifier.animateItem()
|
||||
)
|
||||
} else {
|
||||
NearEarthPass(
|
||||
pass = pass,
|
||||
navigateToRadar = navigateToRadar,
|
||||
modifier = Modifier.animateItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +144,113 @@ private fun DeepSpacePassPreview() {
|
|||
)
|
||||
val satellite = DeepSpaceObject(data)
|
||||
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
||||
MainTheme { PassItem(pass = pass, { _, _ -> }) }
|
||||
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
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_arrow),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
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
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_direction),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.pass_aosLos,
|
||||
pass.aosAzimuth.toInt(),
|
||||
pass.losAzimuth.toInt()
|
||||
),
|
||||
fontSize = 15.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
@ -132,16 +261,20 @@ private fun NearEarthPassPreview() {
|
|||
)
|
||||
val satellite = NearEarthObject(data)
|
||||
val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f)
|
||||
MainTheme { PassItem(pass = pass, { _, _ -> }) }
|
||||
MainTheme { NearEarthPass(pass = pass, { _, _ -> }) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PassItem(pass: OrbitalPass, navToRadar: (Int, Long) -> Unit, modifier: Modifier = Modifier) {
|
||||
private fun NearEarthPass(
|
||||
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 { navToRadar(pass.catNum, pass.aosTime) }) {
|
||||
.clickable { navigateToRadar(pass.catNum, pass.aosTime) }) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp),
|
||||
modifier = Modifier
|
||||
|
|
|
@ -26,11 +26,18 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NamedNavArgument
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
|
||||
fun NavGraphBuilder.radarDestination(radarRoute: String, radarArgs: List<NamedNavArgument>) {
|
||||
composable(radarRoute, radarArgs) { RadarScreen() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RadarScreen() {
|
||||
private fun RadarScreen() {
|
||||
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
||||
val currentPass = viewModel.getPass().collectAsState(null).value
|
||||
val id = currentPass?.catNum ?: 99999
|
||||
|
|
|
@ -36,14 +36,30 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
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.components.CardIcon
|
||||
import com.rtbishop.look4sat.presentation.components.CardLoadingIndicator
|
||||
|
||||
fun NavGraphBuilder.satellitesDestination(navigateToPasses: () -> Unit) {
|
||||
composable(Screen.Satellites.route) {
|
||||
val viewModel = viewModel(
|
||||
modelClass = SatellitesViewModel::class.java,
|
||||
factory = SatellitesViewModel.Factory
|
||||
)
|
||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||
SatellitesScreen(uiState, navigateToPasses)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SatellitesScreen(uiState: SatellitesState, navToPasses: () -> Unit) {
|
||||
private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> Unit) {
|
||||
val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) }
|
||||
if (uiState.isDialogShown) {
|
||||
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
|
||||
|
@ -57,7 +73,7 @@ fun SatellitesScreen(uiState: SatellitesState, navToPasses: () -> Unit) {
|
|||
uiState.takeAction(SatellitesAction.SearchFor(newQuery))
|
||||
}, saveSelection = {
|
||||
uiState.takeAction(SatellitesAction.SaveSelection)
|
||||
navToPasses()
|
||||
navigateToPasses()
|
||||
})
|
||||
MiddleBar(uiState.currentType, { toggleDialog() }, { unselectAll() }, { selectAll() })
|
||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||
|
|
|
@ -33,10 +33,13 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.model.OtherSettings
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.Screen
|
||||
import com.rtbishop.look4sat.presentation.components.CardButton
|
||||
import com.rtbishop.look4sat.presentation.components.gotoUrl
|
||||
import com.rtbishop.look4sat.presentation.components.showToast
|
||||
|
@ -48,8 +51,12 @@ private const val GITHUB_URL = "https://github.com/rt-bishop/Look4Sat/"
|
|||
private const val DONATE_URL = "https://ko-fi.com/rt_bishop"
|
||||
private const val FDROID_URL = "https://f-droid.org/en/packages/com.rtbishop.look4sat/"
|
||||
|
||||
fun NavGraphBuilder.settingsDestination() {
|
||||
composable(Screen.Settings.route) { SettingsScreen() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen() {
|
||||
private fun SettingsScreen() {
|
||||
val viewModel = viewModel(SettingsViewModel::class.java, factory = SettingsViewModel.Factory)
|
||||
val context = LocalContext.current
|
||||
|
||||
|
@ -446,16 +453,11 @@ private fun setUpdateTime(updateTime: Long): String {
|
|||
|
||||
@Composable
|
||||
private fun UpdateIndicator(isUpdating: Boolean, modifier: Modifier = Modifier) = if (isUpdating) {
|
||||
LinearProgressIndicator(
|
||||
modifier = modifier.padding(start = 6.dp),
|
||||
// color = MaterialTheme.colorScheme.primary,
|
||||
// trackColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
LinearProgressIndicator(modifier = modifier.padding(start = 6.dp))
|
||||
} else {
|
||||
LinearProgressIndicator(
|
||||
progress = { 0f },
|
||||
modifier = modifier.padding(start = 6.dp),
|
||||
// color = MaterialTheme.colorScheme.primary,
|
||||
// trackColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
drawStopIndicator = {},
|
||||
modifier = modifier.padding(start = 6.dp)
|
||||
)
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue