Skip to content

Commit

Permalink
🐛: Fix update connected device
Browse files Browse the repository at this point in the history
Fix update connected device from previous commit
  • Loading branch information
EndikaCo committed Jan 9, 2024
1 parent 167fd69 commit eb57398
Show file tree
Hide file tree
Showing 16 changed files with 63 additions and 129 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
Bluetooth Ebike connector
=====

[![Android](https://img.shields.io/badge/Android-grey?style=&logo=android&logoColor=green)](https://www.android.com/)
[![KOTLIN](https://img.shields.io/badge/Kotlin-grey?style=none&logo=Kotlin&logoColor=-5C2D91)](https://kotlinlang.org/)
[![CI](https://github.com/EndikaCo/Bluetooth_bike/actions/workflows/testing.yml/badge.svg)]()
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/EndikaCo/Bluetooth_bike/LICENSE)
[![CI](https://github.com/EndikaCo/Bluetooth_bike/actions/workflows/testing.yml/badge.svg)]()

Simple app to connect to a Ebike via bluetooth, get the data from it, and send commands back to interact with it.

[![Compose](https://img.shields.io/badge/Jetpack_Compose-grey?style=none&logo=)](https://kotlinlang.org/)
[![Android](https://img.shields.io/badge/Android-grey?style=&logo=android&logoColor=green)](https://www.android.com/)
[![KOTLIN](https://img.shields.io/badge/Kotlin-grey?style=none&logo=Kotlin&logoColor=-5C2D91)](https://kotlinlang.org/)
[![Compose](https://img.shields.io/badge/Jetpack_Compose-blue?style=none&logo=)](https://kotlinlang.org/)
[![Bluetooth](https://img.shields.io/badge/bluetooth-grey?style=none&logo=bluetooth&logoColor=white)]()
[![Dagger](https://img.shields.io/badge/Dagger_Hilt-grey?style=&logo=)]()
[![MVVM](https://img.shields.io/badge/MVVM-grey?style=&logo=)]()
[![MVVM](https://img.shields.io/badge/MVVM-orange?style=&logo=)]()
[![State Flow](https://img.shields.io/badge/State_Flow-grey?style=&logo=)]()
[![Canvas](https://img.shields.io/badge/canvas-grey?style=none&logo=canvas&logoColor=white)]()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ class BluetoothController(
override val errors: SharedFlow<String>
get() = _errors.asSharedFlow()

private val _connectedDeviceName = MutableStateFlow("")
override val connectedDeviceName: StateFlow<String>
get() = _connectedDeviceName.asStateFlow()


private val scanDeviceReceiver = ScanDeviceReceiver { device ->
_devices.update { devices ->
val newDevice = device.toBtDeviceDomain(isPaired = false)
Expand Down Expand Up @@ -103,6 +108,7 @@ class BluetoothController(
private val bluetoothStateReceiver = BluetoothStateReceiver { isConnected, bluetoothDevice ->
if (bluetoothAdapter?.bondedDevices?.contains(bluetoothDevice) == true) {
_isConnected.update { isConnected }
_connectedDeviceName.update { if (isConnected) bluetoothDevice.name ?: "" else "Unknown" }
} else {
CoroutineScope(Dispatchers.IO).launch {
_errors.emit("Can't connect to a non-paired device.")
Expand Down Expand Up @@ -155,45 +161,6 @@ class BluetoothController(
bluetoothAdapter?.cancelDiscovery()
}

override fun startBluetoothServer(): Flow<ConnectionResult> {
return flow {
if (!hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
throw SecurityException("No BLUETOOTH_CONNECT permission")
}

currentServerSocket = bluetoothAdapter?.listenUsingRfcommWithServiceRecord(
"chat_service",
UUID.fromString(SERVICE_UUID)
)

var shouldLoop = true
while (shouldLoop) {
currentClientSocket = try {
currentServerSocket?.accept()
} catch (e: IOException) {
shouldLoop = false
null
}
emit(ConnectionResult.ConnectionEstablished)
currentClientSocket?.let { socket ->
currentServerSocket?.close()
val service = BluetoothDataTransferService(socket)
dataTransferService = service

emitAll(
service
.listenForIncomingMessages()
.map {
ConnectionResult.TransferSucceeded(it)
}
)
}
}
}.onCompletion {
closeConnection()
}.flowOn(Dispatchers.IO)
}

override fun connectToDevice(device: BtDevice): Flow<ConnectionResult> {
return flow {
if (!hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@ package com.example.bluetooth_bike.data.bluetooth.mappers
import com.example.bluetooth_bike.domain.model.BtMessage

fun String.toBtMessage(): BtMessage {
val name = substringBefore("#")
val voltage = substringAfter("#").substringBefore("$")
val amperes = substringAfter("$").substringBefore("%")
val speed = substringAfter("%").substringBefore("&")
val trip = substringAfter("&").substringBefore("*")
val total = substringAfter("*")
val parts = this.split("#", "$", "%", "&", "*")
return BtMessage(
voltage = voltage,
amperes = amperes,
speed = speed,
trip = trip,
total = total,
senderName = name,
voltage = parts.getOrNull(1) ?: "",
amperes = parts.getOrNull(2) ?: "",
speed = parts.getOrNull(3) ?: "",
trip = parts.getOrNull(4) ?: "",
total = parts.getOrNull(5) ?: "",
)
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow

interface BluetoothController {
val connectedDeviceName: StateFlow<String>
val devices: StateFlow<List<BtDevice>>
val isScanning: StateFlow<Boolean>
val isConnected: StateFlow<Boolean>
val errors: SharedFlow<String>

fun startDiscovery()
fun stopDiscovery()
fun startBluetoothServer(): Flow<ConnectionResult>
fun connectToDevice(device: BtDevice): Flow<ConnectionResult>
suspend fun trySendMessage(message : String) : String?
suspend fun trySendMessage(message: String): String?
fun closeConnection()
fun release()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ data class BtMessage(
val speed: String,
val trip : String,
val total : String,
val senderName: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ data class UiState(
speed = "00",
trip = "0.0",
total = "0.0",
senderName = "-",
)
),
val time : TimeModel = TimeModel(),
val connectedDevice : BtDevice? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fun PreviewBatteryInfoView() {
speed = "10",
trip = "10",
total = "10",
senderName = "test")
)
BatteryInfoView(mockData)
}

Expand All @@ -96,7 +96,7 @@ fun PreviewBatteryInfoView2() {
speed = "10",
trip = "10.50",
total = "101.50",
senderName = "test")
)
BatteryInfoView(mockData)
}

Expand All @@ -112,6 +112,6 @@ fun PreviewBatteryInfoView3() {
speed = "10",
trip = "10.50",
total = "101.50",
senderName = "test")
)
BatteryInfoView(mockData)}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fun BatteryValuesChart(values: List<BtMessage>) {

Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.background (MaterialTheme.colorScheme.background)
modifier = Modifier.background(MaterialTheme.colorScheme.background)
) {
LineChart(Modifier.size(250.dp, 200.dp), parsedValues)
Spacer(modifier = Modifier.height(10.dp))
Expand Down Expand Up @@ -116,15 +116,23 @@ fun ShowLegend() {
.background(Color.Green, RoundedCornerShape(4.dp))
)
Spacer(modifier = Modifier.width(2.dp))
Text(text = stringResource(R.string.voltage), fontSize = 6.sp, style = MaterialTheme.typography.titleSmall)
Text(
text = stringResource(R.string.voltage),
fontSize = 6.sp,
style = MaterialTheme.typography.titleSmall
)
Spacer(modifier = Modifier.width(10.dp))
Box(
modifier = Modifier
.size(8.dp)
.background(Color.Yellow, RoundedCornerShape(4.dp))
)
Spacer(modifier = Modifier.width(2.dp))
Text(text = stringResource(R.string.amperes), fontSize = 6.sp, style = MaterialTheme.typography.titleSmall)
Text(
text = stringResource(R.string.amperes),
fontSize = 6.sp,
style = MaterialTheme.typography.titleSmall
)

Spacer(modifier = Modifier.width(10.dp))
Box(
Expand All @@ -133,7 +141,11 @@ fun ShowLegend() {
.background(Color.Blue, RoundedCornerShape(4.dp))
)
Spacer(modifier = Modifier.width(2.dp))
Text(text = stringResource(R.string.speed), fontSize = 6.sp, style = MaterialTheme.typography.titleSmall)
Text(
text = stringResource(R.string.speed),
fontSize = 6.sp,
style = MaterialTheme.typography.titleSmall
)
}
}

Expand All @@ -151,16 +163,16 @@ fun Float.mapValueToDifferentRange(
fun PreviewBatteryValuesChart() {

val values = listOf(
BtMessage("65", "30", "30", "100", "1233", "Ebike01"),
BtMessage("66", "2", "25", "100", "1233", "Ebike01"),
BtMessage("59", "50", "00", "100", "1233", "Ebike01"),
BtMessage("67", "0", "10", "100", "1233", "Ebike01"),
BtMessage("66", "0", "00", "100", "1233", "Ebike01"),
BtMessage("68", "0", "00", "100", "1233", "Ebike01"),
BtMessage("63", "0", "00", "100", "1233", "Ebike01"),
BtMessage("65", "0", "00", "100", "1233", "Ebike01"),
BtMessage("65", "30", "30", "100", "1233"),
BtMessage("66", "2", "25", "100", "1233"),
BtMessage("59", "50", "00", "100", "1233"),
BtMessage("67", "0", "10", "100", "1233"),
BtMessage("66", "0", "00", "100", "1233"),
BtMessage("68", "0", "00", "100", "1233"),
BtMessage("63", "0", "00", "100", "1233"),
BtMessage("65", "0", "00", "100", "1233"),
)

val uiState = UiState(
time = TimeModel("Mon", "12:00", "12 Jan"), values = values
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ fun BikeScreen(
onLightClick: () -> Unit
) {
val name =
if (uiState.values.isNotEmpty())
uiState.values.last().senderName
if (uiState.connectedDevice != null && (uiState.connectedDevice.name?.isNotEmpty()) == true)
uiState.connectedDevice.name
else "Unknown"

Scaffold(
Expand Down Expand Up @@ -183,14 +183,14 @@ fun PreviewBikeScreen() {
Bluetooth_bikeTheme {

val values = listOf(
BtMessage("65", "30", "30", "100", "1233", "Test Ebike"),
BtMessage("66", "2", "25", "100", "1233", "Test Ebike"),
BtMessage("59", "50", "00", "100", "1233", "Test Ebike"),
BtMessage("67", "0", "10", "100", "1233", "Test Ebike"),
BtMessage("66", "0", "00", "100", "1233", "Test Ebike"),
BtMessage("68", "0", "00", "100", "1233", "Test Ebike"),
BtMessage("63", "0", "00", "100", "1233", "Test Ebike"),
BtMessage("65", "0", "00", "100", "1233", "Test Ebike"),
BtMessage("65", "30", "30", "100", "1233"),
BtMessage("66", "2", "25", "100", "1233"),
BtMessage("59", "50", "00", "100", "1233"),
BtMessage("67", "0", "10", "100", "1233"),
BtMessage("66", "0", "00", "100", "1233"),
BtMessage("68", "0", "00", "100", "1233"),
BtMessage("63", "0", "00", "100", "1233"),
BtMessage("65", "0", "00", "100", "1233"),
)

BikeScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class BluetoothViewModel @Inject constructor(
_state
) { devices, isScanning, uiState ->
uiState.copy(
connectedDevice = uiState.connectedDevice,
pairedDevices = devices,
isScanning = isScanning,
values = if (uiState.isConnected) uiState.values else emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import org.junit.Test
class BluetoothMessageTest {
@Test
fun `test toBluetoothMessage with valid input`() {
val input = "John#12.5$3.2%50&10*33"
val input = "#12.5$3.2%50&10*33"
val expected = BtMessage(
voltage = "12.5",
amperes = "3.2",
speed = "50",
trip = "10",
total = "33",
senderName = "John"
)
assertEquals(expected, input.toBtMessage())
}
Expand All @@ -28,35 +27,32 @@ class BluetoothMessageTest {
speed = "",
trip = "",
total = "",
senderName = ""
)
assertEquals(expected, input.toBtMessage())
}

@Test
fun `test toBluetoothMessage with missing fields`() {
val input = "Jane#12.5$"
val input = "#12.5$"
val expected = BtMessage(
voltage = "12.5",
amperes = "",
speed = "",
trip = "",
total = "",
senderName = "Jane"
)
assertEquals(expected, input.toBtMessage())
}

@Test
fun `test toBluetoothMessage with extra fields`() {
val input = "Mike#12.5$3.2%50&10*extra"
val input = "#12.5$3.2%50&10*extra"
val expected = BtMessage(
voltage = "12.5",
amperes = "3.2",
speed = "50",
trip = "10",
total = "extra",
senderName = "Mike"
)
assertEquals(expected, input.toBtMessage())
}
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.2.0" apply false
id("com.android.application") version "8.2.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false

//dagger hilt
Expand Down

0 comments on commit eb57398

Please sign in to comment.