Compare commits

...

2 Commits

Author SHA1 Message Date
e7f0ef03ad - Standortbestimmungfunktionalität hinzugefügt 2025-12-20 11:22:12 +01:00
2f781abcc2 - Nur ArcGIS Karte implementiert
- in toml arcgis libraries hinzugefügt
- mindSdk von 27 auf 28 gesetzt
2025-12-19 20:08:56 +01:00
10 changed files with 365 additions and 2 deletions

123
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-12-19T16:43:39.005516700Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=N0AA003656K80600629" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

View File

@@ -12,7 +12,7 @@ android {
defaultConfig {
applicationId = "com.example.snapandsolve"
minSdk = 27
minSdk = 28
targetSdk = 36
versionCode = 1
versionName = "1.0"
@@ -68,4 +68,11 @@ dependencies {
debugImplementation(libs.androidx.compose.ui.test.manifest)
implementation("androidx.compose.material:material-icons-core")
implementation("androidx.compose.material:material-icons-extended")
// ArcGIS Maps for Kotlin - SDK dependency
implementation(libs.arcgis.maps.kotlin)
// Toolkit dependencies
implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
}

View File

@@ -4,6 +4,12 @@
<!-- Kamera Zugriffsberechtigung -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- ArcGIS Pro benötigt-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Für Standortbestimmung -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"

View File

@@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import com.arcgismaps.ApiKey
import com.arcgismaps.ArcGISEnvironment
import com.example.snapandsolve.camera.AlbumViewModel
import com.example.snapandsolve.ui.theme.SnapAndSolveTheme
import kotlinx.coroutines.Dispatchers
@@ -21,6 +23,11 @@ class MainActivity : ComponentActivity() {
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 {

View File

@@ -57,12 +57,17 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.toolkit.geoviewcompose.MapView
import com.example.snapandsolve.camera.AlbumViewModel
import com.example.snapandsolve.camera.AlbumViewState
import com.example.snapandsolve.camera.Intent
import com.example.snapandsolve.ui.theme.AppColor
import com.example.snapandsolve.ui.theme.ButtonColor
import com.example.snapandsolve.ui.theme.WidgetColor
import com.example.snapandsolve.ui.theme.setupLocationDisplay
import kotlinx.coroutines.Dispatchers
@@ -130,10 +135,25 @@ fun ContentScreen(
onDismissReport: () -> Unit
) {
val mapViewModel = remember { MapViewModel(application) }
// ViewModel für die Kamera-Funktionalität
val albumViewModel = remember { AlbumViewModel(Dispatchers.Default) }
// ArcGIS Map erstellen
val map = remember {
createMap() //Funktion zur Erstellung der Map
}
//Standortbestimmung aus locationHelper.kt
val locationDisplay = setupLocationDisplay()
Box(modifier = modifier.fillMaxSize()) {
// HINTERGRUND: Die Map
MapView(
modifier = Modifier.fillMaxSize(),
arcGISMap = map,
locationDisplay = locationDisplay
)
// VORDERGRUND: Das Overlay (wenn showReport = true)
if (showReport) {
ReportOverlay(
onCancel = onDismissReport,
@@ -333,4 +353,21 @@ fun ReportOverlay(
}
}
}
}
fun createMap(): ArcGISMap {
return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
initialViewpoint = Viewpoint(
53.14,
8.20,
20000.0)
}
}

View File

@@ -1,8 +1,64 @@
package com.example.snapandsolve
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.data.ArcGISFeature
import com.arcgismaps.data.CodedValueDomain
import com.arcgismaps.data.ServiceFeatureTable
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.layers.FeatureLayer
import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
import kotlinx.coroutines.launch
class MapViewModel(application: Application ): AndroidViewModel(application) {
val map: ArcGISMap = ArcGISMap(BasemapStyle.OpenOsmStyle).apply {
initialViewpoint = Viewpoint(53.14, 8.20, 20000.0)
}
/*
ALLES UNTER DIESEM KOMMENTAR WIRD NICHT GENUTZT. Aber EVENTUELL nötig zum einbinden von
Layer und Features.
*/
// Hold a reference to the selected feature.
var selectedFeature: ArcGISFeature? by mutableStateOf(null)
val mapViewProxy = MapViewProxy()
//var currentFeatureOperation by mutableStateOf(FeatureOperationType.CREATE)
lateinit var featureLayer: FeatureLayer
// Create a snackbar message to display the result of feature operations.
var snackBarMessage: String by mutableStateOf("")
lateinit var serviceFeatureTable: ServiceFeatureTable
var currentDamageType by mutableStateOf("")
// The list of damage types to update the feature attribute.
var damageTypeList: List<String> = mutableListOf()
init {
viewModelScope.launch {
serviceFeatureTable = ServiceFeatureTable("https://services9.arcgis.com/UVxdrlZq3S3gqt7w/ArcGIS/rest/services/StrassenSchaeden/FeatureServer/0")
serviceFeatureTable.load().onSuccess {
// Get the field from the feature table that will be updated.
val typeDamageField = serviceFeatureTable.fields.first { it.name == "Typ" }
// Get the coded value domain for the field.
val attributeDomain = typeDamageField.domain as CodedValueDomain
// Add the damage types to the list.
attributeDomain.codedValues.forEach {
damageTypeList += it.name
}
}
featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
map.operationalLayers.add(featureLayer)
}
}
}

View File

@@ -0,0 +1,109 @@
package com.example.snapandsolve.ui.theme
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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.core.content.ContextCompat
import com.arcgismaps.location.LocationDisplayAutoPanMode
import com.arcgismaps.mapping.view.LocationDisplay
import com.arcgismaps.toolkit.geoviewcompose.rememberLocationDisplay
import kotlinx.coroutines.launch
/**
* Helper-Klasse für Standort-Funktionalität
*/
class LocationHelper(private val context: Context) {
/**
* Prüft, ob Standort-Berechtigungen erteilt wurden
*/
fun hasLocationPermissions(): Boolean {
val coarseLocation = ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
val fineLocation = ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
return coarseLocation && fineLocation
}
}
/**
* 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(
autoPanMode: LocationDisplayAutoPanMode = LocationDisplayAutoPanMode.Recenter
): LocationDisplay {
val context = androidx.compose.ui.platform.LocalContext.current
val coroutineScope = rememberCoroutineScope()
val locationHelper = LocationHelper(context)
val locationDisplay = rememberLocationDisplay().apply {
setAutoPanMode(autoPanMode)
}
if (locationHelper.hasLocationPermissions()) {
LaunchedEffect(Unit) {
locationDisplay.dataSource.start()
}
} else {
RequestLocationPermissions(
context = context,
onPermissionsGranted = {
coroutineScope.launch {
locationDisplay.dataSource.start()
}
}
)
}
return locationDisplay
}
/**
* Composable zum Anfordern von Standort-Berechtigungen
*/
@Composable
private fun RequestLocationPermissions(
context: Context,
onPermissionsGranted: () -> Unit
) {
val activityResultLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.all { it.value }) {
onPermissionsGranted()
} else {
Toast.makeText(
context,
"Standort-Berechtigung wurde verweigert",
Toast.LENGTH_LONG
).show()
}
}
LaunchedEffect(Unit) {
activityResultLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
}

View File

@@ -9,6 +9,7 @@ lifecycleRuntimeKtx = "2.10.0"
activityCompose = "1.12.1"
composeBom = "2024.09.00"
material3 = "1.4.0"
arcgisMapsKotlin = "200.8.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -26,6 +27,10 @@ androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
arcgis-maps-kotlin = { group = "com.esri", name = "arcgis-maps-kotlin", version.ref = "arcgisMapsKotlin" }
arcgis-maps-kotlin-toolkit-bom = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-bom", version.ref = "arcgisMapsKotlin" }
arcgis-maps-kotlin-toolkit-geoview-compose = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-geoview-compose" }
arcgis-maps-kotlin-toolkit-authentication = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-authentication" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }