aboutsummaryrefslogtreecommitdiff
path: root/integration_tests/kotlin/src/test/kotlin/org/robolectric/integrationtests/kotlin/flow/BluetoothProvisioner.kt
blob: 2094dd13b32832456b963e26a516b2792477a1b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package org.robolectric.integrationtests.kotlin.flow

import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow

/** A class that invokes Android Bluetooth LE APIs. */
class BluetoothProvisioner(applicationContext: Context) {

  val context: Context

  init {
    context = applicationContext
  }

  fun startScan(): Flow<BluetoothDevice> = callbackFlow {
    val scanCallback =
      object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult?) {
          if (result?.device != null) {
            val unused = trySend(result.device)
          }
        }

        override fun onScanFailed(errorCode: Int) {
          cancel("BLE Scan Failed", null)
        }
      }
    val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    val scanner = bluetoothManager.adapter.bluetoothLeScanner
    scanner.startScan(scanCallback)
    awaitClose { scanner.stopScan(scanCallback) }
  }

  fun connectToDevice(device: BluetoothDevice): Flow<BluetoothGatt> = callbackFlow {
    val gattCallback =
      object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
          if (newState == BluetoothProfile.STATE_CONNECTED) {
            val unused = gatt!!.discoverServices()
          } else {
            cancel("Connect Failed", null)
          }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
          if (status == BluetoothGatt.GATT_SUCCESS) {
            val unused = trySend(gatt!!)
          } else {
            cancel("Service discovery failed", null)
          }
        }
      }

    device.connectGatt(context, true, gattCallback)
    awaitClose {}
  }

  fun scanAndConnect() =
    flow<BluetoothGattService> {
      val device = startScan().firstOrNull()
      if (device != null) {
        val gatt = connectToDevice(device).firstOrNull()
        emit(gatt!!.services[0])
      }
    }
}