Вернуть результат из корутины
Столкнулся с задачей и не могу ее решить. В общем я написал класс Scanner, который сканирует Bluetooth устройства. У него есть всего три метода:
- startScan() -> Unit
- stopScan() -> Unit
- scanState() -> StateFlow
Метод scanState() возвращает поток c типом ScannerState. Класс ScannerState по сути является sealed классом, у которого есть два внутренних класса:
- Found(val devices: List) - для списка найденных устройств
- Error(val errorCode: Int) - для ошибок при сканировании
Метод scanState() не может ничего излучать сам по себе до тех пор, пока не будет вызван startScan(). В общем моя задача сделать так чтобы в репозитории был один метод fetchDevices(), который бы возвращал найденные устройства. Но я хочу сделать так, что при обращении к этому методу он так же дергал метод сканнера startScan() и заодно подписывался на метод scanState() и полученные от него результаты смаппить к классу data слоя (пусть он у меня например будет называться DevicesData). В дальнейшем что бы я его смог потом смапить к Domain и Presentation слоям.
Ну и типа репозиторий:
interface DevicesRepository {
suspend fun fetchDevices(): Flow<DevicesData>
class Base(): DevicesRepository {
override suspend fun fetchDevices(): Flow<DevicesData> {
// .. тут реализация
}
}
}
Интерфейс и реализация DevicesScannerDataSource.kt
interface DevicesScannerDataSource {
fun startScan()
fun stopScan()
fun scanState(): StateFlow<DevicesScannerState>
class Base : DevicesScannerDataSource {
private val adapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private val scanResults: MutableMap<String, BluetoothDevice> = mutableMapOf()
private var scanCallback: ScanCallback? = null
private var scanner: BluetoothLeScanner? = null
private val scannerState = MutableStateFlow<DevicesScannerState>(DevicesScannerState.Found(emptyList()))
private val filters: List<ScanFilter>
private val settings: ScanSettings
init {
settings = buildSettings()
filters = buildFilters()
}
override fun startScan() {
if (scanCallback == null) {
scanner = adapter.bluetoothLeScanner
scanCallback = ScanResultsCallback()
scanner?.startScan(filters, settings, scanCallback)
} else {
Log.e("Scan", "Already scan")
}
}
override fun stopScan() {
if (scanCallback != null) {
scanner?.stopScan(scanCallback)
scanCallback = null
scanner = null
}
}
override fun scanState(): StateFlow<DevicesScannerState> = scannerState
private fun buildSettings() = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
private fun buildFilters(): List<ScanFilter> {
return listOf<ScanFilter>(ScanFilter.Builder().build())
}
private fun emitDevices(scanResults: MutableMap<String, BluetoothDevice>) {
scannerState.tryEmit(DevicesScannerState.Found(scanResults.values.toList()))
}
private fun emitError(code: Int) {
scannerState.tryEmit(DevicesScannerState.Error(code))
}
private inner class ScanResultsCallback : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
scanResults[result.device.address] = result.device
emitDevices(scanResults)
}
override fun onBatchScanResults(results: MutableList<ScanResult>) {
results.forEach { result ->
scanResults[result.device.address] = result.device
}
emitDevices(scanResults)
}
override fun onScanFailed(errorCode: Int) {
emitError(errorCode)
}
}
}
}
Класс DevicesScannerState
sealed class DevicesScannerState {
data class Found(val devices: List<BluetoothDevice>): DevicesScannerState()
data class Error(val errorCode: Int): DevicesScannerState()
}