From 72ab76835dec92ea30c2d26009e3576aa8f1dbc7 Mon Sep 17 00:00:00 2001 From: Claudomator Agent Date: Fri, 13 Mar 2026 19:54:43 +0000 Subject: Implement GPS navigation with position, SOG, and COG --- .../com/example/androidapp/LocationService.kt | 54 ++++++++++++++++++ .../kotlin/com/example/androidapp/MainActivity.kt | 64 ++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 android-app/app/src/main/kotlin/com/example/androidapp/LocationService.kt create mode 100644 android-app/app/src/main/kotlin/com/example/androidapp/MainActivity.kt (limited to 'android-app/app/src/main/kotlin/com/example/androidapp') diff --git a/android-app/app/src/main/kotlin/com/example/androidapp/LocationService.kt b/android-app/app/src/main/kotlin/com/example/androidapp/LocationService.kt new file mode 100644 index 0000000..346fdfe --- /dev/null +++ b/android-app/app/src/main/kotlin/com/example/androidapp/LocationService.kt @@ -0,0 +1,54 @@ +package com.example.androidapp + +import android.annotation.SuppressLint +import android.content.Context +import android.location.Location +import android.os.Looper +import com.google.android.gms.location.* +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow + +data class GpsData( + val latitude: Double, + val longitude: Double, + val speedOverGround: Float, // m/s + val courseOverGround: Float // degrees +) + +class LocationService(private val context: Context) { + + private val fusedLocationClient: FusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(context) + + @SuppressLint("MissingPermission") // Permissions handled by the calling component (Activity/Fragment) + fun getLocationUpdates(): Flow = callbackFlow { + val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000) + .setMinUpdateIntervalMillis(500) + .build() + + val locationCallback = object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult) { + locationResult.lastLocation?.let { location -> + val gpsData = GpsData( + latitude = location.latitude, + longitude = location.longitude, + speedOverGround = location.speed, + courseOverGround = location.bearing + ) + trySend(gpsData) + } + } + } + + fusedLocationClient.requestLocationUpdates( + locationRequest, + locationCallback, + Looper.getMainLooper() + ) + + awaitClose { + fusedLocationClient.removeLocationUpdates(locationCallback) + } + } +} diff --git a/android-app/app/src/main/kotlin/com/example/androidapp/MainActivity.kt b/android-app/app/src/main/kotlin/com/example/androidapp/MainActivity.kt new file mode 100644 index 0000000..1d41f4a --- /dev/null +++ b/android-app/app/src/main/kotlin/com/example/androidapp/MainActivity.kt @@ -0,0 +1,64 @@ +package com.example.androidapp + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import org.maplibre.android.MapLibre +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style + +class MainActivity : AppCompatActivity() { + + private var mapView: MapView? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // MapLibre access token only needed for Mapbox styles, but good practice to initialize + MapLibre.getInstance(this) + setContentView(R.layout.activity_main) + + mapView = findViewById(R.id.mapView) + mapView?.onCreate(savedInstanceState) + mapView?.getMapAsync { maplibreMap -> + maplibreMap.setStyle(Style.Builder() + .addSource(RasterSource("noaa-enc-source", + TileSet("2.0.0", "asset://noaa_enc.mbtiles"))) + .addLayer(RasterLayer("noaa-enc-layer", "noaa-enc-source")) + ) + } + } + + override fun onStart() { + super.onStart() + mapView?.onStart() + } + + override fun onResume() { + super.onResume() + mapView?.onResume() + } + + override fun onPause() { + super.onPause() + mapView?.onPause() + } + + override fun onStop() { + super.onStop() + mapView?.onStop() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + mapView?.onSaveInstanceState(outState) + } + + override fun onLowMemory() { + super.onLowMemory() + mapView?.onLowMemory() + } + + override fun onDestroy() { + super.onDestroy() + mapView?.onDestroy() + } +} -- cgit v1.2.3