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.screen.beacon.view.DisplayState
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.domain.model.Ble
@ -75,256 +76,16 @@ fun BeaconScreen(
when(sheetPage){
SheetPage.WRITE -> bottomDialog.show {
val scope = rememberCoroutineScope()
val currentState = viewModel.viewState.value
if(currentState is BeaconContract.State.Display) {
if(currentState is BeaconContract.State.Display && currentState.writeState != null) {
Column() {
when (currentState.writeState) {
is BeaconContract.State.Display.WriteState.DisplayPreview -> {
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 -> {}
Write(
state = currentState.writeState,
onEvent = {
viewModel.setEvent(it)
}
Spacer(modifier = Modifier.height(48.dp))
}
)
}
@ -354,7 +115,8 @@ fun BeaconScreen(
onEvent = {
viewModel.setEvent(it)
},
ble = state.beacon
ble = state.beacon,
origin = state.origin
)
is BeaconContract.State.Loading -> LoadingState()
}

View File

@ -84,13 +84,29 @@ class BeaconViewModel @Inject constructor(
state: BeaconContract.State,
event: BeaconContract.Event.OnBleChanged
) {
setState {
BeaconContract.State.Display(
origin = event.ble,
beacon = bleMapper.map(event.ble) as BleView.Beacon,
writeState = null
)
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 {
BeaconContract.State.Display(
origin = event.ble,
beacon = bleMapper.map(event.ble) as BleView.Beacon,
writeState = null
)
}
}
}
}
private fun reduce(
@ -147,34 +163,49 @@ class BeaconViewModel @Inject constructor(
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 {
setState {
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(
onSuccess = {
setState {
state.copy(
writeState = BeaconContract.State.Display.WriteState.Success
)
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 = {
setState {
currentState.copy(
origin = newBleObject,
beacon = bleMapper.map(newBleObject) as BleView.Beacon,
writeState = BeaconContract.State.Display.WriteState.Success
)
}
},
onFailure = {
setState {
state.copy(
writeState = BeaconContract.State.Display.WriteState.Failure
)
}
}
},
onFailure = {
setState {
state.copy(
writeState = BeaconContract.State.Display.WriteState.Failure
)
}
}
)
)
}
}

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.screen.BleInfoView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.domain.model.Ble
@Composable
fun DisplayState(
onEvent: (BeaconContract.Event) -> Unit,
origin: Ble.Beacon,
ble: BleView.Beacon
) {
@ -39,7 +41,7 @@ fun DisplayState(
horizontal = 8.dp
)
) {
BleInfoView(bleInfo = ble.info)
BleInfoView(bleInfo = origin.info)
}
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(
serial: String,
request: Ble.Beacon.WriteRequest
): Result<Unit, BleException> {
): Result<Unit, BleException> = suspendCancellableCoroutine {
deviceCache[serial]?.let { result ->
deviceCache[serial]?.let { scanResult ->
if(checkPermission()) {
var gatt: BluetoothGatt? = null
val callback = WriteBeaconCallback(app, request) { result ->
gatt?.close()
result.onSuccess {
deviceCache.remove(serial)
resultList.remove(serial)
}
it.resume(result)
}
gatt = scanResult.device.connectGatt(app, false, callback)
} else {
it.resume(Result.failure(BleException.PermissionDenied))
request.tx?.let { writeTx(result.device, it) }?.onFailure {
return Result.failure(it)
}
writeToFlash(serial).onFailure {
return Result.failure(it)
}
deviceCache.remove(serial)
resultList.remove(serial)
}
return Result.success(Unit)
}
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(
@ -567,69 +529,6 @@ class BleRepositoryImpl @Inject constructor(
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(
device: BluetoothDevice,
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
) : Ble(info){
class WriteRequest(
data class WriteRequest(
val tx: BleState.TX?
)