summaryrefslogtreecommitdiff
path: root/android-app/app/src/main
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-04-03 07:25:13 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-04-03 07:25:13 +0000
commitbe56cf32a68ee1b0df2966ff39fd9751fd6afd7a (patch)
tree221ecb14396794e67ba475e107fbdc98b2b818ac /android-app/app/src/main
parenteccd9912ca52a245922a2984e3f88603ac42ef8f (diff)
feat(instruments): replace simulation with real GPS and barometer data
- Drop VMG, Polar %, and PolarDiagramView — no NMEA source on boat - Shrink grid to 3×2 (AWS/HDG/BSP / TWS/COG/SOG) - Move Depth + Baro to expanded section side by side - Initialize all instruments to "—" on startup - Wire LocationService.locationFlow → SOG + COG display (real GPS) - Wire LocationService.barometerStatus → Baro display (device sensor) - Delete startInstrumentSimulation() fake loop entirely Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'android-app/app/src/main')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt69
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/InstrumentHandler.kt36
-rw-r--r--android-app/app/src/main/res/layout/layout_instruments_sheet.xml88
3 files changed, 51 insertions, 142 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
index 252761e..c48cec2 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
@@ -11,7 +11,6 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.FrameLayout
-import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
@@ -22,15 +21,12 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.button.MaterialButton
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.floatingactionbutton.FloatingActionButton
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import java.util.Locale
import org.maplibre.android.MapLibre
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.Style
@@ -185,20 +181,14 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
valueCog = findViewById(R.id.value_cog),
valueBsp = findViewById(R.id.value_bsp),
valueSog = findViewById(R.id.value_sog),
- valueVmg = findViewById(R.id.value_vmg),
valueDepth = findViewById(R.id.value_depth),
- valuePolarPct = findViewById(R.id.value_polar_pct),
- valueBaro = findViewById(R.id.value_baro),
- labelTrend = null, // simplified
- barometerTrendView = null, // simplified
- polarDiagramView = findViewById(R.id.polar_diagram_view)
+ valueBaro = findViewById(R.id.value_baro)
+ )
+ instrumentHandler?.updateDisplay(
+ aws = "—", tws = "—", hdg = "—",
+ cog = "—", bsp = "—", sog = "—",
+ depth = "—", baro = "—"
)
-
- // anchorWatchHandler is initialized when the anchor config UI is available
-
- val mockPolarTable = createMockPolarTable()
- findViewById<PolarDiagramView>(R.id.polar_diagram_view).setPolarTable(mockPolarTable)
- startInstrumentSimulation(mockPolarTable)
}
// Helper to convert dp to px
@@ -277,7 +267,19 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
LocationService.locationFlow.collect { gpsData ->
mapHandler?.centerOnLocation(gpsData.latitude, gpsData.longitude)
val sogKnots = gpsData.speedOverGround * 1.94384
- viewModel.addGpsPoint(gpsData.latitude, gpsData.longitude, sogKnots, gpsData.courseOverGround.toDouble())
+ val cogDeg = gpsData.courseOverGround
+ viewModel.addGpsPoint(gpsData.latitude, gpsData.longitude, sogKnots, cogDeg.toDouble())
+ instrumentHandler?.updateDisplay(
+ sog = "%.1f".format(Locale.getDefault(), sogKnots),
+ cog = "%.0f°".format(Locale.getDefault(), cogDeg)
+ )
+ }
+ }
+ lifecycleScope.launch {
+ LocationService.barometerStatus.collect { status ->
+ if (status.history.isNotEmpty()) {
+ instrumentHandler?.updateDisplay(baro = status.formatPressure())
+ }
}
}
lifecycleScope.launch {
@@ -292,28 +294,6 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
}
}
- private fun startInstrumentSimulation(polarTable: PolarTable) {
- lifecycleScope.launch {
- var simulatedTws = 8.0
- var simulatedTwa = 40.0
- while (true) {
- val bsp = polarTable.interpolateBsp(simulatedTws, simulatedTwa)
- instrumentHandler?.updateDisplay(
- aws = "%.1f".format(Locale.getDefault(), simulatedTws * 1.1),
- tws = "%.1f".format(Locale.getDefault(), simulatedTws),
- bsp = "%.1f".format(Locale.getDefault(), bsp),
- sog = "%.1f".format(Locale.getDefault(), bsp * 0.95),
- vmg = "%.1f".format(Locale.getDefault(), polarTable.curves.firstOrNull { it.twS == simulatedTws }?.calculateVmg(simulatedTwa, bsp) ?: 0.0),
- polarPct = "%.0f%%".format(Locale.getDefault(), polarTable.calculatePolarPercentage(simulatedTws, simulatedTwa, bsp)),
- baro = "1013.2"
- )
- instrumentHandler?.updatePolarDiagram(simulatedTws, simulatedTwa, bsp)
- simulatedTwa = (simulatedTwa + 0.5).let { if (it > 170) 40.0 else it }
- delay(1000)
- }
- }
- }
-
private fun rasterizeDrawable(drawableId: Int): Bitmap {
val drawable = ContextCompat.getDrawable(this, drawableId)!!
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
@@ -323,15 +303,6 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
return bitmap
}
- private fun createMockPolarTable(): PolarTable {
- val curves = listOf(6.0, 8.0, 10.0).map { tws ->
- PolarCurve(tws, listOf(30.0, 45.0, 60.0, 90.0, 120.0, 150.0, 180.0).map { twa ->
- PolarPoint(twa, tws * (0.4 + twa / 200.0))
- })
- }
- return PolarTable(curves)
- }
-
private fun fadeOut(vararg views: View, gone: Boolean = false) {
views.forEach { v ->
v.animate().alpha(0f).setDuration(150).withEndAction {
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/InstrumentHandler.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/InstrumentHandler.kt
index 2f72153..7e09756 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/InstrumentHandler.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/InstrumentHandler.kt
@@ -1,11 +1,6 @@
package org.terst.nav.ui
import android.widget.TextView
-import org.terst.nav.BarometerReading
-import org.terst.nav.BarometerTrendView
-import org.terst.nav.PolarDiagramView
-import org.terst.nav.R
-import java.util.Locale
/**
* Handles the display of instrument data in the UI.
@@ -17,16 +12,11 @@ class InstrumentHandler(
private val valueCog: TextView,
private val valueBsp: TextView,
private val valueSog: TextView,
- private val valueVmg: TextView,
private val valueDepth: TextView,
- private val valuePolarPct: TextView,
- private val valueBaro: TextView,
- private val labelTrend: TextView?,
- private val barometerTrendView: BarometerTrendView?,
- private val polarDiagramView: PolarDiagramView
+ private val valueBaro: TextView
) {
/**
- * Updates the text displays for various instruments.
+ * Updates the text displays for the given instruments. Null arguments leave the current value unchanged.
*/
fun updateDisplay(
aws: String? = null,
@@ -35,11 +25,8 @@ class InstrumentHandler(
cog: String? = null,
bsp: String? = null,
sog: String? = null,
- vmg: String? = null,
depth: String? = null,
- polarPct: String? = null,
- baro: String? = null,
- trend: String? = null
+ baro: String? = null
) {
aws?.let { valueAws.text = it }
tws?.let { valueTws.text = it }
@@ -47,24 +34,7 @@ class InstrumentHandler(
cog?.let { valueCog.text = it }
bsp?.let { valueBsp.text = it }
sog?.let { valueSog.text = it }
- vmg?.let { valueVmg.text = it }
depth?.let { valueDepth.text = it }
- polarPct?.let { valuePolarPct.text = it }
baro?.let { valueBaro.text = it }
- trend?.let { labelTrend?.text = it }
- }
-
- /**
- * Updates the polar diagram view.
- */
- fun updatePolarDiagram(tws: Double, twa: Double, bsp: Double) {
- polarDiagramView.setCurrentPerformance(tws, twa, bsp)
- }
-
- /**
- * Updates the barometer trend chart.
- */
- fun updateBarometerTrend(history: List<BarometerReading>) {
- barometerTrendView?.setHistory(history)
}
}
diff --git a/android-app/app/src/main/res/layout/layout_instruments_sheet.xml b/android-app/app/src/main/res/layout/layout_instruments_sheet.xml
index 0a84418..c651ba2 100644
--- a/android-app/app/src/main/res/layout/layout_instruments_sheet.xml
+++ b/android-app/app/src/main/res/layout/layout_instruments_sheet.xml
@@ -16,14 +16,14 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
- <!-- Grid of Instruments -->
+ <!-- 3×2 grid: AWS/HDG/BSP top row, TWS/COG/SOG bottom row -->
<androidx.gridlayout.widget.GridLayout
android:id="@+id/instrument_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:columnCount="3"
- app:rowCount="3"
+ app:rowCount="2"
app:layout_constraintTop_toBottomOf="@id/drag_handle">
<!-- Wind: AWS -->
@@ -35,7 +35,7 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="AWS" />
- <TextView android:id="@+id/value_aws" style="@style/InstrumentPrimaryValue" tools:text="12.5" />
+ <TextView android:id="@+id/value_aws" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
<!-- Compass: HDG -->
@@ -47,7 +47,7 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="HDG" />
- <TextView android:id="@+id/value_hdg" style="@style/InstrumentPrimaryValue" tools:text="225" />
+ <TextView android:id="@+id/value_hdg" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
<!-- Performance: BSP -->
@@ -59,7 +59,7 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="BSP" />
- <TextView android:id="@+id/value_bsp" style="@style/InstrumentPrimaryValue" tools:text="6.2" />
+ <TextView android:id="@+id/value_bsp" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
<!-- Wind: TWS -->
@@ -71,7 +71,7 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="TWS" />
- <TextView android:id="@+id/value_tws" style="@style/InstrumentPrimaryValue" tools:text="15.0" />
+ <TextView android:id="@+id/value_tws" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
<!-- Compass: COG -->
@@ -83,7 +83,7 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="COG" />
- <TextView android:id="@+id/value_cog" style="@style/InstrumentPrimaryValue" tools:text="230" />
+ <TextView android:id="@+id/value_cog" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
<!-- Performance: SOG -->
@@ -95,72 +95,40 @@
android:gravity="center"
android:padding="8dp">
<TextView style="@style/InstrumentLabel" android:text="SOG" />
- <TextView android:id="@+id/value_sog" style="@style/InstrumentPrimaryValue" tools:text="6.5" />
+ <TextView android:id="@+id/value_sog" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
- <!-- VMG -->
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_columnWeight="1"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="8dp">
- <TextView style="@style/InstrumentLabel" android:text="VMG" />
- <TextView android:id="@+id/value_vmg" style="@style/InstrumentPrimaryValue" tools:text="4.2" />
- </LinearLayout>
+ </androidx.gridlayout.widget.GridLayout>
+
+ <!-- Expanded: Depth + Baro side by side -->
+ <LinearLayout
+ android:id="@+id/expanded_instruments"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="24dp"
+ app:layout_constraintTop_toBottomOf="@id/instrument_grid"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
- <!-- Depth -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:layout_columnWeight="1"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="8dp">
+ android:layout_weight="1"
+ android:orientation="vertical">
<TextView style="@style/InstrumentLabel" android:text="Depth" />
- <TextView android:id="@+id/value_depth" style="@style/InstrumentPrimaryValue" tools:text="12.0" />
+ <TextView android:id="@+id/value_depth" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
- <!-- Polar % -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:layout_columnWeight="1"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="8dp">
- <TextView style="@style/InstrumentLabel" android:text="Polar %" />
- <TextView android:id="@+id/value_polar_pct" style="@style/InstrumentPrimaryValue" tools:text="95%" />
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <TextView style="@style/InstrumentLabel" android:text="Baro" />
+ <TextView android:id="@+id/value_baro" style="@style/InstrumentPrimaryValue" tools:text="—" />
</LinearLayout>
- </androidx.gridlayout.widget.GridLayout>
-
- <!-- Additional Detail (Visible when expanded) -->
- <TextView
- android:id="@+id/label_baro"
- style="@style/InstrumentLabel"
- android:text="Barometer"
- android:layout_marginTop="24dp"
- app:layout_constraintTop_toBottomOf="@id/instrument_grid"
- app:layout_constraintStart_toStartOf="parent" />
-
- <TextView
- android:id="@+id/value_baro"
- style="@style/InstrumentPrimaryValue"
- tools:text="1013.2 hPa"
- app:layout_constraintTop_toBottomOf="@id/label_baro"
- app:layout_constraintStart_toStartOf="parent" />
-
- <org.terst.nav.PolarDiagramView
- android:id="@+id/polar_diagram_view"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginTop="24dp"
- app:layout_constraintDimensionRatio="1:1"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/value_baro"
- app:layout_constraintBottom_toBottomOf="parent" />
+ </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>