This commit is contained in:
Vineyro 2023-04-03 15:19:12 +07:00
parent 806ceb1fa8
commit 4226f18d96
10 changed files with 366 additions and 181 deletions

View File

@ -0,0 +1,32 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -5,18 +5,20 @@ import llc.arma.ble.app.ui.common.ViewSideEffect
import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.app.ui.common.ViewState
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
class BleListContract { class BleListContract {
sealed class Event : ViewEvent { sealed class Event : ViewEvent {
data class OnConnectToBle( data class OnConnectToBle(
val ble: BleInfo val bleAddress: String
) : Event() ) : Event()
} }
data class State( data class State(
val connectedBleList: List<ConnectedBleInfo>,
val bleList: List<BleInfo> val bleList: List<BleInfo>
) : ViewState ) : ViewState

View File

@ -1,5 +1,6 @@
package llc.arma.ble.app.ui.screen.ble package llc.arma.ble.app.ui.screen.ble
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@ -7,10 +8,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.BatteryFull import androidx.compose.material.icons.rounded.*
import androidx.compose.material.icons.rounded.NetworkCell
import androidx.compose.material.icons.rounded.Nfc
import androidx.compose.material.icons.rounded.Thermostat
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -24,6 +22,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
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.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -53,7 +52,9 @@ fun BleListScreen(
if(state.bleList.isEmpty()){ if(state.bleList.isEmpty()){
LinearProgressIndicator( LinearProgressIndicator(
strokeCap = StrokeCap.Round, strokeCap = StrokeCap.Round,
modifier = Modifier.fillMaxWidth().height(3.dp) modifier = Modifier
.fillMaxWidth()
.height(3.dp)
) )
} }
@ -62,12 +63,20 @@ fun BleListScreen(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
items(items = state.connectedBleList){
ConnectedBleItem(ble = it) {
viewModel.setEvent(BleListContract.Event.OnConnectToBle(it.serial))
}
}
items(items = state.bleList) { items(items = state.bleList) {
BleItem( BleItem(
ble = it, ble = it,
onClick = { onClick = {
viewModel.setEvent(BleListContract.Event.OnConnectToBle(it)) viewModel.setEvent(BleListContract.Event.OnConnectToBle(it.serial))
} }
) )
@ -181,3 +190,61 @@ private fun BleItem(
} }
} }
@Composable
private fun ConnectedBleItem(
ble: ConnectedBleInfo,
onClick: () -> Unit
){
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.clickable { onClick() }
.background(MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = .99f))
.padding(vertical = 8.dp, horizontal = 16.dp)
) {
ItemIcon {
Icon(
modifier = Modifier.align(Alignment.Center),
imageVector = Icons.Rounded.Link,
contentDescription = null
)
}
Column {
Text(text = ble.name)
Text(
style = MaterialTheme.typography.bodyMedium,
text = ble.serial
)
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.alpha(0.7f)
) {
Text(
style = MaterialTheme.typography.bodyMedium,
text = "Соединено"
)
}
}
}
}
}

View File

