improve beacon ui

This commit is contained in:
Vineyro 2023-04-06 14:52:05 +07:00
parent 2a43916ecc
commit c19417e00f
7 changed files with 654 additions and 402 deletions

View File

@ -21,6 +21,7 @@ import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.rememberBottomDialogState import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.beacon.view.DisplayState import llc.arma.ble.app.ui.screen.beacon.view.DisplayState
import llc.arma.ble.app.ui.screen.beacon.view.PowerEdit import llc.arma.ble.app.ui.screen.beacon.view.PowerEdit
import llc.arma.ble.app.ui.screen.beacon.view.Write
import llc.arma.ble.app.ui.screen.thermometer.localizedName import llc.arma.ble.app.ui.screen.thermometer.localizedName
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
@ -75,257 +76,17 @@ fun BeaconScreen(
when(sheetPage){ when(sheetPage){
SheetPage.WRITE -> bottomDialog.show { SheetPage.WRITE -> bottomDialog.show {
val scope = rememberCoroutineScope()
val currentState = viewModel.viewState.value val currentState = viewModel.viewState.value
if(currentState is BeaconContract.State.Display) { if(currentState is BeaconContract.State.Display && currentState.writeState != null) {
Column() { Write(
state = currentState.writeState,
when (currentState.writeState) { onEvent = {
is BeaconContract.State.Display.WriteState.DisplayPreview -> { viewModel.setEvent(it)
}
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Записать изменения?",
style = MaterialTheme.typography.titleLarge
) )
currentState.writeState.writeRequest.tx?.let {
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Мощность"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = "${currentState.origin.state.tx.localizedName} db -> ${it.localizedName} db"
)
}
}
}
}
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
viewModel.setEvent(BeaconContract.Event.OnWriteBle)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Записать"
)
}
}
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant,
onClick = {
scope.launch {
viewModel.setEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
text = "Отменить"
)
}
}
}
is BeaconContract.State.Display.WriteState.Writing -> {
Box {
Column() {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Запись",
style = MaterialTheme.typography.titleLarge
)
CircularProgressIndicator(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(bottom = 48.dp)
)
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant,
onClick = {
scope.launch {
viewModel.setEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
text = "Отменить"
)
}
}
}
}
}
BeaconContract.State.Display.WriteState.Success -> {
Box {
Column {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Запись завершена",
style = MaterialTheme.typography.titleLarge
)
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary,
onClick = {
scope.launch {
viewModel.setEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}
}
BeaconContract.State.Display.WriteState.Failure -> {
Box {
Column {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Ошибка записи",
style = MaterialTheme.typography.titleLarge
)
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary,
onClick = {
scope.launch {
viewModel.setEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}
}
else -> {}
}
Spacer(modifier = Modifier.height(48.dp))
}
} }
} }
@ -354,7 +115,8 @@ fun BeaconScreen(
onEvent = { onEvent = {
viewModel.setEvent(it) viewModel.setEvent(it)
}, },
ble = state.beacon ble = state.beacon,
origin = state.origin
) )
is BeaconContract.State.Loading -> LoadingState() is BeaconContract.State.Loading -> LoadingState()
} }

View File

@ -84,6 +84,19 @@ class BeaconViewModel @Inject constructor(
state: BeaconContract.State, state: BeaconContract.State,
event: BeaconContract.Event.OnBleChanged event: BeaconContract.Event.OnBleChanged
) { ) {
when(state){
is BeaconContract.State.Display -> {
setState {
state.copy(
origin = Ble.Beacon(
info = event.ble.info,
state = state.origin.state
)
)
}
}
is BeaconContract.State.Loading -> {
setState { setState {
BeaconContract.State.Display( BeaconContract.State.Display(
origin = event.ble, origin = event.ble,
@ -92,6 +105,9 @@ class BeaconViewModel @Inject constructor(
) )
} }
} }
}
}
private fun reduce( private fun reduce(
state: BeaconContract.State, state: BeaconContract.State,
@ -147,22 +163,35 @@ class BeaconViewModel @Inject constructor(
if(state is BeaconContract.State.Display){ if(state is BeaconContract.State.Display){
state.writeState?.let { state.writeState?.let { request ->
if(it is BeaconContract.State.Display.WriteState.DisplayPreview) { if(request is BeaconContract.State.Display.WriteState.DisplayPreview) {
viewModelScope.launch { viewModelScope.launch {
setState { setState {
state.copy( state.copy(
writeState = BeaconContract.State.Display.WriteState.Writing(it.writeRequest) writeState = BeaconContract.State.Display.WriteState.Writing(request.writeRequest)
) )
} }
writeBle(state.beacon.info.serial, it.writeRequest).fold( val currentState = viewState.value
if(currentState is BeaconContract.State.Display) {
val newBleObject = Ble.Beacon(
info = currentState.origin.info,
state = currentState.origin.state.copy(
tx = request.writeRequest.tx ?: state.origin.state.tx
)
)
writeBle(state.beacon.info.serial, request.writeRequest).fold(
onSuccess = { onSuccess = {
setState { setState {
state.copy( currentState.copy(
origin = newBleObject,
beacon = bleMapper.map(newBleObject) as BleView.Beacon,
writeState = BeaconContract.State.Display.WriteState.Success writeState = BeaconContract.State.Display.WriteState.Success
) )
} }
@ -187,3 +216,5 @@ class BeaconViewModel @Inject constructor(
} }
} }
}

View File

@ -18,10 +18,12 @@ import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.domain.model.Ble
@Composable @Composable
fun DisplayState( fun DisplayState(
onEvent: (BeaconContract.Event) -> Unit, onEvent: (BeaconContract.Event) -> Unit,
origin: Ble.Beacon,
ble: BleView.Beacon ble: BleView.Beacon
) { ) {
@ -39,7 +41,7 @@ fun DisplayState(
horizontal = 8.dp horizontal = 8.dp
) )
) { ) {
BleInfoView(bleInfo = ble.info) BleInfoView(bleInfo = origin.info)
} }
Column( Column(

View File

@ -0,0 +1,346 @@
package llc.arma.ble.app.ui.screen.beacon.view
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import llc.arma.ble.R
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.localizedName
@Composable
fun Write(
state: BeaconContract.State.Display.WriteState,
onEvent: (BeaconContract.Event) -> Unit
) {
Column(
modifier = Modifier.animateContentSize()
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Запись изменений",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(20.dp))
when (state) {
is BeaconContract.State.Display.WriteState.DisplayPreview -> {
if(state.writeRequest.tx != null) {
state.writeRequest.tx.let {
Box(
modifier = Modifier.padding(
vertical = 0.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Мощность"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = "${it.localizedName} db"
)
}
}
}
}
Spacer(modifier = Modifier.height(20.dp))
Surface(
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(BeaconContract.Event.OnWriteBle)
},
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Записать"
)
}
}
Surface(
shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant,
onClick = {
onEvent(BeaconContract.Event.OnHideWriteBlePreview)
},
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
text = "Отменить"
)
}
}
} else {
Spacer(modifier = Modifier.height(38.dp))
Text(
text = "Нет изменений",
modifier = Modifier
.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(64.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary,
onClick = {
onEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}
is BeaconContract.State.Display.WriteState.Writing -> {
Box {
Column() {
Spacer(modifier = Modifier.height(28.dp))
CircularProgressIndicator(
strokeCap = StrokeCap.Round,
modifier = Modifier
.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(48.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant,
onClick = {
onEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
text = "Отменить"
)
}
}
}
}
}
BeaconContract.State.Display.WriteState.Success -> {
Box {
Column {
Box(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Image(
modifier = Modifier
.size(125.dp)
.align(Alignment.Center),
painter = painterResource(R.drawable.ic_done),
contentDescription = null
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = "Успешно завершено"
)
Spacer(modifier = Modifier.height(20.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary,
onClick = {
onEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}
}
BeaconContract.State.Display.WriteState.Failure -> {
Box {
Column {
Box(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Image(
modifier = Modifier
.size(125.dp)
.align(Alignment.Center),
painter = painterResource(R.drawable.ic_error),
contentDescription = null
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = "Ошибка записи"
)
Spacer(modifier = Modifier.height(20.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary,
onClick = {
onEvent(BeaconContract.Event.OnHideWriteBlePreview)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}
}
}
}
}

View File

@ -231,7 +231,7 @@ class BleRepositoryImpl @Inject constructor(
} }
delay(500) delay(1_000)
} }
@ -297,7 +297,7 @@ class BleRepositoryImpl @Inject constructor(
} }
delay(500) delay(1_000)
} }
@ -310,7 +310,7 @@ class BleRepositoryImpl @Inject constructor(
} }
return llc.arma.ble.domain.Result.failure(BleException.UnexpectedResponse) return Result.failure(BleException.UnexpectedResponse)
} }
@ -465,37 +465,6 @@ class BleRepositoryImpl @Inject constructor(
} }
/*request.tx?.let {
Log.d("write", "tx")
writeTx(result.device, it)
}?.onFailure {
Log.d("write", "tx fail")
return Result.failure(it)
}
request.historyInterval?.let {
Log.d("write", "in")
writeSaveInterval(result.device, it)
}?.onFailure {
Log.d("write", "in fail")
return Result.failure(it)
}
request.saveHistory?.let {
Log.d("write", "hs")
writeSaveEnabled(result.device, it)
}?.onFailure {
Log.d("write", "hs fail")
return Result.failure(it)
}
Log.d("write", "fs")
writeToFlash(serial).onFailure {
Log.d("write", "fs fail")
return Result.failure(it)
}*/
} }
} }
@ -503,44 +472,37 @@ class BleRepositoryImpl @Inject constructor(
override suspend fun writeBle( override suspend fun writeBle(
serial: String, serial: String,
request: Ble.Beacon.WriteRequest request: Ble.Beacon.WriteRequest
): Result<Unit, BleException> { ): Result<Unit, BleException> = suspendCancellableCoroutine {
deviceCache[serial]?.let { result -> deviceCache[serial]?.let { scanResult ->
request.tx?.let { writeTx(result.device, it) }?.onFailure { if(checkPermission()) {
return Result.failure(it)
}
writeToFlash(serial).onFailure { var gatt: BluetoothGatt? = null
return Result.failure(it)
}
val callback = WriteBeaconCallback(app, request) { result ->
gatt?.close()
result.onSuccess {
deviceCache.remove(serial) deviceCache.remove(serial)
resultList.remove(serial) resultList.remove(serial)
}
it.resume(result)
} }
return Result.success(Unit) gatt = scanResult.device.connectGatt(app, false, callback)
} else {
it.resume(Result.failure(BleException.PermissionDenied))
} }
private suspend fun writeToFlash(
serial: String
): Result<Unit, BleException>{
deviceCache[serial]?.device?.let { result ->
return writeCharacteristic(
device = result,
serviceId = serviceUUID,
characteristicId = flashWriteUUID,
writeData = byteArrayOf(9, 1)
)
} }
return Result.success(Unit)
} }
override suspend fun changeBlePassword( override suspend fun changeBlePassword(
@ -567,69 +529,6 @@ class BleRepositoryImpl @Inject constructor(
return Result.success(Unit) return Result.success(Unit)
} }
private suspend fun writeTx(
device: BluetoothDevice,
tx: Ble.BleState.TX
): Result<Unit, BleException> {
return writeCharacteristic(
device = device,
serviceId = serviceUUID,
characteristicId = txWriteUUID,
writeData = byteArrayOf(
when(tx) {
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
}
)
)
}
private suspend fun writeSaveInterval(
device: BluetoothDevice,
interval: Long
): Result<Unit, BleException> {
fun UInt.to4ByteArrayInBigEndian(): ByteArray =
(3 downTo 0).map {
(this shr (it * Byte.SIZE_BITS)).toByte()
}.reversed().toByteArray()
return writeCharacteristic(
device = device,
serviceId = serviceUUID,
characteristicId = intervalWriteUUID,
writeData = mutableListOf<Byte>(3).apply {
addAll((interval / 1_000).toUInt().to4ByteArrayInBigEndian().toList())
}.toByteArray()
)
}
private suspend fun writeSaveEnabled(
device: BluetoothDevice,
enabled: Boolean
): Result<Unit, BleException> {
return writeCharacteristic(
device = device,
serviceId = serviceUUID,
characteristicId = saveEnabledWriteUUID,
writeData = mutableListOf<Byte>(4).apply {
add(if(enabled) 1 else 0)
}.toByteArray()
)
}
private suspend fun readCharacteristic( private suspend fun readCharacteristic(
device: BluetoothDevice, device: BluetoothDevice,
serviceId: UUID, serviceId: UUID,

View File

@ -0,0 +1,212 @@
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 WriteBeaconCallback(
private val app: Application,
private var request: Ble.Beacon.WriteRequest,
private val onResult: (Result<Unit, BleException>) -> Unit
) : BluetoothGattCallback() {
private var flashed = false
override fun onConnectionStateChange(
gatt: BluetoothGatt,
status: Int,
newState: Int
) {
super.onConnectionStateChange(gatt, status, newState)
Log.d("beacon", "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("beacon", "onServicesDiscovered $status")
super.onServicesDiscovered(gatt, status)
onCycle(gatt, status)
}
private fun onCycle(
gatt: BluetoothGatt,
status: Int
){
if(request.tx != null) {
var uuid: Pair<UUID, ByteArray>? = null
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("beacon", "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))
}
}
fun BluetoothGatt.writeCharacteristic(
characteristic: BluetoothGattCharacteristic,
data: ByteArray
): Result<Unit, BleException> {
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
}
}
}

View File

@ -9,7 +9,7 @@ sealed class Ble(
val state: BleState val state: BleState
) : Ble(info){ ) : Ble(info){
class WriteRequest( data class WriteRequest(
val tx: BleState.TX? val tx: BleState.TX?
) )