- Dokumentation
- Bereinigung von Code
This commit is contained in:
@@ -8,12 +8,12 @@ import androidx.compose.ui.graphics.ImageBitmap
|
||||
*/
|
||||
data class AlbumViewState(
|
||||
/**
|
||||
* holds the URL of the temporary file which stores the image taken by the camera.
|
||||
* Speichert die URL der temporären Datei des Kamerabildes.
|
||||
*/
|
||||
val tempFileUrl: Uri? = null,
|
||||
|
||||
/**
|
||||
* holds the list of images taken by camera or selected pictures from the gallery.
|
||||
* Speichert eine Liste der Bilder, die entweder über die Kamera aufgenommen oder aus der Galerie ausgewählt wurden.
|
||||
*/
|
||||
val selectedPictures: List<ImageBitmap> = emptyList(),
|
||||
)
|
||||
@@ -167,7 +167,7 @@ class ProximityNotificationService : Service() {
|
||||
|
||||
Log.d("ProximityService", "Feature point SR: ${featureGeometry.spatialReference?.wkid}")
|
||||
|
||||
// KRITISCHER FIX: Beide Punkte ins gleiche Koordinatensystem bringen
|
||||
// Beide Punkte ins gleiche Koordinatensystem bringen von UTM ins WGS
|
||||
val projectedCurrentPoint = if (currentPoint.spatialReference?.wkid != featureGeometry.spatialReference?.wkid) {
|
||||
GeometryEngine.projectOrNull(currentPoint, featureGeometry.spatialReference!!) as? Point
|
||||
} else {
|
||||
@@ -179,7 +179,7 @@ class ProximityNotificationService : Service() {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
// KORRIGIERTE Distanzberechnung mit GeometryEngine
|
||||
// korrigierte Distanzberechnung mit GeometryEngine
|
||||
val distanceResult = GeometryEngine.distanceGeodeticOrNull(
|
||||
point1 = projectedCurrentPoint,
|
||||
point2 = featureGeometry,
|
||||
@@ -214,14 +214,14 @@ class ProximityNotificationService : Service() {
|
||||
}
|
||||
|
||||
private fun sendDamageNotification(feature: ArcGISFeature, distance: Double) {
|
||||
// Versuche verschiedene Attribut-Namen für die Kategorie
|
||||
|
||||
val kategorie = feature.attributes["Kategorie"]?.toString()
|
||||
?: feature.attributes["kategorie"]?.toString()
|
||||
?: feature.attributes["Category"]?.toString()
|
||||
?: feature.attributes["category"]?.toString()
|
||||
?: "Straßenschaden" // Fallback
|
||||
|
||||
// Versuche verschiedene Attribut-Namen für die Beschreibung
|
||||
|
||||
val beschreibung = feature.attributes["Beschreibung"]?.toString()
|
||||
?: feature.attributes["beschreibung"]?.toString()
|
||||
?: feature.attributes["Description"]?.toString()
|
||||
@@ -229,7 +229,7 @@ class ProximityNotificationService : Service() {
|
||||
|
||||
val distanceText = String.format("%.0f", distance)
|
||||
|
||||
// Baue den Notification-Text
|
||||
// Notification-Text
|
||||
val notificationText = if (beschreibung != null && beschreibung.isNotEmpty()) {
|
||||
"$kategorie: $beschreibung - ca. ${distanceText}m entfernt"
|
||||
} else {
|
||||
|
||||
@@ -68,7 +68,7 @@ fun DamageFilterDialog(
|
||||
// Status-Liste
|
||||
val statusTypes = listOf("neu", "in Bearbeitung", "Schaden behoben")
|
||||
|
||||
// WICHTIG: Wenn keine Filter aktiv sind, alle Typen standardmäßig auswählen!
|
||||
|
||||
var selectedFilters by remember {
|
||||
mutableStateOf(
|
||||
if (currentFilters.isEmpty()) damageTypes.toSet() else currentFilters
|
||||
@@ -121,7 +121,7 @@ fun DamageFilterDialog(
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
// ===== TYP-FILTER =====
|
||||
// Filtertyp
|
||||
Text(
|
||||
text = "Schadenstypen:",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
@@ -161,7 +161,7 @@ fun DamageFilterDialog(
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = 16.dp))
|
||||
|
||||
// ===== STATUS-FILTER =====
|
||||
// Filter nach Status
|
||||
Text(
|
||||
text = "Bearbeitungsstatus:",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
@@ -198,7 +198,7 @@ fun DamageFilterDialog(
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = 16.dp))
|
||||
|
||||
// ===== DATUMS-FILTER =====
|
||||
// Filter nach Datum
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -372,8 +372,8 @@ fun DamageFilterDialog(
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension-Funktion für MapViewModel
|
||||
* Wendet Filter auf den FeatureLayer an (Typ + Status + Datum - UNABHÄNGIG)
|
||||
* Erweiterungs-Funktion für MapViewModel
|
||||
* Wendet Filter auf den FeatureLayer an
|
||||
*/
|
||||
suspend fun MapViewModel.applyDamageFilter(
|
||||
selectedTypes: Set<String>,
|
||||
@@ -387,22 +387,22 @@ suspend fun MapViewModel.applyDamageFilter(
|
||||
try {
|
||||
val whereClauses = mutableListOf<String>()
|
||||
|
||||
// ===== TYP-FILTER =====
|
||||
// WICHTIG: Nur hinzufügen wenn nicht ALLE Typen ausgewählt sind
|
||||
// Filter Typ
|
||||
|
||||
if (selectedTypes.isNotEmpty() && selectedTypes.size < DAMAGE_TYPES.size) {
|
||||
val typeList = selectedTypes.joinToString("', '", "'", "'")
|
||||
whereClauses.add("Typ IN ($typeList)")
|
||||
}
|
||||
|
||||
// ===== STATUS-FILTER =====
|
||||
// Nur hinzufügen wenn nicht ALLE Status ausgewählt sind
|
||||
// Filter Status
|
||||
|
||||
if (selectedStatus.isNotEmpty() && selectedStatus.size < statusTypes.size) {
|
||||
val statusList = selectedStatus.joinToString("', '", "'", "'")
|
||||
whereClauses.add("status IN ($statusList)")
|
||||
}
|
||||
|
||||
// ===== DATUMS-FILTER =====
|
||||
// Feldname: EditDate
|
||||
// Filter nach Datum
|
||||
|
||||
if (startDate != null || endDate != null) {
|
||||
val dateField = "EditDate"
|
||||
|
||||
@@ -419,7 +419,7 @@ suspend fun MapViewModel.applyDamageFilter(
|
||||
}
|
||||
}
|
||||
|
||||
// ===== ALLE KLAUSELN KOMBINIEREN =====
|
||||
// Alle Klauseln kombinieren
|
||||
val whereClause = whereClauses.joinToString(" AND ")
|
||||
|
||||
featureLayer.definitionExpression = whereClause
|
||||
@@ -469,7 +469,7 @@ suspend fun MapViewModel.applyDamageFilter(
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension-Funktion für MapViewModel
|
||||
* Erweiterte-Funktion für MapViewModel
|
||||
* Gibt die aktuell aktiven Filter zurück
|
||||
*/
|
||||
fun MapViewModel.getActiveFilters(): Set<String> {
|
||||
|
||||
@@ -152,7 +152,7 @@ fun DamageListDialog(
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = 12.dp))
|
||||
|
||||
// ===== ENTFERNUNGS-FILTER =====
|
||||
// Filtern nach Entfernung
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -192,7 +192,7 @@ fun DamageListDialog(
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// ===== SORTIERUNG =====
|
||||
// Sortieren
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = "Sortieren nach:",
|
||||
@@ -205,7 +205,7 @@ fun DamageListDialog(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
// Distance Button
|
||||
// Entfernung Button
|
||||
FilterChip(
|
||||
selected = sortBy == SortBy.DISTANCE,
|
||||
onClick = { sortBy = SortBy.DISTANCE },
|
||||
@@ -220,7 +220,7 @@ fun DamageListDialog(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
// Relevance Button
|
||||
// Relevanz Button
|
||||
FilterChip(
|
||||
selected = sortBy == SortBy.RELEVANCE,
|
||||
onClick = { sortBy = SortBy.RELEVANCE },
|
||||
@@ -239,7 +239,7 @@ fun DamageListDialog(
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// ===== RELEVANZ-FILTER (nur wenn nach Relevanz sortiert) =====
|
||||
// Nach Relevanz filtern
|
||||
if (sortBy == SortBy.RELEVANCE) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
@@ -380,7 +380,7 @@ fun DamageListDialog(
|
||||
}
|
||||
|
||||
/**
|
||||
* Einzelnes Schadens-Item in der Liste MIT FOTO und BEWERTUNG
|
||||
* Einzelnes Schadens-Item in der Liste mit Foto und Bewertung
|
||||
*/
|
||||
@Composable
|
||||
fun DamageListItemWithPhoto(
|
||||
@@ -401,7 +401,7 @@ fun DamageListItemWithPhoto(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// LINKS: Foto
|
||||
//LINKS: Foto
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(100.dp)
|
||||
@@ -500,7 +500,7 @@ fun DamageListItemWithPhoto(
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension-Funktion für MapViewModel
|
||||
* Erweiterte-Funktion für MapViewModel
|
||||
* Lädt alle Schäden im Umkreis
|
||||
*/
|
||||
suspend fun MapViewModel.loadDamagesNearby(
|
||||
|
||||
@@ -19,7 +19,6 @@ 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.Attachment
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -35,12 +34,13 @@ fun FeatureInfoDialog(
|
||||
) {
|
||||
if (feature == null) return
|
||||
|
||||
var attachments by remember { mutableStateOf<List<Attachment>>(emptyList()) }
|
||||
|
||||
var photoBitmaps by remember { mutableStateOf<List<androidx.compose.ui.graphics.ImageBitmap>>(emptyList()) }
|
||||
var isLoadingPhotos by remember { mutableStateOf(true) }
|
||||
|
||||
// Lade Fotos beim Öffnen
|
||||
LaunchedEffect(feature) {
|
||||
isLoadingPhotos = true
|
||||
try {
|
||||
// Lade Feature falls nötig
|
||||
if (feature.loadStatus.value != com.arcgismaps.LoadStatus.Loaded) {
|
||||
@@ -49,23 +49,22 @@ fun FeatureInfoDialog(
|
||||
|
||||
// Hole Attachments
|
||||
feature.fetchAttachments().onSuccess { fetchedAttachments ->
|
||||
attachments = fetchedAttachments
|
||||
val loadedBitmaps = mutableListOf<androidx.compose.ui.graphics.ImageBitmap>()
|
||||
|
||||
// Lade Foto-Daten
|
||||
val bitmaps = mutableListOf<androidx.compose.ui.graphics.ImageBitmap>()
|
||||
fetchedAttachments.forEach { attachment ->
|
||||
attachment.fetchData().onSuccess { data ->
|
||||
try {
|
||||
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
|
||||
if (bitmap != null) {
|
||||
bitmaps.add(bitmap.asImageBitmap())
|
||||
loadedBitmaps.add(bitmap.asImageBitmap())
|
||||
// Update die UI-Liste direkt, wenn ein Bild fertig ist
|
||||
photoBitmaps = loadedBitmaps.toList()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("DEBUG: Fehler beim Laden des Bildes: ${e.message}")
|
||||
println("DEBUG: Fehler beim Decodieren des Bildes: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
photoBitmaps = bitmaps
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("DEBUG: Fehler beim Laden der Attachments: ${e.message}")
|
||||
@@ -95,7 +94,7 @@ fun FeatureInfoDialog(
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
|
||||
Divider(modifier = Modifier.padding(bottom = 16.dp))
|
||||
HorizontalDivider(modifier = Modifier.padding(bottom = 16.dp))
|
||||
|
||||
// Beschreibung
|
||||
val description = feature.attributes["Beschreibung"]?.toString()
|
||||
@@ -113,7 +112,7 @@ fun FeatureInfoDialog(
|
||||
}
|
||||
|
||||
// Fotos
|
||||
if (isLoadingPhotos) {
|
||||
if (isLoadingPhotos && photoBitmaps.isEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -122,7 +121,7 @@ fun FeatureInfoDialog(
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(24.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Lade Fotos...")
|
||||
Text("Suche Fotos...")
|
||||
}
|
||||
} else if (photoBitmaps.isNotEmpty()) {
|
||||
Text(
|
||||
@@ -149,7 +148,6 @@ fun FeatureInfoDialog(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
@@ -200,7 +198,6 @@ fun FeatureInfoDialog(
|
||||
}
|
||||
}
|
||||
|
||||
// Info-Text
|
||||
Text(
|
||||
text = "Hast du diesen Schaden auch gesehen?",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
@@ -211,24 +208,19 @@ fun FeatureInfoDialog(
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// Daumen runter
|
||||
OutlinedButton(
|
||||
) {//Daumen runter
|
||||
Button(
|
||||
onClick = {
|
||||
onRate(feature, false)
|
||||
onDismiss()
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("👎", style = MaterialTheme.typography.headlineMedium)
|
||||
Text("Nein", style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
|
||||
// Daumen hoch
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -237,17 +229,13 @@ fun FeatureInfoDialog(
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("👍", style = MaterialTheme.typography.headlineMedium)
|
||||
Text("Ja", style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schließen Button
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
@@ -261,10 +249,8 @@ fun FeatureInfoDialog(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extension-Funktion für MapViewModel
|
||||
* Erweiterungs-Funktion für MapViewModel
|
||||
* Aktualisiert die Community-Bewertung eines Features
|
||||
*/
|
||||
suspend fun MapViewModel.updateFeatureRating(
|
||||
@@ -274,62 +260,31 @@ suspend fun MapViewModel.updateFeatureRating(
|
||||
): Boolean {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// Hole aktuelle Bewertung
|
||||
val currentRating = (feature.attributes["communitycounter"] as? Number)?.toInt() ?: 0
|
||||
val newRating = if (isPositive) currentRating + 1 else maxOf(0, currentRating - 1)
|
||||
|
||||
// Berechne neue Bewertung
|
||||
val newRating = if (isPositive) {
|
||||
currentRating + 1
|
||||
} else {
|
||||
maxOf(0, currentRating - 1) // Verhindert negative Werte
|
||||
}
|
||||
|
||||
println("DEBUG: Rating-Update: $currentRating -> $newRating (${if(isPositive) "+" else "-"})")
|
||||
|
||||
// Aktualisiere Feature-Attribut
|
||||
feature.attributes["communitycounter"] = newRating
|
||||
|
||||
// Speichere in Feature Table
|
||||
serviceFeatureTable.updateFeature(feature).onSuccess {
|
||||
println("DEBUG: updateFeature erfolgreich")
|
||||
|
||||
// Synchronisiere mit ArcGIS Online
|
||||
serviceFeatureTable.applyEdits().onSuccess {
|
||||
println("DEBUG: applyEdits erfolgreich - Rating gespeichert")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val message = if (isPositive) {
|
||||
"✓ Schaden bestätigt! (${currentRating} → ${newRating})"
|
||||
} else {
|
||||
"✓ Bewertung verringert (${currentRating} → ${newRating})"
|
||||
}
|
||||
val message = if (isPositive) "✓ Schaden bestätigt!" else "✓ Bewertung verringert"
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
snackBarMessage = message
|
||||
}
|
||||
}.onFailure { error ->
|
||||
println("DEBUG: applyEdits fehlgeschlagen: ${error.message}")
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Sync-Fehler: ${error.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}.onFailure { error ->
|
||||
println("DEBUG: updateFeature fehlgeschlagen: ${error.message}")
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Update-Fehler: ${error.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
println("DEBUG: Rating-Update Exception: ${e.message}")
|
||||
e.printStackTrace()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Fehler beim Bewerten: ${e.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
Toast.makeText(context, "Fehler: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fun SettingsScreen(
|
||||
val context = LocalContext.current
|
||||
val isProximityActive by ProximityNotificationService.isRunning.collectAsState()
|
||||
|
||||
// NEU: Notification Permission Launcher
|
||||
// Notification Permission Launcher
|
||||
val notificationPermissionLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted ->
|
||||
@@ -65,7 +65,7 @@ fun SettingsScreen(
|
||||
.padding(paddingValues)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
// Benachrichtigungen Section
|
||||
// Benachrichtigungen
|
||||
Text(
|
||||
text = "Benachrichtigungen",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
@@ -180,7 +180,7 @@ fun SettingsScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// Weitere Einstellungen können hier hinzugefügt werden
|
||||
|
||||
Text(
|
||||
text = "Informationen",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
|
||||
@@ -43,8 +43,6 @@ class LocationHelper(private val context: Context) {
|
||||
/**
|
||||
* Composable zum Einrichten des Location Display
|
||||
*
|
||||
* @param autoPanMode Wie die Karte dem Standort folgen soll (default: Recenter)
|
||||
* @return LocationDisplay-Objekt, das an MapView übergeben werden kann
|
||||
*/
|
||||
@Composable
|
||||
fun setupLocationDisplay(
|
||||
|
||||
Reference in New Issue
Block a user