@ -8,11 +8,13 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.domain.usecase.GetBleAroundFlow import llc.arma.ble.domain.usecase.GetBleAroundFlow
import llc.arma.ble.domain.usecase.GetConnectedBleDevices
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class BleListViewModel @Inject constructor( class BleListViewModel @Inject constructor(
getBleAroundFlow: GetBleAroundFlow getBleAroundFlow: GetBleAroundFlow,
getConnectedBleDevices: GetConnectedBleDevices
) : BaseViewModel<BleListContract.State, BleListContract.Event, BleListContract.Effect>() { ) : BaseViewModel<BleListContract.State, BleListContract.Event, BleListContract.Effect>() {
init { init {
@ -21,7 +23,10 @@ class BleListViewModel @Inject constructor(
it.fold( it.fold(
onSuccess = { onSuccess = {
setState { setState {
BleListContract.State(it) BleListContract.State(
connectedBleList = emptyList(),
bleList = it
)
} }
}, },
onFailure = { onFailure = {
@ -33,7 +38,7 @@ class BleListViewModel @Inject constructor(
} }
override fun setInitialState(): BleListContract.State = BleListContract.State(emptyList()) override fun setInitialState(): BleListContract.State = BleListContract.State(emptyList(), emptyList())
override fun handleEvents(event: BleListContract.Event) { override fun handleEvents(event: BleListContract.Event) {
when(event){ when(event){
@ -46,7 +51,7 @@ class BleListViewModel @Inject constructor(
event: BleListContract.Event.OnConnectToBle event: BleListContract.Event.OnConnectToBle
) { ) {
setEffect { setEffect {
BleListContract.Effect.Navigation.NavigateToBle(serial = event.ble.serial) BleListContract.Effect.Navigation.NavigateToBle(serial = event.bleAddress)
} }
} }

View File

@ -61,7 +61,7 @@ fun TemperatureHistory(
val viewModel = hiltViewModel<TemperatureHistoryViewModel>() val viewModel = hiltViewModel<TemperatureHistoryViewModel>()
val state = viewModel.viewState.value val state = viewModel.viewState.value
LaunchedEffect("ble.serial") { LaunchedEffect(ble.serial) {
viewModel.setEvent(TemperatureHistoryContract.Event.OnStart(ble.serial)) viewModel.setEvent(TemperatureHistoryContract.Event.OnStart(ble.serial))
} }
@ -138,6 +138,15 @@ fun Display(
is ProgressState.Finished -> { is ProgressState.Finished -> {
if(state.loadingHistoryState.data.isEmpty()){
Text(
modifier = Modifier.align(Alignment.Center),
text = "Нет данных"
)
} else {
val producer = remember(state.loadingHistoryState.data) { val producer = remember(state.loadingHistoryState.data) {
state.loadingHistoryState.data.mapIndexed { index, measurePoint -> state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
TemperatureEntry(measurePoint.date, index.toFloat(), measurePoint.value) TemperatureEntry(measurePoint.date, index.toFloat(), measurePoint.value)
@ -148,8 +157,8 @@ fun Display(
val axisValueFormatter = val axisValueFormatter =
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues -> AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
(chartValues.chartEntryModel.entries.first() (chartValues.chartEntryModel.entries.firstOrNull()
.getOrNull(value.toInt()) as? TemperatureEntry) ?.getOrNull(value.toInt()) as? TemperatureEntry)
?.localDate ?.localDate
?.let { formatter.format(Date(it)) } ?.let { formatter.format(Date(it)) }
.orEmpty() .orEmpty()
@ -177,6 +186,8 @@ fun Display(
} }
} }
}
is ProgressState.Indeterminate -> { is ProgressState.Indeterminate -> {
CircularProgressIndicator( CircularProgressIndicator(

View File

@ -21,6 +21,7 @@ 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.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.repository.BleRepository
import llc.arma.ble.domain.usecase.GetBleBySerial import llc.arma.ble.domain.usecase.GetBleBySerial
import java.nio.charset.Charset import java.nio.charset.Charset
@ -84,15 +85,31 @@ class BleRepositoryImpl @Inject constructor(
private val deviceCache = mutableMapOf<String, ScanResult>() private val deviceCache = mutableMapOf<String, ScanResult>()
val resultList = mutableMapOf<String, BleInfo>() val resultList = mutableMapOf<String, BleInfo>()
override fun getConnectedBle(): List<ConnectedBleInfo> {
if(checkPermission()) {
return app.getSystemService(BluetoothManager::class.java)
.getConnectedDevices(BluetoothProfile.GATT)
.filter {
it.name.contains("arma", true)
}
.map {
ConnectedBleInfo(
name = it.name,
serial = it.address
)
}
}else{
return emptyList()
}
}
override fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>> { override fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>> {
return if( return if(checkPermission()){
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission(
app,
Manifest.permission.BLUETOOTH_SCAN
) == PackageManager.PERMISSION_GRANTED
){
callbackFlow { callbackFlow {
@ -105,11 +122,7 @@ class BleRepositoryImpl @Inject constructor(
super.onScanResult(callbackType, result) super.onScanResult(callbackType, result)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
if (result.scanRecord?.deviceName?.contains("ArmA") == true) { if (result.scanRecord?.deviceName?.contains("ArmA") == true) {
@ -177,11 +190,7 @@ class BleRepositoryImpl @Inject constructor(
deviceCache[serial]?.let { result -> deviceCache[serial]?.let { result ->
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
if (it.isActive) { if (it.isActive) {
@ -334,11 +343,7 @@ class BleRepositoryImpl @Inject constructor(
deviceCache[serial]?.device?.let { deviceCache[serial]?.device?.let {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt = it.connectGatt(app, false, ReadHistoryCallback(app) { gatt = it.connectGatt(app, false, ReadHistoryCallback(app) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -533,8 +538,7 @@ class BleRepositoryImpl @Inject constructor(
characteristicId: UUID characteristicId: UUID
): Result<ByteArray, BleException> = suspendCancellableCoroutine { ): Result<ByteArray, BleException> = suspendCancellableCoroutine {
var result: ByteArray? = null var result: ByteArray?
var bleGatt: BluetoothGatt? = null
val callback = object : BluetoothGattCallback() { val callback = object : BluetoothGattCallback() {
@ -550,11 +554,7 @@ class BleRepositoryImpl @Inject constructor(
if (newState == BluetoothProfile.STATE_CONNECTED) { if (newState == BluetoothProfile.STATE_CONNECTED) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.discoverServices() gatt.discoverServices()
@ -590,11 +590,7 @@ class BleRepositoryImpl @Inject constructor(
characteristic.uuid == characteristicId characteristic.uuid == characteristicId
}?.let { char -> }?.let { char ->
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.readCharacteristic(char) gatt.readCharacteristic(char)
@ -616,24 +612,17 @@ class BleRepositoryImpl @Inject constructor(
} }
@Deprecated("Deprecated in Java")
override fun onCharacteristicRead( override fun onCharacteristicRead(
gatt: BluetoothGatt, gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
status: Int status: Int
) { ) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
super.onCharacteristicRead(gatt, characteristic, status) super.onCharacteristicRead(gatt, characteristic, status)
onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status)
result = characteristic.value
if (result != null) {
it.resume(Result.success(result!!))
} else {
bleGatt?.close()
it.resume(Result.failure(BleException.UnexpectedResponse))
} }
gatt.close()
} }
override fun onCharacteristicRead( override fun onCharacteristicRead(
@ -643,16 +632,21 @@ class BleRepositoryImpl @Inject constructor(
status: Int status: Int
) { ) {
super.onCharacteristicRead(gatt, characteristic, value, status) super.onCharacteristicRead(gatt, characteristic, value, status)
onCommonCharacteristicRead(gatt, characteristic, value, status)
}
fun onCommonCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int
) {
Log.d("read", "onCharacteristicRead $status") Log.d("read", "onCharacteristicRead $status")
if(status == BluetoothGatt.GATT_SUCCESS) { if(status == BluetoothGatt.GATT_SUCCESS) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.close() gatt.close()
result = value result = value
@ -673,11 +667,8 @@ class BleRepositoryImpl @Inject constructor(
} }
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app, device.connectGatt(app, false, callback)
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED) {
bleGatt = device.connectGatt(app, false, callback)
} else { } else {
it.resume(Result.failure(BleException.PermissionDenied)) it.resume(Result.failure(BleException.PermissionDenied))
} }
@ -707,11 +698,7 @@ class BleRepositoryImpl @Inject constructor(
if (newState == BluetoothProfile.STATE_CONNECTED) { if (newState == BluetoothProfile.STATE_CONNECTED) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.discoverServices() gatt.discoverServices()
} else { } else {
@ -751,11 +738,7 @@ class BleRepositoryImpl @Inject constructor(
characteristic.uuid == characteristicId characteristic.uuid == characteristicId
}?.let { char -> }?.let { char ->
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.writeCharacteristic(char, writeData) gatt.writeCharacteristic(char, writeData)
@ -788,11 +771,7 @@ class BleRepositoryImpl @Inject constructor(
Log.d("write", "onCharacteristicWrite $status") Log.d("write", "onCharacteristicWrite $status")
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.close() gatt.close()
@ -816,10 +795,7 @@ class BleRepositoryImpl @Inject constructor(
} }
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED) {
bleGatt = device.connectGatt(app, false, callback) bleGatt = device.connectGatt(app, false, callback)
@ -831,12 +807,27 @@ class BleRepositoryImpl @Inject constructor(
} }
} fun checkPermission(): Boolean {
fun BluetoothGatt.writeCharacteristic( 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
}
}
fun BluetoothGatt.writeCharacteristic(
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
data: ByteArray data: ByteArray
){ ): Result<Unit, BleException>{
return if(checkPermission()){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
@ -845,4 +836,12 @@ fun BluetoothGatt.writeCharacteristic(
writeCharacteristic(characteristic) writeCharacteristic(characteristic)
} }
Result.success(Unit)
} else {
Result.failure(BleException.PermissionDenied)
}
}
} }

View File

@ -47,11 +47,7 @@ class ReadHistoryCallback(
if(newState == BluetoothGatt.STATE_CONNECTED){ if(newState == BluetoothGatt.STATE_CONNECTED){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.discoverServices() gatt.discoverServices()
} else { } else {
@ -77,11 +73,7 @@ class ReadHistoryCallback(
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
gatt.getService(serviceUUID)?.getCharacteristic(temperatureHistoryReadUUID)?.let { gatt.getService(serviceUUID)?.getCharacteristic(temperatureHistoryReadUUID)?.let {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
readProperty = Property.DATA_SIZE readProperty = Property.DATA_SIZE
gatt.writeCharacteristic(it, byteArrayOf(2)) gatt.writeCharacteristic(it, byteArrayOf(2))
@ -114,9 +106,11 @@ class ReadHistoryCallback(
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
status: Int status: Int
) { ) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
super.onCharacteristicRead(gatt, characteristic, status) super.onCharacteristicRead(gatt, characteristic, status)
onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status) onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status)
} }
}
override fun onCharacteristicRead( override fun onCharacteristicRead(
gatt: BluetoothGatt, gatt: BluetoothGatt,
@ -125,7 +119,7 @@ class ReadHistoryCallback(
status: Int status: Int
) { ) {
super.onCharacteristicRead(gatt, characteristic, value, status) super.onCharacteristicRead(gatt, characteristic, value, status)
//onCommonCharacteristicRead(gatt, characteristic, value, status) onCommonCharacteristicRead(gatt, characteristic, value, status)
} }
private fun onCommonCharacteristicRead( private fun onCommonCharacteristicRead(
@ -137,6 +131,19 @@ class ReadHistoryCallback(
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
when(readProperty){ when(readProperty){
Property.DATA_SIZE -> { Property.DATA_SIZE -> {
if(value.contentEquals(byteArrayOf(0, 0))) {
onResult(
Result.success(
ProgressState.Finished(
emptyList()
)
)
)
gatt.close()
} else {
val writeData = mutableListOf( val writeData = mutableListOf(
1.toByte(), 1.toByte(),
0.toByte(), 0.toByte(),
@ -147,6 +154,8 @@ class ReadHistoryCallback(
readProperty = Property.PACKAGE readProperty = Property.PACKAGE
gatt.writeCharacteristic(characteristic, writeData) gatt.writeCharacteristic(characteristic, writeData)
}
} }
Property.PACKAGE -> { Property.PACKAGE -> {
@ -177,11 +186,7 @@ class ReadHistoryCallback(
if(nextPackageDataCount != 0.toUByte()){ if(nextPackageDataCount != 0.toUByte()){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.writeCharacteristic(characteristic, byteArrayOf(5)) gatt.writeCharacteristic(characteristic, byteArrayOf(5))
gatt.readCharacteristic(characteristic) gatt.readCharacteristic(characteristic)
@ -209,9 +214,9 @@ class ReadHistoryCallback(
gatt.close() gatt.close()
} }
} } else {
if(value[0] == 251.toByte()) { if (value[0] == 251.toByte()) {
val nextPackageDataCount = value[1].toUByte() val nextPackageDataCount = value[1].toUByte()
val temperatureDataArray = value.toList().subList(2, value.size) val temperatureDataArray = value.toList().subList(2, value.size)
@ -224,7 +229,7 @@ class ReadHistoryCallback(
onResult(Result.success(ProgressState.Progress(expectedDataSize!!.toFloat() / resultTemperaturePackage.size.toFloat()))) onResult(Result.success(ProgressState.Progress(expectedDataSize!!.toFloat() / resultTemperaturePackage.size.toFloat())))
if(nextPackageDataCount != 0.toUByte()){ if (nextPackageDataCount != 0.toUByte()) {
val writeData = byteArrayOf(5) val writeData = byteArrayOf(5)
@ -246,6 +251,11 @@ class ReadHistoryCallback(
) )
gatt.close() gatt.close()
} }
} else {
onResult(Result.failure(BleException.UnexpectedResponse))
gatt.close()
}
} }
} }
else -> { else -> {
@ -267,11 +277,7 @@ class ReadHistoryCallback(
super.onCharacteristicWrite(gatt, characteristic, status) super.onCharacteristicWrite(gatt, characteristic, status)
if(status == BluetoothGatt.GATT_SUCCESS){ if(status == BluetoothGatt.GATT_SUCCESS){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || ActivityCompat.checkSelfPermission( if (checkPermission()) {
app,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
) {
gatt.readCharacteristic(characteristic) gatt.readCharacteristic(characteristic)
@ -289,4 +295,41 @@ class ReadHistoryCallback(
} }
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
}
}
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.value = data
writeCharacteristic(characteristic)
}
Result.success(Unit)
} else {
Result.failure(BleException.PermissionDenied)
}
}
} }

View File

@ -0,0 +1,8 @@
package llc.arma.ble.domain.model
import java.util.UUID
class ConnectedBleInfo(
val name: String,
val serial: String,
)

View File

@ -6,10 +6,13 @@ 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.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.usecase.GetBleBySerial import llc.arma.ble.domain.usecase.GetBleBySerial
interface BleRepository { interface BleRepository {
fun getConnectedBle(): List<ConnectedBleInfo>
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>> fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
suspend fun getBleBySerial(serial: String): Result<Ble, GetBleBySerial.GetBleException> suspend fun getBleBySerial(serial: String): Result<Ble, GetBleBySerial.GetBleException>

View File

@ -0,0 +1,15 @@
package llc.arma.ble.domain.usecase
import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject
class GetConnectedBleDevices @Inject constructor(
private val bleRepository: BleRepository
) {
operator fun invoke(): List<ConnectedBleInfo>{
return bleRepository.getConnectedBle()
}
}