diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index 4481f7680..8fec49323 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -978,6 +978,15 @@ class MainActivity : AppCompatActivity(), Logging,
handler.removeCallbacksAndMessages(null)
return true
}
+ R.id.advanced_settings -> {
+ val fragmentManager: FragmentManager = supportFragmentManager
+ val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
+ val nameFragment = AdvancedSettingsFragment()
+ fragmentTransaction.add(R.id.mainActivityLayout, nameFragment)
+ fragmentTransaction.addToBackStack(null)
+ fragmentTransaction.commit()
+ return true
+ }
else -> super.onOptionsItemSelected(item)
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt b/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt
index 2ceae83ea..827624cd5 100644
--- a/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt
@@ -3,11 +3,11 @@ package com.geeksville.mesh.model
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.R
-enum class ChannelOption(val modemConfig: MeshProtos.ChannelSettings.ModemConfig, val configRes: Int) {
- SHORT(MeshProtos.ChannelSettings.ModemConfig.Bw500Cr45Sf128, R.string.modem_config_short),
- MEDIUM(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128, R.string.modem_config_medium),
- LONG(MeshProtos.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512, R.string.modem_config_long),
- VERY_LONG(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr48Sf4096, R.string.modem_config_very_long);
+enum class ChannelOption(val modemConfig: MeshProtos.ChannelSettings.ModemConfig, val configRes: Int, val minBroadcastPeriodSecs: Int) {
+ SHORT(MeshProtos.ChannelSettings.ModemConfig.Bw500Cr45Sf128, R.string.modem_config_short, 3),
+ MEDIUM(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128, R.string.modem_config_medium, 12),
+ LONG(MeshProtos.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512, R.string.modem_config_long, 240),
+ VERY_LONG(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr48Sf4096, R.string.modem_config_very_long, 375);
companion object {
fun fromConfig(modemConfig: MeshProtos.ChannelSettings.ModemConfig?): ChannelOption? {
@@ -17,5 +17,6 @@ enum class ChannelOption(val modemConfig: MeshProtos.ChannelSettings.ModemConfig
}
return null
}
+ val defaultMinBroadcastPeriod = VERY_LONG.minBroadcastPeriodSecs
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt
new file mode 100644
index 000000000..4c970bf90
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt
@@ -0,0 +1,83 @@
+package com.geeksville.mesh.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import com.geeksville.android.Logging
+import com.geeksville.android.hideKeyboard
+import com.geeksville.mesh.R
+import com.geeksville.mesh.databinding.AdvancedSettingsBinding
+import com.geeksville.mesh.model.ChannelOption
+import com.geeksville.mesh.model.UIViewModel
+import com.geeksville.mesh.service.MeshService
+import com.google.android.material.snackbar.Snackbar
+
+class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
+ private val MAX_INT_DEVICE = 0xFFFFFFFF
+ private var _binding: AdvancedSettingsBinding? = null
+
+ private val binding get() = _binding!!
+
+ private val model: UIViewModel by activityViewModels()
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ _binding = AdvancedSettingsBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ model.radioConfig.observe(viewLifecycleOwner, { _ ->
+ binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString())
+ binding.lsSleepEditText.setText(model.lsSleepSecs.toString())
+ })
+
+ model.isConnected.observe(viewLifecycleOwner, Observer { connectionState ->
+ val connected = connectionState == MeshService.ConnectionState.CONNECTED
+ binding.positionBroadcastPeriodView.isEnabled = connected
+ binding.lsSleepView.isEnabled = connected
+ })
+
+ binding.positionBroadcastPeriodEditText.on(EditorInfo.IME_ACTION_DONE) {
+ val textEdit = binding.positionBroadcastPeriodEditText
+ val n = textEdit.text.toString().toIntOrNull()
+ val minBroadcastPeriodSecs =
+ ChannelOption.fromConfig(model.radioConfig.value?.channelSettings?.modemConfig)?.minBroadcastPeriodSecs
+ ?: ChannelOption.defaultMinBroadcastPeriod
+
+ if (n != null && n < MAX_INT_DEVICE && (n == 0 || n >= minBroadcastPeriodSecs)) {
+ model.positionBroadcastSecs = n
+ } else {
+ // restore the value in the edit field
+ textEdit.setText(model.positionBroadcastSecs.toString())
+ val errorText =
+ if (n == null || n < 0 || n >= MAX_INT_DEVICE)
+ "Bad value: ${textEdit.text.toString()}"
+ else
+ getString(R.string.broadcast_period_too_small).format(minBroadcastPeriodSecs)
+
+ Snackbar.make(requireView(), errorText, Snackbar.LENGTH_LONG).show()
+ }
+ requireActivity().hideKeyboard()
+ }
+
+ binding.lsSleepEditText.on(EditorInfo.IME_ACTION_DONE) {
+ val str = binding.lsSleepEditText.text.toString()
+ val n = str.toIntOrNull()
+ if (n != null && n < MAX_INT_DEVICE && n >= 0) {
+ model.lsSleepSecs = n
+ } else {
+ Snackbar.make(requireView(), "Bad value: $str", Snackbar.LENGTH_LONG).show()
+ }
+ requireActivity().hideKeyboard()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
index a582d3c5c..73fad2ec9 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
@@ -30,7 +30,6 @@ import com.geeksville.android.Logging
import com.geeksville.android.hideKeyboard
import com.geeksville.android.isGooglePlayAvailable
import com.geeksville.mesh.MainActivity
-import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.R
import com.geeksville.mesh.android.bluetoothManager
import com.geeksville.mesh.android.usbManager
@@ -462,7 +461,6 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
@SuppressLint("NewApi")
class SettingsFragment : ScreenFragment("Settings"), Logging {
- private val MAX_INT_DEVICE = 0xFFFFFFFF
private var _binding: SettingsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
@@ -574,28 +572,18 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
/// Setup the ui widgets unrelated to BLE scanning
private fun initCommonUI() {
- // We want to leave these visible in the IDE, but make sure they default to not visible until we have valid data
- binding.positionBroadcastPeriodView.visibility = View.GONE
- binding.lsSleepView.visibility = View.GONE
model.ownerName.observe(viewLifecycleOwner, { name ->
binding.usernameEditText.setText(name)
})
- model.radioConfig.observe(viewLifecycleOwner, { _ ->
- binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString())
- binding.lsSleepEditText.setText(model.lsSleepSecs.toString())
- })
+
// Only let user edit their name or set software update while connected to a radio
model.isConnected.observe(viewLifecycleOwner, Observer { connectionState ->
val connected = connectionState == MeshService.ConnectionState.CONNECTED
binding.usernameView.isEnabled = connected
- // Don't even show advanced fields until after we have a connection
- binding.positionBroadcastPeriodView.visibility = if (connected) View.VISIBLE else View.GONE
- binding.lsSleepView.visibility = if (connected) View.VISIBLE else View.GONE
-
if (connectionState == MeshService.ConnectionState.DISCONNECTED)
model.ownerName.value = ""
@@ -619,27 +607,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
requireActivity().hideKeyboard()
}
- binding.positionBroadcastPeriodEditText.on(EditorInfo.IME_ACTION_DONE) {
- val str = binding.positionBroadcastPeriodEditText.text.toString()
- val n = str.toIntOrNull()
- if (n != null && n <= MAX_INT_DEVICE && n >= 0) {
- model.positionBroadcastSecs = n
- } else {
- binding.scanStatusText.text = "Bad value: $str"
- }
- requireActivity().hideKeyboard()
- }
-
- binding.lsSleepEditText.on(EditorInfo.IME_ACTION_DONE) {
- val str = binding.lsSleepEditText.text.toString()
- val n = str.toIntOrNull()
- if (n != null && n < MAX_INT_DEVICE && n >= 0) {
- model.lsSleepSecs = n
- } else {
- binding.scanStatusText.text = "Bad value: $str"
- }
- requireActivity().hideKeyboard()
- }
val app = (requireContext().applicationContext as GeeksvilleApplication)
diff --git a/app/src/main/res/layout/advanced_settings.xml b/app/src/main/res/layout/advanced_settings.xml
new file mode 100644
index 000000000..755b3e585
--- /dev/null
+++ b/app/src/main/res/layout/advanced_settings.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml
index 45a7b535c..c207a44ac 100644
--- a/app/src/main/res/layout/settings_fragment.xml
+++ b/app/src/main/res/layout/settings_fragment.xml
@@ -152,48 +152,6 @@
app:layout_constraintTop_toBottomOf="@+id/updateFirmwareButton" />
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index a68555fad..fa17012a7 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -16,6 +16,10 @@
android:checkable="true"
android:checked="false"
android:title="Protocol stress test" />
+
- Broadcast position period (in seconds), 0 - disable
Device sleep period (in seconds)
Notifications about messages
+ Minimum broadcast period for this channel is %d