summaryrefslogtreecommitdiff
path: root/android-app/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'android-app/app/src/main')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportFragment.kt17
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportGenerator.kt60
-rw-r--r--android-app/app/src/main/res/layout/fragment_trip_report.xml4
3 files changed, 78 insertions, 3 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportFragment.kt
index 632f616..d567ed6 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportFragment.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportFragment.kt
@@ -22,10 +22,14 @@ class TripReportFragment : Fragment() {
)
}
+ private lateinit var tvReportTitle: TextView
private lateinit var tvNarrativeContent: TextView
private lateinit var btnRefresh: MaterialButton
private lateinit var progress: ProgressBar
+ private var pirateMode = false
+ private val generator = TripReportGenerator()
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -35,12 +39,23 @@ class TripReportFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ tvReportTitle = view.findViewById(R.id.tv_report_title)
tvNarrativeContent = view.findViewById(R.id.tv_narrative_content)
btnRefresh = view.findViewById(R.id.btn_refresh_report)
progress = view.findViewById(R.id.progress_report)
btnRefresh.setOnClickListener { viewModel.generateReport() }
+ tvReportTitle.setOnLongClickListener {
+ pirateMode = !pirateMode
+ tvReportTitle.text = if (pirateMode) "☠ Trip Report ☠" else "Trip Report"
+ val s = viewModel.state.value
+ if (s is TripReportState.Success) {
+ tvNarrativeContent.text = generator.generateNarrative(s.summary, pirate = pirateMode)
+ }
+ true
+ }
+
viewLifecycleOwner.lifecycleScope.launch {
viewModel.state.collect { state -> renderState(state) }
}
@@ -60,7 +75,7 @@ class TripReportFragment : Fragment() {
is TripReportState.Success -> {
progress.visibility = View.GONE
btnRefresh.isEnabled = true
- tvNarrativeContent.text = state.narrative
+ tvNarrativeContent.text = generator.generateNarrative(state.summary, pirate = pirateMode)
}
is TripReportState.Error -> {
progress.visibility = View.GONE
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportGenerator.kt b/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportGenerator.kt
index 2c7f77f..a101152 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportGenerator.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/tripreport/TripReportGenerator.kt
@@ -73,7 +73,10 @@ class TripReportGenerator {
return r * c
}
- fun generateNarrative(summary: TripSummary): String {
+ fun generateNarrative(summary: TripSummary, pirate: Boolean = false): String =
+ if (pirate) generatePirateNarrative(summary) else generateStandardNarrative(summary)
+
+ private fun generateStandardNarrative(summary: TripSummary): String {
val zone = ZoneId.systemDefault()
val dateFmt = DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.getDefault()).withZone(zone)
val timeFmt = DateTimeFormatter.ofPattern("HHmm", Locale.getDefault()).withZone(zone)
@@ -120,4 +123,59 @@ class TripReportGenerator {
return sb.toString().trimEnd()
}
+
+ private fun generatePirateNarrative(summary: TripSummary): String {
+ val zone = ZoneId.systemDefault()
+ val dateFmt = DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.getDefault()).withZone(zone)
+ val timeFmt = DateTimeFormatter.ofPattern("HHmm", Locale.getDefault()).withZone(zone)
+ val startInst = Instant.ofEpochMilli(summary.startTimeMs)
+ val endInst = Instant.ofEpochMilli(summary.endTimeMs)
+
+ val durationMs = summary.endTimeMs - summary.startTimeMs
+ val durationStr = if (durationMs >= 3_600_000) {
+ val h = durationMs / 3_600_000
+ val m = (durationMs % 3_600_000) / 60_000
+ "${h}h ${m}m"
+ } else {
+ "${durationMs / 60_000}m"
+ }
+
+ val sb = StringBuilder()
+ sb.appendLine("☠ VOYAGE · ${dateFmt.format(startInst).uppercase(Locale.getDefault())}")
+ sb.appendLine("${timeFmt.format(startInst)}–${timeFmt.format(endInst)} · $durationStr")
+ sb.appendLine()
+ sb.appendLine("Made %.1f nm by the log · avg %.1f kt · peaked %.1f kt".format(
+ summary.distanceNm, summary.avgSogKts, summary.maxSogKts))
+
+ val conditions = buildList<String> {
+ summary.maxWaveHeightM?.let { add("seas ran %.1f m".format(it)) }
+ val minT = summary.minAirTempC
+ val maxT = summary.maxAirTempC
+ if (minT != null && maxT != null) {
+ add(if (minT == maxT) "%.0f°C".format(minT) else "%.0f–%.0f°C".format(minT, maxT))
+ }
+ }
+ if (conditions.isNotEmpty()) {
+ sb.appendLine()
+ sb.appendLine(conditions.joinToString(" "))
+ }
+
+ if (summary.logEntries.isNotEmpty()) {
+ val exclamations = listOf(
+ "Arr, ", "Blimey! ", "Shiver me timbers — ",
+ "Avast! ", "By Neptune's beard, ", "Splice the mainbrace! "
+ )
+ sb.appendLine()
+ for ((i, entry) in summary.logEntries.sortedBy { it.timestampMs }.withIndex()) {
+ val time = timeFmt.format(Instant.ofEpochMilli(entry.timestampMs))
+ val photoMarker = if (entry.photoPath != null) " [portrait]" else ""
+ val prefix = exclamations[i % exclamations.size]
+ sb.appendLine("$time $prefix${entry.text}$photoMarker")
+ }
+ }
+
+ sb.appendLine()
+ sb.append("— Arr. Fair winds and following seas. —")
+ return sb.toString().trimEnd()
+ }
}
diff --git a/android-app/app/src/main/res/layout/fragment_trip_report.xml b/android-app/app/src/main/res/layout/fragment_trip_report.xml
index 7228fa9..c8092ba 100644
--- a/android-app/app/src/main/res/layout/fragment_trip_report.xml
+++ b/android-app/app/src/main/res/layout/fragment_trip_report.xml
@@ -12,12 +12,14 @@
android:padding="24dp">
<TextView
+ android:id="@+id/tv_report_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Trip Report"
android:textSize="24sp"
android:textStyle="bold"
- android:layout_marginBottom="24dp" />
+ android:layout_marginBottom="24dp"
+ android:longClickable="true" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"