Reverted using SharedElement API and removing NavBar

develop
Arty Bishop 2025-07-20 15:30:04 +01:00
rodzic 688bcbca5e
commit 028cad0418
12 zmienionych plików z 272 dodań i 122 usunięć

Wyświetl plik

@ -1,32 +1,97 @@
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.Text
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.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
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")
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
data object Map : Screen("Map", R.drawable.ic_map, "map")
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
data object Info : Screen("Info", R.drawable.ic_info, "info")
}
@Composable
fun MainScreen() {
val navController: NavHostController = rememberNavController()
Scaffold { innerPadding ->
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
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 = navController,
startDestination = "passes",
navController = innerNavController,
startDestination = Screen.Passes.route,
modifier = Modifier.padding(innerPadding)
) {
satellitesDestination(navController)
passesDestination(navController)
radarDestination(navController)
mapDestination(navController)
settingsDestination(navController)
satellitesDestination(navigateToPasses)
passesDestination(navigateToRadar)
mapDestination()
settingsDestination()
infoDestination()
}
}
}
@Composable
private fun MainNavBar(navController: NavController) {
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings, Screen.Info)
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 }
launchSingleTop = true
restoreState = false
}
}
}
)
}
}
}

Wyświetl plik

@ -0,0 +1,91 @@
package com.rtbishop.look4sat.presentation.info
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
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
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
private fun InfoScreen() {
LazyColumn(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
item { CardCredits() }
}
}
@Preview(showBackground = true)
@Composable
private fun CardCreditsPreview() = MainTheme { CardCredits() }
@Composable
private fun CardCredits(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
ElevatedCard(modifier = modifier.fillMaxWidth()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Text(
text = stringResource(id = R.string.outro_title),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(6.dp)
)
Text(
text = stringResource(
id = R.string.outro_thanks
), fontSize = 16.sp, textAlign = TextAlign.Center
)
Text(
text = stringResource(id = R.string.outro_license),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(6.dp)
)
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton(
onClick = { uriHandler.openUri(LICENSE_URL) },
text = stringResource(id = R.string.btn_license),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = { uriHandler.openUri(POLICY_URL) },
text = stringResource(id = R.string.btn_privacy),
modifier = Modifier.weight(1f)
)
}
}
}
}

Wyświetl plik

