summaryrefslogtreecommitdiff
path: root/android-app/app
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator>2026-03-13 19:54:43 +0000
committerClaudomator Agent <agent@claudomator>2026-03-13 19:54:43 +0000
commit72ab76835dec92ea30c2d26009e3576aa8f1dbc7 (patch)
treed696d00ca523a2ae3d63fd575058c09e4ec5cb91 /android-app/app
parent0b3fc43df0f522a9d2b308c79b35870e3afb08db (diff)
Implement GPS navigation with position, SOG, and COG
Diffstat (limited to 'android-app/app')
-rw-r--r--android-app/app/build.gradle3
-rw-r--r--android-app/app/src/main/AndroidManifest.xml23
-rw-r--r--android-app/app/src/main/kotlin/com/example/androidapp/LocationService.kt54
-rw-r--r--android-app/app/src/main/kotlin/com/example/androidapp/MainActivity.kt64
-rw-r--r--android-app/app/src/main/res/layout/activity_main.xml18
5 files changed, 162 insertions, 0 deletions
diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle
index 968b305..bd903a0 100644
--- a/android-app/app/build.gradle
+++ b/android-app/app/build.gradle
@@ -51,6 +51,9 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'com.google.android.gms:play-services-location:21.0.1' // Added for FusedLocationProviderClient
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2fce535
--- /dev/null
+++ b/android-app/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AndroidApp">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest> \ No newline at end of file
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<GpsData> = 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()
+ }
+}
diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..a618e78
--- /dev/null
+++ b/android-app/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <org.maplibre.android.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file