diff --git a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt index a85c56cf4..51759b722 100644 --- a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt @@ -50,10 +50,10 @@ class BTScanModel @Inject constructor( init { combine( - bluetoothRepository.state.value.bondedDevices, + bluetoothRepository.state, usbRepository.serialDevicesWithDrivers ) { ble, usb -> - bleDevices.value = ble + bleDevices.value = ble.bondedDevices usbDevices.value = usb }.onEach { setupScan() }.launchIn(viewModelScope) diff --git a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothBroadcastReceiver.kt b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothBroadcastReceiver.kt index 3c6ee25e2..da87fe13e 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothBroadcastReceiver.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothBroadcastReceiver.kt @@ -1,6 +1,7 @@ package com.geeksville.mesh.repository.bluetooth import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -14,7 +15,10 @@ import javax.inject.Inject class BluetoothBroadcastReceiver @Inject constructor( private val bluetoothRepository: BluetoothRepository ) : BroadcastReceiver() { - internal val intentFilter get() = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) // Can be used for registering + internal val intentFilter = IntentFilter().apply { + addAction(BluetoothAdapter.ACTION_STATE_CHANGED) + addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + } override fun onReceive(context: Context, intent: Intent) = exceptionReporter { if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) { @@ -24,8 +28,11 @@ class BluetoothBroadcastReceiver @Inject constructor( BluetoothAdapter.STATE_ON -> bluetoothRepository.refreshState() } } + if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { + bluetoothRepository.refreshState() + } } private val Intent.bluetoothAdapterState: Int - get() = getIntExtra(BluetoothAdapter.EXTRA_STATE,-1) -} \ No newline at end of file + get() = getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt index 1ab08434e..4c7f2a805 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt @@ -10,7 +10,6 @@ import androidx.lifecycle.coroutineScope import com.geeksville.mesh.android.Logging import com.geeksville.mesh.CoroutineDispatchers import com.geeksville.mesh.android.hasBluetoothPermission -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -74,10 +73,14 @@ class BluetoothRepository @Inject constructor( } }?.let { adapter -> /// ask the adapter if we have access + val enabled = adapter.isEnabled + val bondedDevices = adapter.bondedDevices ?: emptySet() + BluetoothState( hasPermissions = true, - enabled = adapter.isEnabled, - bondedDevices = createBondedDevicesFlow(adapter), + enabled = enabled, + bondedDevices = if (!enabled) emptyList() + else bondedDevices.filter { it.name?.matches(Regex(BLE_NAME_PATTERN)) == true }, ) } ?: BluetoothState() @@ -85,22 +88,7 @@ class BluetoothRepository @Inject constructor( debug("Detected our bluetooth access=$newState") } - /** - * Creates a cold Flow used to obtain the set of bonded devices. - */ - @SuppressLint("MissingPermission") // Already checked prior to calling - private suspend fun createBondedDevicesFlow(adapter: BluetoothAdapter): Flow> { - return flow> { - val devices = adapter.bondedDevices ?: emptySet() - while (true) { - emit(devices.filter { it.name?.matches(Regex(BLE_NAME_PATTERN)) == true }) - delay(REFRESH_DELAY_MS) - } - }.flowOn(dispatchers.default).distinctUntilChanged() - } - companion object { const val BLE_NAME_PATTERN = "^.*_([0-9a-fA-F]{4})$" - const val REFRESH_DELAY_MS = 1000L } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothState.kt b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothState.kt index 4aa4ceaac..95985f701 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothState.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothState.kt @@ -1,8 +1,7 @@ package com.geeksville.mesh.repository.bluetooth import android.bluetooth.BluetoothDevice -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf +import com.geeksville.mesh.util.anonymize /** * A snapshot in time of the state of the bluetooth subsystem. @@ -12,6 +11,9 @@ data class BluetoothState( val hasPermissions: Boolean = false, /** If we have adequate permissions and bluetooth is enabled */ val enabled: Boolean = false, - /** If enabled, a cold flow of the currently bonded devices */ - val bondedDevices: Flow> = flowOf(emptyList()) -) + /** If enabled, a list of the currently bonded devices */ + val bondedDevices: List = emptyList() +) { + override fun toString(): String = + "BluetoothState(hasPermissions=$hasPermissions, enabled=$enabled, bondedDevices=${bondedDevices.map { it.anonymize }})" +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt index 8937c20da..e91ba166b 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt @@ -74,7 +74,7 @@ class RadioInterfaceService @Inject constructor( init { processLifecycle.coroutineScope.launch { bluetoothRepository.state.collect { state -> - if (state.enabled && !isStarted) { + if (state.enabled) { startInterface() } else if (radioIf is BluetoothInterface) { stopInterface()