Поправки по графикам

This commit is contained in:
Vineyro 2023-07-28 16:57:05 +07:00
parent 62205d27a0
commit bd10b5b6b6
10 changed files with 399 additions and 264 deletions

View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View File

@ -13,8 +13,8 @@ android {
applicationId "llc.arma.ble" applicationId "llc.arma.ble"
minSdk 24 minSdk 24
targetSdk 33 targetSdk 33
versionCode 4 versionCode 5
versionName "1.2.1" versionName "1.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@ -1,6 +1,8 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer package llc.arma.ble.app.ui.screen.inspection.accelerometer
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -32,6 +34,7 @@ enum class SheetPage {
MEASURE, POWER, WRITE, HISTORY, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT MEASURE, POWER, WRITE, HISTORY, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT
} }
@OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun AccelerometerScreen( fun AccelerometerScreen(
ble: Ble.Accelerometer, ble: Ble.Accelerometer,
@ -42,6 +45,7 @@ fun AccelerometerScreen(
val bottomDialog = rememberBottomDialogState() val bottomDialog = rememberBottomDialogState()
LaunchedEffect(ble){ LaunchedEffect(ble){
viewModel.setEvent(AccelerometerContract.Event.OnBleChanged(ble)) viewModel.setEvent(AccelerometerContract.Event.OnBleChanged(ble))
} }
@ -50,6 +54,15 @@ fun AccelerometerScreen(
mutableStateOf<SheetPage?>(null) mutableStateOf<SheetPage?>(null)
} }
LaunchedEffect(
key1 = bottomDialog.sheetState?.currentValue,
block = {
if(bottomDialog.sheetState?.currentValue == ModalBottomSheetValue.Hidden) {
sheetPage = null
}
}
)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
LaunchedEffect(sheetPage) { LaunchedEffect(sheetPage) {

View File

@ -86,47 +86,6 @@ fun AccelSpectreEdit(
modifier = Modifier modifier = Modifier
) { ) {
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnAccelViewModeEdit(next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE))
}
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Accel view mode"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = accelMode.localized
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowDown,
contentDescription = null
)
}
}
Box( Box(
modifier = Modifier.padding( modifier = Modifier.padding(
vertical = 8.dp, vertical = 8.dp,

View File

@ -33,6 +33,9 @@ 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
import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.ChartEntry
import com.patrykandpatrick.vico.core.entry.FloatEntry
import com.patrykandpatrick.vico.core.extension.sumByFloat
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.common.ProgressState
@ -82,23 +85,67 @@ fun AccelerometerHistory(
val title = when(state){ val title = when(state){
is AccelerometerHistoryContract.State.Display -> { is AccelerometerHistoryContract.State.Display -> {
when (state.loadingHistoryState) { if (state.previousHistory !== null) {
is ProgressState.Finished -> "${fftMode.localized} (${state.loadingHistoryState.data.size})" "${fftMode.localized} (${state.previousHistory.size})"
else -> fftMode.localized }else {
fftMode.localized
} }
} }
AccelerometerHistoryContract.State.Exception -> fftMode.localized AccelerometerHistoryContract.State.Exception -> fftMode.localized
} }
Text( Row(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(end = 16.dp),
text = title, text = title,
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge
) )
if(state is AccelerometerHistoryContract.State.Display) {
when (state.loadingHistoryState) {
is ProgressState.Indeterminate -> {
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
strokeCap = StrokeCap.Round,
)
}
is ProgressState.Progress -> {
val progressAnimDuration = 1500
val progressAnimation by animateFloatAsState(
targetValue = state.loadingHistoryState.value,
animationSpec = tween(
durationMillis = progressAnimDuration,
easing = FastOutSlowInEasing
)
)
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
strokeCap = StrokeCap.Round,
progress = progressAnimation,
)
}
else -> {}
}
}
}
IconButton( IconButton(
onClick = { onClick = {
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale)) viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale))
}, },
enabled = when(state){ enabled = when(state){
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
@ -128,6 +175,13 @@ fun AccelerometerHistory(
} }
val axisValueFormatter = AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
(chartValues.chartEntryModel.entries.firstOrNull()
?.getOrNull(value.toInt()) as? AccelerometerEntry)
?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) }
.orEmpty()
}
@Composable @Composable
fun Display( fun Display(
@ -139,11 +193,19 @@ fun Display(
.fillMaxSize() .fillMaxSize()
) { ) {
when (state.loadingHistoryState) { val data = if(state.loadingHistoryState is ProgressState.Finished){
state.loadingHistoryState.data
} else {
state.previousHistory
}
is ProgressState.Finished -> { val producer = remember {
ChartEntryModelProducer(listOf<FloatEntry>())
}
if(state.loadingHistoryState.data.isEmpty()){ if(data != null){
if(data.isEmpty()){
Text( Text(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
@ -152,39 +214,22 @@ fun Display(
} else { } else {
val producer = remember(state.loadingHistoryState.data) { LaunchedEffect(data){
state.loadingHistoryState.data.mapIndexed { index, measurePoint -> producer.setEntries(
data.mapIndexed { index, measurePoint ->
AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value) AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value)
}.let {
ChartEntryModelProducer(it)
} }
} )
val axisValueFormatter =
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
(chartValues.chartEntryModel.entries.firstOrNull()
?.getOrNull(value.toInt()) as? AccelerometerEntry)
?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) }
.orEmpty()
} }
val lineChart = columnChart( val lineChart = columnChart(
spacing = 1.5.dp spacing = 1.5.dp
) )
LaunchedEffect(state.loadingHistoryState.data){
lineChart.bounds
lineChart.setBounds(
left = lineChart.bounds.left / 20,
top = lineChart.bounds.top,
right = lineChart.bounds.right / 20,
bottom = lineChart.bounds.bottom,
)
}
val scrollState = rememberChartScrollState() val scrollState = rememberChartScrollState()
Chart( Chart(
diffAnimationSpec = tween(0),
isZoomEnabled = true, isZoomEnabled = true,
chartScrollState = scrollState, chartScrollState = scrollState,
chart = lineChart, chart = lineChart,
@ -199,13 +244,10 @@ fun Display(
) )
/*LaunchedEffect(scrollState.maxValue) {
scrollState.scrollBy(scrollState.maxValue)
}*/
} }
} } else {
when (state.loadingHistoryState) {
is ProgressState.Indeterminate -> { is ProgressState.Indeterminate -> {
CircularProgressIndicator( CircularProgressIndicator(
@ -232,7 +274,8 @@ fun Display(
) )
} }
else -> {}
}
} }
} }
@ -286,6 +329,7 @@ class AccelerometerHistoryContract {
sealed class State : ViewState { sealed class State : ViewState {
data class Display( data class Display(
val previousHistory : List<Ble.Accelerometer.MeasurePoint>?,
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>> val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>>
) : State() ) : State()
@ -306,10 +350,12 @@ class AccelerometerHistoryViewModel @Inject constructor(
private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial
) : BaseViewModel<AccelerometerHistoryContract.State, AccelerometerHistoryContract.Event, AccelerometerHistoryContract.Effect>() { ) : BaseViewModel<AccelerometerHistoryContract.State, AccelerometerHistoryContract.Event, AccelerometerHistoryContract.Effect>() {
private var job: Job? = null
private var lastSerial: String? = null private var lastSerial: String? = null
override fun setInitialState() = AccelerometerHistoryContract.State.Display( override fun setInitialState() = AccelerometerHistoryContract.State.Display(
ProgressState.Indeterminate loadingHistoryState = ProgressState.Indeterminate,
previousHistory = null
) )
override fun handleEvents(event: AccelerometerHistoryContract.Event) { override fun handleEvents(event: AccelerometerHistoryContract.Event) {
@ -323,6 +369,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
state: AccelerometerHistoryContract.State, state: AccelerometerHistoryContract.State,
event: AccelerometerHistoryContract.Event.OnStart event: AccelerometerHistoryContract.Event.OnStart
) { ) {
viewModelScope.launch { viewModelScope.launch {
if(state is AccelerometerHistoryContract.State.Display) { if(state is AccelerometerHistoryContract.State.Display) {
@ -332,10 +379,28 @@ class AccelerometerHistoryViewModel @Inject constructor(
lastSerial = event.serial lastSerial = event.serial
setState { setState {
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate) AccelerometerHistoryContract.State.Display(
loadingHistoryState = ProgressState.Indeterminate,
previousHistory = when(state.loadingHistoryState){
is ProgressState.Finished -> state.loadingHistoryState.data
is ProgressState.Indeterminate -> null
is ProgressState.Progress -> null
}
)
} }
getAccelerometerSpectreBySerial( } else {
setState {
AccelerometerHistoryContract.State.Display(
loadingHistoryState = ProgressState.Indeterminate,
previousHistory = null
)
}
}
job?.cancel()
job = getAccelerometerSpectreBySerial(
serial = event.serial, serial = event.serial,
accelMode = event.accelMode, accelMode = event.accelMode,
fftAxis = event.fftAxis, fftAxis = event.fftAxis,
@ -343,10 +408,29 @@ class AccelerometerHistoryViewModel @Inject constructor(
frequency = event.frequency, frequency = event.frequency,
accelScale = event.accelScale accelScale = event.accelScale
).onEach { ).onEach {
val currentState = viewState.value
Log.d("currentState", currentState.toString())
if(currentState is AccelerometerHistoryContract.State.Display) {
it.fold( it.fold(
onSuccess = { onSuccess = {
setState { setState {
AccelerometerHistoryContract.State.Display(it)
AccelerometerHistoryContract.State.Display(
loadingHistoryState = it,
previousHistory = when (it) {
is ProgressState.Finished -> {
it.data
}
is ProgressState.Indeterminate -> currentState.previousHistory
is ProgressState.Progress -> currentState.previousHistory
}
)
} }
}, },
onFailure = { onFailure = {
@ -355,12 +439,11 @@ class AccelerometerHistoryViewModel @Inject constructor(
} }
} }
) )
}.launchIn(this)
//}
} }
}.launchIn(this)
} }
} }
@ -369,7 +452,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
state: AccelerometerHistoryContract.State, state: AccelerometerHistoryContract.State,
event: AccelerometerHistoryContract.Event.OnRefreshHistory event: AccelerometerHistoryContract.Event.OnRefreshHistory
) { ) {
viewModelScope.launch { /*viewModelScope.launch {
setState { setState {
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate) AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate)
@ -397,7 +480,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
) )
}.launchIn(this) }.launchIn(this)
} }*/
} }
} }

View File

@ -381,7 +381,7 @@ class AccelerometerMeasureViewModel @Inject constructor(
z = ((9806.65f / (Short.MAX_VALUE / 2)) * it.z) * (accelScale.k / 2) z = ((9806.65f / (Short.MAX_VALUE / 2)) * it.z) * (accelScale.k / 2)
) )
) )
} }.takeLast(10)
AccelerometerMeasureContract.State.Display(dataList) AccelerometerMeasureContract.State.Display(dataList)
} }

