Fix bug GATT_FAILURE
This commit is contained in:
parent
ce95316494
commit
0d7019a7be
|
|
@ -37,6 +37,7 @@ import kotlin.random.nextInt
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Refresh
|
import androidx.compose.material.icons.rounded.Refresh
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState
|
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState
|
||||||
import com.patrykandpatrick.vico.core.axis.AxisPosition
|
import com.patrykandpatrick.vico.core.axis.AxisPosition
|
||||||
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
|
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
|
||||||
|
|
@ -73,7 +74,7 @@ fun TemperatureHistory(
|
||||||
viewModel.setEvent(TemperatureHistoryContract.Event.LoadHistory(ble.serial))
|
viewModel.setEvent(TemperatureHistoryContract.Event.LoadHistory(ble.serial))
|
||||||
}
|
}
|
||||||
|
|
||||||
Column() {
|
Column {
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(horizontal = 12.dp),
|
modifier = Modifier.padding(horizontal = 12.dp),
|
||||||
|
|
@ -90,7 +91,10 @@ fun TemperatureHistory(
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setEvent(TemperatureHistoryContract.Event.LoadHistory(ble.serial))
|
viewModel.setEvent(TemperatureHistoryContract.Event.LoadHistory(ble.serial))
|
||||||
},
|
},
|
||||||
enabled = state.loadingHistoryState is ProgressState.Finished
|
enabled = when(state){
|
||||||
|
is TemperatureHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||||
|
TemperatureHistoryContract.State.Exception -> true
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.Refresh,
|
imageVector = Icons.Rounded.Refresh,
|
||||||
|
|
@ -102,71 +106,67 @@ fun TemperatureHistory(
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
when (state.loadingHistoryState) {
|
when(state){
|
||||||
is ProgressState.Finished -> {
|
is TemperatureHistoryContract.State.Display -> Display(state = state)
|
||||||
|
TemperatureHistoryContract.State.Exception -> Exception()
|
||||||
Text(text = "${state.loadingHistoryState.data.size}")
|
}
|
||||||
|
|
||||||
val producer = state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
|
}
|
||||||
TemperatureEntry(measurePoint.date, index.toFloat(), measurePoint.value) }.let {
|
|
||||||
ChartEntryModelProducer(it)
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Display(
|
||||||
|
state: TemperatureHistoryContract.State.Display
|
||||||
|
) {
|
||||||
|
when (state.loadingHistoryState) {
|
||||||
|
is ProgressState.Finished -> {
|
||||||
|
|
||||||
|
Text(text = "${state.loadingHistoryState.data.size}")
|
||||||
|
|
||||||
|
val producer = state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
|
||||||
|
TemperatureEntry(measurePoint.date, index.toFloat(), measurePoint.value) }.let {
|
||||||
|
ChartEntryModelProducer(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val axisValueFormatter = AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
|
||||||
|
(chartValues.chartEntryModel.entries.first().getOrNull(value.toInt()) as? TemperatureEntry)
|
||||||
|
?.localDate
|
||||||
|
?.let { formatter.format(Date(it)) }
|
||||||
|
.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
val lineChart = lineChart(
|
||||||
|
spacing = 110.dp
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(modifier = Modifier.padding(8.dp)) {
|
||||||
|
|
||||||
|
val scrollState = rememberChartScrollState()
|
||||||
|
|
||||||
|
LaunchedEffect(scrollState.maxValue){
|
||||||
|
scrollState.scrollBy(scrollState.maxValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
val axisValueFormatter = AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
|
Chart(
|
||||||
(chartValues.chartEntryModel.entries.first().getOrNull(value.toInt()) as? TemperatureEntry)
|
chartScrollState = scrollState,
|
||||||
?.localDate
|
chart = lineChart,
|
||||||
?.let { formatter.format(Date(it)) }
|
chartModelProducer = producer,
|
||||||
.orEmpty()
|
startAxis = startAxis(),
|
||||||
}
|
bottomAxis = bottomAxis(
|
||||||
|
valueFormatter = axisValueFormatter,
|
||||||
val lineChart = lineChart(
|
labelRotationDegrees = 0f,
|
||||||
spacing = 110.dp
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1.5f),
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(modifier = Modifier.padding(8.dp)) {
|
|
||||||
|
|
||||||
val scrollState = rememberChartScrollState()
|
|
||||||
|
|
||||||
LaunchedEffect(scrollState.maxValue){
|
|
||||||
scrollState.scrollBy(scrollState.maxValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
Chart(
|
|
||||||
chartScrollState = scrollState,
|
|
||||||
chart = lineChart,
|
|
||||||
chartModelProducer = producer,
|
|
||||||
startAxis = startAxis(),
|
|
||||||
bottomAxis = bottomAxis(
|
|
||||||
valueFormatter = axisValueFormatter,
|
|
||||||
labelRotationDegrees = 0f,
|
|
||||||
),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.aspectRatio(1.5f),
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is ProgressState.Indeterminate -> {
|
}
|
||||||
|
is ProgressState.Indeterminate -> {
|
||||||
Box(modifier = Modifier.padding(8.dp)) {
|
|
||||||
|
|
||||||
Box(
|
Box(modifier = Modifier.padding(8.dp)) {
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.aspectRatio(2f),
|
|
||||||
){
|
|
||||||
|
|
||||||
CircularProgressIndicator(
|
|
||||||
strokeCap = StrokeCap.Round,
|
|
||||||
modifier = Modifier.align(Alignment.Center)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ProgressState.Progress -> Box(modifier = Modifier.padding(8.dp)) {
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -174,15 +174,8 @@ fun TemperatureHistory(
|
||||||
.aspectRatio(2f),
|
.aspectRatio(2f),
|
||||||
){
|
){
|
||||||
|
|
||||||
val progressAnimDuration = 1500
|
|
||||||
val progressAnimation by animateFloatAsState(
|
|
||||||
targetValue = state.loadingHistoryState.value,
|
|
||||||
animationSpec = tween(durationMillis = progressAnimDuration, easing = FastOutSlowInEasing)
|
|
||||||
)
|
|
||||||
|
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
strokeCap = StrokeCap.Round,
|
strokeCap = StrokeCap.Round,
|
||||||
progress = progressAnimation,
|
|
||||||
modifier = Modifier.align(Alignment.Center)
|
modifier = Modifier.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -190,6 +183,48 @@ fun TemperatureHistory(
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is ProgressState.Progress -> Box(modifier = Modifier.padding(8.dp)) {
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(2f),
|
||||||
|
){
|
||||||
|
|
||||||
|
val progressAnimDuration = 1500
|
||||||
|
val progressAnimation by animateFloatAsState(
|
||||||
|
targetValue = state.loadingHistoryState.value,
|
||||||
|
animationSpec = tween(durationMillis = progressAnimDuration, easing = FastOutSlowInEasing)
|
||||||
|
)
|
||||||
|
|
||||||
|
CircularProgressIndicator(
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
|
progress = progressAnimation,
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Exception(
|
||||||
|
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(2f),
|
||||||
|
){
|
||||||
|
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = "Во время загрузки произошла ошибка",
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,9 +240,15 @@ class TemperatureHistoryContract {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class State(
|
sealed class State : ViewState {
|
||||||
val loadingHistoryState : ProgressState<List<Ble.Thermometer.MeasurePoint>>
|
|
||||||
) : ViewState
|
data class Display(
|
||||||
|
val loadingHistoryState : ProgressState<List<Ble.Thermometer.MeasurePoint>>
|
||||||
|
) : State()
|
||||||
|
|
||||||
|
object Exception : State()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
sealed class Effect : ViewSideEffect {
|
sealed class Effect : ViewSideEffect {
|
||||||
|
|
||||||
|
|
@ -222,7 +263,7 @@ class TemperatureHistoryViewModel @Inject constructor(
|
||||||
private val getTemperatureHistoryBySerial: GetTemperatureHistoryBySerial
|
private val getTemperatureHistoryBySerial: GetTemperatureHistoryBySerial
|
||||||
) : BaseViewModel<TemperatureHistoryContract.State, TemperatureHistoryContract.Event, TemperatureHistoryContract.Effect>() {
|
) : BaseViewModel<TemperatureHistoryContract.State, TemperatureHistoryContract.Event, TemperatureHistoryContract.Effect>() {
|
||||||
|
|
||||||
override fun setInitialState() = TemperatureHistoryContract.State(
|
override fun setInitialState() = TemperatureHistoryContract.State.Display(
|
||||||
ProgressState.Indeterminate
|
ProgressState.Indeterminate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -239,18 +280,20 @@ class TemperatureHistoryViewModel @Inject constructor(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
TemperatureHistoryContract.State(ProgressState.Indeterminate)
|
TemperatureHistoryContract.State.Display(ProgressState.Indeterminate)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTemperatureHistoryBySerial(event.serial).onEach {
|
getTemperatureHistoryBySerial(event.serial).onEach {
|
||||||
it.fold(
|
it.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
setState {
|
setState {
|
||||||
TemperatureHistoryContract.State(it)
|
TemperatureHistoryContract.State.Display(it)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
|
setState {
|
||||||
|
TemperatureHistoryContract.State.Exception
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import android.Manifest
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.bluetooth.*
|
import android.bluetooth.*
|
||||||
import android.bluetooth.le.ScanCallback
|
import android.bluetooth.le.ScanCallback
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
import android.bluetooth.le.ScanResult
|
import android.bluetooth.le.ScanResult
|
||||||
import android.bluetooth.le.ScanSettings
|
import android.bluetooth.le.ScanSettings
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
|
@ -25,6 +27,7 @@ import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class BleRepositoryImpl @Inject constructor(
|
class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
@ -316,7 +319,15 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
|
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
|
||||||
(this[idx].toUInt() and 0xFFu)
|
(this[idx].toUInt() and 0xFFu)
|
||||||
|
|
||||||
deviceCache[serial]?.device?.let { device ->
|
findDeviceBySerial(serial).fold(
|
||||||
|
onSuccess = {
|
||||||
|
return@fold it
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
emit(Result.failure(it))
|
||||||
|
return@flow
|
||||||
|
}
|
||||||
|
).let { device ->
|
||||||
|
|
||||||
emit(Result.success(ProgressState.Indeterminate))
|
emit(Result.success(ProgressState.Indeterminate))
|
||||||
|
|
||||||
|
|
@ -330,7 +341,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
val countDataArray = readCharacteristic(
|
val countDataArray = readCharacteristic(
|
||||||
device = device,
|
device = device,
|
||||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb")
|
||||||
).fold(
|
).fold(
|
||||||
onFailure = {
|
onFailure = {
|
||||||
emit(Result.failure(it))
|
emit(Result.failure(it))
|
||||||
|
|
@ -355,7 +366,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
val firstPackageResponse = readCharacteristic(
|
val firstPackageResponse = readCharacteristic(
|
||||||
device = device,
|
device = device,
|
||||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb")
|
||||||
).fold(
|
).fold(
|
||||||
onFailure = {
|
onFailure = {
|
||||||
emit(Result.failure(it))
|
emit(Result.failure(it))
|
||||||
|
|
@ -397,7 +408,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
val readResponse = readCharacteristic(
|
val readResponse = readCharacteristic(
|
||||||
device = device,
|
device = device,
|
||||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb")
|
||||||
).fold(
|
).fold(
|
||||||
onFailure = {
|
onFailure = {
|
||||||
emit(Result.failure(it))
|
emit(Result.failure(it))
|
||||||
|
|
@ -406,20 +417,34 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
onSuccess = { return@fold it }
|
onSuccess = { return@fold it }
|
||||||
)
|
)
|
||||||
|
|
||||||
dataCount = readResponse[1].toUByte()
|
if(readResponse[0] == 251.toByte()) {
|
||||||
|
|
||||||
temperatureDataArray = readResponse.toList().subList(2, readResponse.size)
|
dataCount = readResponse[1].toUByte()
|
||||||
|
|
||||||
temperaturePackage.addAll(
|
temperatureDataArray = readResponse.toList().subList(2, readResponse.size)
|
||||||
temperatureDataArray.chunked(2).map {
|
|
||||||
(it[0] + it[1] * 256).toFloat() / 100f
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
emit(Result.success(ProgressState.Progress(totalDataSize.toFloat() / temperaturePackage.size.toFloat())))
|
temperaturePackage.addAll(
|
||||||
|
temperatureDataArray.chunked(2).map {
|
||||||
|
(it[0] + it[1] * 256).toFloat() / 100f
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
emit(Result.success(ProgressState.Progress(totalDataSize.toFloat() / temperaturePackage.size.toFloat())))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
emit(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readCharacteristic(
|
||||||
|
device = device,
|
||||||
|
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||||
|
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb")
|
||||||
|
)
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
Result.success(
|
Result.success(
|
||||||
ProgressState.Finished(
|
ProgressState.Finished(
|
||||||
|
|
@ -433,6 +458,10 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
emit(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -559,14 +588,19 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
characteristicId: UUID
|
characteristicId: UUID
|
||||||
): Result<ByteArray, BleException> = suspendCancellableCoroutine {
|
): Result<ByteArray, BleException> = suspendCancellableCoroutine {
|
||||||
|
|
||||||
|
var result: ByteArray? = null
|
||||||
|
var bleGatt: BluetoothGatt? = null
|
||||||
|
|
||||||
val callback = object : BluetoothGattCallback() {
|
val callback = object : BluetoothGattCallback() {
|
||||||
|
|
||||||
override fun onConnectionStateChange(
|
override fun onConnectionStateChange(
|
||||||
gatt: BluetoothGatt?,
|
gatt: BluetoothGatt,
|
||||||
status: Int,
|
status: Int,
|
||||||
newState: Int
|
newState: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
Log.d("read", "onConnectionStateChange $newState $status")
|
||||||
|
|
||||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
||||||
|
|
@ -574,7 +608,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
Manifest.permission.BLUETOOTH_CONNECT
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
gatt?.discoverServices()
|
gatt.discoverServices()
|
||||||
} else {
|
} else {
|
||||||
it.resume(Result.failure(BleException.PermissionDenied))
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
}
|
}
|
||||||
|
|
@ -584,14 +618,16 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServicesDiscovered(
|
override fun onServicesDiscovered(
|
||||||
gatt: BluetoothGatt?,
|
gatt: BluetoothGatt,
|
||||||
status: Int
|
status: Int
|
||||||
) {
|
) {
|
||||||
super.onServicesDiscovered(gatt, status)
|
super.onServicesDiscovered(gatt, status)
|
||||||
|
|
||||||
|
Log.d("read", "onServicesDiscovered $status")
|
||||||
|
|
||||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
|
||||||
gatt?.services?.firstOrNull { service ->
|
gatt.services?.firstOrNull { service ->
|
||||||
service.uuid == serviceId
|
service.uuid == serviceId
|
||||||
}?.characteristics?.firstOrNull { characteristic ->
|
}?.characteristics?.firstOrNull { characteristic ->
|
||||||
characteristic.uuid == characteristicId
|
characteristic.uuid == characteristicId
|
||||||
|
|
@ -620,6 +656,9 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
status: Int
|
status: Int
|
||||||
) {
|
) {
|
||||||
super.onCharacteristicRead(gatt, characteristic, value, status)
|
super.onCharacteristicRead(gatt, characteristic, value, status)
|
||||||
|
|
||||||
|
Log.d("read", "onCharacteristicRead $status")
|
||||||
|
|
||||||
if (ActivityCompat.checkSelfPermission(
|
if (ActivityCompat.checkSelfPermission(
|
||||||
app,
|
app,
|
||||||
Manifest.permission.BLUETOOTH_CONNECT
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
|
@ -627,9 +666,17 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
) {
|
) {
|
||||||
it.resume(Result.failure(BleException.PermissionDenied))
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
}else {
|
}else {
|
||||||
gatt.disconnect()
|
gatt.close()
|
||||||
it.resume(Result.success(value))
|
result = value
|
||||||
|
if(result != null){
|
||||||
|
it.resume(Result.success(result!!))
|
||||||
|
} else {
|
||||||
|
bleGatt?.close()
|
||||||
|
it.resume(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -641,7 +688,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
) {
|
) {
|
||||||
it.resume(Result.failure(BleException.PermissionDenied))
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
} else {
|
} else {
|
||||||
device.connectGatt(app, true, callback)
|
bleGatt = device.connectGatt(app, true, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -653,14 +700,18 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
writeData: ByteArray
|
writeData: ByteArray
|
||||||
) = suspendCancellableCoroutine {
|
) = suspendCancellableCoroutine {
|
||||||
|
|
||||||
|
var bleGatt: BluetoothGatt? = null
|
||||||
|
|
||||||
val callback = object : BluetoothGattCallback() {
|
val callback = object : BluetoothGattCallback() {
|
||||||
|
|
||||||
override fun onConnectionStateChange(
|
override fun onConnectionStateChange(
|
||||||
gatt: BluetoothGatt?,
|
gatt: BluetoothGatt,
|
||||||
status: Int,
|
status: Int,
|
||||||
newState: Int
|
newState: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
Log.d("write", "onConnectionStateChange $newState")
|
||||||
|
|
||||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
||||||
|
|
@ -668,7 +719,13 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
Manifest.permission.BLUETOOTH_CONNECT
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
gatt?.discoverServices()
|
gatt.discoverServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(newState == BluetoothProfile.STATE_DISCONNECTED && status == BluetoothGatt.GATT_FAILURE){
|
||||||
|
bleGatt?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -676,14 +733,14 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServicesDiscovered(
|
override fun onServicesDiscovered(
|
||||||
gatt: BluetoothGatt?,
|
gatt: BluetoothGatt,
|
||||||
status: Int
|
status: Int
|
||||||
) {
|
) {
|
||||||
super.onServicesDiscovered(gatt, status)
|
super.onServicesDiscovered(gatt, status)
|
||||||
|
Log.d("write", "onServicesDiscovered $status")
|
||||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
|
||||||
gatt?.services?.firstOrNull { service ->
|
gatt.services?.firstOrNull { service ->
|
||||||
service.uuid == serviceId
|
service.uuid == serviceId
|
||||||
}?.characteristics?.firstOrNull { characteristic ->
|
}?.characteristics?.firstOrNull { characteristic ->
|
||||||
characteristic.uuid == characteristicId
|
characteristic.uuid == characteristicId
|
||||||
|
|
@ -712,10 +769,13 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
override fun onCharacteristicWrite(
|
override fun onCharacteristicWrite(
|
||||||
gatt: BluetoothGatt,
|
gatt: BluetoothGatt,
|
||||||
characteristic: BluetoothGattCharacteristic?,
|
characteristic: BluetoothGattCharacteristic,
|
||||||
status: Int
|
status: Int
|
||||||
) {
|
) {
|
||||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||||
|
|
||||||
|
Log.d("write", "onCharacteristicWrite $status")
|
||||||
|
|
||||||
if (ActivityCompat.checkSelfPermission(
|
if (ActivityCompat.checkSelfPermission(
|
||||||
app,
|
app,
|
||||||
Manifest.permission.BLUETOOTH_CONNECT
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
|
@ -723,7 +783,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
gatt.disconnect()
|
gatt.close()
|
||||||
it.resume(Unit)
|
it.resume(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -731,7 +791,63 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.connectGatt(app, true, callback)
|
bleGatt = device.connectGatt(app, true, callback)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun findDeviceBySerial(serial: String): Result<BluetoothDevice, BleException> = suspendCancellableCoroutine {
|
||||||
|
|
||||||
|
val bleCallback = object : ScanCallback() {
|
||||||
|
|
||||||
|
override fun onScanResult(
|
||||||
|
callbackType: Int,
|
||||||
|
result: ScanResult
|
||||||
|
) {
|
||||||
|
|
||||||
|
super.onScanResult(callbackType, result)
|
||||||
|
|
||||||
|
if(it.isActive) {
|
||||||
|
|
||||||
|
if (ActivityCompat.checkSelfPermission(
|
||||||
|
app,
|
||||||
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
it.resume(Result.success(result.device))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
it.resume(
|
||||||
|
Result.failure(BleException.PermissionDenied)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val bleScanner =
|
||||||
|
app.getSystemService(BluetoothManager::class.java).adapter.bluetoothLeScanner
|
||||||
|
|
||||||
|
bleScanner.startScan(
|
||||||
|
listOf(ScanFilter.Builder().setDeviceAddress(serial).build()),
|
||||||
|
ScanSettings.Builder()
|
||||||
|
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||||
|
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
|
||||||
|
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
|
||||||
|
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
|
||||||
|
.setReportDelay(400L)
|
||||||
|
.build(),
|
||||||
|
bleCallback)
|
||||||
|
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
bleScanner.stopScan(bleCallback)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,6 @@ sealed class BleException {
|
||||||
|
|
||||||
object PermissionDenied : BleException()
|
object PermissionDenied : BleException()
|
||||||
|
|
||||||
|
object UnexpectedResponse : BleException()
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue