From 266fbfed1f2070d58f4afb2e23f5880f30e03371 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 11 Oct 2025 20:42:03 +0100 Subject: [PATCH] Added more tweaks to components and dependencies --- .../look4sat/presentation/MainScreen.kt | 9 +- .../presentation/common/Components.kt | 126 +++++++++++------- .../look4sat/presentation/map/MapScreen.kt | 2 +- .../presentation/passes/PassesScreen.kt | 77 ++++++----- .../presentation/radar/RadarScreen.kt | 2 +- gradle/libs.versions.toml | 6 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 7 files changed, 135 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt index 3c5803c4..585464b7 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt @@ -1,9 +1,6 @@ package com.rtbishop.look4sat.presentation -import androidx.compose.foundation.background -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.rememberNavController @@ -39,11 +36,7 @@ fun MainScreen(navController: NavHostController = rememberNavController()) { openMap = { navController.navigate(Screen.Map.route) }, openSettings = { navController.navigate(Screen.Settings.route) } ) - NavHost( - navController = navController, - startDestination = Screen.Passes.route, - modifier = Modifier.background(color = MaterialTheme.colorScheme.background) - ) { + NavHost(navController = navController, startDestination = Screen.Passes.route) { satellitesDestination { navController.navigateUp() } passesDestination(navActions) radarDestination { navController.navigateUp() } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt index 81d734c3..79b43f5a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt @@ -1,9 +1,12 @@ package com.rtbishop.look4sat.presentation.common +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.MarqueeSpacing -import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee -import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -18,12 +21,15 @@ 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.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ElevatedButton import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -98,17 +104,17 @@ fun RowScope.TimerRow(timeString: String, isTimeAos: Boolean) { @Composable @Preview(showBackground = true) private fun NextPassRowPreview() = MainTheme { - NextPassRow(pass = getDefaultPass()) + TopBar { NextPassRow(pass = getDefaultPass()) } } @Composable -fun NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier) { - ElevatedCard(modifier = modifier.height(48.dp)) { +fun RowScope.NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier) { + ElevatedCard(modifier = modifier.height(48.dp).weight(1f)) { Column( verticalArrangement = Arrangement.spacedBy((-2).dp), modifier = Modifier .fillMaxSize() - .background(color = MaterialTheme.colorScheme.surface) +// .background(color = MaterialTheme.colorScheme.background) .padding(start = 6.dp, top = 1.dp, end = 6.dp, bottom = 0.dp) ) { val passSatId = stringResource(id = R.string.pass_satId, pass.catNum) @@ -319,58 +325,86 @@ fun Modifier.layoutPadding(): Modifier { } @Composable -fun ScreenColumn(floatingBar: @Composable () -> Unit = {}, content: @Composable (Boolean) -> Unit) { - Box(contentAlignment = Alignment.BottomCenter) { - Column( - modifier = Modifier.layoutPadding(), - verticalArrangement = Arrangement.spacedBy(LocalSpacing.current.extraSmall) - ) { content(isVerticalLayout()) } - floatingBar() - } -} - -@Composable -fun PrimaryButton(action: () -> Unit, resId: Int, modifier: Modifier = Modifier) { - val clickableMod = modifier.clickable { action() } - val colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary) - ElevatedCard(modifier = Modifier.size(96.dp, 48.dp), colors = colors, shape = MaterialTheme.shapes.large) { - Box(modifier = clickableMod.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(painter = painterResource(resId), contentDescription = null) +fun ScreenColumn( + topBar: @Composable (Boolean) -> Unit = {}, + floatingBar: @Composable () -> Unit = {}, + content: @Composable (Boolean) -> Unit = {} +) { + val surfaceCorners = LocalSpacing.current.large + val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass + val isVerticalLayout = windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT + Surface(color = MaterialTheme.colorScheme.background) { + Box(modifier = Modifier.layoutPadding(), contentAlignment = Alignment.BottomCenter) { + Column(verticalArrangement = Arrangement.spacedBy(LocalSpacing.current.extraSmall)) { + topBar(isVerticalLayout) + Surface( + modifier = Modifier.fillMaxSize(), + shape = RoundedCornerShape(topStart = surfaceCorners, topEnd = surfaceCorners), + color = MaterialTheme.colorScheme.surfaceContainer + ) { content(isVerticalLayout) } + } + floatingBar() } } } @Composable -fun SecondaryButton(action: () -> Unit, resId: Int, modifier: Modifier = Modifier) { - val clickableMod = modifier.clickable { action() } - ElevatedCard(modifier = Modifier.size(48.dp), shape = MaterialTheme.shapes.large) { - Box(modifier = clickableMod.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(painter = painterResource(resId), contentDescription = null) - } +fun TopBar( + isVerticalLayout: Boolean, + startAction: @Composable () -> Unit, + topInfo: @Composable (RowScope.() -> Unit), + bottomInfo: @Composable (RowScope.() -> Unit), + endAction: @Composable () -> Unit +) { + val topBarRow = @Composable { content: @Composable RowScope.() -> Unit -> + Row( + modifier = Modifier.height(48.dp), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically + ) { content() } + } + if (isVerticalLayout) { + topBarRow { startAction().also { topInfo() }.also { endAction() } } + topBarRow { bottomInfo() } + } else { + topBarRow { startAction().also { topInfo() }.also { bottomInfo() }.also { endAction() } } } } @Composable fun FloatingBar( - startAction: () -> Unit, startIconResId: Int, - centerAction: () -> Unit, centerIconResId: Int, - endAction: () -> Unit, endIconResId: Int + visible: Boolean, + startAction: @Composable () -> Unit, + centerAction: @Composable () -> Unit, + endAction: @Composable () -> Unit ) { val spacing = LocalSpacing.current - Surface( - color = MaterialTheme.colorScheme.background, - shape = MaterialTheme.shapes.extraLarge, - modifier = Modifier.padding(bottom = spacing.medium) - .border(width = 1.dp, color = MaterialTheme.colorScheme.surfaceVariant, shape = MaterialTheme.shapes.extraLarge) - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(spacing.medium), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = spacing.large, vertical = spacing.medium) + AnimatedVisibility(visible = visible, enter = fadeIn(tween(250)), exit = fadeOut(tween(250))) { + Surface( + modifier = Modifier.padding(bottom = spacing.large), + shape = CircleShape, + color = MaterialTheme.colorScheme.background, + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.surfaceVariant) ) { - SecondaryButton(action = startAction, resId = startIconResId) - PrimaryButton(action = centerAction, resId = centerIconResId) - SecondaryButton(action = endAction, resId = endIconResId) + Row( + modifier = Modifier.padding(horizontal = spacing.medium, vertical = spacing.small), + horizontalArrangement = Arrangement.spacedBy(spacing.extraSmall), + verticalAlignment = Alignment.CenterVertically + ) { startAction().also { centerAction() }.also { endAction() } } } } } + +@Composable +fun PrimaryAction(action: () -> Unit, resId: Int, descResId: Int) { + FilledIconButton(onClick = action, modifier = Modifier.width(72.dp)) { + Icon(painterResource(resId), stringResource(descResId)) + } +} + +@Composable +fun SecondaryAction(action: () -> Unit, resId: Int, descResId: Int) { + IconButton(onClick = action) { + Icon(painterResource(resId), stringResource(descResId)) + } +} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt index bde69b77..38d024b4 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt @@ -114,7 +114,7 @@ private fun MapScreen(uiState: State, mapView: MapView) { TimerRow(timeString = timeString, isTimeAos = isTimeAos) IconCard(action = selectNext, resId = R.drawable.ic_arrow) } - NextPassRow(pass = uiState.value.orbitalPass) + TopBar { NextPassRow(pass = uiState.value.orbitalPass) } } else { TopBar { IconCard(action = selectPrev, resId = R.drawable.ic_arrow, modifier = rotateMod) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt index a2a9eb0f..7fd4bdc6 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt @@ -1,9 +1,5 @@ package com.rtbishop.look4sat.presentation.passes -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -59,7 +55,9 @@ import com.rtbishop.look4sat.presentation.common.FloatingBar import com.rtbishop.look4sat.presentation.common.IconCard import com.rtbishop.look4sat.presentation.common.InfoDialog import com.rtbishop.look4sat.presentation.common.NextPassRow +import com.rtbishop.look4sat.presentation.common.PrimaryAction import com.rtbishop.look4sat.presentation.common.ScreenColumn +import com.rtbishop.look4sat.presentation.common.SecondaryAction import com.rtbishop.look4sat.presentation.common.TimerRow import com.rtbishop.look4sat.presentation.common.TopBar import com.rtbishop.look4sat.presentation.common.infiniteMarquee @@ -88,7 +86,11 @@ private fun PassesScreen(uiState: PassesState, navActions: NavActions) { val showPassesDialog = { uiState.takeAction(PassesAction.TogglePassesDialog) } val showRadiosDialog = { uiState.takeAction(PassesAction.ToggleRadiosDialog) } if (uiState.isPassesDialogShown) { - PassesDialog(hours = uiState.hours, elevation = uiState.elevation, cancel = showPassesDialog) { hours, elevation -> + PassesDialog( + hours = uiState.hours, + elevation = uiState.elevation, + cancel = showPassesDialog + ) { hours, elevation -> uiState.takeAction(PassesAction.FilterPasses(hours, elevation)) } } @@ -107,36 +109,51 @@ private fun PassesScreen(uiState: PassesState, navActions: NavActions) { } val gridState = rememberLazyGridState() ScreenColumn( - floatingBar = { PassesFloatingBar(gridState.isScrolling(), navActions) } - ) { isVerticalLayout -> - if (isVerticalLayout) { - TopBar { - IconCard(action = { showPassesDialog() }, resId = R.drawable.ic_filter) - TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos) - IconCard(action = { showRadiosDialog() }, resId = R.drawable.ic_satellite) - } - NextPassRow(pass = uiState.nextPass) - } else { - TopBar { - IconCard(action = { showPassesDialog() }, resId = R.drawable.ic_filter) - TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos) - NextPassRow(pass = uiState.nextPass, modifier = Modifier.weight(1f)) - IconCard(action = { showRadiosDialog() }, resId = R.drawable.ic_satellite) - } + topBar = { isVerticalLayout -> + TopBar( + isVerticalLayout = isVerticalLayout, + startAction = { + IconCard(action = showPassesDialog, resId = R.drawable.ic_filter) + }, + topInfo = { + TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos) + }, + bottomInfo = { + NextPassRow(pass = uiState.nextPass) + }, + endAction = { + IconCard(action = showRadiosDialog, resId = R.drawable.ic_satellite) + } + ) + }, + floatingBar = { + PassesFloatingBar(visible = gridState.isScrolling(), actions = navActions) } - PassesList(uiState.isRefreshing, uiState.itemsList, navActions.openRadar, refreshList, gridState) + ) { isVerticalLayout -> + PassesList( + isRefreshing = uiState.isRefreshing, + passes = uiState.itemsList, + navigateToRadar = navActions.openRadar, + refreshPasses = refreshList, + gridState = gridState + ) } } @Composable -private fun PassesFloatingBar(visible: Boolean, navActions: NavActions) { - AnimatedVisibility(visible = visible, enter = fadeIn(tween(250)), exit = fadeOut(tween(250))) { - FloatingBar( - startAction = navActions.openSettings, startIconResId = R.drawable.ic_settings, - centerAction = navActions.openSatellites, centerIconResId = R.drawable.ic_sputnik, - endAction = navActions.openMap, endIconResId = R.drawable.ic_map - ) - } +private fun PassesFloatingBar(visible: Boolean, actions: NavActions) { + FloatingBar( + visible = visible, + startAction = { + SecondaryAction(actions.openSettings, R.drawable.ic_settings, R.string.btn_settings) + }, + centerAction = { + PrimaryAction(actions.openSatellites, R.drawable.ic_sputnik, R.string.btn_satellites) + }, + endAction = { + SecondaryAction(actions.openMap, R.drawable.ic_map, R.string.btn_map) + } + ) } @OptIn(ExperimentalMaterial3Api::class) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt index 4579b3d5..a97ef24c 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt @@ -88,7 +88,7 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) { TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos) IconCard(action = addToCalendar, resId = R.drawable.ic_calendar) } - NextPassRow(pass = upcomingPass) + TopBar { NextPassRow(pass = upcomingPass) } } else { TopBar { IconCard(action = navigateUp, resId = R.drawable.ic_back) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ec3dad43..15d3c07c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,16 +5,16 @@ kotlin = "2.2.20" androidx-core-ktx = "1.17.0" androidx-core-splashscreen = "1.0.1" -androidx-room = "2.8.1" +androidx-room = "2.8.2" -compose-bom = "2025.09.01" +compose-bom = "2025.10.00" compose-activity = "1.11.0" compose-lifecycle = "2.9.4" compose-navigation = "2.9.5" other-coroutines = "1.10.2" other-json = "20250517" -other-okhttp = "5.1.0" +other-okhttp = "5.2.1" other-osmdroid = "6.1.20" test-junit4 = "4.13.2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a84e188..0fc4db2d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ +#Sat Oct 11 13:47:56 BST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME