XYOracleNetwork/sdk-ble-android

View on GitHub
ble-android-library/src/main/kotlin/network/xyo/ble/generic/gatt/peripheral/actions/XYBluetoothGattDiscover.kt

Summary

Maintainability
C
7 hrs
Test Coverage
package network.xyo.ble.generic.gatt.peripheral.actions

import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattService
import kotlinx.coroutines.*
import network.xyo.ble.generic.gatt.peripheral.XYBluetoothGattCallback
import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
import network.xyo.ble.generic.gatt.peripheral.ThreadSafeBluetoothGattWrapper
import network.xyo.ble.generic.gatt.peripheral.ble

class XYBluetoothGattDiscover(
    gatt: ThreadSafeBluetoothGattWrapper,
    gattCallback: XYBluetoothGattCallback,
    timeout: Long = 1500000L)
    : XYBluetoothGattAction<List<BluetoothGattService>>(gatt, gattCallback, timeout) {

    suspend fun start(): XYBluetoothResult<List<BluetoothGattService>> {
        log.info("discover")
        val listenerName = "XYBluetoothGattDiscover${hashCode()}"
        var error: XYBluetoothResultErrorCode = XYBluetoothResultErrorCode.None
        var value: List<BluetoothGattService>? = gatt.services

        if (value?.isEmpty() == false) {
            log.info("discover: Returning previous discover: ${value.size}")
        } else {
            try {
                withTimeout(timeout) {
                    value = suspendCancellableCoroutine { cont ->
                        log.info("discover: Doing real discover")
                        val listener = object : BluetoothGattCallback() {
                            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
                                super.onServicesDiscovered(gatt, status)
                                gattCallback.removeListener(listenerName)
                                if (status != BluetoothGatt.GATT_SUCCESS) {
                                    error = XYBluetoothResultErrorCode.ServiceDiscoveryFailed

                                    completeStartCoroutine(cont)
                                } else {
                                    // success - send back the services
                                    log.info("discover: Returning new services")
                                    this@XYBluetoothGattDiscover.value = gatt?.services
                                    completeStartCoroutine(cont, this@XYBluetoothGattDiscover.value)
                                }
                            }

                            override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                                super.onConnectionStateChange(gatt, status, newState)
                                if (newState != BluetoothGatt.STATE_CONNECTED) {
                                    error = XYBluetoothResultErrorCode.Disconnected
                                    gattCallback.removeListener(listenerName)
                                    completeStartCoroutine(cont)
                                }
                            }
                        }
                        gattCallback.addListener(listenerName, listener)
                        ble.launch {
                            val discoverStarted = gatt.discoverServices().value
                            if (discoverStarted != true) {
                                error = XYBluetoothResultErrorCode.DiscoverServicesFailedToStart
                                gattCallback.removeListener(listenerName)
                                completeStartCoroutine(cont)
                            }
                        }
                    }
                }
            } catch (ex: TimeoutCancellationException) {
                error = XYBluetoothResultErrorCode.Timeout
                gattCallback.removeListener(listenerName)
                log.error(ex)
            }
        }
        return XYBluetoothResult(value, error)
    }
}