feat(map): dark status bar on map screen, white on dashboard/manage; faster map animations

Made-with: Cursor
This commit is contained in:
2026-03-05 21:36:02 +01:00
parent 863961405d
commit c2eb2df8c0
2 changed files with 65 additions and 16 deletions

View File

@@ -49,6 +49,9 @@ import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Marker
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.toDrawable
@@ -83,6 +86,7 @@ fun MapScreen(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MapScreenContent(
uiState: State,
@@ -90,12 +94,19 @@ private fun MapScreenContent(
showMap: Boolean
) {
val context = LocalContext.current
val centerOnMarker = uiState.selectedMarkerId?.let { id ->
uiState.items.find { it.id == id }
}
val sheetHeightFraction = if (uiState.devicePanelState != null) 0.5f else 0f
Box(modifier = Modifier.fillMaxSize()) {
if (showMap) {
AirMQMap(
items = uiState.items,
onMarkerClick = { onEvent(Event.MarkerClicked(it)) }
onMarkerClick = { onEvent(Event.MarkerClicked(it)) },
centerOnMarker = centerOnMarker,
sheetHeightFraction = sheetHeightFraction,
modifier = Modifier.fillMaxSize()
)
} else {
Box(
@@ -143,16 +154,20 @@ private fun MapScreenContent(
}
uiState.devicePanelState?.let { panelData ->
MapDevicePanel(
data = panelData,
onClose = { onEvent(Event.DevicePanelClosed) },
onOpenDevice = { onEvent(Event.DeviceOpenClicked) },
onRangeSelected = { onEvent(Event.TimeRangeSelected(it)) },
onDateBack = { onEvent(Event.DateBackClicked) },
onDateForward = { onEvent(Event.DateForwardClicked) },
onSensorSelected = { onEvent(Event.DeviceSensorSelected(it)) },
modifier = Modifier.align(Alignment.BottomCenter)
)
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
onDismissRequest = { onEvent(Event.DevicePanelClosed) },
sheetState = sheetState
) {
MapDevicePanelContent(
data = panelData,
onOpenDevice = { onEvent(Event.DeviceOpenClicked) },
onRangeSelected = { onEvent(Event.TimeRangeSelected(it)) },
onDateBack = { onEvent(Event.DateBackClicked) },
onDateForward = { onEvent(Event.DateForwardClicked) },
onSensorSelected = { onEvent(Event.DeviceSensorSelected(it)) }
)
}
}
if (uiState.isLoading) {
@@ -172,7 +187,10 @@ private fun MapScreenContent(
@Composable
private fun AirMQMap(
items: List<MapMarker>,
onMarkerClick: (String) -> Unit
onMarkerClick: (String) -> Unit,
centerOnMarker: MapMarker? = null,
sheetHeightFraction: Float = 0f,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
@@ -202,8 +220,31 @@ private fun AirMQMap(
}
}
val mapAnimationSpeedMs = 200L
LaunchedEffect(centerOnMarker, sheetHeightFraction) {
centerOnMarker?.let { marker ->
val markerGeo = GeoPoint(marker.latitude, marker.longitude)
val zoomLevel = 15.5
if (sheetHeightFraction > 0f) {
mapView.post {
val height = mapView.height
val width = mapView.width
if (height > 0 && width > 0) {
val sheetHeightPx = (height * sheetHeightFraction).toInt()
val offsetCenterY = height / 2 + sheetHeightPx / 2
val projection = mapView.projection
val offsetGeo = projection.fromPixels(width / 2, offsetCenterY)
mapView.controller.animateTo(offsetGeo, zoomLevel, mapAnimationSpeedMs)
}
}
} else {
mapView.controller.animateTo(markerGeo, zoomLevel, mapAnimationSpeedMs)
}
}
}
AndroidView(
modifier = Modifier.fillMaxSize(),
modifier = modifier.fillMaxSize(),
factory = { mapView },
update = { map ->
map.overlays.removeAll { it is Marker }
@@ -226,8 +267,14 @@ private fun AirMQMap(
map.overlays.add(marker)
}
items.firstOrNull()?.let { first ->
map.controller.animateTo(GeoPoint(first.latitude, first.longitude))
if (centerOnMarker == null) {
items.firstOrNull()?.let { first ->
map.controller.animateTo(
GeoPoint(first.latitude, first.longitude),
map.zoomLevelDouble,
200L
)
}
}
map.invalidate()
}

View File

@@ -71,7 +71,9 @@ fun AirMQNavGraph(modifier: Modifier = Modifier) {
val view = LocalView.current
SideEffect {
val window = (view.context as? Activity)?.window ?: return@SideEffect
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !showBottomBar
// Map: dark icons (light map background); Dashboard/Manage: white icons (dark nav bar)
val lightStatusBars = currentRoute == AirMqRoutes.MAP || !showBottomBar
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = lightStatusBars
}
Scaffold(