From d98b441f2f9ca8b11a04406240dd19ecc0cac7ab Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Mon, 6 Apr 2026 18:03:39 +0000 Subject: feat(nav): replace Map+Instruments with Map+Layers in bottom nav MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layers acts as an action button — shows LayerPickerSheet and snaps back to Map so it never stays selected. Instruments tab removed; sheet expand/collapse via swipe as before. Co-Authored-By: Claude Sonnet 4.6 --- android-app/app/src/main/res/menu/bottom_nav_menu.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'android-app/app/src/main/res/menu') diff --git a/android-app/app/src/main/res/menu/bottom_nav_menu.xml b/android-app/app/src/main/res/menu/bottom_nav_menu.xml index b29fb08..e7fc15d 100644 --- a/android-app/app/src/main/res/menu/bottom_nav_menu.xml +++ b/android-app/app/src/main/res/menu/bottom_nav_menu.xml @@ -5,9 +5,9 @@ android:icon="@drawable/ic_map" android:title="Map" /> + android:id="@+id/nav_layers" + android:icon="@drawable/ic_layers" + android:title="Layers" /> Date: Fri, 10 Apr 2026 22:47:27 +0000 Subject: Add Learn tab; move quit to Safety; remove persistent MOB FAB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI cleanup: - Remove always-visible MOB FAB — Safety screen button is sufficient - Remove floating quit button; move to bottom of Safety screen - fragment_container is now clickable/focusable, blocking map touch-through for all overlays (trip reports, log, safety) - Record Track FAB hidden while any overlay is shown, visible on map tab New Learn tab (5th nav item): - Migration guides for Navionics and Sea People (local markdown via DocFragment) - ASA Course Catalog, ASA Online Learning, ColRegs, Sailing Flashcards (open in browser) - Remove blanket assets/ .gitignore so markdown docs are tracked https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX --- android-app/.gitignore | 4 +- .../app/src/main/assets/docs/migrate_navionics.md | 56 +++++ .../app/src/main/assets/docs/migrate_sea_people.md | 59 ++++++ .../src/main/kotlin/org/terst/nav/MainActivity.kt | 20 +- .../kotlin/org/terst/nav/ui/learn/LearnFragment.kt | 56 +++++ .../org/terst/nav/ui/safety/SafetyFragment.kt | 5 + android-app/app/src/main/res/drawable/ic_learn.xml | 9 + .../app/src/main/res/layout/activity_main.xml | 35 +-- .../app/src/main/res/layout/fragment_learn.xml | 235 +++++++++++++++++++++ .../app/src/main/res/layout/fragment_safety.xml | 11 + .../app/src/main/res/menu/bottom_nav_menu.xml | 4 + 11 files changed, 450 insertions(+), 44 deletions(-) create mode 100644 android-app/app/src/main/assets/docs/migrate_navionics.md create mode 100644 android-app/app/src/main/assets/docs/migrate_sea_people.md create mode 100644 android-app/app/src/main/kotlin/org/terst/nav/ui/learn/LearnFragment.kt create mode 100644 android-app/app/src/main/res/drawable/ic_learn.xml create mode 100644 android-app/app/src/main/res/layout/fragment_learn.xml (limited to 'android-app/app/src/main/res/menu') diff --git a/android-app/.gitignore b/android-app/.gitignore index 7db1cd2..acd3de6 100644 --- a/android-app/.gitignore +++ b/android-app/.gitignore @@ -11,8 +11,8 @@ build/ *.iws local.properties -# Generated files -app/src/main/assets/ +# Generated assets (add specific patterns here if needed) +# app/src/main/assets/ — removed blanket ignore; docs are tracked # Keystore *.jks diff --git a/android-app/app/src/main/assets/docs/migrate_navionics.md b/android-app/app/src/main/assets/docs/migrate_navionics.md new file mode 100644 index 0000000..83b28fa --- /dev/null +++ b/android-app/app/src/main/assets/docs/migrate_navionics.md @@ -0,0 +1,56 @@ +# Migrating from Navionics + +Welcome to Nav. This guide covers the key differences and how to move your data over. + +--- + +## What's Different + +| Feature | Navionics | Nav | +|---|---|---| +| Charts | Navionics SonarChart + Raster | OpenFreeMap vector tiles | +| Track recording | In-app GPX | GPX saved to Documents/Nav/ | +| Trip log | Voyage log | Voice log (transcribed) | +| Departure planning | None | Pre-trip briefing with sail plan | +| Wind overlay | Basic | Wind particles + hourly forecast | + +--- + +## Exporting Your Tracks from Navionics + +1. Open Navionics on your phone or tablet +2. Go to **My Charts → Tracks** +3. Tap a track → **Share → Export GPX** +4. Save or AirDrop the `.gpx` file to your device + +--- + +## Importing Tracks into Nav + +Nav automatically reads GPX files from the **Documents/Nav/** folder on your device. + +1. Move your exported `.gpx` files to `Documents/Nav/` using the Files app +2. Re-open Nav — tracks are loaded on startup +3. Similar-conditions trip comparison in the Pre-Trip Briefing will use these tracks + +--- + +## Waypoints + +Nav does not currently manage waypoints. Use Navionics or a dedicated chart plotter for waypoint routing. Nav focuses on departure briefing, track recording, and the sail log. + +--- + +## Charts + +Nav uses **OpenFreeMap** vector tiles. These are free, fast, and work well for coastal cruising. They do not include depth contours or hazard overlays — continue using Navionics or a NOAA chart app for navigation in unfamiliar waters. + +--- + +## Daily Workflow + +**Before you sail:** Open the **Safety → Plan Trip** screen for your departure briefing — sail plan, heading recommendation, watch items, and a look at how conditions will change over your trip window. + +**On the water:** Tap **Log** to dictate voice notes. The GPS track records automatically when you tap the record button. + +**After you return:** The post-trip report summarises distance, duration, and max SOG. diff --git a/android-app/app/src/main/assets/docs/migrate_sea_people.md b/android-app/app/src/main/assets/docs/migrate_sea_people.md new file mode 100644 index 0000000..b1920b4 --- /dev/null +++ b/android-app/app/src/main/assets/docs/migrate_sea_people.md @@ -0,0 +1,59 @@ +# Migrating from Sea People + +Welcome to Nav. This guide covers what transfers over and what works differently. + +--- + +## What's Different + +| Feature | Sea People | Nav | +|---|---|---| +| Social feed | Community posts | Not applicable — Nav is single-user | +| Trip log | Text entries + photos | Voice log (transcribed audio) | +| Boat profiles | Basic boat card | Detailed sail inventory with reef points | +| Weather | Integrated forecast | Open-Meteo hourly + NOAA marine | +| Departure planning | Manual notes | Pre-trip briefing with sail plan & route | + +--- + +## Exporting Your Log from Sea People + +1. Open Sea People → **Profile → My Logs** +2. Use the **Export** option (CSV or PDF depending on your version) +3. Save entries you want to keep for reference + +Nav's voice log works differently — entries are dictated on the water and transcribed. You won't be able to import Sea People entries directly, but you can review them alongside Nav going forward. + +--- + +## Boat Profiles + +Nav stores a detailed boat profile with your sail inventory: + +- **Headsails** with wind range for each (e.g. 155% Genoa ≤ 13 kt, 100% Jib 10–21 kt, 65% Blade 18+ kt) +- **Main reef points** +- **Hull length** (used for hull-speed estimates in the departure briefing) + +Your boats are pre-seeded on first launch. Profiles are stored locally and used in the **Pre-Trip Briefing** to give sail recommendations specific to your boat. + +--- + +## Trip Log Workflow + +**On the water:** Tap the **Log** tab and use the microphone button to dictate a note. Notes are timestamped with your GPS position. + +**Voice notes work well for:** +- Sea state observations ("1.5m swell, 10s period, comfortable") +- Traffic ("fishing vessel on starboard, gave way") +- Sail changes ("reefed main at 18 kt") +- Time stamps ("rounded the point, heading west") + +--- + +## Daily Workflow + +**Before you sail:** Safety → Plan Trip for your departure briefing. + +**On the water:** Log tab for voice notes; tap the record FAB to start the GPS track. + +**After you return:** The post-trip report generates a narrative summary you can save or share. 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 86dd531..996892e 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 @@ -54,9 +54,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { private lateinit var bottomSheetBehavior: BottomSheetBehavior private lateinit var fragmentContainer: FrameLayout private lateinit var fabRecordTrack: FloatingActionButton - private lateinit var fabMob: FloatingActionButton private lateinit var fabRecenter: MaterialButton - private lateinit var btnQuit: MaterialButton private lateinit var bottomSheet: CardView private lateinit var bottomNav: BottomNavigationView private lateinit var mapCrosshair: View @@ -117,9 +115,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { private fun initializeUI() { fragmentContainer = findViewById(R.id.fragment_container) fabRecordTrack = findViewById(R.id.fab_record_track) - fabMob = findViewById(R.id.fab_mob) fabRecenter = findViewById(R.id.fab_recenter) - btnQuit = findViewById(R.id.btn_quit) bottomSheet = findViewById(R.id.instrument_bottom_sheet) bottomNav = findViewById(R.id.bottom_navigation) mapCrosshair = findViewById(R.id.map_crosshair) @@ -166,9 +162,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { } else false } - fabMob.setOnClickListener { onActivateMob() } fabRecenter.setOnClickListener { mapHandler?.recenter() } - btnQuit.setOnClickListener { onQuitRequested() } lifecycleScope.launch { viewModel.isRecording.collect { recording -> @@ -280,6 +274,12 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN true } + R.id.nav_learn -> { + showOverlay(org.terst.nav.ui.learn.LearnFragment()) + bottomSheetBehavior.isHideable = true + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + true + } else -> false } } @@ -287,6 +287,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { private fun showOverlay(fragment: androidx.fragment.app.Fragment) { fragmentContainer.visibility = View.VISIBLE + fabRecordTrack.visibility = View.GONE supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, fragment) .commit() @@ -294,9 +295,10 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { private fun hideOverlays() { fragmentContainer.visibility = View.GONE + fabRecordTrack.visibility = View.VISIBLE } - private fun onQuitRequested() { + override fun onQuitRequested() { if (viewModel.isRecording.value) { androidx.appcompat.app.AlertDialog.Builder(this) .setMessage("Recording in progress. Quit and discard the current track?") @@ -405,9 +407,9 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { mapCrosshair.visibility = if (following) View.GONE else View.VISIBLE if (following) { fadeOut(fabRecenter, gone = true) - fadeIn(bottomNav, fabMob, fabRecordTrack) + fadeIn(bottomNav, fabRecordTrack) } else { - fadeOut(bottomNav, fabMob, fabRecordTrack, gone = true) + fadeOut(bottomNav, fabRecordTrack, gone = true) fadeIn(fabRecenter) } } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/learn/LearnFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/learn/LearnFragment.kt new file mode 100644 index 0000000..8440edb --- /dev/null +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/learn/LearnFragment.kt @@ -0,0 +1,56 @@ +package org.terst.nav.ui.learn + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.google.android.material.card.MaterialCardView +import org.terst.nav.R +import org.terst.nav.ui.doc.DocFragment + +class LearnFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? = inflater.inflate(R.layout.fragment_learn, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + // Migration guides — open local markdown docs + view.findViewById(R.id.card_migrate_navionics).setOnClickListener { + openDoc("docs/migrate_navionics.md") + } + view.findViewById(R.id.card_migrate_seapeople).setOnClickListener { + openDoc("docs/migrate_sea_people.md") + } + + // ASA / external links — open in browser + view.findViewById(R.id.card_asa_courses).setOnClickListener { + openUrl("https://www.asa.com/courses/") + } + view.findViewById(R.id.card_asa_online).setOnClickListener { + openUrl("https://www.asa.com/online-courses/") + } + view.findViewById(R.id.card_colregs).setOnClickListener { + openUrl("https://www.navcen.uscg.gov/international-regulations-for-preventing-collisions-at-sea") + } + view.findViewById(R.id.card_flashcards).setOnClickListener { + openUrl("https://quizlet.com/subject/sailing/") + } + } + + private fun openDoc(path: String) { + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container, DocFragment.newInstance(path)) + .addToBackStack(null) + .commit() + } + + private fun openUrl(url: String) { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + } +} diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/safety/SafetyFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/safety/SafetyFragment.kt index 4bc0c7a..6dd7411 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/safety/SafetyFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/safety/SafetyFragment.kt @@ -15,6 +15,7 @@ class SafetyFragment : Fragment() { interface SafetyListener { fun onActivateMob() fun onConfigureAnchor() + fun onQuitRequested() } private var listener: SafetyListener? = null @@ -55,6 +56,10 @@ class SafetyFragment : Fragment() { .addToBackStack(null) .commit() } + + view.findViewById(R.id.button_quit).setOnClickListener { + listener?.onQuitRequested() + } } fun updateAnchorStatus(statusText: String) { diff --git a/android-app/app/src/main/res/drawable/ic_learn.xml b/android-app/app/src/main/res/drawable/ic_learn.xml new file mode 100644 index 0000000..1574693 --- /dev/null +++ b/android-app/app/src/main/res/drawable/ic_learn.xml @@ -0,0 +1,9 @@ + + + diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml index 5943949..1bb88b3 100644 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ b/android-app/app/src/main/res/layout/activity_main.xml @@ -59,26 +59,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" + android:clickable="true" + android:focusable="true" android:background="?attr/colorSurface" /> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android-app/app/src/main/res/layout/fragment_safety.xml b/android-app/app/src/main/res/layout/fragment_safety.xml index f90420e..60ea1aa 100644 --- a/android-app/app/src/main/res/layout/fragment_safety.xml +++ b/android-app/app/src/main/res/layout/fragment_safety.xml @@ -113,4 +113,15 @@ android:text="PLAN TRIP (PRE-TRIP REPORT)" app:layout_constraintTop_toBottomOf="@id/card_anchor" /> + + diff --git a/android-app/app/src/main/res/menu/bottom_nav_menu.xml b/android-app/app/src/main/res/menu/bottom_nav_menu.xml index e7fc15d..3037b7e 100644 --- a/android-app/app/src/main/res/menu/bottom_nav_menu.xml +++ b/android-app/app/src/main/res/menu/bottom_nav_menu.xml @@ -16,4 +16,8 @@ android:id="@+id/nav_safety" android:icon="@drawable/ic_safety" android:title="Safety" /> + -- cgit v1.2.3