- Schadensliste wurde implementiert
This commit is contained in:
6
.idea/appInsightsSettings.xml
generated
Normal file
6
.idea/appInsightsSettings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AppInsightsSettings">
|
||||
<option name="selectedTabId" value="Android Vitals" />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/deploymentTargetSelector.xml
generated
12
.idea/deploymentTargetSelector.xml
generated
@@ -4,14 +4,22 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-12-21T13:16:32.335138100Z">
|
||||
<DropdownSelection timestamp="2026-01-25T17:20:21.835673100Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\sklau\.android\avd\Medium_Phone.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
<DialogSelection>
|
||||
<targets>
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\sklau\.android\avd\Medium_Phone.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</targets>
|
||||
</DialogSelection>
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
||||
13
.idea/deviceManager.xml
generated
Normal file
13
.idea/deviceManager.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DeviceTable">
|
||||
<option name="columnSorters">
|
||||
<list>
|
||||
<ColumnSorterState>
|
||||
<option name="column" value="Name" />
|
||||
<option name="order" value="ASCENDING" />
|
||||
</ColumnSorterState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/markdown.xml
generated
Normal file
8
.idea/markdown.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -133,6 +133,8 @@ fun ContentScreen(
|
||||
// NEU: State für Filter-Dialog
|
||||
var showFilterDialog by remember { mutableStateOf(false) }
|
||||
|
||||
var showDamageList by remember { mutableStateOf(false) }
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
// Map
|
||||
MapSegment(
|
||||
@@ -140,6 +142,18 @@ fun ContentScreen(
|
||||
mapViewModel = mapViewModel
|
||||
)
|
||||
|
||||
// Dialog
|
||||
if (showDamageList) {
|
||||
DamageListDialog(
|
||||
onDismiss = { showDamageList = false },
|
||||
onDamageClick = { feature ->
|
||||
mapViewModel.selectedFeature = feature
|
||||
mapViewModel.showFeatureInfo = true
|
||||
},
|
||||
mapViewModel = mapViewModel
|
||||
)
|
||||
}
|
||||
|
||||
// Report Overlay
|
||||
if (showReport) {
|
||||
ReportOverlay(
|
||||
@@ -196,7 +210,7 @@ fun ContentScreen(
|
||||
SliderMenuItem(
|
||||
text = "Schadensliste",
|
||||
icon = Icons.Default.FormatListNumbered,
|
||||
onClick = { /* TODO */ }
|
||||
onClick = { showDamageList = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
|
||||
var reopenReport by mutableStateOf(false)
|
||||
private set
|
||||
var showFeatureInfo by mutableStateOf(false)
|
||||
private set
|
||||
public set
|
||||
var selectedFeature: ArcGISFeature? by mutableStateOf(null)
|
||||
val mapViewProxy = MapViewProxy()
|
||||
var reportDraft by mutableStateOf(ReportDraft())
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
package com.example.snapandsolve.ui.theme
|
||||
|
||||
import MapViewModel
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Image
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.MyLocation
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.arcgismaps.data.ArcGISFeature
|
||||
import com.arcgismaps.data.QueryParameters
|
||||
import com.arcgismaps.geometry.Point
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
|
||||
/**
|
||||
* Data class für einen Schaden mit Entfernung und Foto
|
||||
*/
|
||||
data class DamageWithDistance(
|
||||
val feature: ArcGISFeature,
|
||||
val distanceInMeters: Double,
|
||||
val typ: String,
|
||||
val beschreibung: String,
|
||||
val objectId: Long,
|
||||
val rating: Int,
|
||||
val photo: ImageBitmap? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Berechnet die Entfernung zwischen zwei Koordinaten mit Haversine-Formel
|
||||
*/
|
||||
fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
|
||||
val R = 6371000.0 // Erdradius in Metern
|
||||
|
||||
val dLat = Math.toRadians(lat2 - lat1)
|
||||
val dLon = Math.toRadians(lon2 - lon1)
|
||||
|
||||
val a = sin(dLat / 2) * sin(dLat / 2) +
|
||||
cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
|
||||
sin(dLon / 2) * sin(dLon / 2)
|
||||
|
||||
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
||||
|
||||
return R * c
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog für Schadensliste mit Entfernungsfilter
|
||||
*/
|
||||
@Composable
|
||||
fun DamageListDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onDamageClick: (ArcGISFeature) -> Unit,
|
||||
mapViewModel: MapViewModel
|
||||
) {
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
|
||||
var damages by remember { mutableStateOf<List<DamageWithDistance>>(emptyList()) }
|
||||
var isLoading by remember { mutableStateOf(true) }
|
||||
var maxDistance by remember { mutableStateOf(1000f) }
|
||||
var userLocation by remember { mutableStateOf<Point?>(null) }
|
||||
var errorMessage by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(maxDistance) {
|
||||
isLoading = true
|
||||
errorMessage = null
|
||||
|
||||
val location = mapViewModel.locationDisplay?.location?.value?.position
|
||||
|
||||
if (location == null) {
|
||||
errorMessage = "Standort nicht verfügbar. Bitte GPS aktivieren."
|
||||
isLoading = false
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
userLocation = location
|
||||
|
||||
val result = mapViewModel.loadDamagesNearby(location, maxDistance.toDouble(), context)
|
||||
damages = result
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
Dialog(onDismissRequest = onDismiss) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(0.9f),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(20.dp)
|
||||
) {
|
||||
// Header
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Schäden in der Nähe",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
IconButton(onClick = onDismiss) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Schließen")
|
||||
}
|
||||
}
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = 12.dp))
|
||||
|
||||
// Entfernungs-Slider
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.MyLocation,
|
||||
contentDescription = "Umkreis",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Umkreis: ${if (maxDistance >= 1000) "${(maxDistance / 1000).roundToInt()} km" else "${maxDistance.toInt()} m"}",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
|
||||
Slider(
|
||||
value = maxDistance,
|
||||
onValueChange = { maxDistance = it },
|
||||
valueRange = 100f..5000f,
|
||||
steps = 48,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text("100 m", style = MaterialTheme.typography.bodySmall)
|
||||
Text("5 km", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
if (!isLoading && errorMessage == null) {
|
||||
Text(
|
||||
text = "${damages.size} Schäden gefunden",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
|
||||
// Content
|
||||
when {
|
||||
isLoading -> {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text("Lade Schäden...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage != null -> {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "⚠️",
|
||||
style = MaterialTheme.typography.displayLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = errorMessage!!,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
damages.isEmpty() -> {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "🔍",
|
||||
style = MaterialTheme.typography.displayLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Keine Schäden im Umkreis",
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(damages) { damage ->
|
||||
DamageListItemWithPhoto(
|
||||
damage = damage,
|
||||
onClick = {
|
||||
onDamageClick(damage.feature)
|
||||
onDismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Einzelnes Schadens-Item in der Liste MIT FOTO
|
||||
*/
|
||||
@Composable
|
||||
fun DamageListItemWithPhoto(
|
||||
damage: DamageWithDistance,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(100.dp)
|
||||
.clickable(onClick = onClick),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// LINKS: Foto
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(100.dp)
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(topStart = 12.dp, bottomStart = 12.dp))
|
||||
) {
|
||||
if (damage.photo != null) {
|
||||
Image(
|
||||
bitmap = damage.photo,
|
||||
contentDescription = "Schadensfoto",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(RoundedCornerShape(topStart = 12.dp, bottomStart = 12.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text("📷", style = MaterialTheme.typography.headlineLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RECHTS: Info
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
// Typ mit Emoji
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = getEmojiForType(damage.typ),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Text(
|
||||
text = damage.typ,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
// Beschreibung
|
||||
Text(
|
||||
text = damage.beschreibung.take(40) + if (damage.beschreibung.length > 40) "..." else "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1
|
||||
)
|
||||
|
||||
// Info-Row
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
Text("📍", style = MaterialTheme.typography.labelSmall)
|
||||
Text(
|
||||
text = formatDistance(damage.distanceInMeters),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
|
||||
if (damage.rating > 0) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
Text("👥", style = MaterialTheme.typography.labelSmall)
|
||||
Text(
|
||||
text = "${damage.rating}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension-Funktion für MapViewModel
|
||||
* Lädt alle Schäden im Umkreis
|
||||
*/
|
||||
suspend fun MapViewModel.loadDamagesNearby(
|
||||
userLocation: Point,
|
||||
maxDistanceMeters: Double,
|
||||
context: Context
|
||||
): List<DamageWithDistance> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
println("DEBUG 1: Lade Schäden im Umkreis von ${maxDistanceMeters}m")
|
||||
println("DEBUG 1.1: serviceFeatureTable = $serviceFeatureTable")
|
||||
|
||||
val queryParams = QueryParameters().apply {
|
||||
whereClause = "1=1"
|
||||
}
|
||||
|
||||
println("DEBUG 2: QueryParameters erstellt")
|
||||
val queryResult = serviceFeatureTable?.queryFeatures(queryParams)?.getOrNull()
|
||||
|
||||
println("DEBUG 3: queryResult = $queryResult")
|
||||
|
||||
if (queryResult == null) {
|
||||
println("DEBUG ERROR: queryResult ist NULL!")
|
||||
return@withContext emptyList()
|
||||
}
|
||||
|
||||
val resultList = queryResult.toList()
|
||||
println("DEBUG 3.1: resultList.size = ${resultList.size}")
|
||||
|
||||
if (resultList.isEmpty()) {
|
||||
println("DEBUG: queryResult ist LEER (0 Features)")
|
||||
return@withContext emptyList()
|
||||
}
|
||||
|
||||
val damagesWithDistance = mutableListOf<DamageWithDistance>()
|
||||
var processedCount = 0
|
||||
var addedCount = 0
|
||||
|
||||
queryResult.forEach { geoElement ->
|
||||
processedCount++
|
||||
println("DEBUG 4.$processedCount: Verarbeite Feature...")
|
||||
|
||||
val feature = geoElement as? ArcGISFeature
|
||||
if (feature == null) {
|
||||
println("DEBUG 4.$processedCount: Feature ist NULL, überspringe")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
feature.load().getOrNull()
|
||||
|
||||
val featureGeometry = feature.geometry as? Point
|
||||
if (featureGeometry == null) {
|
||||
println("DEBUG 4.$processedCount: Geometrie ist NULL")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
// Transformiere Feature-Koordinaten in die gleiche räumliche Referenz wie userLocation
|
||||
val targetSpatialRef = userLocation.spatialReference
|
||||
if (targetSpatialRef == null) {
|
||||
println("DEBUG 4.$processedCount: User SpatialReference ist NULL")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val transformedGeometry = com.arcgismaps.geometry.GeometryEngine.projectOrNull(
|
||||
featureGeometry,
|
||||
targetSpatialRef
|
||||
) as? Point
|
||||
|
||||
if (transformedGeometry == null) {
|
||||
println("DEBUG 4.$processedCount: Transformation fehlgeschlagen")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
// Entfernung berechnen mit Haversine-Formel
|
||||
val distance = calculateDistance(
|
||||
userLocation.y,
|
||||
userLocation.x,
|
||||
transformedGeometry.y,
|
||||
transformedGeometry.x
|
||||
)
|
||||
|
||||
println("DEBUG 4.$processedCount: Distance = $distance, max = $maxDistanceMeters")
|
||||
|
||||
if (distance <= maxDistanceMeters) {
|
||||
addedCount++
|
||||
val typ = feature.attributes["Typ"]?.toString() ?: "Unbekannt"
|
||||
val beschreibung = feature.attributes["Beschreibung"]?.toString() ?: "Keine Beschreibung"
|
||||
val objectId = (feature.attributes["OBJECTID"] as? Number)?.toLong() ?: 0L
|
||||
val rating = (feature.attributes["communitycounter"] as? Number)?.toInt() ?: 0
|
||||
|
||||
// Foto laden (async)
|
||||
val photo = try {
|
||||
val attachments = feature.fetchAttachments().getOrNull()
|
||||
val firstAttachment = attachments?.firstOrNull()
|
||||
if (firstAttachment != null) {
|
||||
val data = firstAttachment.fetchData().getOrNull()
|
||||
if (data != null) {
|
||||
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
|
||||
bitmap?.asImageBitmap()
|
||||
} else null
|
||||
} else null
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
damagesWithDistance.add(
|
||||
DamageWithDistance(
|
||||
feature = feature,
|
||||
distanceInMeters = distance,
|
||||
typ = typ,
|
||||
beschreibung = beschreibung,
|
||||
objectId = objectId,
|
||||
rating = rating,
|
||||
photo = photo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val sorted = damagesWithDistance.sortedBy { it.distanceInMeters }
|
||||
println("DEBUG: $processedCount Features verarbeitet, $addedCount hinzugefügt")
|
||||
sorted
|
||||
|
||||
} catch (e: Exception) {
|
||||
println("DEBUG ERROR: ${e.message}")
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Fehler: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun formatDistance(meters: Double): String {
|
||||
return if (meters < 1000) {
|
||||
"${meters.roundToInt()} m"
|
||||
} else {
|
||||
"${"%.1f".format(meters / 1000)} km"
|
||||
}
|
||||
}
|
||||
|
||||
fun getEmojiForType(typ: String): String {
|
||||
return when (typ) {
|
||||
"Straße" -> "🛣️"
|
||||
"Gehweg" -> "🚶"
|
||||
"Fahrradweg" -> "🚴"
|
||||
"Beleuchtung" -> "💡"
|
||||
"Sonstiges" -> "📍"
|
||||
else -> "•"
|
||||
}
|
||||
}
|
||||
0
docs/DamageListSystem.md
Normal file
0
docs/DamageListSystem.md
Normal file
Reference in New Issue
Block a user