mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Fix #36 - allow users to select 'none' for the preferred radio
If none is selected, we do not leave our service running. And we do not start it on boot.
This commit is contained in:
parent
7a3cda314e
commit
ce14fde33b
4 changed files with 67 additions and 40 deletions
|
|
@ -4,4 +4,13 @@ import java.io.Closeable
|
||||||
|
|
||||||
interface IRadioInterface : Closeable {
|
interface IRadioInterface : Closeable {
|
||||||
fun handleSendToRadio(p: ByteArray)
|
fun handleSendToRadio(p: ByteArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
class NopInterface : IRadioInterface {
|
||||||
|
override fun handleSendToRadio(p: ByteArray) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +105,10 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
private val serviceJob = Job()
|
private val serviceJob = Job()
|
||||||
val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||||
|
|
||||||
|
private val nopIf = NopInterface()
|
||||||
|
private var radioIf: IRadioInterface = nopIf
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the user turns on bluetooth after we start, make sure to try and reconnected then
|
* If the user turns on bluetooth after we start, make sure to try and reconnected then
|
||||||
*/
|
*/
|
||||||
|
|
@ -123,9 +127,7 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
|
|
||||||
/// Send a packet/command out the radio link, this routine can block if it needs to
|
/// Send a packet/command out the radio link, this routine can block if it needs to
|
||||||
private fun handleSendToRadio(p: ByteArray) {
|
private fun handleSendToRadio(p: ByteArray) {
|
||||||
radioIf?.let { r ->
|
radioIf.handleSendToRadio(p)
|
||||||
r.handleSendToRadio(p)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle an incoming packet from the radio, broadcasts it as an android intent
|
// Handle an incoming packet from the radio, broadcasts it as an android intent
|
||||||
|
|
@ -166,11 +168,9 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private var radioIf: IRadioInterface? = null
|
|
||||||
|
|
||||||
/** Start our configured interface (if it isn't already running) */
|
/** Start our configured interface (if it isn't already running) */
|
||||||
private fun startInterface() {
|
private fun startInterface() {
|
||||||
if (radioIf != null)
|
if (radioIf != nopIf)
|
||||||
warn("Can't start interface - $radioIf is already running")
|
warn("Can't start interface - $radioIf is already running")
|
||||||
else {
|
else {
|
||||||
val address = getBondedDeviceAddress(this)
|
val address = getBondedDeviceAddress(this)
|
||||||
|
|
@ -189,6 +189,7 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
radioIf = when (c) {
|
radioIf = when (c) {
|
||||||
'x' -> BluetoothInterface(this, rest)
|
'x' -> BluetoothInterface(this, rest)
|
||||||
's' -> SerialInterface(this, rest)
|
's' -> SerialInterface(this, rest)
|
||||||
|
'n' -> nopIf
|
||||||
else -> TODO("Unexpected radio interface type")
|
else -> TODO("Unexpected radio interface type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,23 +198,24 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
|
|
||||||
|
|
||||||
private fun stopInterface() {
|
private fun stopInterface() {
|
||||||
info("stopping interface $radioIf")
|
val r = radioIf
|
||||||
radioIf?.let { r ->
|
info("stopping interface $r")
|
||||||
radioIf = null
|
radioIf = nopIf
|
||||||
r.close()
|
r.close()
|
||||||
|
|
||||||
if (logSends)
|
if (logSends)
|
||||||
sentPacketsLog.close()
|
sentPacketsLog.close()
|
||||||
if (logReceives)
|
if (logReceives)
|
||||||
receivedPacketsLog.close()
|
receivedPacketsLog.close()
|
||||||
|
|
||||||
|
// Don't broadcast disconnects if we were just using the nop device
|
||||||
|
if (radioIf != nopIf)
|
||||||
onDisconnect(isPermanent = true) // Tell any clients we are now offline
|
onDisconnect(isPermanent = true) // Tell any clients we are now offline
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun setBondedDeviceAddress(addr: String?) {
|
fun setBondedDeviceAddress(addressIn: String?) {
|
||||||
// Record that this use has configured a radio
|
// Record that this use has configured a radio
|
||||||
GeeksvilleApplication.analytics.track(
|
GeeksvilleApplication.analytics.track(
|
||||||
"mesh_bond"
|
"mesh_bond"
|
||||||
|
|
@ -224,15 +226,18 @@ class RadioInterfaceService : Service(), Logging {
|
||||||
stopInterface()
|
stopInterface()
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Setting bonded device to $addr")
|
// The device address "n" can be used to mean none
|
||||||
|
val address = if ("n" == addressIn) null else addressIn
|
||||||
|
|
||||||
|
debug("Setting bonded device to $address")
|
||||||
|
|
||||||
getPrefs(this).edit(commit = true) {
|
getPrefs(this).edit(commit = true) {
|
||||||
this.remove(DEVADDR_KEY_OLD) // remove any old version of the key
|
this.remove(DEVADDR_KEY_OLD) // remove any old version of the key
|
||||||
|
|
||||||
if (addr == null)
|
if (address == null)
|
||||||
this.remove(DEVADDR_KEY)
|
this.remove(DEVADDR_KEY)
|
||||||
else
|
else
|
||||||
putString(DEVADDR_KEY, addr)
|
putString(DEVADDR_KEY, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force the service to reconnect
|
// Force the service to reconnect
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,11 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
debug("BTScanModel created")
|
debug("BTScanModel created")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class BTScanEntry(val name: String, val address: String, val bonded: Boolean) {
|
data class DeviceListEntry(val name: String, val address: String, val bonded: Boolean) {
|
||||||
// val isSelected get() = macAddress == selectedMacAddr
|
// val isSelected get() = macAddress == selectedMacAddr
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "BTScanEntry(name=${name.anonymize}, addr=${address.anonymize})"
|
return "DeviceListEntry(name=${name.anonymize}, addr=${address.anonymize})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,6 +131,8 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use the string for the NopInterface
|
||||||
|
val selectedNotNull: String get() = selectedAddress ?: "n"
|
||||||
|
|
||||||
private val scanCallback = object : ScanCallback() {
|
private val scanCallback = object : ScanCallback() {
|
||||||
override fun onScanFailed(errorCode: Int) {
|
override fun onScanFailed(errorCode: Int) {
|
||||||
|
|
@ -145,31 +147,37 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||||
|
|
||||||
val addr = result.device.address
|
val addr = result.device.address
|
||||||
|
val fullAddr = "x$addr" // full address with the bluetooh prefix
|
||||||
// prevent logspam because weill get get lots of redundant scan results
|
// prevent logspam because weill get get lots of redundant scan results
|
||||||
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
|
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
|
||||||
val oldDevs = devices.value!!
|
val oldDevs = devices.value!!
|
||||||
val oldEntry = oldDevs[addr]
|
val oldEntry = oldDevs[fullAddr]
|
||||||
if (oldEntry == null || oldEntry.bonded != isBonded) {
|
if (oldEntry == null || oldEntry.bonded != isBonded) {
|
||||||
val entry = BTScanEntry(
|
val entry = DeviceListEntry(
|
||||||
result.device.name
|
result.device.name
|
||||||
?: "unnamed-$addr", // autobug: some devices might not have a name, if someone is running really old device code?
|
?: "unnamed-$addr", // autobug: some devices might not have a name, if someone is running really old device code?
|
||||||
"x$addr",
|
fullAddr,
|
||||||
isBonded
|
isBonded
|
||||||
)
|
)
|
||||||
debug("onScanResult ${entry}")
|
debug("onScanResult ${entry}")
|
||||||
|
|
||||||
// If nothing was selected, by default select the first thing we see
|
// If nothing was selected, by default select the first valid thing we see
|
||||||
if (selectedAddress == null && entry.bonded)
|
if (selectedAddress == null && entry.bonded)
|
||||||
changeScanSelection(
|
changeScanSelection(
|
||||||
GeeksvilleApplication.currentActivity as MainActivity,
|
GeeksvilleApplication.currentActivity as MainActivity,
|
||||||
addr
|
addr
|
||||||
)
|
)
|
||||||
oldDevs[addr] = entry // Add/replace entry
|
addDevice(entry) // Add/replace entry
|
||||||
devices.value = oldDevs // trigger gui updates
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addDevice(entry: DeviceListEntry) {
|
||||||
|
val oldDevs = devices.value!!
|
||||||
|
oldDevs[entry.address] = entry // Add/replace entry
|
||||||
|
devices.value = oldDevs // trigger gui updates
|
||||||
|
}
|
||||||
|
|
||||||
fun stopScan() {
|
fun stopScan() {
|
||||||
if (scanner != null) {
|
if (scanner != null) {
|
||||||
debug("stopping scan")
|
debug("stopping scan")
|
||||||
|
|
@ -193,8 +201,8 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
warn("No bluetooth adapter. Running under emulation?")
|
warn("No bluetooth adapter. Running under emulation?")
|
||||||
|
|
||||||
val testnodes = listOf(
|
val testnodes = listOf(
|
||||||
BTScanEntry("Meshtastic_ab12", "xaa", false),
|
DeviceListEntry("Meshtastic_ab12", "xaa", false),
|
||||||
BTScanEntry("Meshtastic_32ac", "xbb", true)
|
DeviceListEntry("Meshtastic_32ac", "xbb", true)
|
||||||
)
|
)
|
||||||
|
|
||||||
devices.value = (testnodes.map { it.address to it }).toMap().toMutableMap()
|
devices.value = (testnodes.map { it.address to it }).toMap().toMutableMap()
|
||||||
|
|
@ -220,6 +228,9 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
if (scanner == null) {
|
if (scanner == null) {
|
||||||
debug("starting scan")
|
debug("starting scan")
|
||||||
|
|
||||||
|
// Include a placeholder for "None"
|
||||||
|
addDevice(DeviceListEntry(context.getString(R.string.none), "n", true))
|
||||||
|
|
||||||
// filter and only accept devices that have a sw update service
|
// filter and only accept devices that have a sw update service
|
||||||
val filter =
|
val filter =
|
||||||
ScanFilter.Builder()
|
ScanFilter.Builder()
|
||||||
|
|
@ -240,7 +251,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val devices = object : MutableLiveData<MutableMap<String, BTScanEntry>>(mutableMapOf()) {
|
val devices = object : MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf()) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -262,7 +273,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
|
|
||||||
/// Called by the GUI when a new device has been selected by the user
|
/// Called by the GUI when a new device has been selected by the user
|
||||||
/// Returns true if we were able to change to that item
|
/// Returns true if we were able to change to that item
|
||||||
fun onSelected(activity: MainActivity, it: BTScanEntry): Boolean {
|
fun onSelected(activity: MainActivity, it: DeviceListEntry): Boolean {
|
||||||
// If the device is paired, let user select it, otherwise start the pairing flow
|
// If the device is paired, let user select it, otherwise start the pairing flow
|
||||||
if (it.bonded) {
|
if (it.bonded) {
|
||||||
changeScanSelection(activity, it.address)
|
changeScanSelection(activity, it.address)
|
||||||
|
|
@ -293,7 +304,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||||
|
|
||||||
/// Change to a new macaddr selection, updating GUI and radio
|
/// Change to a new macaddr selection, updating GUI and radio
|
||||||
fun changeScanSelection(context: MainActivity, newAddr: String) {
|
fun changeScanSelection(context: MainActivity, newAddr: String) {
|
||||||
info("Changing BT device to ${newAddr.anonymize}")
|
info("Changing device to ${newAddr.anonymize}")
|
||||||
selectedAddress = newAddr
|
selectedAddress = newAddr
|
||||||
changeDeviceSelection(context, newAddr)
|
changeDeviceSelection(context, newAddr)
|
||||||
}
|
}
|
||||||
|
|
@ -437,12 +448,12 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDeviceButton(device: BTScanModel.BTScanEntry, enabled: Boolean) {
|
private fun addDeviceButton(device: BTScanModel.DeviceListEntry, enabled: Boolean) {
|
||||||
val b = RadioButton(requireActivity())
|
val b = RadioButton(requireActivity())
|
||||||
b.text = device.name
|
b.text = device.name
|
||||||
b.id = View.generateViewId()
|
b.id = View.generateViewId()
|
||||||
b.isEnabled = enabled
|
b.isEnabled = enabled
|
||||||
b.isChecked = device.address == scanModel.selectedAddress
|
b.isChecked = device.address == scanModel.selectedNotNull
|
||||||
deviceRadioGroup.addView(b)
|
deviceRadioGroup.addView(b)
|
||||||
|
|
||||||
// Once we have at least one device, don't show the "looking for" animation - it makes uers think
|
// Once we have at least one device, don't show the "looking for" animation - it makes uers think
|
||||||
|
|
@ -496,20 +507,21 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
|
|
||||||
var hasShownOurDevice = false
|
var hasShownOurDevice = false
|
||||||
devices.values.forEach { device ->
|
devices.values.forEach { device ->
|
||||||
hasShownOurDevice =
|
if (device.address == scanModel.selectedNotNull)
|
||||||
hasShownOurDevice || device.address == scanModel.selectedAddress
|
hasShownOurDevice = true
|
||||||
addDeviceButton(device, true)
|
addDeviceButton(device, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The device the user is already paired with is offline currently, still show it
|
// The device the user is already paired with is offline currently, still show it
|
||||||
// it in the list, but greyed out
|
// it in the list, but greyed out
|
||||||
val selectedAddr = scanModel.selectedBluetooth
|
val selectedAddr = scanModel.selectedAddress
|
||||||
if (!hasShownOurDevice && selectedAddr != null) {
|
if (!hasShownOurDevice && selectedAddr != null) {
|
||||||
val bDevice = scanModel.bluetoothAdapter!!.getRemoteDevice(selectedAddr)
|
val bDevice =
|
||||||
|
scanModel.bluetoothAdapter!!.getRemoteDevice(scanModel.selectedBluetooth)
|
||||||
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
|
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
|
||||||
val curDevice = BTScanModel.BTScanEntry(
|
val curDevice = BTScanModel.DeviceListEntry(
|
||||||
bDevice.name,
|
bDevice.name,
|
||||||
bDevice.address,
|
selectedAddr,
|
||||||
bDevice.bondState == BOND_BONDED
|
bDevice.bondState == BOND_BONDED
|
||||||
)
|
)
|
||||||
addDeviceButton(curDevice, false)
|
addDeviceButton(curDevice, false)
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,5 @@
|
||||||
<string name="update_to">Update to %s</string>
|
<string name="update_to">Update to %s</string>
|
||||||
<string name="app_too_old">Application too old</string>
|
<string name="app_too_old">Application too old</string>
|
||||||
<string name="must_update">You must update this application on the Google Play store (or Github). It is too old to talk to this radio.</string>
|
<string name="must_update">You must update this application on the Google Play store (or Github). It is too old to talk to this radio.</string>
|
||||||
|
<string name="none">None</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue