package llc.arma.ble.data import android.Manifest import android.app.Application import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothProfile import android.content.pm.PackageManager import android.os.Build import android.util.Log import androidx.core.app.ActivityCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.model.Ble import java.util.UUID class WriteThermometerCallback( private val app: Application, private var request: Ble.Thermometer.WriteRequest, private val onResult: (Result) -> Unit ) : BluetoothGattCallback() { private var flashed = false override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { super.onConnectionStateChange(gatt, status, newState) Log.d("th", "onConnectionStateChange $status $newState") if(checkPermission()) { if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices() } else { onResult(Result.failure(BleException.UnexpectedResponse)) } } else { onResult(Result.failure(BleException.PermissionDenied)) } } override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { Log.d("th", "onServicesDiscovered $status") super.onServicesDiscovered(gatt, status) onCycle(gatt, status) } private fun onCycle( gatt: BluetoothGatt, status: Int ){ if(request.tx != null || request.saveHistory != null || request.historyInterval != null) { fun UInt.to4ByteArrayInLittleEndian(): ByteArray = (3 downTo 0).map { (this shr (it * Byte.SIZE_BITS)).toByte() }.toByteArray() var uuid: Pair? = null uuid = request.historyInterval?.let { this.request = request.copy( historyInterval = null ) Pair( intervalWriteUUID, mutableListOf(3).apply { addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) }.toByteArray() ) } uuid = request.saveHistory?.let { this.request = request.copy( saveHistory = null ) Pair( saveEnabledWriteUUID, mutableListOf(4).apply { add(if (it) 1 else 0) }.toByteArray() ) } ?: uuid uuid = request.tx?.let { this.request = request.copy( tx = null ) Pair( txWriteUUID, byteArrayOf( when (it) { Ble.BleState.TX.MINUS_40 -> -40 Ble.BleState.TX.MINUS_20 -> -20 Ble.BleState.TX.MINUS_16 -> -16 Ble.BleState.TX.MINUS_12 -> -12 Ble.BleState.TX.MINUS_8 -> -8 Ble.BleState.TX.MINUS_4 -> -4 Ble.BleState.TX.ZERO -> 0 Ble.BleState.TX.PLUS_3 -> 3 Ble.BleState.TX.PLUS_4 -> 4 } ) ) } ?: uuid uuid?.let { uuid -> gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { it.uuid == uuid.first }?.let { gatt.writeCharacteristic(it, uuid.second) return } } onResult(Result.failure(BleException.UnexpectedResponse)) } else { if(flashed.not()){ flashed = true gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { it.uuid == flashWriteUUID }?.let { gatt.writeCharacteristic(it, byteArrayOf(9)) return } onResult(Result.failure(BleException.UnexpectedResponse)) } else { onResult(Result.success(Unit)) } } } override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { Log.d("th", "onCharacteristicWrite $status") super.onCharacteristicWrite(gatt, characteristic, status) if(checkPermission()) { if(status == BluetoothGatt.GATT_SUCCESS || flashed) { onCycle(gatt, status) } else { onResult(Result.failure(BleException.UnexpectedResponse)) } } else { onResult(Result.failure(BleException.PermissionDenied)) } } private fun BluetoothGatt.writeCharacteristic( characteristic: BluetoothGattCharacteristic, data: ByteArray ): Result { return if(checkPermission()){ Log.d("write", data.asUByteArray().joinToString(" ") { it.toString(16).padStart(2, '0') }) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) }else{ characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT characteristic.value = data writeCharacteristic(characteristic) } Result.success(Unit) } else { Result.failure(BleException.PermissionDenied) } } private fun checkPermission(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED } else { return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED } } }