From 97d86523ab157e76002a7813662973c8da3e0a21 Mon Sep 17 00:00:00 2001 From: fr2651 Date: Tue, 13 Jan 2026 16:23:04 +0100 Subject: [PATCH] - Code bereinigt - MapView in MapSegment ausgelagert --- .../com/example/snapandsolve/MainActivity.kt | 8 -- .../com/example/snapandsolve/MainScreen.kt | 72 ++++++----- .../com/example/snapandsolve/MapSegment.kt | 122 ++++++++++++++++++ 3 files changed, 161 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/example/snapandsolve/MapSegment.kt diff --git a/app/src/main/java/com/example/snapandsolve/MainActivity.kt b/app/src/main/java/com/example/snapandsolve/MainActivity.kt index c10aa31..e4dad24 100644 --- a/app/src/main/java/com/example/snapandsolve/MainActivity.kt +++ b/app/src/main/java/com/example/snapandsolve/MainActivity.kt @@ -20,14 +20,6 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - viewModel = AlbumViewModel(coroutineContext = Dispatchers.Default) - - /* - Wird gebraucht um die Karte in ArcGIS anzuzeigen. Die Prüfung ob man Zugang hat oder nicht - wurde gelöscht. - */ - ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ARCGIS_TOKEN) enableEdgeToEdge() setContent { diff --git a/app/src/main/java/com/example/snapandsolve/MainScreen.kt b/app/src/main/java/com/example/snapandsolve/MainScreen.kt index 80c7d05..d122b82 100644 --- a/app/src/main/java/com/example/snapandsolve/MainScreen.kt +++ b/app/src/main/java/com/example/snapandsolve/MainScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.FilterAlt +import androidx.compose.material.icons.filled.FormatListNumbered import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.* import androidx.compose.runtime.* @@ -44,9 +45,8 @@ import kotlinx.coroutines.Dispatchers fun MainScreen(modifier: Modifier = Modifier, application: Application) { var showReport by rememberSaveable { mutableStateOf(false) } var sliderOpen by remember { mutableStateOf(false) } - - // ViewModel Initialisierung val mapViewModel = remember { MapViewModel(application) } + val albumViewModel = remember { AlbumViewModel(Dispatchers.Default) } Scaffold( modifier = Modifier.fillMaxSize(), @@ -63,19 +63,26 @@ fun MainScreen(modifier: Modifier = Modifier, application: Application) { }, floatingActionButton = { LargeFloatingActionButton( - onClick = { showReport = true; sliderOpen = false }, + onClick = { + showReport = true + sliderOpen = false + }, modifier = Modifier.offset(y = 64.dp), containerColor = ButtonColor - ) { Icon(Icons.Default.Add, contentDescription = "Add") } + ) { + Icon(Icons.Default.Add, contentDescription = "Add") + } }, floatingActionButtonPosition = FabPosition.Center, ) { innerPadding -> ContentScreen( modifier = Modifier.padding(innerPadding), - mapViewModel = mapViewModel, + mapViewModel, + albumViewModel, showReport = showReport, sliderOpen = sliderOpen, - onDismissReport = { showReport = false }) + onDismissReport = { showReport = false } + ) } } @@ -83,26 +90,20 @@ fun MainScreen(modifier: Modifier = Modifier, application: Application) { fun ContentScreen( modifier: Modifier = Modifier, mapViewModel: MapViewModel, + albumViewModel: AlbumViewModel, showReport: Boolean, sliderOpen: Boolean, onDismissReport: () -> Unit ) { - val albumViewModel = remember { AlbumViewModel(Dispatchers.Default) } - val locationDisplay = setupLocationDisplay() - - // VERBINDUNG ZUM VIEWMODEL (Hier wird das GPS-Werkzeug übergeben) - LaunchedEffect(locationDisplay) { - mapViewModel.locationDisplay = locationDisplay - } Box(modifier = modifier.fillMaxSize()) { - MapView( + // HINTERGRUND: Die Map + MapSegment( modifier = Modifier.fillMaxSize(), - arcGISMap = mapViewModel.map, - mapViewProxy = mapViewModel.mapViewProxy, - locationDisplay = locationDisplay + mapViewModel = mapViewModel, ) + // VORDERGRUND: Das Overlay (wenn showReport = true) if (showReport) { ReportOverlay( onCancel = onDismissReport, @@ -118,18 +119,29 @@ fun ContentScreen( ) } + // Slider von Links SideSlider(visible = sliderOpen) { - SliderMenuItem(text = "Schäden filtern", icon = Icons.Default.FilterAlt, onClick = {}) - } + Text( + "Menü", + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(bottom = 12.dp) + ) - if (mapViewModel.snackBarMessage.isNotEmpty()) { - Snackbar(modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 80.dp)) { - Text(mapViewModel.snackBarMessage) - } - LaunchedEffect(mapViewModel.snackBarMessage) { - kotlinx.coroutines.delay(4000) - mapViewModel.snackBarMessage = "" - } + SliderMenuItem( + text = "Schäden filtern", + icon = Icons.Default.FilterAlt, + onClick = { + /* TODO */ + } + ) + + SliderMenuItem( + text = "Schadensliste", + icon = Icons.Default.FormatListNumbered, + onClick = { + /* TODO */ + } + ) } } } @@ -349,10 +361,4 @@ fun ReportOverlay( } } } -} - -fun createMap(): ArcGISMap { - return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { - initialViewpoint = Viewpoint(53.14, 8.20, 20000.0) - } } \ No newline at end of file diff --git a/app/src/main/java/com/example/snapandsolve/MapSegment.kt b/app/src/main/java/com/example/snapandsolve/MapSegment.kt new file mode 100644 index 0000000..e38c3b3 --- /dev/null +++ b/app/src/main/java/com/example/snapandsolve/MapSegment.kt @@ -0,0 +1,122 @@ +package com.example.snapandsolve + +import MapViewModel +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat +import com.arcgismaps.ApiKey +import com.arcgismaps.ArcGISEnvironment +import com.arcgismaps.location.LocationDisplayAutoPanMode +import com.arcgismaps.toolkit.geoviewcompose.MapView +import com.arcgismaps.toolkit.geoviewcompose.rememberLocationDisplay +import kotlinx.coroutines.launch + +@Composable +fun MapSegment( + modifier: Modifier = Modifier, + mapViewModel: MapViewModel +) { + val context = LocalContext.current + val coroutineScope = rememberCoroutineScope() + + ArcGISEnvironment.applicationContext = context.applicationContext + ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ARCGIS_TOKEN) + + // val snackbarHostState = remember { SnackbarHostState() } + + val locationDisplay = rememberLocationDisplay().apply { + setAutoPanMode(LocationDisplayAutoPanMode.Off) + } + + if (checkPermissions(context)) { + // Permissions are already granted. + LaunchedEffect(Unit) { + locationDisplay.dataSource.start() + } + } else { + RequestPermissions( + context = context, + onPermissionsGranted = { + coroutineScope.launch { + locationDisplay.dataSource.start() + } + } + ) + } + + Column( + modifier = modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + MapView( + modifier = Modifier.weight(90f), + arcGISMap = mapViewModel.map, + locationDisplay = locationDisplay, + mapViewProxy = mapViewModel.mapViewProxy, + onSingleTapConfirmed = {}//mapViewModel::onTap, + ) { + /* TODO */ + } + } +} + +fun checkPermissions(context: Context): Boolean { + // Check permissions to see if both permissions are granted. + // Coarse location permission. + val permissionCheckCoarseLocation = ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + // Fine location permission. + val permissionCheckFineLocation = ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + + return permissionCheckCoarseLocation && permissionCheckFineLocation +} + +fun showError(context: Context, message: String) { + Toast.makeText(context, message, Toast.LENGTH_LONG).show() +} + +@Composable +fun RequestPermissions(context: Context, onPermissionsGranted: () -> Unit) { + + // Create an activity result launcher using permissions contract and handle the result. + val activityResultLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + // Check if both fine & coarse location permissions are true. + if (permissions.all { it.value }) { + onPermissionsGranted() + } else { + showError(context, "Location permissions were denied") + } + } + + LaunchedEffect(Unit) { + activityResultLauncher.launch( + // Request both fine and coarse location permissions. + arrayOf( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION + ) + ) + } +} \ No newline at end of file