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 llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.model.Ble import java.util.UUID class WriteAccelerometerCallback( private val app: Application, private var request: Ble.Accelerometer.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) 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 ) { super.onServicesDiscovered(gatt, status) onCycle(gatt, status) } private fun onCycle( gatt: BluetoothGatt, status: Int ){ Log.d("write", "${request.tx != null} ${request.saveHistory != null} ${request.historyInterval != null}") 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: Triple? = null uuid = request.historyInterval?.let { Triple( intervalWriteUUID, mutableListOf(3).apply { addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) }.toByteArray(), request.copy( historyInterval = null ) ) } uuid = request.saveHistory?.let { Triple( saveEnabledWriteUUID, mutableListOf(4).apply { add(if (it is Ble.Accelerometer.History.Enabled) 1 else 0) if(it is Ble.Accelerometer.History.Enabled) { add(it.mode.sendData) add(it.scale.sendData) } }.toByteArray(), request.copy( saveHistory = null ) ) } ?: uuid uuid = request.tx?.let { Triple( 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 } ), request.copy( tx = null ) ) } ?: uuid uuid?.let { uuid -> gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { it.uuid == uuid.first }?.let { gatt.writeCharacteristic(it, uuid.second) request = uuid.third 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 ) { 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)) } } fun BluetoothGatt.writeCharacteristic( characteristic: BluetoothGattCharacteristic, data: ByteArray ): Result { return if(checkPermission()){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) }else{ characteristic.writeType characteristic.value = data writeCharacteristic(characteristic) } Result.success(Unit) } else { Result.failure(BleException.PermissionDenied) } } 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 } } }