diff --git a/app/src/main/java/com/geeksville/mesh/model/Channel.kt b/app/src/main/java/com/geeksville/mesh/model/Channel.kt
index 665a8de25..06855b66a 100644
--- a/app/src/main/java/com/geeksville/mesh/model/Channel.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/Channel.kt
@@ -17,10 +17,15 @@ data class Channel(
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf
)
+ private val cleartextPSK = ByteString.EMPTY
+ private val defaultPSK = byteArrayOfInts(1) // a shortstring code to indicate we need our default PSK
+
// TH=he unsecured channel that devices ship with
val defaultChannel = Channel(
ChannelProtos.ChannelSettings.newBuilder()
- .setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128).build()
+ .setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
+ .setPsk(ByteString.copyFrom(defaultPSK))
+ .build()
)
}
@@ -50,7 +55,7 @@ data class Channel(
val pskIndex = settings.psk.byteAt(0).toInt()
if (pskIndex == 0)
- ByteString.EMPTY // Treat as an empty PSK (no encryption)
+ cleartextPSK
else {
// Treat an index of 1 as the old channelDefaultKey and work up from there
val bytes = channelDefaultKey.clone()
diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
index 9ca623088..a8f5457b1 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
@@ -18,7 +18,6 @@ import com.geeksville.android.Logging
import com.geeksville.android.hideKeyboard
import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.ChannelProtos
-import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.R
import com.geeksville.mesh.databinding.ChannelFragmentBinding
import com.geeksville.mesh.model.Channel
@@ -50,6 +49,7 @@ fun ImageView.setOpaque() {
class ChannelFragment : ScreenFragment("Channel"), Logging {
private var _binding: ChannelFragmentBinding? = null
+
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
@@ -81,6 +81,12 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
val channels = model.channels.value
val channel = channels?.primaryChannel
+ val connected = model.isConnected.value == MeshService.ConnectionState.CONNECTED
+
+ // Only let buttons work if we are connected to the radio
+ binding.shareButton.isEnabled = connected
+ binding.resetButton.isEnabled = connected
+
binding.editableCheckbox.isChecked = false // start locked
if (channel != null) {
binding.qrView.visibility = View.VISIBLE
@@ -89,7 +95,6 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
// For now, we only let the user edit/save channels while the radio is awake - because the service
// doesn't cache radioconfig writes.
- val connected = model.isConnected.value == MeshService.ConnectionState.CONNECTED
binding.editableCheckbox.isEnabled = connected
binding.qrView.setImageBitmap(channels.getChannelQR())
@@ -143,6 +148,28 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
}
}
+ /// Send new channel settings to the device
+ private fun installSettings(newChannel: ChannelProtos.ChannelSettings) {
+ val newSet =
+ ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
+ // Try to change the radio, if it fails, tell the user why and throw away their redits
+ try {
+ model.setChannels(newSet)
+ // Since we are writing to radioconfig, that will trigger the rest of the GUI update (QR code etc)
+ } catch (ex: RemoteException) {
+ errormsg("ignoring channel problem", ex)
+
+ setGUIfromModel() // Throw away user edits
+
+ // Tell the user to try again
+ Snackbar.make(
+ binding.editableCheckbox,
+ R.string.radio_sleeping,
+ Snackbar.LENGTH_SHORT
+ ).show()
+ }
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -150,6 +177,21 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
requireActivity().hideKeyboard()
}
+ binding.resetButton.setOnClickListener { _ ->
+ // User just locked it, we should warn and then apply changes to radio
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(R.string.reset_to_defaults)
+ .setMessage(R.string.are_you_shure_change_default)
+ .setNeutralButton(R.string.cancel) { _, _ ->
+ setGUIfromModel() // throw away any edits
+ }
+ .setPositiveButton(getString(R.string.accept)) { _, _ ->
+ debug("Switching back to default channel")
+ installSettings(Channel.defaultChannel.settings)
+ }
+ .show()
+ }
+
// Note: Do not use setOnCheckedChanged here because we don't want to be called when we programmatically disable editing
binding.editableCheckbox.setOnClickListener { _ ->
val checked = binding.editableCheckbox.isChecked
@@ -178,41 +220,26 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
ignoreCase = true
)
) {
+ // Install a new customized channel
+
debug("ASSIGNING NEW AES256 KEY")
val random = SecureRandom()
val bytes = ByteArray(32)
random.nextBytes(bytes)
newSettings.psk = ByteString.copyFrom(bytes)
+
+ val selectedChannelOptionString =
+ binding.filledExposedDropdown.editableText.toString()
+ val modemConfig = getModemConfig(selectedChannelOptionString)
+
+ if (modemConfig != ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED)
+ newSettings.modemConfig = modemConfig
} else {
debug("Switching back to default channel")
newSettings = Channel.defaultChannel.settings.toBuilder()
}
- val selectedChannelOptionString =
- binding.filledExposedDropdown.editableText.toString()
- val modemConfig = getModemConfig(selectedChannelOptionString)
-
- if (modemConfig != ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED)
- newSettings.modemConfig = modemConfig
-
- val newChannel = newSettings.build()
- val newSet = ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
- // Try to change the radio, if it fails, tell the user why and throw away their redits
- try {
- model.setChannels(newSet)
- // Since we are writing to radioconfig, that will trigger the rest of the GUI update (QR code etc)
- } catch (ex: RemoteException) {
- errormsg("ignoring channel problem", ex)
-
- setGUIfromModel() // Throw away user edits
-
- // Tell the user to try again
- Snackbar.make(
- binding.editableCheckbox,
- R.string.radio_sleeping,
- Snackbar.LENGTH_SHORT
- ).show()
- }
+ installSettings(newSettings.build())
}
}
.show()
diff --git a/app/src/main/res/drawable/ic_twotone_public_24.xml b/app/src/main/res/drawable/ic_twotone_public_24.xml
new file mode 100644
index 000000000..403051e61
--- /dev/null
+++ b/app/src/main/res/drawable/ic_twotone_public_24.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/app/src/main/res/layout/channel_fragment.xml b/app/src/main/res/layout/channel_fragment.xml
index 461eafab0..597bcb4bf 100644
--- a/app/src/main/res/layout/channel_fragment.xml
+++ b/app/src/main/res/layout/channel_fragment.xml
@@ -1,6 +1,7 @@
@@ -91,30 +92,46 @@
+ android:layout_height="wrap_content"
+ android:hint="@string/set_channel_options" />
+
+
+ app:layout_constraintBottom_toBottomOf="@id/bottomButtonsGuideline"
+ app:layout_constraintStart_toEndOf="@+id/resetButton">
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 11f071ffd..93413f1e8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -97,4 +97,8 @@
Couldn\'t change channel, because radio is not yet connected. Please try again.55.332244 34.442211Save messages as csv...
+ Set channel options
+ Reset
+ Are you sure you want to change to the default channel?
+ Reset to defaults