@ -37,12 +37,12 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
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 com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
@ -80,8 +80,8 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
}
private val labelRect = Rect()
fun NavGraphBuilder.mapDestination(navController: NavHostController) {
composable("map") {
fun NavGraphBuilder.mapDestination() {
composable(Screen.Map.route) {
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
val mapView = rememberMapViewWithLifecycle()

Wyświetl plik

@ -36,7 +36,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.predict.DeepSpaceObject
@ -44,12 +43,12 @@ 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.InfoDialog
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.radar.navigateToRadar
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -57,16 +56,13 @@ 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(navController: NavHostController) {
composable("passes") {
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.collectAsStateWithLifecycle().value
val navigateToRadar = { catNum: Int, aosTime: Long ->
navController.navigateToRadar(catNum, aosTime)
}
PassesScreen(uiState, navigateToRadar)
}
}

Wyświetl plik

@ -27,6 +27,7 @@ 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
@ -45,21 +46,21 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.utility.toDegrees
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
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.components.getDefaultPass
fun NavGraphBuilder.radarDestination(navController: NavHostController) {
val radarRoute = "radar?catNum={catNum}&aosTime={aosTime}"
fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
val radarArgs = listOf(
navArgument("catNum") { defaultValue = 0 },
navArgument("aosTime") { defaultValue = 0L }
@ -67,14 +68,10 @@ fun NavGraphBuilder.radarDestination(navController: NavHostController) {
composable(radarRoute, radarArgs) {
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
RadarScreen(uiState) { navController.navigateUp() }
RadarScreen(uiState, navigateBack)
}
}
fun NavHostController.navigateToRadar(catNum: Int, aosTime: Long) {
navigate("radar?catNum=${catNum}&aosTime=${aosTime}")
}
@Composable
private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
// BluetoothCIV.init(LocalContext.current)
@ -84,91 +81,94 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
}
}
Column(
modifier = Modifier.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
TimerRow {
CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
}
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) {
uiState.orbitalPos?.let { position ->
ElevatedCard {
RadarViewCompose(
item = position,
items = uiState.satTrack,
azimElev = uiState.orientationValues,
shouldShowSweep = uiState.shouldShowSweep,
shouldUseCompass = uiState.shouldUseCompass
)
}
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
Scaffold { innerPadding ->
val paddingMod = Modifier.padding(innerPadding)
Column(
modifier = paddingMod.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
TimerRow {
CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
}
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) {
uiState.orbitalPos?.let { position ->
ElevatedCard {
RadarViewCompose(
item = position,
items = uiState.satTrack,
azimElev = uiState.orientationValues,
shouldShowSweep = uiState.shouldShowSweep,
shouldUseCompass = uiState.shouldUseCompass
)
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 6.dp, vertical = 4.dp)
) {
RadarTextBottom(
position.altitude,
stringResource(R.string.radar_alt_text),
true
)
RadarTextBottom(
position.distance,
stringResource(R.string.radar_dist_text),
false
)
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()) {
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()
ElevatedCard(modifier = Modifier.fillMaxSize()) {
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

@ -41,23 +41,23 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
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
import com.rtbishop.look4sat.presentation.components.InfoDialog
fun NavGraphBuilder.satellitesDestination(navController: NavHostController) {
composable("satellites") {
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) { navController.navigateUp() }
SatellitesScreen(uiState, navigateToPasses)
}
}

Wyświetl plik

@ -37,22 +37,19 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
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 java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
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.settingsDestination(navController: NavHostController) {
composable("settings") {
fun NavGraphBuilder.settingsDestination() {
composable(Screen.Settings.route) {
val viewModel = viewModel(
modelClass = SettingsViewModel::class.java,
factory = SettingsViewModel.Factory
@ -62,6 +59,9 @@ fun NavGraphBuilder.settingsDestination(navController: NavHostController) {
}
}
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"
@Composable
private fun SettingsScreen(uiState: SettingsState) {
// Permissions setup

Wyświetl plik

@ -69,7 +69,7 @@
<!--Fragments-->
<string name="entries_search_hint">Поиск по Имени / Id</string>
<string name="entries_search_hint">Имя / Id</string>
<string name="passes_error">Нет предстоящих пролётов спутников</string>
<string name="passes_empty_db">Пожалуйста, обновите базу данных спутников через меню Настроек</string>

Wyświetl plik

@ -69,7 +69,7 @@
<!--Fragments-->
<string name="entries_search_hint">නමින් / Id මගින් සොයන්න</string>
<string name="entries_search_hint">Name / Id</string>
<string name="passes_error">ප්‍රදර්ශනය කිරීමට ඉදිරියට එන චන්ද්‍රිකා පසුකර යෑම් නැත.</string>
<string name="passes_empty_db">පෙන්වීමට දත්ත නැත. කරුණාකර දත්ත සමුදාය යාවත්කාලීන කරන්න.</string>

Wyświetl plik

@ -69,7 +69,7 @@
<!--Fragments-->
<string name="entries_search_hint">按名称或 ID 搜索</string>
<string name="entries_search_hint">Name / Id</string>
<string name="passes_error">没有即将过境的卫星</string>
<string name="passes_empty_db">没有数据可显示,请通过设置菜单更新卫星数据。</string>

Wyświetl plik

@ -81,7 +81,7 @@
<!--Fragments-->
<string name="entries_search_hint">Search by Name / Id</string>
<string name="entries_search_hint">Name / Id</string>
<string name="passes_error">There are no upcoming satellite passes to display</string>
<string name="passes_empty_db">No data to show. Please update the database via Settings menu.</string>

Wyświetl plik

@ -1,2 +0,0 @@
Added calendar reminder support by zhou2008
Added Sinhala translation by Dilshan Hapuarachchi