diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-13 19:54:43 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-13 19:54:43 +0000 |
| commit | 72ab76835dec92ea30c2d26009e3576aa8f1dbc7 (patch) | |
| tree | d696d00ca523a2ae3d63fd575058c09e4ec5cb91 /android-app/app | |
| parent | 0b3fc43df0f522a9d2b308c79b35870e3afb08db (diff) | |
Implement GPS navigation with position, SOG, and COG
Diffstat (limited to 'android-app/app')
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 |