View File

@ -579,63 +579,9 @@ class BleRepositoryImpl @Inject constructor(
if (checkPermission()) { if (checkPermission()) {
writeCharacteristic( gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback(
device = it, app, accelScale, accelMode, fftAxis, fftMode, frequency
serviceId = serviceUUID, ) {
characteristicId = accelerometerReadUUID,
writeData = byteArrayOf(
4,
accelMode.sendData,
accelScale.sendData,
fftMode.sendData,
fftAxis.sendData,
frequency.sendData,
1
)
).onFailure {
CoroutineScope(Dispatchers.IO).launch {
send(Result.failure(BleException.PermissionDenied))
}
}
var result = false
while (result.not()){
writeCharacteristic(
device = it,
serviceId = serviceUUID,
characteristicId = accelerometerReadUUID,
writeData = byteArrayOf(4, 0)
).onFailure {
CoroutineScope(Dispatchers.IO).launch {
send(Result.failure(BleException.PermissionDenied))
}
}
readCharacteristic(
device = it,
serviceId = serviceUUID,
characteristicId = accelerometerReadUUID
).fold(
onSuccess = { readData ->
Log.d("read", readData.joinToString(", "))
if(readData.isNotEmpty() && readData.first() == (0).toByte()){
result = true
} else {
delay(200)
}
},
onFailure = {
CoroutineScope(Dispatchers.IO).launch {
send(Result.failure(BleException.PermissionDenied))
}
}
)
}
gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback(app) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
send(it) send(it)
} }
@ -919,8 +865,6 @@ class BleRepositoryImpl @Inject constructor(
) { ) {
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 ->
@ -1068,8 +1012,6 @@ class BleRepositoryImpl @Inject constructor(
) { ) {
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 ->
@ -1114,8 +1056,6 @@ class BleRepositoryImpl @Inject constructor(
) { ) {
super.onCharacteristicWrite(gatt, characteristic, status) super.onCharacteristicWrite(gatt, characteristic, status)
Log.d("write", "onCharacteristicWrite $status")
if (checkPermission()) { if (checkPermission()) {
gatt.close() gatt.close()

View File

@ -67,6 +67,8 @@ class ReadAccelerometerCallback(
) { ) {
super.onServicesDiscovered(gatt, status) super.onServicesDiscovered(gatt, status)
Log.d("accel", "onServicesDiscovered")
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
@ -80,11 +82,9 @@ class ReadAccelerometerCallback(
fftMode.sendData, fftMode.sendData,
fftAxis.sendData, fftAxis.sendData,
frequency.sendData, frequency.sendData,
2 1
) )
Log.d("payload", payload.joinToString(" - "))
gatt.writeCharacteristic(it, payload) gatt.writeCharacteristic(it, payload)
} else { } else {
@ -107,14 +107,14 @@ class ReadAccelerometerCallback(
) { ) {
super.onCharacteristicWrite(gatt, characteristic, status) super.onCharacteristicWrite(gatt, characteristic, status)
Log.d("accel", "request written")
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
if (checkPermission()) { if (checkPermission()) {
Log.d("write", "enable notifications")
gatt.setCharacteristicNotification(it, true) gatt.setCharacteristicNotification(it, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@ -162,6 +162,8 @@ class ReadAccelerometerCallback(
value: ByteArray, value: ByteArray,
){ ){
Log.d("accel", "notification")
val data = value.toList().chunked(2).map { val data = value.toList().chunked(2).map {
it.toByteArray().get2byteShortAt(0) it.toByteArray().get2byteShortAt(0)
} }

View File

@ -5,6 +5,7 @@ import android.app.Application
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
@ -13,8 +14,14 @@ import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder.LITTLE_ENDIAN import java.nio.ByteOrder.LITTLE_ENDIAN
import java.util.UUID
fun ByteArray.get2byteShortAt(idx: Int): Int { fun ByteArray.get2byteShortAt(idx: Int): Int {
val shorts = ShortArray(1) val shorts = ShortArray(1)
@ -22,11 +29,21 @@ fun ByteArray.get2byteShortAt(idx: Int): Int {
return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort() return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort()
} }
class ReadAccelerometerSpectreCallback( class ReadAccelerometerSpectreCallback(
private val app: Application, private val app: Application,
private val accelScale: AccelScale,
private val accelMode: AccelViewMode,
private val fftAxis: FftAxis,
private val fftMode: FftViewMode,
private val frequency: FftFrequency,
private val onResult: (Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>) -> Unit private val onResult: (Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>) -> Unit
) : BluetoothGattCallback() { ) : BluetoothGattCallback() {
enum class Property {
DATA_SIZE, PACKAGE
}
private fun ByteArray.get4byteUIntAt(idx: Int) = private fun ByteArray.get4byteUIntAt(idx: Int) =
((this[idx + 3].toUInt() and 0xFFu) shl 24) or ((this[idx + 3].toUInt() and 0xFFu) shl 24) or
((this[idx + 2].toUInt() and 0xFFu) shl 16) or ((this[idx + 2].toUInt() and 0xFFu) shl 16) or
@ -50,13 +67,17 @@ class ReadAccelerometerSpectreCallback(
) { ) {
super.onConnectionStateChange(gatt, status, newState) super.onConnectionStateChange(gatt, status, newState)
Log.d("spectre", "onConnectionStateChange")
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
if(newState == BluetoothGatt.STATE_CONNECTED){ if(newState == BluetoothGatt.STATE_CONNECTED){
if (checkPermission()) { if (checkPermission()) {
gatt.discoverServices() gatt.discoverServices()
} else { } else {
onResult(Result.failure(BleException.UnexpectedResponse)) onResult(Result.failure(BleException.UnexpectedResponse))
gatt.close() gatt.close()
@ -77,22 +98,41 @@ class ReadAccelerometerSpectreCallback(
status: Int status: Int
) { ) {
super.onServicesDiscovered(gatt, status) super.onServicesDiscovered(gatt, status)
Log.d("read", "discover ${status}") Log.d("spectre", "onServicesDiscovered")
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerHistoryReadUUID)?.let { enableNotifications(gatt)
}
}
private fun enableNotifications(
gatt: BluetoothGatt
){
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
if (checkPermission()) { if (checkPermission()) {
Log.d("read", "discovered") gatt.setCharacteristicNotification(it, true)
readProperty = Property.DATA_SIZE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
gatt.writeCharacteristic(it, byteArrayOf(2)) gatt.writeDescriptor(it.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")),
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
} else {
val descriptor = it.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(descriptor)
}
Log.d("spectre", "enable notification")
onResult(Result.success(ProgressState.Indeterminate))
resultAccelerometerPackage.clear()
} else { } else {
onResult(Result.failure(BleException.PermissionDenied)) onResult(Result.failure(BleException.PermissionDenied))
gatt.close() gatt.close()
} }
return return
@ -103,12 +143,7 @@ class ReadAccelerometerSpectreCallback(
} }
}
//private var lastMeasureSystemTime: Long? = null
private var initialValue: Long? = null private var initialValue: Long? = null
//private var bleRealTime: Long? = null
private var frequencyInterval: Long? = null private var frequencyInterval: Long? = null
private val resultAccelerometerPackage: MutableList<Float> = mutableListOf() private val resultAccelerometerPackage: MutableList<Float> = mutableListOf()
@ -127,6 +162,42 @@ class ReadAccelerometerSpectreCallback(
} }
} }
@Deprecated("Deprecated in Java")
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
super.onCharacteristicChanged(gatt, characteristic)
onCommonCharacteristicChanged(gatt, characteristic, characteristic.value)
}
}
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray
) {
super.onCharacteristicChanged(gatt, characteristic, value)
onCommonCharacteristicChanged(gatt, characteristic, value)
}
private fun onCommonCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
){
if(characteristic.uuid == accelerometerReadUUID) {
Log.d("spectre", "changed")
readProperty = Property.DATA_SIZE
gatt.getService(serviceUUID).getCharacteristic(accelerometerHistoryReadUUID)?.let {
gatt.writeCharacteristic(it, byteArrayOf(2))
}
}
}
override fun onCharacteristicRead( override fun onCharacteristicRead(
gatt: BluetoothGatt, gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
@ -144,6 +215,8 @@ class ReadAccelerometerSpectreCallback(
status: Int status: Int
){ ){
Log.d("spectre", "onCharacteristicRead")
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
when(readProperty){ when(readProperty){
Property.DATA_SIZE -> { Property.DATA_SIZE -> {
@ -209,6 +282,7 @@ class ReadAccelerometerSpectreCallback(
} }
} else { } else {
onResult( onResult(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
@ -221,7 +295,9 @@ class ReadAccelerometerSpectreCallback(
) )
) )
) )
gatt.close()
start(gatt)
} }
} else { } else {
@ -247,6 +323,7 @@ class ReadAccelerometerSpectreCallback(
gatt.readCharacteristic(characteristic) gatt.readCharacteristic(characteristic)
} else { } else {
onResult( onResult(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
@ -259,7 +336,9 @@ class ReadAccelerometerSpectreCallback(
) )
) )
) )
gatt.close()
start(gatt)
} }
} else { } else {
onResult(Result.failure(BleException.UnexpectedResponse)) onResult(Result.failure(BleException.UnexpectedResponse))
@ -285,6 +364,11 @@ class ReadAccelerometerSpectreCallback(
status: Int status: Int
) { ) {
super.onCharacteristicWrite(gatt, characteristic, status) super.onCharacteristicWrite(gatt, characteristic, status)
Log.d("spectre", "request written $readProperty")
if(readProperty !== null) {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
if (checkPermission()) { if (checkPermission()) {
@ -302,10 +386,66 @@ class ReadAccelerometerSpectreCallback(
onResult(Result.failure(BleException.UnexpectedResponse)) onResult(Result.failure(BleException.UnexpectedResponse))
gatt.close() gatt.close()
} }
}
} }
fun checkPermission(): Boolean { override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
super.onMtuChanged(gatt, mtu, status)
Log.d("spectre", "mtu $mtu")
}
override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
Log.d("spectre", "descriptor written")
super.onDescriptorWrite(gatt, descriptor, status)
start(gatt)
}
private fun start(
gatt: BluetoothGatt,
){
Log.d("spectre", "start")
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
if (checkPermission()) {
val payload = byteArrayOf(
4,
accelMode.sendData,
accelScale.sendData,
fftMode.sendData,
fftAxis.sendData,
frequency.sendData,
2
)
readProperty = null
gatt.writeCharacteristic(it, payload)
onResult(Result.success(ProgressState.Indeterminate))
resultAccelerometerPackage.clear()
} else {
onResult(Result.failure(BleException.PermissionDenied))
gatt.close()
}
return
}
}
private fun checkPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) ==
@ -320,7 +460,7 @@ class ReadAccelerometerSpectreCallback(
} }
} }
fun BluetoothGatt.writeCharacteristic( private fun BluetoothGatt.writeCharacteristic(
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
data: ByteArray data: ByteArray
): Result<Unit, BleException>{ ): Result<Unit, BleException>{

View File

@ -133,7 +133,6 @@ class ReadHistoryCallback(
value: ByteArray, value: ByteArray,
status: Int status: Int
){ ){
Log.d("read", value[0].toString())
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
when(readProperty){ when(readProperty){