diff --git a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt
index 80913a63..09145d28 100644
--- a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt
@@ -27,6 +27,8 @@ import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
@@ -48,8 +50,10 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
observeNightFilterState()
+ val settingsRepo = (applicationContext as IContainerProvider).getMainContainer().settingsRepo
setContent {
- MainTheme(isDarkTheme = true) { NavRoot() }
+ val otherSettings by settingsRepo.otherSettings.collectAsState()
+ MainTheme(isDarkTheme = !otherSettings.stateOfLightTheme) { NavRoot() }
}
}
diff --git a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/MainTheme.kt b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/MainTheme.kt
index fe5a4acf..69cf257c 100644
--- a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/MainTheme.kt
+++ b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/MainTheme.kt
@@ -61,8 +61,8 @@ fun MainTheme(isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable
SideEffect {
val window = (view.context as ComponentActivity).window
val insetsController = WindowCompat.getInsetsController(window, view)
- insetsController.isAppearanceLightStatusBars = false
- insetsController.isAppearanceLightNavigationBars = false
+ insetsController.isAppearanceLightStatusBars = !isDarkTheme
+ insetsController.isAppearanceLightNavigationBars = !isDarkTheme
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
diff --git a/core/presentation/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml
index ed1b585f..c36c1738 100644
--- a/core/presentation/src/main/res/values/strings.xml
+++ b/core/presentation/src/main/res/values/strings.xml
@@ -266,6 +266,7 @@
Radar sweep
Use sensors
Night filter
+ Light theme
Radar compass offset
Radar compass offset (elev)
diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt
index fec5a37d..46d7402b 100644
--- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt
+++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt
@@ -122,6 +122,39 @@ private val moonIconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
android.graphics.PorterDuffColorFilter("#E0E0E0".toColorInt(), android.graphics.PorterDuff.Mode.SRC_IN)
}
+/** Accent used for the station marker; recolored per theme so it stays visible on a light map. */
+private var mapAccentColor = "#FFE082".toColorInt()
+
+/** The dark-map filter (grayscale + invert) is only applied when NOT in light theme; cached to avoid rebuilds. */
+private val darkTileFilter by lazy { createColorFilter() }
+
+/**
+ * Recolor the shared overlay paints for the current theme. The light OSM tiles are white, so the
+ * dark-theme amber/grey overlays would vanish; light theme swaps them for dark, high-contrast colors.
+ * Must run before the set*() overlay builders on each frame, as several icons cache the paint color.
+ */
+private fun applyMapColors(isLightUi: Boolean) {
+ if (isLightUi) {
+ mapAccentColor = "#715C0C".toColorInt() // dark amber (matches app lightScheme primary)
+ footprintPaint.color = mapAccentColor
+ textPaint.color = "#1E1B13".toColorInt()
+ textPaint.setShadowLayer(3f, 3f, 3f, Color.WHITE)
+ sunIconPaint.colorFilter =
+ android.graphics.PorterDuffColorFilter(mapAccentColor, android.graphics.PorterDuff.Mode.SRC_IN)
+ moonIconPaint.colorFilter =
+ android.graphics.PorterDuffColorFilter("#4C4639".toColorInt(), android.graphics.PorterDuff.Mode.SRC_IN)
+ } else {
+ mapAccentColor = "#FFE082".toColorInt()
+ footprintPaint.color = mapAccentColor
+ textPaint.color = mapAccentColor
+ textPaint.setShadowLayer(3f, 3f, 3f, Color.BLACK)
+ sunIconPaint.colorFilter =
+ android.graphics.PorterDuffColorFilter(mapAccentColor, android.graphics.PorterDuff.Mode.SRC_IN)
+ moonIconPaint.colorFilter =
+ android.graphics.PorterDuffColorFilter("#E0E0E0".toColorInt(), android.graphics.PorterDuff.Mode.SRC_IN)
+ }
+}
+
@Composable
fun MapDestination() {
val context = LocalContext.current
@@ -177,6 +210,8 @@ private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView:
ElevatedCard(modifier = Modifier.weight(1f)) {
Box(contentAlignment = Alignment.BottomCenter) {
AndroidView({ mapView }) { view ->
+ applyMapColors(uiState.isLightUi)
+ view.overlayManager.tilesOverlay.setColorFilter(if (uiState.isLightUi) null else darkTileFilter)
uiState.stationPosition?.let { setStationPosition(it, view) }
uiState.track?.let { setSatelliteTrack(it, view) }
uiState.footprint?.let { setFootprint(it, view) }
@@ -288,7 +323,9 @@ private fun setStationPosition(stationPos: GeoPos, mapView: MapView) {
mapView.overlays[OVERLAY_STATION] = Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
- icon = ContextCompat.getDrawable(mapView.context, R.drawable.ic_position)
+ icon = ContextCompat.getDrawable(mapView.context, R.drawable.ic_position)?.apply {
+ setTint(mapAccentColor)
+ }
position = GeoPoint(stationPos.latitude, stationPos.longitude)
}
}
@@ -585,7 +622,7 @@ private fun rememberMapViewWithLifecycle(): MapView {
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT
overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT
- overlayManager.tilesOverlay.setColorFilter(createColorFilter())
+ // Tile color filter (dark inversion vs light) is applied per-theme in the AndroidView update block.
setScrollableAreaLimitLatitude(maxLat, minLat, 0)
overlays.addAll(Array(OVERLAY_COUNT) { FolderOverlay() })
}
diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt
index 59407aa4..91524290 100644
--- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt
+++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt
@@ -80,7 +80,7 @@ class MapViewModel(
init {
viewModelScope.launch {
settingsRepo.otherSettings.collectLatest { settings ->
- _uiState.update { it.copy(isUtc = settings.stateOfUtc) }
+ _uiState.update { it.copy(isUtc = settings.stateOfUtc, isLightUi = settings.stateOfLightTheme) }
}
}
val (selectedCatNum, _) = satelliteRepo.selectedPass.value
diff --git a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt
index ea67b40b..8192c3c8 100644
--- a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt
+++ b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsScreen.kt
@@ -495,12 +495,24 @@ private fun OtherCard(settings: OtherSettings, onAction: (SettingsAction) -> Uni
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(4.dp))
+ // Appearance: light theme (paired with a spacer to align with the two-column rows)
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ SwitchTile(R.string.prefs_other_switch_light_theme, settings.stateOfLightTheme) {
+ onAction(SettingsAction.ToggleLightTheme(it))
+ }
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ Spacer(modifier = Modifier.height(4.dp))
// Display preferences: UTC clock + night filter
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
SwitchTile(R.string.prefs_other_switch_utc, settings.stateOfUtc) {
onAction(SettingsAction.ToggleUtc(it))
}
- SwitchTile(R.string.prefs_other_switch_night_mode, settings.stateOfNightMode) {
+ SwitchTile(
+ R.string.prefs_other_switch_night_mode,
+ settings.stateOfNightMode,
+ enabled = !settings.stateOfLightTheme
+ ) {
onAction(SettingsAction.ToggleNightMode(it))
}
}
@@ -573,7 +585,12 @@ private fun SwitchRow(labelResId: Int, checked: Boolean, onCheckedChange: (Boole
}
@Composable
-private fun RowScope.SwitchTile(labelResId: Int, checked: Boolean, onCheckedChange: (Boolean) -> Unit) {
+private fun RowScope.SwitchTile(
+ labelResId: Int,
+ checked: Boolean,
+ enabled: Boolean = true,
+ onCheckedChange: (Boolean) -> Unit
+) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
@@ -583,7 +600,7 @@ private fun RowScope.SwitchTile(labelResId: Int, checked: Boolean, onCheckedChan
text = stringResource(id = labelResId),
modifier = Modifier.weight(1f)
)
- Switch(checked = checked, onCheckedChange = onCheckedChange)
+ Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
}
}
diff --git a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt
index 351b0e69..f933398c 100644
--- a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt
+++ b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt
@@ -127,7 +127,10 @@ class SettingsViewModel(
is SettingsAction.ToggleUpdate -> settingsRepo.updateOtherSettings { it.copy(stateOfAutoUpdate = action.value) }
is SettingsAction.ToggleSweep -> settingsRepo.updateOtherSettings { it.copy(stateOfSweep = action.value) }
is SettingsAction.ToggleSensor -> settingsRepo.updateOtherSettings { it.copy(stateOfSensors = action.value) }
- is SettingsAction.ToggleLightTheme -> settingsRepo.updateOtherSettings { it.copy(stateOfLightTheme = action.value) }
+ is SettingsAction.ToggleLightTheme -> settingsRepo.updateOtherSettings {
+ // Night filter (red-only overlay) is incompatible with the light theme, so turn it off.
+ it.copy(stateOfLightTheme = action.value, stateOfNightMode = if (action.value) false else it.stateOfNightMode)
+ }
is SettingsAction.ToggleNightMode -> settingsRepo.updateOtherSettings { it.copy(stateOfNightMode = action.value) }
is SettingsAction.SetRadarCompassOffset -> settingsRepo.updateOtherSettings { it.copy(radarCompassOffset = action.value) }
is SettingsAction.SetRadarCompassOffsetElev -> settingsRepo.updateOtherSettings { it.copy(radarCompassOffsetElev = action.value) }