From ad7a189c5174ecf380957aab2bf0955521f05197 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 13:48:26 +0800 Subject: [PATCH] add default channel button --- .../java/com/geeksville/mesh/model/Channel.kt | 9 ++- .../com/geeksville/mesh/ui/ChannelFragment.kt | 81 ++++++++++++------- .../res/drawable/ic_twotone_public_24.xml | 15 ++++ app/src/main/res/layout/channel_fragment.xml | 33 ++++++-- app/src/main/res/values/strings.xml | 4 + 5 files changed, 105 insertions(+), 37 deletions(-) create mode 100644 app/src/main/res/drawable/ic_twotone_public_24.xml 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" /> +