diff options
| author | Claude <noreply@anthropic.com> | 2026-05-15 20:32:22 +0000 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-05-15 21:25:54 +0000 |
| commit | baa07bc5c062d4f358e8c655260bf32810184634 (patch) | |
| tree | c2698d415ec210108f3aed20fa3e3662c0859230 /android-app/app/src/main/kotlin/org/terst | |
| parent | 555483b7248b0cb75bb9ed83b47880349f0c6ee0 (diff) | |
fix: saved tracks browser, GPX import persistence
Saved tracks browser:
- TrackDetailSheet converted from BottomSheetDialogFragment to Fragment so
it fills the overlay instead of floating over the map
- Adapter tap pushes detail fragment onto the back stack; back button returns
to the list naturally via popBackStack()
- SavedTracksFragment back button calls hideOverlays() (made internal) to
close the overlay and return to the map
- Layout updated to full-screen: header with back button, RecyclerView fills
remaining space via layout_weight instead of fixed 300dp
GPX import persistence:
- TrackRepository.importTrack() saves parsed points to Documents/Nav/ via
TrackStorage (URI is persisted to SharedPreferences) and adds to
in-memory list
- MainViewModel.addImportedTrack() now calls importTrack() so files opened
via the GPX handler load on future launches
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
Diffstat (limited to 'android-app/app/src/main/kotlin/org/terst')
5 files changed, 42 insertions, 22 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 10eb0a8..1bd0b08 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 @@ -45,7 +45,6 @@ import org.terst.nav.settings.HardwareSource import org.terst.nav.thermal.ThermalState import org.terst.nav.thermal.thermalFlow import org.terst.nav.track.SavedTracksFragment -import org.terst.nav.track.TrackDetailSheet import org.terst.nav.ui.* import org.terst.nav.ui.doc.DocFragment import org.terst.nav.ui.map.ParticleWindView @@ -396,7 +395,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { .commit() } - private fun hideOverlays() { + internal fun hideOverlays() { fragmentContainer.visibility = View.GONE fabRecordTrack.visibility = View.VISIBLE fabLayers.visibility = View.VISIBLE diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt index b73b034..b2181c9 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch +import org.terst.nav.MainActivity import org.terst.nav.R import org.terst.nav.ui.MainViewModel import java.time.Instant @@ -28,7 +29,7 @@ class SavedTracksFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.findViewById<View>(R.id.btn_back).setOnClickListener { - parentFragmentManager.popBackStack() + (requireActivity() as MainActivity).hideOverlays() } val rv = view.findViewById<RecyclerView>(R.id.rv_saved_tracks) @@ -38,8 +39,11 @@ class SavedTracksFragment : Fragment() { val adapter = SavedTrackAdapter { track -> viewModel.selectTrack(track) - parentFragmentManager.popBackStack() - TrackDetailSheet.from(track).show(parentFragmentManager, "track_detail") + parentFragmentManager + .beginTransaction() + .replace(R.id.fragment_container, TrackDetailSheet.newInstance()) + .addToBackStack(null) + .commit() } rv.adapter = adapter diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackDetailSheet.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackDetailSheet.kt index 0ec42f7..c1764a8 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackDetailSheet.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackDetailSheet.kt @@ -5,11 +5,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.terst.nav.R import org.terst.nav.ui.MainViewModel import java.time.Instant @@ -28,24 +28,26 @@ data class LogEvent( val detail: String ) -class TrackDetailSheet : BottomSheetDialogFragment() { +class TrackDetailSheet : Fragment() { private val viewModel: MainViewModel by activityViewModels() companion object { - private const val ARG_TRACK_INDEX = "track_index" - - fun from(track: SavedTrack): TrackDetailSheet { - // We pass the track via the shared ViewModel — the BottomSheet reads selectedTrack. - return TrackDetailSheet() - } + fun newInstance() = TrackDetailSheet() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = inflater.inflate(R.layout.layout_track_detail_sheet, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val track = viewModel.selectedTrack.value ?: run { dismiss(); return } + val track = viewModel.selectedTrack.value ?: run { + parentFragmentManager.popBackStack() + return + } + + view.findViewById<View>(R.id.btn_back).setOnClickListener { + parentFragmentManager.popBackStack() + } val titleFmt = DateTimeFormatter.ofPattern("dd MMM yyyy · HH:mm", Locale.getDefault()) .withZone(ZoneId.systemDefault()) @@ -80,12 +82,10 @@ class TrackDetailSheet : BottomSheetDialogFragment() { val points = track.points val tacks = track.tacks - // Start / end points.firstOrNull()?.let { p -> events += LogEvent(p.lat, p.lon, p.timestampMs, "🚩", "Start", formatPointDetail(p)) } - // Tacks — detected maneuvers for (t in tacks) { val p = points[t.index] val delta = TackDetector.angleDiff(t.cogBefore, t.cogAfter) @@ -96,7 +96,6 @@ class TrackDetailSheet : BottomSheetDialogFragment() { "%.0f° → %.0f° (Δ%.0f°)".format(t.cogBefore, t.cogAfter, abs(delta))) } - // Significant speed changes (> 1.5 kt shift sustained over 2 consecutive points) for (i in 1 until points.size - 1) { val prev = points[i - 1].sogKnots val curr = points[i].sogKnots diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt index 7f765a1..20fc214 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt @@ -60,6 +60,23 @@ class TrackRepository(context: Context) { summary } + /** + * Saves a track parsed from an external GPX file into Documents/Nav/ and + * adds it to the in-memory list. Returns true if storage succeeded. + */ + suspend fun importTrack(points: List<TrackPoint>): Boolean = withContext(Dispatchers.IO) { + if (points.isEmpty()) return@withContext false + val startMs = points.first().timestampMs + val saved = storage.saveTrack(points, startMs) + if (saved) { + _pastTracks.add(0, points) + NavLogger.i("track", "importTrack: saved ${points.size} pts startMs=$startMs") + } else { + NavLogger.e("track", "importTrack: save failed for ${points.size} pts") + } + saved + } + fun addPoint(point: TrackPoint): Boolean { if (!isRecording) return false activePoints.add(point) diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt index dcbd98e..579ccdf 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt @@ -18,7 +18,6 @@ import org.terst.nav.track.SavedTrack import org.terst.nav.track.TrackPoint import org.terst.nav.track.TrackRepository import org.terst.nav.track.TrackStats -import org.terst.nav.track.toSavedTrack import org.terst.nav.settings.FeatureFlags import org.terst.nav.settings.HardwareSource import org.terst.nav.vessel.CrewMember @@ -411,11 +410,13 @@ class MainViewModel( fun selectTrack(track: SavedTrack) { _selectedTrack.value = track } fun clearSelectedTrack() { _selectedTrack.value = null } - /** Adds a track parsed from an external GPX file (not persisted to storage). */ + /** Saves a track parsed from an external GPX file to storage and updates state. */ fun addImportedTrack(points: List<TrackPoint>) { - val updated = _pastTracks.value.toMutableList().also { it.add(0, points) } - _pastTracks.value = updated - _savedTracks.value = updated.filter { it.isNotEmpty() }.map { it.toSavedTrack() } + viewModelScope.launch { + trackRepository.importTrack(points) + _pastTracks.value = trackRepository.getPastTracks() + _savedTracks.value = trackRepository.getSavedTracks() + } } fun panToTrackPoint(lat: Double, lon: Double) { |
