refactor: improve `BluetoothState.bondedDevices` updates

- add `BluetoothState` updates from `BluetoothDevice.ACTION_BOND_STATE_CHANGED` intents
- convert `bondedDevices` to List instead of nested Flow
pull/650/head
andrekir 2023-06-29 21:29:38 -03:00
rodzic cdea292169
commit b41d92be32
5 zmienionych plików z 27 dodań i 30 usunięć

Wyświetl plik

@ -50,10 +50,10 @@ class BTScanModel @Inject constructor(
init { init {
combine( combine(
bluetoothRepository.state.value.bondedDevices, bluetoothRepository.state,
usbRepository.serialDevicesWithDrivers usbRepository.serialDevicesWithDrivers
) { ble, usb -> ) { ble, usb ->
bleDevices.value = ble bleDevices.value = ble.bondedDevices
usbDevices.value = usb usbDevices.value = usb
}.onEach { setupScan() }.launchIn(viewModelScope) }.onEach { setupScan() }.launchIn(viewModelScope)

Wyświetl plik

@ -1,6 +1,7 @@
package com.geeksville.mesh.repository.bluetooth package com.geeksville.mesh.repository.bluetooth
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -14,7 +15,10 @@ import javax.inject.Inject
class BluetoothBroadcastReceiver @Inject constructor( class BluetoothBroadcastReceiver @Inject constructor(
private val bluetoothRepository: BluetoothRepository private val bluetoothRepository: BluetoothRepository
) : BroadcastReceiver() { ) : 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 { override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) { if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
@ -24,8 +28,11 @@ class BluetoothBroadcastReceiver @Inject constructor(
BluetoothAdapter.STATE_ON -> bluetoothRepository.refreshState() BluetoothAdapter.STATE_ON -> bluetoothRepository.refreshState()
} }
} }
if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
bluetoothRepository.refreshState()
}
} }
private val Intent.bluetoothAdapterState: Int private val Intent.bluetoothAdapterState: Int
get() = getIntExtra(BluetoothAdapter.EXTRA_STATE,-1) get() = getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
} }

Wyświetl plik

@ -10,7 +10,6 @@ import androidx.lifecycle.coroutineScope
import com.geeksville.mesh.android.Logging import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.CoroutineDispatchers import com.geeksville.mesh.CoroutineDispatchers
import com.geeksville.mesh.android.hasBluetoothPermission import com.geeksville.mesh.android.hasBluetoothPermission
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -74,10 +73,14 @@ class BluetoothRepository @Inject constructor(
} }
}?.let { adapter -> }?.let { adapter ->
/// ask the adapter if we have access /// ask the adapter if we have access
val enabled = adapter.isEnabled
val bondedDevices = adapter.bondedDevices ?: emptySet()
BluetoothState( BluetoothState(
hasPermissions = true, hasPermissions = true,
enabled = adapter.isEnabled, enabled = enabled,
bondedDevices = createBondedDevicesFlow(adapter), bondedDevices = if (!enabled) emptyList()
else bondedDevices.filter { it.name?.matches(Regex(BLE_NAME_PATTERN)) == true },
) )
} ?: BluetoothState() } ?: BluetoothState()
@ -85,22 +88,7 @@ class BluetoothRepository @Inject constructor(
debug("Detected our bluetooth access=$newState") 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<List<BluetoothDevice>> {
return flow<List<BluetoothDevice>> {
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 { companion object {
const val BLE_NAME_PATTERN = "^.*_([0-9a-fA-F]{4})$" const val BLE_NAME_PATTERN = "^.*_([0-9a-fA-F]{4})$"
const val REFRESH_DELAY_MS = 1000L
} }
} }

Wyświetl plik

@ -1,8 +1,7 @@
package com.geeksville.mesh.repository.bluetooth package com.geeksville.mesh.repository.bluetooth
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import kotlinx.coroutines.flow.Flow import com.geeksville.mesh.util.anonymize
import kotlinx.coroutines.flow.flowOf
/** /**
* A snapshot in time of the state of the bluetooth subsystem. * A snapshot in time of the state of the bluetooth subsystem.
@ -12,6 +11,9 @@ data class BluetoothState(
val hasPermissions: Boolean = false, val hasPermissions: Boolean = false,
/** If we have adequate permissions and bluetooth is enabled */ /** If we have adequate permissions and bluetooth is enabled */
val enabled: Boolean = false, val enabled: Boolean = false,
/** If enabled, a cold flow of the currently bonded devices */ /** If enabled, a list of the currently bonded devices */
val bondedDevices: Flow<List<BluetoothDevice>> = flowOf(emptyList()) val bondedDevices: List<BluetoothDevice> = emptyList()
) ) {
override fun toString(): String =
"BluetoothState(hasPermissions=$hasPermissions, enabled=$enabled, bondedDevices=${bondedDevices.map { it.anonymize }})"
}

Wyświetl plik

@ -74,7 +74,7 @@ class RadioInterfaceService @Inject constructor(
init { init {
processLifecycle.coroutineScope.launch { processLifecycle.coroutineScope.launch {
bluetoothRepository.state.collect { state -> bluetoothRepository.state.collect { state ->
if (state.enabled && !isStarted) { if (state.enabled) {
startInterface() startInterface()
} else if (radioIf is BluetoothInterface) { } else if (radioIf is BluetoothInterface) {
stopInterface() stopInterface()