Release GPS2Audio v2.5.45 Explorer POI autoload
This commit is contained in:
@@ -121,8 +121,11 @@ import de.waypointaudio.util.LatLngBbox
|
||||
import de.waypointaudio.util.WaypointAudioDuration
|
||||
import de.waypointaudio.viewmodel.WaypointViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import de.waypointaudio.util.PoiSubtype
|
||||
import org.maplibre.android.annotations.IconFactory
|
||||
import org.maplibre.android.annotations.Marker
|
||||
import org.maplibre.android.annotations.MarkerOptions
|
||||
@@ -324,6 +327,15 @@ fun ExplorerScreenMapLibre(
|
||||
var currentMapZoom by remember { mutableStateOf(13.0) }
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
// v2.5.45 — Auto-POI-Load State
|
||||
var autoPoiLoadEnabled by remember { mutableStateOf(true) } // standardmäßig aktiviert
|
||||
var poiAutoLoadJob by remember { mutableStateOf<Job?>(null) }
|
||||
var lastAutoLoadLat by remember { mutableStateOf<Double?>(null) }
|
||||
var lastAutoLoadLon by remember { mutableStateOf<Double?>(null) }
|
||||
var lastAutoLoadZoom by remember { mutableStateOf<Double?>(null) }
|
||||
var lastAutoLoadCats by remember { mutableStateOf<Set<de.waypointaudio.data.PoiCategory>?>(null) }
|
||||
var autoPoiStatus by remember { mutableStateOf<String?>(null) } // kurzer Status-Text
|
||||
|
||||
var lastLat by remember { mutableStateOf<Double?>(null) }
|
||||
var lastLng by remember { mutableStateOf<Double?>(null) }
|
||||
var lastAccuracyM by remember { mutableStateOf<Float?>(null) }
|
||||
@@ -455,10 +467,14 @@ fun ExplorerScreenMapLibre(
|
||||
}
|
||||
}
|
||||
// v2.5.38 — POI-Layer-Auswahl persistent speichern.
|
||||
// v2.5.45 — Bei Kategorie-Änderung: Cache leeren und Auto-Load-Cursor zurücksetzen.
|
||||
LaunchedEffect(layerState.poiLayers) {
|
||||
if (styleHydrated && layerState.poiLayers != mapStyleSettings.poiLayers) {
|
||||
runCatching { mapStyleStore.setPoiLayers(layerState.poiLayers) }
|
||||
}
|
||||
// Kategorie-Wechsel → nächster Camera-Idle lädt frisch
|
||||
lastAutoLoadCats = null
|
||||
de.waypointaudio.util.ExplorerPoiClient.clearCache()
|
||||
}
|
||||
// v2.5.38 — POI-Sichtbarkeit auf den geladenen MapLibre-Style anwenden.
|
||||
// Greift auf heuristische Layer-ID-Präfixe des OpenMapTiles-Schemas zurück.
|
||||
@@ -498,16 +514,17 @@ fun ExplorerScreenMapLibre(
|
||||
val iconFactory = IconFactory.getInstance(context)
|
||||
for ((cat, pois) in explorerPoiResults) {
|
||||
for (poi in pois) {
|
||||
// v2.5.45 — Subtype-Icon: Piktogramm variiert nach Untertyp, Farbe bleibt Kategorie
|
||||
val iconBitmap = if (showPoiNames) {
|
||||
buildExplorerPoiIconWithLabel(cat, poi.displayName)
|
||||
buildExplorerPoiIconWithLabel(cat, poi.subtype, poi.displayName)
|
||||
} else {
|
||||
buildExplorerPoiIcon(cat)
|
||||
buildExplorerPoiIcon(cat, poi.subtype)
|
||||
}
|
||||
val icon = iconFactory.fromBitmap(iconBitmap)
|
||||
val mo = MarkerOptions()
|
||||
.position(LatLng(poi.latitude, poi.longitude))
|
||||
.title(poi.displayName)
|
||||
.snippet(poi.categoryLabel)
|
||||
.snippet(poi.subtype.displayLabel) // v2.5.45: Subtype-Label statt categoryLabel
|
||||
.icon(icon)
|
||||
val marker = runCatching { ml.addMarker(mo) }.getOrNull() ?: continue
|
||||
explorerPoiMarkers.add(marker)
|
||||
@@ -2131,8 +2148,65 @@ fun ExplorerScreenMapLibre(
|
||||
override fun onMoveEnd(detector: org.maplibre.android.gestures.MoveGestureDetector) {}
|
||||
})
|
||||
// v2.5.41 — Zoom-Tracking für zoom-abhängige POI-Label-Anzeige
|
||||
// v2.5.45 — Auto-POI-Load: debounced nach Kamera-Idle
|
||||
ml.addOnCameraIdleListener {
|
||||
currentMapZoom = ml.cameraPosition.zoom
|
||||
// Auto-Load nur wenn aktiviert, POI-Layer an, Zoom genügend
|
||||
if (autoPoiLoadEnabled && !explorerPoiLoading) {
|
||||
val cp = ml.cameraPosition
|
||||
val zoom = cp.zoom
|
||||
val cLat = cp.target?.latitude ?: return@addOnCameraIdleListener
|
||||
val cLon = cp.target?.longitude ?: return@addOnCameraIdleListener
|
||||
val activeCats = buildSet<de.waypointaudio.data.PoiCategory> {
|
||||
if (layerState.poiLayers.showSights) add(de.waypointaudio.data.PoiCategory.SIGHTS)
|
||||
if (layerState.poiLayers.showGastronomy) add(de.waypointaudio.data.PoiCategory.GASTRONOMY)
|
||||
if (layerState.poiLayers.showMobility) add(de.waypointaudio.data.PoiCategory.MOBILITY)
|
||||
if (layerState.poiLayers.showInfrastructure) add(de.waypointaudio.data.PoiCategory.INFRASTRUCTURE)
|
||||
}
|
||||
if (de.waypointaudio.util.ExplorerPoiClient.shouldAutoLoad(
|
||||
zoom, cLat, cLon,
|
||||
lastAutoLoadLat, lastAutoLoadLon, lastAutoLoadZoom,
|
||||
lastAutoLoadCats, activeCats
|
||||
)
|
||||
) {
|
||||
// Debounce: 1.5 s
|
||||
poiAutoLoadJob?.cancel()
|
||||
poiAutoLoadJob = scope.launch {
|
||||
delay(1500L)
|
||||
if (explorerPoiLoading) return@launch
|
||||
val bounds = ml.projection.visibleRegion.latLngBounds
|
||||
val bbox = de.waypointaudio.util.LatLngBbox(
|
||||
south = bounds.southWest.latitude,
|
||||
west = bounds.southWest.longitude,
|
||||
north = bounds.northEast.latitude,
|
||||
east = bounds.northEast.longitude
|
||||
)
|
||||
explorerPoiLoading = true
|
||||
autoPoiStatus = null
|
||||
try {
|
||||
val results = de.waypointaudio.util.ExplorerPoiClient.loadPois(
|
||||
context = context,
|
||||
bbox = bbox,
|
||||
categories = activeCats,
|
||||
onRadiusClamped = {}
|
||||
)
|
||||
explorerPoiResults = results
|
||||
lastAutoLoadLat = cLat
|
||||
lastAutoLoadLon = cLon
|
||||
lastAutoLoadZoom = zoom
|
||||
lastAutoLoadCats = activeCats
|
||||
val total = results.values.sumOf { it.size }
|
||||
autoPoiStatus = if (total == 0) null else "$total POIs"
|
||||
} catch (e: Exception) {
|
||||
// Alten Bestand behalten, nur dezente Fehlermeldung
|
||||
autoPoiStatus = null
|
||||
explorerPoiSnackbar = "POI-Aktualisierung fehlgeschlagen."
|
||||
} finally {
|
||||
explorerPoiLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mv.onStart()
|
||||
@@ -2274,6 +2348,12 @@ fun ExplorerScreenMapLibre(
|
||||
},
|
||||
onShowRoutingInfo = { showRoutingInfo = true },
|
||||
onShowRoutePlanner = { showRoutePlanner = true },
|
||||
// v2.5.45 — Auto-Load-Toggle
|
||||
poiAutoLoadEnabled = autoPoiLoadEnabled,
|
||||
onPoiAutoLoadChange = { enabled ->
|
||||
autoPoiLoadEnabled = enabled
|
||||
if (!enabled) poiAutoLoadJob?.cancel()
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(top = 8.dp, end = 8.dp)
|
||||
@@ -2320,42 +2400,72 @@ fun ExplorerScreenMapLibre(
|
||||
)
|
||||
}
|
||||
|
||||
// v2.5.40 — Explorer-POI-Lade-Button (mittig unten, über Attribution)
|
||||
if (layerState.poiLayers.let {
|
||||
it.showSights || it.showGastronomy || it.showMobility || it.showInfrastructure
|
||||
}) {
|
||||
// v2.5.45 — Explorer-POI-Bar (Auto-Load-Status + manueller Fallback)
|
||||
// Kompaktes Pill-Design: links Status/Ladeindikator, rechts manueller Reload-Button.
|
||||
// Nur sichtbar wenn mindestens ein POI-Layer aktiv ist.
|
||||
val anyPoiLayerActiveForBar = layerState.poiLayers.let {
|
||||
it.showSights || it.showGastronomy || it.showMobility || it.showInfrastructure
|
||||
}
|
||||
if (anyPoiLayerActiveForBar) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.95f),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.92f),
|
||||
shadowElevation = 3.dp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 32.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
// Status-Icon/Spinner
|
||||
if (explorerPoiLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(14.dp),
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(13.dp),
|
||||
strokeWidth = 1.8.dp,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
Icons.Filled.Explore,
|
||||
if (autoPoiLoadEnabled) Icons.Filled.Explore else Icons.Filled.Explore,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
modifier = Modifier.size(13.dp),
|
||||
tint = MaterialTheme.colorScheme.onSecondaryContainer.copy(
|
||||
alpha = if (autoPoiLoadEnabled) 1f else 0.55f
|
||||
)
|
||||
)
|
||||
}
|
||||
// Status-Text
|
||||
val statusText = when {
|
||||
explorerPoiLoading -> "POIs werden aktualisiert…"
|
||||
autoPoiStatus != null -> "POIs: $autoPoiStatus"
|
||||
autoPoiLoadEnabled && currentMapZoom < de.waypointaudio.util.ExplorerPoiClient.AUTO_LOAD_MIN_ZOOM
|
||||
-> "Hereinzoomen für Auto-Load"
|
||||
autoPoiLoadEnabled -> "POIs: Auto-Laden aktiv"
|
||||
else -> "POIs: manuell laden"
|
||||
}
|
||||
Text(
|
||||
text = if (explorerPoiLoading) "POIs werden geladen …"
|
||||
else "POIs im Kartenbereich laden",
|
||||
text = statusText,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
// Trennstrich
|
||||
Text(
|
||||
"·",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.5f)
|
||||
)
|
||||
// Manueller Neu-Laden Button
|
||||
Text(
|
||||
text = "↻",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
|
||||
color = if (explorerPoiLoading)
|
||||
MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.4f)
|
||||
else
|
||||
MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
modifier = Modifier.clickable(enabled = !explorerPoiLoading) {
|
||||
val ml = mapLibreMap ?: return@clickable
|
||||
val bounds = ml.projection.visibleRegion.latLngBounds
|
||||
@@ -2371,7 +2481,11 @@ fun ExplorerScreenMapLibre(
|
||||
if (layerState.poiLayers.showMobility) add(PoiCategory.MOBILITY)
|
||||
if (layerState.poiLayers.showInfrastructure) add(PoiCategory.INFRASTRUCTURE)
|
||||
}
|
||||
// Manueller Reload: Cache leeren, Cursor zurücksetzen
|
||||
de.waypointaudio.util.ExplorerPoiClient.clearCache()
|
||||
lastAutoLoadCats = null
|
||||
explorerPoiLoading = true
|
||||
autoPoiStatus = null
|
||||
scope.launch {
|
||||
try {
|
||||
val results = ExplorerPoiClient.loadPois(
|
||||
@@ -2379,17 +2493,21 @@ fun ExplorerScreenMapLibre(
|
||||
bbox = bbox,
|
||||
categories = cats,
|
||||
onRadiusClamped = {
|
||||
explorerPoiSnackbar = "Kartenausschnitt zu groß – Abfrage auf max. 5 km Radius begrenzt."
|
||||
explorerPoiSnackbar = "Kartenausschnitt zu groß – Abfrage auf max. 5 km begrenzt."
|
||||
}
|
||||
)
|
||||
val total = results.values.sumOf { it.size }
|
||||
explorerPoiResults = results
|
||||
// v2.5.44 — POI-Zusammenfassung im Snackbar
|
||||
explorerPoiSnackbar = if (total == 0) {
|
||||
"Keine POIs in diesem Bereich gefunden."
|
||||
} else {
|
||||
val cp = ml.cameraPosition
|
||||
lastAutoLoadLat = cp.target?.latitude
|
||||
lastAutoLoadLon = cp.target?.longitude
|
||||
lastAutoLoadZoom = cp.zoom
|
||||
lastAutoLoadCats = cats
|
||||
autoPoiStatus = if (total > 0) "$total" else null
|
||||
explorerPoiSnackbar = if (total == 0)
|
||||
"Keine POIs in diesem Bereich."
|
||||
else
|
||||
ExplorerPoiClient.summarize(results)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
explorerPoiSnackbar = "POI-Laden fehlgeschlagen: ${e.message?.take(60) ?: "Netzwerkfehler"}"
|
||||
} finally {
|
||||
@@ -2446,12 +2564,13 @@ fun ExplorerScreenMapLibre(
|
||||
else "%.1f km entfernt".format(d / 1000.0)
|
||||
} else null
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
// v2.5.45 — Subtype-Label (konkreter) statt nur Kategorie
|
||||
Surface(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer
|
||||
) {
|
||||
Text(
|
||||
poi.categoryLabel,
|
||||
poi.subtype.displayLabel, // z.B. "Burg/Schloss" statt "Sehenswürdigkeit"
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp)
|
||||
@@ -3506,8 +3625,20 @@ fun MapCompactAttributionBadge(modifier: Modifier = Modifier) {
|
||||
* MOBILITY — Indigo (#3F51B5) + Abgerundetes Quadrat + Bus-Symbol (B)
|
||||
* INFRASTRUCTURE — Koralle (#E53935) + Kreis + Plus-Symbol (+)
|
||||
*/
|
||||
private fun buildExplorerPoiIcon(category: de.waypointaudio.data.PoiCategory): Bitmap {
|
||||
/**
|
||||
* v2.5.45 — Subtype-Overload: rückwärtskompatibel (Default: GENERIC je Kategorie)
|
||||
*/
|
||||
private fun buildExplorerPoiIcon(
|
||||
category: de.waypointaudio.data.PoiCategory,
|
||||
subtype: de.waypointaudio.util.PoiSubtype = when (category) {
|
||||
de.waypointaudio.data.PoiCategory.SIGHTS -> de.waypointaudio.util.PoiSubtype.GENERIC_SIGHTS
|
||||
de.waypointaudio.data.PoiCategory.GASTRONOMY -> de.waypointaudio.util.PoiSubtype.GENERIC_GASTRONOMY
|
||||
de.waypointaudio.data.PoiCategory.MOBILITY -> de.waypointaudio.util.PoiSubtype.GENERIC_MOBILITY
|
||||
de.waypointaudio.data.PoiCategory.INFRASTRUCTURE-> de.waypointaudio.util.PoiSubtype.GENERIC_INFRASTRUCTURE
|
||||
}
|
||||
): Bitmap {
|
||||
// v2.5.43 — Echte Canvas-Piktogramme statt Buchstaben/Zeichen
|
||||
// v2.5.45 — Subtype-Piktogramme für Sights/Gastro/Mobil/Infra
|
||||
val sizePx = 56
|
||||
|
||||
// Kategorie-Farben — dezent, App-Teal-Stil passend
|
||||
@@ -3518,13 +3649,15 @@ private fun buildExplorerPoiIcon(category: de.waypointaudio.data.PoiCategory): B
|
||||
de.waypointaudio.data.PoiCategory.INFRASTRUCTURE-> 0xFF558B2F.toInt() // Dunkelgrün (Infrastruktur/Info)
|
||||
}
|
||||
|
||||
// Sentinel-Byte-Wert je Kategorie (eindeutige Pixel-Markierung für MapLibre-Cache)
|
||||
val sentinelAlpha = when (category) {
|
||||
de.waypointaudio.data.PoiCategory.SIGHTS -> 0xFC
|
||||
de.waypointaudio.data.PoiCategory.GASTRONOMY -> 0xFD
|
||||
de.waypointaudio.data.PoiCategory.MOBILITY -> 0xFE
|
||||
de.waypointaudio.data.PoiCategory.INFRASTRUCTURE-> 0xFF
|
||||
// v2.5.45 — Sentinel-Wert berücksichtigt Kategorie + Subtype-Ordinal,
|
||||
// um MapLibre-Icon-Cache-Kollisionen zwischen verschiedenen Subtypen derselben Kategorie zu verhindern.
|
||||
val categoryBase = when (category) {
|
||||
de.waypointaudio.data.PoiCategory.SIGHTS -> 0
|
||||
de.waypointaudio.data.PoiCategory.GASTRONOMY -> 32
|
||||
de.waypointaudio.data.PoiCategory.MOBILITY -> 64
|
||||
de.waypointaudio.data.PoiCategory.INFRASTRUCTURE-> 96
|
||||
}
|
||||
val sentinelAlpha = ((categoryBase + subtype.ordinal) % 200 + 55).coerceIn(1, 254)
|
||||
|
||||
val bmp = android.graphics.Bitmap.createBitmap(sizePx, sizePx, android.graphics.Bitmap.Config.ARGB_8888)
|
||||
val canvas = android.graphics.Canvas(bmp)
|
||||
@@ -3605,101 +3738,269 @@ private fun buildExplorerPoiIcon(category: de.waypointaudio.data.PoiCategory): B
|
||||
}
|
||||
}
|
||||
|
||||
// ── Weißes Piktogramm per Canvas ─────────────────────────────────────────
|
||||
|
||||
// ── Weißes Piktogramm per Canvas (v2.5.45: Subtype-Varianten) ──────────────────
|
||||
paint.color = 0xFFFFFFFF.toInt()
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
paint.strokeWidth = 2.2f
|
||||
|
||||
when (category) {
|
||||
de.waypointaudio.data.PoiCategory.SIGHTS -> {
|
||||
// Stern-Marker: Stern-Hintergrund ist schon der Rahmen.
|
||||
// Piktogramm: kleiner innerer Punkt + Strahllinien wie eine Landmarke/Monument-Nadel
|
||||
// Vertikaler Stab (Monument-Pin)
|
||||
val stW = 3.5f
|
||||
val stH = 14f
|
||||
val stTop = cy - 10f
|
||||
val stBot = cy + 4f
|
||||
val stLeft = cx - stW / 2f
|
||||
canvas.drawRect(stLeft, stTop, stLeft + stW, stBot, paint)
|
||||
// Kleines Dreieck-Dach oben (Turm/Monument)
|
||||
when (subtype) {
|
||||
// ─── SIGHTS Subtypes ────────────────────────────────────────────────────────
|
||||
de.waypointaudio.util.PoiSubtype.CASTLE -> {
|
||||
// Turm/Burg: Zinnenkranz + Tor
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
val tw = 8f; val th = 14f
|
||||
val tl = cx - tw/2f; val tt = cy - th/2f - 1f
|
||||
canvas.drawRect(tl, tt + 4f, tl + tw, tt + th, paint)
|
||||
for (zi in 0..2) {
|
||||
val zx = tl + zi * (tw/2f) - 0.5f
|
||||
canvas.drawRect(zx, tt, zx + 2.5f, tt + 4f, paint)
|
||||
}
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 1.8f
|
||||
val tgW = 4f
|
||||
canvas.drawArc(android.graphics.RectF(cx - tgW, tt + th - tgW*1.5f - 2f, cx + tgW, tt + th - 2f), 180f, -180f, false, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.CHURCH -> {
|
||||
// Kreuz
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 2f, cy - 12f, cx + 2f, cy + 10f, paint)
|
||||
canvas.drawRect(cx - 7f, cy - 6f, cx + 7f, cy - 2f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.MONASTERY -> {
|
||||
// Kleines Kreuz + Bogen
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 1.8f, cy - 10f, cx + 1.8f, cy + 8f, paint)
|
||||
canvas.drawRect(cx - 6f, cy - 5f, cx + 6f, cy - 2f, paint)
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 1.8f
|
||||
canvas.drawArc(android.graphics.RectF(cx - 7f, cy - 1f, cx + 7f, cy + 10f), 0f, 180f, false, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.MUSEUM -> {
|
||||
// Giebelhaus
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
val roofP = android.graphics.Path().apply {
|
||||
moveTo(cx, cy - 12f); lineTo(cx - 10f, cy - 4f); lineTo(cx + 10f, cy - 4f); close()
|
||||
}
|
||||
canvas.drawPath(roofP, paint)
|
||||
canvas.drawLine(cx - 10f, cy + 8f, cx + 10f, cy + 8f, paint)
|
||||
paint.strokeWidth = 1.8f
|
||||
for (si in 0..2) { val sx = cx - 7f + si * 7f; canvas.drawLine(sx, cy - 4f, sx, cy + 8f, paint) }
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.VIEWPOINT -> {
|
||||
// Fernglas
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.2f
|
||||
canvas.drawCircle(cx - 5f, cy + 2f, 5f, paint)
|
||||
canvas.drawCircle(cx + 5f, cy + 2f, 5f, paint)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 2f, cy - 8f, cx + 2f, cy - 3f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.MONUMENT, de.waypointaudio.util.PoiSubtype.HISTORIC_OTHER -> {
|
||||
// Säule
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 2.5f, cy - 9f, cx + 2.5f, cy + 7f, paint)
|
||||
canvas.drawRect(cx - 5f, cy - 11f, cx + 5f, cy - 8f, paint)
|
||||
canvas.drawRect(cx - 6f, cy + 6f, cx + 6f, cy + 9f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.RUIN -> {
|
||||
// Gebrochene Mauer
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
val rb = cy + 8f
|
||||
canvas.drawRect(cx - 10f, cy - 2f, cx - 4f, rb, paint)
|
||||
canvas.drawRect(cx - 10f, cy - 8f, cx - 7f, cy - 2f, paint)
|
||||
canvas.drawRect(cx + 2f, cy, cx + 10f, rb, paint)
|
||||
canvas.drawRect(cx + 5f, cy - 5f, cx + 9f, cy, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.GALLERY -> {
|
||||
// Bilderrahmen
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.2f
|
||||
canvas.drawRoundRect(android.graphics.RectF(cx - 9f, cy - 9f, cx + 9f, cy + 9f), 2f, 2f, paint)
|
||||
paint.strokeWidth = 1.5f
|
||||
canvas.drawRect(cx - 5f, cy - 5f, cx + 5f, cy + 5f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.THEATRE -> {
|
||||
// Zwei Drama-Bogen
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
canvas.drawArc(android.graphics.RectF(cx - 10f, cy - 8f, cx - 1f, cy + 4f), 0f, 270f, false, paint)
|
||||
canvas.drawArc(android.graphics.RectF(cx + 1f, cy - 8f, cx + 10f, cy + 4f), 270f, 270f, false, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.ZOO -> {
|
||||
// Pfoten-Symbol
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawCircle(cx, cy + 4f, 6f, paint)
|
||||
canvas.drawCircle(cx - 6f, cy - 3f, 3f, paint)
|
||||
canvas.drawCircle(cx, cy - 7f, 3f, paint)
|
||||
canvas.drawCircle(cx + 6f, cy - 3f, 3f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.GENERIC_SIGHTS, de.waypointaudio.util.PoiSubtype.ATTRACTION -> {
|
||||
// Monument-Nadel (wie v2.5.44)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
val stTop = cy - 10f; val stBot = cy + 4f
|
||||
canvas.drawRect(cx - 1.75f, stTop, cx + 1.75f, stBot, paint)
|
||||
val roofPath = android.graphics.Path().apply {
|
||||
moveTo(cx, stTop - 5f)
|
||||
lineTo(cx - 5f, stTop)
|
||||
lineTo(cx + 5f, stTop)
|
||||
close()
|
||||
moveTo(cx, stTop - 5f); lineTo(cx - 5f, stTop); lineTo(cx + 5f, stTop); close()
|
||||
}
|
||||
canvas.drawPath(roofPath, paint)
|
||||
// Sockel/Basis unten
|
||||
canvas.drawRect(cx - 6f, stBot, cx + 6f, stBot + 2.5f, paint)
|
||||
}
|
||||
de.waypointaudio.data.PoiCategory.GASTRONOMY -> {
|
||||
// Tasse mit Henkel und Dampf-Linie
|
||||
val cupLeft = cx - 9f
|
||||
val cupRight = cx + 9f
|
||||
val cupTop = cy - 3f
|
||||
val cupBot = cy + 9f
|
||||
// Tassenform: Trapez (breiter unten)
|
||||
val cupPath = android.graphics.Path().apply {
|
||||
moveTo(cupLeft + 2f, cupTop)
|
||||
lineTo(cupLeft, cupBot)
|
||||
lineTo(cupRight, cupBot)
|
||||
lineTo(cupRight - 2f, cupTop)
|
||||
close()
|
||||
// ─── GASTRONOMY Subtypes ────────────────────────────────────────────────────
|
||||
de.waypointaudio.util.PoiSubtype.RESTAURANT -> {
|
||||
// Messer + Gabel
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
canvas.drawLine(cx - 5f, cy + 9f, cx - 5f, cy - 10f, paint)
|
||||
paint.strokeWidth = 1.5f
|
||||
canvas.drawLine(cx - 7f, cy - 10f, cx - 7f, cy - 4f, paint)
|
||||
canvas.drawLine(cx - 3f, cy - 10f, cx - 3f, cy - 4f, paint)
|
||||
paint.strokeWidth = 2.0f
|
||||
canvas.drawLine(cx + 5f, cy + 9f, cx + 5f, cy - 4f, paint)
|
||||
val knifePath = android.graphics.Path().apply {
|
||||
moveTo(cx + 5f, cy - 4f); lineTo(cx + 8f, cy - 10f); lineTo(cx + 5f, cy - 10f); close()
|
||||
}
|
||||
paint.style = android.graphics.Paint.Style.STROKE
|
||||
paint.strokeWidth = 2.3f
|
||||
paint.style = android.graphics.Paint.Style.FILL; canvas.drawPath(knifePath, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.CAFE -> {
|
||||
// Tasse mit Dampf
|
||||
val cupLeft = cx - 9f; val cupRight = cx + 9f; val cupTop = cy - 3f; val cupBot = cy + 9f
|
||||
val cupPath = android.graphics.Path().apply {
|
||||
moveTo(cupLeft + 2f, cupTop); lineTo(cupLeft, cupBot); lineTo(cupRight, cupBot); lineTo(cupRight - 2f, cupTop); close()
|
||||
}
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.3f
|
||||
canvas.drawPath(cupPath, paint)
|
||||
// Untertasse
|
||||
paint.strokeWidth = 2f
|
||||
canvas.drawLine(cupLeft - 2f, cupBot + 2f, cupRight + 2f, cupBot + 2f, paint)
|
||||
// Henkel rechts
|
||||
val hRect = android.graphics.RectF(cupRight - 1f, cupTop + 4f, cupRight + 6f, cupBot - 2f)
|
||||
canvas.drawArc(hRect, -90f, 180f, false, paint)
|
||||
// Dampf-Wellenlinie oben (eine kleine Kurve)
|
||||
paint.strokeWidth = 2f; canvas.drawLine(cupLeft - 2f, cupBot + 2f, cupRight + 2f, cupBot + 2f, paint)
|
||||
canvas.drawArc(android.graphics.RectF(cupRight - 1f, cupTop + 4f, cupRight + 6f, cupBot - 2f), -90f, 180f, false, paint)
|
||||
paint.strokeWidth = 1.8f
|
||||
val steamPath = android.graphics.Path().apply {
|
||||
moveTo(cx - 3f, cupTop - 3f)
|
||||
quadTo(cx - 1f, cupTop - 7f, cx + 1f, cupTop - 3f)
|
||||
quadTo(cx + 3f, cupTop + 1f, cx + 5f, cupTop - 3f)
|
||||
}
|
||||
canvas.drawPath(steamPath, paint)
|
||||
moveTo(cx - 3f, cupTop - 3f); quadTo(cx - 1f, cupTop - 7f, cx + 1f, cupTop - 3f)
|
||||
}; canvas.drawPath(steamPath, paint)
|
||||
}
|
||||
de.waypointaudio.data.PoiCategory.MOBILITY -> {
|
||||
// Bus-Silhouette: rechteckiger Körper, Räder, Fenster
|
||||
val busLeft = cx - 10f
|
||||
val busRight = cx + 10f
|
||||
val busTop = cy - 8f
|
||||
val busBot = cy + 6f
|
||||
// Buskarosserie
|
||||
paint.style = android.graphics.Paint.Style.STROKE
|
||||
paint.strokeWidth = 2.2f
|
||||
val busRect = android.graphics.RectF(busLeft, busTop, busRight, busBot)
|
||||
canvas.drawRoundRect(busRect, 3f, 3f, paint)
|
||||
// Frontscheibe (linke Seite)
|
||||
de.waypointaudio.util.PoiSubtype.BAR -> {
|
||||
// Bierglas
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
val glassPath = android.graphics.Path().apply {
|
||||
moveTo(cx - 7f, cy - 9f); lineTo(cx - 9f, cy + 9f); lineTo(cx + 9f, cy + 9f); lineTo(cx + 7f, cy - 9f); close()
|
||||
}
|
||||
canvas.drawPath(glassPath, paint)
|
||||
canvas.drawArc(android.graphics.RectF(cx + 7f, cy - 5f, cx + 13f, cy + 5f), -90f, 180f, false, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.FAST_FOOD -> {
|
||||
// Burger-Silhouette
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawArc(android.graphics.RectF(cx - 9f, cy - 10f, cx + 9f, cy + 2f), 180f, 180f, true, paint)
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.8f
|
||||
canvas.drawLine(cx - 9f, cy + 2f, cx + 9f, cy + 2f, paint)
|
||||
canvas.drawLine(cx - 9f, cy + 6f, cx + 9f, cy + 6f, paint)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawArc(android.graphics.RectF(cx - 9f, cy + 4f, cx + 9f, cy + 12f), 0f, 180f, true, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.GENERIC_GASTRONOMY -> {
|
||||
// Tasse (Fallback, wie v2.5.44)
|
||||
val cupLeft = cx - 9f; val cupRight = cx + 9f; val cupTop = cy - 3f; val cupBot = cy + 9f
|
||||
val cupPath = android.graphics.Path().apply {
|
||||
moveTo(cupLeft + 2f, cupTop); lineTo(cupLeft, cupBot); lineTo(cupRight, cupBot); lineTo(cupRight - 2f, cupTop); close()
|
||||
}
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.3f
|
||||
canvas.drawPath(cupPath, paint)
|
||||
paint.strokeWidth = 2f; canvas.drawLine(cupLeft - 2f, cupBot + 2f, cupRight + 2f, cupBot + 2f, paint)
|
||||
canvas.drawArc(android.graphics.RectF(cupRight - 1f, cupTop + 4f, cupRight + 6f, cupBot - 2f), -90f, 180f, false, paint)
|
||||
}
|
||||
// ─── MOBILITY Subtypes ──────────────────────────────────────────────────────
|
||||
de.waypointaudio.util.PoiSubtype.PARKING -> {
|
||||
// "P"-Piktogramm
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 3f
|
||||
canvas.drawLine(cx - 5f, cy - 10f, cx - 5f, cy + 10f, paint)
|
||||
canvas.drawArc(android.graphics.RectF(cx - 5f, cy - 10f, cx + 8f, cy + 0f), -90f, 180f, false, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.BUS_STOP -> {
|
||||
// Bus-Silhouette (wie v2.5.44)
|
||||
val busLeft = cx - 10f; val busRight = cx + 10f; val busTop = cy - 8f; val busBot = cy + 6f
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.2f
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft, busTop, busRight, busBot), 3f, 3f, paint)
|
||||
canvas.drawLine(busLeft + 2f, busTop + 2f, busLeft + 2f, busTop + 7f, paint)
|
||||
// Fenster 1
|
||||
val w1 = android.graphics.RectF(busLeft + 4f, busTop + 2f, busLeft + 9f, busTop + 6f)
|
||||
paint.strokeWidth = 1.5f
|
||||
canvas.drawRoundRect(w1, 1f, 1f, paint)
|
||||
// Fenster 2
|
||||
val w2 = android.graphics.RectF(busLeft + 11f, busTop + 2f, busLeft + 16f, busTop + 6f)
|
||||
canvas.drawRoundRect(w2, 1f, 1f, paint)
|
||||
// Rad links
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft + 4f, busTop + 2f, busLeft + 9f, busTop + 6f), 1f, 1f, paint)
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft + 11f, busTop + 2f, busLeft + 16f, busTop + 6f), 1f, 1f, paint)
|
||||
paint.strokeWidth = 2f
|
||||
canvas.drawCircle(busLeft + 5f, busBot + 1f, 3f, paint)
|
||||
// Rad rechts
|
||||
canvas.drawCircle(busRight - 5f, busBot + 1f, 3f, paint)
|
||||
}
|
||||
de.waypointaudio.data.PoiCategory.INFRASTRUCTURE -> {
|
||||
// Info-"i"-Piktogramm: großer Punkt oben, vertikaler Stab unten
|
||||
de.waypointaudio.util.PoiSubtype.TRAIN_STATION -> {
|
||||
// Zug (vereinfacht)
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
canvas.drawRoundRect(android.graphics.RectF(cx - 12f, cy - 7f, cx + 12f, cy + 5f), 3f, 3f, paint)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 6f, cy - 11f, cx - 3f, cy - 7f, paint)
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 1.8f
|
||||
canvas.drawCircle(cx - 7f, cy + 7f, 3f, paint)
|
||||
canvas.drawCircle(cx + 2f, cy + 7f, 3f, paint)
|
||||
canvas.drawCircle(cx + 9f, cy + 7f, 3f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.TRAM_STOP -> {
|
||||
// Straßenbahn
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
canvas.drawRoundRect(android.graphics.RectF(cx - 10f, cy - 7f, cx + 10f, cy + 5f), 3f, 3f, paint)
|
||||
canvas.drawLine(cx - 10f, cy - 9f, cx + 10f, cy - 9f, paint)
|
||||
paint.strokeWidth = 1.5f; canvas.drawLine(cx, cy - 9f, cx, cy - 7f, paint)
|
||||
canvas.drawCircle(cx - 5f, cy + 7f, 2.5f, paint)
|
||||
canvas.drawCircle(cx + 5f, cy + 7f, 2.5f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.FUEL -> {
|
||||
// Zapfsäule
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.0f
|
||||
canvas.drawRect(cx - 6f, cy - 5f, cx + 6f, cy + 9f, paint)
|
||||
canvas.drawLine(cx - 6f, cy - 5f, cx - 10f, cy - 9f, paint)
|
||||
canvas.drawLine(cx - 10f, cy - 9f, cx - 10f, cy - 3f, paint)
|
||||
paint.strokeWidth = 1.5f; canvas.drawRect(cx - 4f, cy - 3f, cx + 4f, cy + 3f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.GENERIC_MOBILITY -> {
|
||||
// Bus (Fallback, wie v2.5.44)
|
||||
val busLeft = cx - 10f; val busRight = cx + 10f; val busTop = cy - 8f; val busBot = cy + 6f
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 2.2f
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft, busTop, busRight, busBot), 3f, 3f, paint)
|
||||
canvas.drawLine(busLeft + 2f, busTop + 2f, busLeft + 2f, busTop + 7f, paint)
|
||||
paint.strokeWidth = 1.5f
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft + 4f, busTop + 2f, busLeft + 9f, busTop + 6f), 1f, 1f, paint)
|
||||
canvas.drawRoundRect(android.graphics.RectF(busLeft + 11f, busTop + 2f, busLeft + 16f, busTop + 6f), 1f, 1f, paint)
|
||||
paint.strokeWidth = 2f
|
||||
canvas.drawCircle(busLeft + 5f, busBot + 1f, 3f, paint)
|
||||
canvas.drawCircle(busRight - 5f, busBot + 1f, 3f, paint)
|
||||
}
|
||||
// ─── INFRASTRUCTURE Subtypes ────────────────────────────────────────────────
|
||||
de.waypointaudio.util.PoiSubtype.PHARMACY -> {
|
||||
// Apotheken-Kreuz
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawRect(cx - 3f, cy - 11f, cx + 3f, cy + 11f, paint)
|
||||
canvas.drawRect(cx - 11f, cy - 3f, cx + 11f, cy + 3f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.HOSPITAL -> {
|
||||
// H-Symbol
|
||||
paint.style = android.graphics.Paint.Style.STROKE; paint.strokeWidth = 3f
|
||||
canvas.drawLine(cx - 7f, cy - 10f, cx - 7f, cy + 10f, paint)
|
||||
canvas.drawLine(cx + 7f, cy - 10f, cx + 7f, cy + 10f, paint)
|
||||
canvas.drawLine(cx - 7f, cy, cx + 7f, cy, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.TOILETS -> {
|
||||
// WC-Piktogramm (Person-Silhouette)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
canvas.drawCircle(cx - 6f, cy - 9f, 3f, paint)
|
||||
val dressPath = android.graphics.Path().apply {
|
||||
moveTo(cx - 10f, cy + 8f); lineTo(cx - 6f, cy - 6f); lineTo(cx - 2f, cy + 8f); close()
|
||||
}; canvas.drawPath(dressPath, paint)
|
||||
canvas.drawCircle(cx + 6f, cy - 9f, 3f, paint)
|
||||
canvas.drawRect(cx + 4f, cy - 6f, cx + 8f, cy + 8f, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.DRINKING_WATER -> {
|
||||
// Wassertropfen
|
||||
val dropPath = android.graphics.Path().apply {
|
||||
moveTo(cx, cy + 10f)
|
||||
cubicTo(cx - 10f, cy + 2f, cx - 10f, cy - 6f, cx, cy - 12f)
|
||||
cubicTo(cx + 10f, cy - 6f, cx + 10f, cy + 2f, cx, cy + 10f)
|
||||
}
|
||||
paint.style = android.graphics.Paint.Style.FILL; canvas.drawPath(dropPath, paint)
|
||||
}
|
||||
de.waypointaudio.util.PoiSubtype.GENERIC_INFRASTRUCTURE -> {
|
||||
// Info-i (wie v2.5.44)
|
||||
paint.style = android.graphics.Paint.Style.FILL
|
||||
// Punkt oben
|
||||
canvas.drawCircle(cx, cy - 8f, 3.5f, paint)
|
||||
// Stab unten — leicht breiter, abgerundet
|
||||
val stW2 = 4.5f
|
||||
val rect2 = android.graphics.RectF(cx - stW2 / 2f, cy - 2f, cx + stW2 / 2f, cy + 9f)
|
||||
canvas.drawRoundRect(rect2, 2f, 2f, paint)
|
||||
// Basis-Strich
|
||||
canvas.drawRoundRect(android.graphics.RectF(cx - stW2/2f, cy - 2f, cx + stW2/2f, cy + 9f), 2f, 2f, paint)
|
||||
canvas.drawRoundRect(android.graphics.RectF(cx - 7f, cy + 8f, cx + 7f, cy + 10f), 2f, 2f, paint)
|
||||
}
|
||||
}
|
||||
@@ -3712,6 +4013,7 @@ private fun buildExplorerPoiIcon(category: de.waypointaudio.data.PoiCategory): B
|
||||
|
||||
private fun buildExplorerPoiIconWithLabel(
|
||||
category: de.waypointaudio.data.PoiCategory,
|
||||
subtype: de.waypointaudio.util.PoiSubtype = de.waypointaudio.util.PoiSubtype.GENERIC_SIGHTS,
|
||||
name: String
|
||||
): Bitmap {
|
||||
val iconSize = 56 // gleiche Größe wie buildExplorerPoiIcon
|
||||
@@ -3736,7 +4038,7 @@ private fun buildExplorerPoiIconWithLabel(
|
||||
val paint = android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG)
|
||||
|
||||
// Zeichne Kategorie-Icon zentriert oben
|
||||
val iconBmp = buildExplorerPoiIcon(category)
|
||||
val iconBmp = buildExplorerPoiIcon(category, subtype)
|
||||
val iconLeft = (totalW - iconSize) / 2f
|
||||
canvas.drawBitmap(iconBmp, iconLeft, 0f, null)
|
||||
iconBmp.recycle()
|
||||
@@ -3844,11 +4146,17 @@ private fun ExplorerPoiLegend(
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
"Symbole zeigen Untertypen",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.55f),
|
||||
modifier = Modifier.padding(top = 1.dp)
|
||||
)
|
||||
Text(
|
||||
"antippen zum Einklappen",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.45f),
|
||||
modifier = Modifier.padding(top = 1.dp)
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.35f),
|
||||
modifier = Modifier.padding(top = 0.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user