diff options
Diffstat (limited to 'android-app/app/src/main')
4 files changed, 170 insertions, 0 deletions
diff --git a/android-app/app/src/main/kotlin/com/example/androidapp/safety/AnchorWatchState.kt b/android-app/app/src/main/kotlin/com/example/androidapp/safety/AnchorWatchState.kt new file mode 100644 index 0000000..507736e --- /dev/null +++ b/android-app/app/src/main/kotlin/com/example/androidapp/safety/AnchorWatchState.kt @@ -0,0 +1,23 @@ +package com.example.androidapp.safety + +/** + * Holds UI-facing state for the anchor watch setup screen and provides + * the suggested watch-circle radius derived from depth and rode out. + */ +class AnchorWatchState { + + /** + * Returns the recommended watch-circle radius (metres) for the given depth + * and amount of rode deployed. + * + * Uses the Pythagorean formula via [ScopeCalculator.watchCircleRadius] when + * the geometry is valid (rode > depth + freeboard). Falls back to [rodeOutM] + * itself as the maximum possible swing radius when the rode is too short to + * form a catenary angle. + */ + fun calculateRecommendedWatchCircleRadius(depthM: Double, rodeOutM: Double): Double { + val vertical = depthM + 2.0 // 2 m default freeboard + return if (rodeOutM > vertical) ScopeCalculator.watchCircleRadius(rodeOutM, depthM) + else rodeOutM + } +} diff --git a/android-app/app/src/main/kotlin/com/example/androidapp/ui/anchorwatch/AnchorWatchHandler.kt b/android-app/app/src/main/kotlin/com/example/androidapp/ui/anchorwatch/AnchorWatchHandler.kt new file mode 100644 index 0000000..bc82795 --- /dev/null +++ b/android-app/app/src/main/kotlin/com/example/androidapp/ui/anchorwatch/AnchorWatchHandler.kt @@ -0,0 +1,58 @@ +package com.example.androidapp.ui.anchorwatch + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.androidapp.R +import com.example.androidapp.databinding.FragmentAnchorWatchBinding +import com.example.androidapp.safety.AnchorWatchState + +class AnchorWatchHandler : Fragment() { + + private var _binding: FragmentAnchorWatchBinding? = null + private val binding get() = _binding!! + + private val anchorWatchState = AnchorWatchState() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAnchorWatchBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val watcher = object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit + override fun afterTextChanged(s: Editable?) = updateSuggestedRadius() + } + binding.etDepth.addTextChangedListener(watcher) + binding.etRodeOut.addTextChangedListener(watcher) + } + + private fun updateSuggestedRadius() { + val depth = binding.etDepth.text.toString().toDoubleOrNull() + val rode = binding.etRodeOut.text.toString().toDoubleOrNull() + + if (depth != null && rode != null && depth >= 0.0 && rode > 0.0) { + val radius = anchorWatchState.calculateRecommendedWatchCircleRadius(depth, rode) + binding.tvSuggestedRadius.text = + getString(R.string.anchor_suggested_radius_fmt, radius) + } else { + binding.tvSuggestedRadius.text = getString(R.string.anchor_suggested_radius_empty) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/android-app/app/src/main/res/layout/fragment_anchor_watch.xml b/android-app/app/src/main/res/layout/fragment_anchor_watch.xml new file mode 100644 index 0000000..96b9969 --- /dev/null +++ b/android-app/app/src/main/res/layout/fragment_anchor_watch.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_watch_title" + android:textSize="20sp" + android:textStyle="bold" + android:layout_marginBottom="24dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_depth_label" + android:textSize="14sp" + android:layout_marginBottom="4dp" /> + + <EditText + android:id="@+id/etDepth" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/anchor_depth_hint" + android:inputType="numberDecimal" + android:importantForAutofill="no" + android:layout_marginBottom="16dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_rode_label" + android:textSize="14sp" + android:layout_marginBottom="4dp" /> + + <EditText + android:id="@+id/etRodeOut" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/anchor_rode_hint" + android:inputType="numberDecimal" + android:importantForAutofill="no" + android:layout_marginBottom="24dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_suggested_radius_label" + android:textSize="14sp" + android:layout_marginBottom="4dp" /> + + <TextView + android:id="@+id/tvSuggestedRadius" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_suggested_radius_empty" + android:textSize="18sp" + android:textStyle="bold" + android:layout_marginBottom="4dp" /> + + <TextView + android:id="@+id/tvSuggestedRadiusHint" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/anchor_suggested_radius_hint" + android:textSize="12sp" + android:alpha="0.6" /> + + </LinearLayout> + +</ScrollView> diff --git a/android-app/app/src/main/res/values/strings.xml b/android-app/app/src/main/res/values/strings.xml index 499ba8d..756f5e3 100755 --- a/android-app/app/src/main/res/values/strings.xml +++ b/android-app/app/src/main/res/values/strings.xml @@ -58,4 +58,14 @@ <string name="temp_fmt">%.0f °C</string> <string name="precip_fmt">%d%%</string> <string name="permission_rationale">Location is needed to show weather for your current position.</string> + <string name="nav_anchor_watch">Anchor</string> + <string name="anchor_watch_title">Anchor Watch</string> + <string name="anchor_depth_label">Depth (m)</string> + <string name="anchor_depth_hint">e.g. 5.0</string> + <string name="anchor_rode_label">Rode Out (m)</string> + <string name="anchor_rode_hint">e.g. 30.0</string> + <string name="anchor_suggested_radius_label">Suggested Watch Radius</string> + <string name="anchor_suggested_radius_empty">—</string> + <string name="anchor_suggested_radius_fmt">%.1f m</string> + <string name="anchor_suggested_radius_hint">Calculated from rode and depth using Pythagorean scope formula (2 m freeboard assumed)</string> </resources> |
