diff options
15 files changed, 250 insertions, 52 deletions
diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml index 1701e17..6c792f2 100644 --- a/android-app/app/src/main/AndroidManifest.xml +++ b/android-app/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-feature android:name="android.hardware.camera" android:required="false" /> <!-- Documents/ write access — not needed on API 29+ (MediaStore handles it) --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" diff --git a/android-app/app/src/main/assets/docs/getting_started.md b/android-app/app/src/main/assets/docs/getting_started.md new file mode 100644 index 0000000..3e56138 --- /dev/null +++ b/android-app/app/src/main/assets/docs/getting_started.md @@ -0,0 +1,18 @@ +# Getting Started with Nav + +Welcome to your sailing companion. Nav is designed to provide you with all the essential information you need while on the water. + +## Navigation Bar +Use the navigation bar at the bottom to switch between: +- **Map:** View your current position, AIS targets, and tidal currents. +- **Instruments:** View detailed performance data like wind, speed, and depth. +- **Log:** Access your trip log and record voice entries. +- **Safety:** Quick access to MOB and Anchor Watch. + +## Instrument Panel +The instrument panel can be expanded from the bottom of the map. +- **Swipe up** to see more details. +- **Swipe down** to minimize it and see more of the map. + +## Help & Documentation +You can find more detailed information by tapping the "Help" icon in any of the main screens. diff --git a/android-app/app/src/main/assets/docs/instruments.md b/android-app/app/src/main/assets/docs/instruments.md new file mode 100644 index 0000000..bf01ea8 --- /dev/null +++ b/android-app/app/src/main/assets/docs/instruments.md @@ -0,0 +1,19 @@ +# Sailing Instruments + +The instrument panel provides real-time data from your sensors and boat polars. + +## Wind Data +- **AWS (Apparent Wind Speed):** The wind speed felt by the boat. +- **TWS (True Wind Speed):** The actual wind speed across the water. +- **TWD (True Wind Direction):** The compass direction the wind is blowing from. + +## Boat Performance +- **BSP (Boat Speed):** Speed through the water (from your speedo). +- **SOG (Speed Over Ground):** Actual speed relative to the earth (from GPS). +- **VMG (Velocity Made Good):** Speed directly upwind or downwind. +- **Polar %:** Your current speed relative to the theoretical maximum for your boat. + +## Navigation +- **HDG (Heading):** The direction the bow is pointing. +- **COG (Course Over Ground):** The actual path the boat is tracking (includes leeway and current). +- **Depth:** Current depth below the keel. diff --git a/android-app/app/src/main/assets/docs/safety.md b/android-app/app/src/main/assets/docs/safety.md new file mode 100644 index 0000000..f8bf85d --- /dev/null +++ b/android-app/app/src/main/assets/docs/safety.md @@ -0,0 +1,19 @@ +# Safety First + +Nav includes critical safety features that work offline. + +## MOB (Man Overboard) +The **MOB button** is always accessible in the Safety tab. +1. Tap the large **MOB** button immediately if someone falls overboard. +2. The app will record the current position and start a timer. +3. A large arrow will show the bearing and distance back to the MOB point. +4. An audible alarm will sound continuously. +5. Tap **RECOVERED** only once the person is safely back on board. + +## Anchor Watch +Monitor your boat's movement while at anchor. +1. Tap the **Anchor** icon in the Safety tab. +2. Configure your watch circle radius based on your scope. +3. Tap **SET ANCHOR** to start monitoring. +4. If your boat moves outside the circle, a high-priority alarm will sound. +5. The alarm works in the background even if the screen is off. 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 ead98ae..6c091ff 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 @@ -484,6 +484,10 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { fabFishing.visibility = View.GONE fabLayers.visibility = View.GONE fabRecenter.visibility = View.GONE + if (::bottomSheetBehavior.isInitialized) { + bottomSheetBehavior.isHideable = true + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, fragment) .commit() @@ -600,14 +604,25 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { private fun checkForegroundPermissions() { val fineLocation = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) val coarseLocation = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) + val locationGranted = fineLocation == PackageManager.PERMISSION_GRANTED || + coarseLocation == PackageManager.PERMISSION_GRANTED + + val toRequest = mutableListOf<String>() + if (!locationGranted) { + toRequest += Manifest.permission.ACCESS_FINE_LOCATION + toRequest += Manifest.permission.ACCESS_COARSE_LOCATION + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) { + toRequest += Manifest.permission.POST_NOTIFICATIONS + } - if (fineLocation == PackageManager.PERMISSION_GRANTED || coarseLocation == PackageManager.PERMISSION_GRANTED) { + if (locationGranted) { startServices() + if (toRequest.isNotEmpty()) requestPermissionLauncher.launch(toRequest.toTypedArray()) } else { - requestPermissionLauncher.launch(arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - )) + requestPermissionLauncher.launch(toRequest.toTypedArray()) } } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/UnitPrefs.kt b/android-app/app/src/main/kotlin/org/terst/nav/UnitPrefs.kt index c676390..20de3e4 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/UnitPrefs.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/UnitPrefs.kt @@ -7,6 +7,7 @@ enum class TempUnit { CELSIUS, FAHRENHEIT } enum class DepthUnit { FEET, METERS } enum class PressureUnit { HPA, INHG } enum class SpeedUnit { KNOTS, MPH, KPH } +enum class DistanceUnit { NM, KM, MI } /** * Persists and applies the user's preferred display units. @@ -32,6 +33,10 @@ class UnitPrefs(context: Context) { get() = runCatching { SpeedUnit.valueOf(prefs.getString(KEY_SPEED, null) ?: "") }.getOrDefault(SpeedUnit.KNOTS) set(v) { prefs.edit().putString(KEY_SPEED, v.name).apply() } + var distanceUnit: DistanceUnit + get() = runCatching { DistanceUnit.valueOf(prefs.getString(KEY_DISTANCE, null) ?: "") }.getOrDefault(DistanceUnit.NM) + set(v) { prefs.edit().putString(KEY_DISTANCE, v.name).apply() } + fun formatTemp(tempC: Double): String = when (tempUnit) { TempUnit.CELSIUS -> "%.0f".format(Locale.getDefault(), tempC) TempUnit.FAHRENHEIT -> "%.0f".format(Locale.getDefault(), tempC * 9.0 / 5.0 + 32.0) @@ -74,10 +79,23 @@ class UnitPrefs(context: Context) { SpeedUnit.KPH -> "kph" } + fun formatDistance(nm: Double): String = when (distanceUnit) { + DistanceUnit.NM -> "%.1f".format(Locale.getDefault(), nm) + DistanceUnit.KM -> "%.1f".format(Locale.getDefault(), nm * 1.852) + DistanceUnit.MI -> "%.1f".format(Locale.getDefault(), nm * 1.15078) + } + + fun distanceUnitLabel(): String = when (distanceUnit) { + DistanceUnit.NM -> "nm" + DistanceUnit.KM -> "km" + DistanceUnit.MI -> "mi" + } + companion object { private const val KEY_TEMP = "temp_unit" private const val KEY_DEPTH = "depth_unit" private const val KEY_PRESSURE = "pressure_unit" private const val KEY_SPEED = "speed_unit" + private const val KEY_DISTANCE = "distance_unit" } } 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 7dd0e07..e194efc 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 @@ -352,9 +352,11 @@ class TrackDetailSheet : Fragment() { "%.0f° → %.0f° (Δ%.0f°)".format(t.cogBefore, t.cogAfter, abs(delta))) } - // Log entries saved during or associated with this track's time window + // Log entries saved during or associated with this track's time window. + // Photo-only entries (no real text) are excluded — photos belong to log entries, not as standalone events. val logEntries = NavApplication.logbookRepository.getAll() .filter { it.timestampMs in track.startMs..track.endMs } + .filter { it.text.isNotBlank() && it.text != "(photo only)" } for (entry in logEntries) { val lat = entry.lat ?: nearestTrackPoint(points, entry.timestampMs)?.lat ?: continue val lon = entry.lon ?: nearestTrackPoint(points, entry.timestampMs)?.lon ?: continue @@ -447,17 +449,41 @@ private class LogEventAdapter( if (bm != null) { holder.ivThumb.setImageBitmap(bm) holder.ivThumb.visibility = View.VISIBLE + holder.ivThumb.setOnClickListener { showFullscreenPhoto(holder.itemView.context, path) } } else { holder.ivThumb.setImageDrawable(null) holder.ivThumb.visibility = View.GONE + holder.ivThumb.setOnClickListener(null) } } else { holder.ivThumb.setImageDrawable(null) holder.ivThumb.visibility = View.GONE + holder.ivThumb.setOnClickListener(null) } } } +private fun showFullscreenPhoto(context: android.content.Context, pathOrUri: String) { + val uri = if (pathOrUri.startsWith("content://")) Uri.parse(pathOrUri) else Uri.fromFile(File(pathOrUri)) + val imageView = android.widget.ImageView(context).apply { + setImageURI(uri) + scaleType = android.widget.ImageView.ScaleType.FIT_CENTER + setBackgroundColor(android.graphics.Color.BLACK) + layoutParams = android.view.ViewGroup.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.MATCH_PARENT + ) + } + android.app.AlertDialog.Builder(context) + .setView(imageView) + .setPositiveButton("Close", null) + .show() + .window?.setLayout( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.MATCH_PARENT + ) +} + private fun decodeThumbnail(context: android.content.Context, pathOrUri: String, targetW: Int, targetH: Int): android.graphics.Bitmap? { if (pathOrUri.startsWith("content://")) { return runCatching { diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/LayerPickerSheet.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/LayerPickerSheet.kt index b60af50..47d29c4 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/LayerPickerSheet.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/LayerPickerSheet.kt @@ -13,6 +13,7 @@ import com.google.android.material.card.MaterialCardView import com.google.android.material.chip.ChipGroup import com.google.android.material.switchmaterial.SwitchMaterial import org.terst.nav.DepthUnit +import org.terst.nav.DistanceUnit import org.terst.nav.PressureUnit import org.terst.nav.R import org.terst.nav.SpeedUnit @@ -178,6 +179,22 @@ class LayerPickerSheet( } onUnitsChanged() } + + val chipDistance = view.findViewById<ChipGroup>(R.id.chip_group_distance) + chipDistance.check(when (unitPrefs.distanceUnit) { + DistanceUnit.NM -> R.id.chip_nm + DistanceUnit.KM -> R.id.chip_km + DistanceUnit.MI -> R.id.chip_mi + }) + chipDistance.setOnCheckedStateChangeListener { _, ids -> + unitPrefs.distanceUnit = when (ids.firstOrNull()) { + R.id.chip_nm -> DistanceUnit.NM + R.id.chip_km -> DistanceUnit.KM + R.id.chip_mi -> DistanceUnit.MI + else -> return@setOnCheckedStateChangeListener + } + onUnitsChanged() + } } private fun dpToPx(dp: Int): Int = diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapLayerManager.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapLayerManager.kt index f6ee4fb..8cee462 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapLayerManager.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapLayerManager.kt @@ -2,6 +2,8 @@ package org.terst.nav.ui import android.content.Context import org.maplibre.android.maps.Style +import org.maplibre.android.style.layers.BackgroundLayer +import org.maplibre.android.style.layers.FillLayer import org.maplibre.android.style.layers.PropertyFactory import org.maplibre.android.style.layers.RasterLayer import org.maplibre.android.style.sources.RasterSource @@ -129,25 +131,38 @@ class MapLayerManager(context: Context) { } /** - * Inserts base-map rasters at indices 0/1/2 so OFM vector layers sit on top. - * Call this immediately after the style finishes loading. + * Inserts base-map rasters above the last OFM fill/background layer so they appear + * above OFM water/land fills but below OFM road lines and label symbols. + * addLayerAt(0) would put rasters below everything, where OFM fills cover them. + * addLayer() (appending) would put rasters on top of labels, hiding them. + * addLayerAbove(lastFill) is the sweet spot: imagery shows through, labels overlay. */ fun addBaseLayersToStyle(style: Style) { - android.util.Log.d("MapLayers", "addBaseLayersToStyle preset=$basePreset ofmLayers=${style.layers.size}") - // Insert in ascending index order so final order from bottom is: ocean(0) → satellite(1) → charts(2) → OFM… - style.addLayerAt(RasterLayer(LAYER_OCEAN, SOURCE_OCEAN).apply { - setProperties(PropertyFactory.rasterOpacity(1f)) - setProperties(PropertyFactory.visibility(visibilityFor(basePreset, LAYER_OCEAN))) - }, 0) - style.addLayerAt(RasterLayer(LAYER_SATELLITE, SOURCE_SATELLITE).apply { - setProperties(PropertyFactory.rasterOpacity(1f)) - setProperties(PropertyFactory.visibility(visibilityFor(basePreset, LAYER_SATELLITE))) - }, 1) - style.addLayerAt(RasterLayer(LAYER_CHARTS, SOURCE_CHARTS).apply { + val lastFillId = style.layers + .lastOrNull { it is BackgroundLayer || it is FillLayer }?.id + android.util.Log.d("MapLayers", "addBaseLayersToStyle preset=$basePreset ofmLayers=${style.layers.size} insertAbove=$lastFillId") + + fun addBase(layer: RasterLayer) { + if (lastFillId != null) style.addLayerAbove(layer, lastFillId) + else style.addLayerAt(layer, 0) + } + + // Add in reverse visual order (charts first = lowest in our stack) + // because each addLayerAbove(x, lastFillId) inserts x just above lastFillId, + // pushing previous insertions higher. Final order: ocean → satellite → charts → OFM lines/labels. + addBase(RasterLayer(LAYER_CHARTS, SOURCE_CHARTS).apply { setProperties(PropertyFactory.rasterOpacity(opacityFor(basePreset))) setProperties(PropertyFactory.visibility(visibilityFor(basePreset, LAYER_CHARTS))) - }, 2) - android.util.Log.d("MapLayers", "base layers inserted, ocean=${visibilityFor(basePreset, LAYER_OCEAN)} satellite=${visibilityFor(basePreset, LAYER_SATELLITE)}") + }) + addBase(RasterLayer(LAYER_SATELLITE, SOURCE_SATELLITE).apply { + setProperties(PropertyFactory.rasterOpacity(1f)) + setProperties(PropertyFactory.visibility(visibilityFor(basePreset, LAYER_SATELLITE))) + }) + addBase(RasterLayer(LAYER_OCEAN, SOURCE_OCEAN).apply { + setProperties(PropertyFactory.rasterOpacity(1f)) + setProperties(PropertyFactory.visibility(visibilityFor(basePreset, LAYER_OCEAN))) + }) + android.util.Log.d("MapLayers", "base layers inserted above $lastFillId, ocean=${visibilityFor(basePreset, LAYER_OCEAN)}") } /** 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 c3bef8f..91c3489 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 @@ -13,8 +13,10 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.button.MaterialButton import com.google.android.material.materialswitch.MaterialSwitch +import org.terst.nav.DepthUnit import org.terst.nav.NavApplication import org.terst.nav.R +import org.terst.nav.UnitPrefs import org.terst.nav.ais.CpaAlert import org.terst.nav.ais.CpaSeverity import org.terst.nav.ais.label @@ -63,6 +65,11 @@ class SafetyFragment : Fragment() { chevronAnchor.text = if (expanding) "▼" else "▶" } + val unitPrefs = UnitPrefs(requireContext()) + val unitLabel = unitPrefs.depthUnitLabel() + view.findViewById<TextView>(R.id.tv_anchor_depth_label).text = "Depth ($unitLabel)" + view.findViewById<TextView>(R.id.tv_anchor_rode_label).text = "Rode Out ($unitLabel)" + val etDepth = view.findViewById<EditText>(R.id.et_anchor_depth) val etRode = view.findViewById<EditText>(R.id.et_anchor_rode) val tvRadius = view.findViewById<TextView>(R.id.tv_anchor_suggested_radius) @@ -70,11 +77,13 @@ class SafetyFragment : Fragment() { 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?) { - val depth = etDepth.text.toString().toDoubleOrNull() - val rode = etRode.text.toString().toDoubleOrNull() - tvRadius.text = if (depth != null && rode != null && depth >= 0.0 && rode > 0.0) { - val r = AnchorWatchState.calculateRecommendedWatchCircleRadius(depth, 2.0, rode) - getString(R.string.anchor_suggested_radius_fmt, r) + val inputDepth = etDepth.text.toString().toDoubleOrNull() + val inputRode = etRode.text.toString().toDoubleOrNull() + tvRadius.text = if (inputDepth != null && inputRode != null && inputDepth >= 0.0 && inputRode > 0.0) { + val depthM = if (unitPrefs.depthUnit == DepthUnit.FEET) inputDepth * 0.3048 else inputDepth + val rodeM = if (unitPrefs.depthUnit == DepthUnit.FEET) inputRode * 0.3048 else inputRode + val r = AnchorWatchState.calculateRecommendedWatchCircleRadius(depthM, 2.0, rodeM) + "${unitPrefs.formatDepth(r)} $unitLabel" } else { getString(R.string.anchor_suggested_radius_empty) } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt index 51a0685..83e8c07 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt @@ -164,12 +164,7 @@ class VoiceLogFragment : Fragment() { } btnClear.setOnClickListener { clearEntry() } - // Show log-entry section only while recording - viewLifecycleOwner.lifecycleScope.launch { - mainViewModel.isRecording.collect { recording -> - layoutLogEntry.visibility = if (recording) View.VISIBLE else View.GONE - } - } + layoutLogEntry.visibility = View.VISIBLE // Log entries list val tvNoEntries = view.findViewById<TextView>(R.id.tv_no_log_entries) 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 ed1b7ce..99aa192 100644 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ b/android-app/app/src/main/res/layout/activity_main.xml @@ -126,7 +126,7 @@ android:layout_height="wrap_content" app:behavior_hideable="false" app:behavior_peekHeight="120dp" - app:cardElevation="16dp" + app:cardElevation="4dp" app:strokeWidth="0dp" app:shapeAppearance="@style/ShapeAppearance.Nav.BottomSheet" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> 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 78f8754..4a8efa9 100644 --- a/android-app/app/src/main/res/layout/fragment_safety.xml +++ b/android-app/app/src/main/res/layout/fragment_safety.xml @@ -4,7 +4,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/colorSurface"> + android:background="?attr/colorSurface" + android:clipToPadding="false" + android:paddingBottom="80dp"> <LinearLayout android:layout_width="match_parent" @@ -179,6 +181,7 @@ android:layout_marginEnd="8dp"> <TextView + android:id="@+id/tv_anchor_depth_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/anchor_depth_label" @@ -203,6 +206,7 @@ android:layout_marginStart="8dp"> <TextView + android:id="@+id/tv_anchor_rode_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/anchor_rode_label" diff --git a/android-app/app/src/main/res/layout/fragment_voice_log.xml b/android-app/app/src/main/res/layout/fragment_voice_log.xml index b1c440c..cacc6d6 100644 --- a/android-app/app/src/main/res/layout/fragment_voice_log.xml +++ b/android-app/app/src/main/res/layout/fragment_voice_log.xml @@ -3,7 +3,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:clipToPadding="false" + android:paddingBottom="80dp"> <LinearLayout android:layout_width="match_parent" @@ -11,13 +13,12 @@ android:orientation="vertical" android:padding="20dp"> - <!-- ── Log entry section — visible only while recording ── --> + <!-- ── Log entry section ── --> <LinearLayout android:id="@+id/layout_log_entry" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:visibility="gone"> + android:orientation="vertical"> <TextView android:layout_width="wrap_content" diff --git a/android-app/app/src/main/res/layout/layout_layer_picker_sheet.xml b/android-app/app/src/main/res/layout/layout_layer_picker_sheet.xml index ce52108..b7cf00e 100644 --- a/android-app/app/src/main/res/layout/layout_layer_picker_sheet.xml +++ b/android-app/app/src/main/res/layout/layout_layer_picker_sheet.xml @@ -51,7 +51,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -81,7 +81,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -111,7 +111,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -141,7 +141,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -196,7 +196,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -225,7 +225,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -254,7 +254,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -308,7 +308,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -337,7 +337,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -366,7 +366,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -420,7 +420,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -449,7 +449,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -503,7 +503,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="4dp" - app:layout_columnWeight="1" + android:layout_columnWeight="1" app:cardCornerRadius="12dp" app:strokeWidth="0dp"> <LinearLayout @@ -658,7 +658,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:gravity="center_vertical"> + android:gravity="center_vertical" + android:layout_marginBottom="10dp"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" @@ -693,5 +694,45 @@ </com.google.android.material.chip.ChipGroup> </LinearLayout> + <!-- Distance --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Distance" + android:textSize="14sp" + android:textColor="@color/instrument_text_normal" /> + <com.google.android.material.chip.ChipGroup + android:id="@+id/chip_group_distance" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:singleSelection="true" + app:selectionRequired="true"> + <com.google.android.material.chip.Chip + android:id="@+id/chip_nm" + style="@style/Widget.Material3.Chip.Filter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="nm" /> + <com.google.android.material.chip.Chip + android:id="@+id/chip_km" + style="@style/Widget.Material3.Chip.Filter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="km" /> + <com.google.android.material.chip.Chip + android:id="@+id/chip_mi" + style="@style/Widget.Material3.Chip.Filter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="mi" /> + </com.google.android.material.chip.ChipGroup> + </LinearLayout> + </LinearLayout> </androidx.core.widget.NestedScrollView> |
