Skip to content

Commit c9652ab

Browse files
committed
partial implementation of BluetoothConnection class
1 parent 2d22244 commit c9652ab

File tree

13 files changed

+192
-9
lines changed

13 files changed

+192
-9
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# BluetoothChatMpp
22

3-
Mobile multiplatform bluetooth chat.
3+
Mobile multiplatform bluetooth chat.
4+
5+
## Warning
6+
7+
The project is under active development, so there is no guarantee that the **master** branch works correctly.

android-app/src/main/java/ru/tetraquark/bluetoothchatmpp/App.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import ru.tetraquark.bluetoothchatmpp.mpplibrary.PlatformInjector
66

77
class App : Application() {
88

9+
companion object {
10+
const val APP_UUID = "9cdc912c-ea58-4135-944d-d0bf7f07353d"
11+
}
12+
913
override fun onCreate() {
1014
super.onCreate()
1115

12-
val appInjector = AppInjector(PlatformInjector(this))
16+
val appInjector = AppInjector(PlatformInjector(this, APP_UUID))
1317
}
1418
}

common/bluetooth/src/androidMain/kotlin/ru/tetraquark/mpp/bluetooth/BluetoothAdapter.kt

+26-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import android.content.Context
88
import android.content.Intent
99
import android.content.IntentFilter
1010
import androidx.lifecycle.*
11+
import java.io.IOException
12+
import java.util.*
1113
import kotlin.reflect.KProperty
1214

13-
actual class BluetoothAdapter {
15+
actual class BluetoothAdapter(
16+
actual val uuid: String,
17+
actual val messagesBufferLength: Int = 1024
18+
) {
1419

1520
private val androidBluetoothAdapter by BluetoothAdapterDelegate()
1621

@@ -83,6 +88,26 @@ actual class BluetoothAdapter {
8388
@SuppressLint("HardwareIds")
8489
actual fun getDeviceAddress(): String = androidBluetoothAdapter.address
8590

91+
actual fun connectTo(bluetoothRemoteDevice: BluetoothRemoteDevice): BluetoothConnection {
92+
val remoteDevice = try {
93+
androidBluetoothAdapter.getRemoteDevice(bluetoothRemoteDevice.address)
94+
} catch (wrongAddressException: IllegalArgumentException) {
95+
throw BluetoothException("The remote device address is invalid.")
96+
}
97+
98+
val blSocket = try {
99+
// TODO: type of connection (secure or insecure)
100+
remoteDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid))
101+
} catch (ioException: IOException) {
102+
throw BluetoothException("The remote device connection error.")
103+
}
104+
105+
return BluetoothConnection(
106+
remoteDevice = bluetoothRemoteDevice,
107+
bluetoothSocket = blSocket,
108+
byteBufferLength = messagesBufferLength
109+
)
110+
}
86111

87112
private class BluetoothAdapterDelegate {
88113

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package ru.tetraquark.mpp.bluetooth
2+
3+
import android.bluetooth.BluetoothSocket
4+
import java.io.IOException
5+
import java.util.concurrent.ConcurrentHashMap
6+
import java.util.concurrent.ExecutorService
7+
import java.util.concurrent.Executors
8+
import java.util.concurrent.atomic.AtomicBoolean
9+
10+
actual class BluetoothConnection(
11+
actual val remoteDevice: BluetoothRemoteDevice,
12+
private val bluetoothSocket: BluetoothSocket,
13+
private val byteBufferLength: Int = 1024,
14+
private val connectionExecutor: ExecutorService = Executors.newSingleThreadExecutor()
15+
) {
16+
17+
private val inputStream = bluetoothSocket.inputStream
18+
private val outputStream = bluetoothSocket.outputStream
19+
20+
private val isRunning = AtomicBoolean(false)
21+
private val listenersMap = ConcurrentHashMap<Int, ConnectionListener>()
22+
23+
init {
24+
runSocketObserver()
25+
}
26+
27+
actual fun isConnected(): Boolean = isRunning.get()
28+
29+
actual fun send(data: ByteArray) {
30+
outputStream.write(data)
31+
}
32+
33+
actual fun close() {
34+
try {
35+
isRunning.set(false)
36+
connectionExecutor.shutdown()
37+
bluetoothSocket.close()
38+
39+
notifyObservers {
40+
onClose()
41+
}
42+
} catch (ioException: IOException) { }
43+
}
44+
45+
actual fun addConnectionListener(listener: ConnectionListener) {
46+
listenersMap[listener.hashCode()] = listener
47+
}
48+
49+
actual fun removeConnectionListener(listener: ConnectionListener) {
50+
listenersMap.remove(listener.hashCode())
51+
}
52+
53+
private fun runSocketObserver() {
54+
isRunning.set(true)
55+
56+
connectionExecutor.execute {
57+
while(isRunning.get()) {
58+
try {
59+
val buffer = ByteArray(byteBufferLength)
60+
inputStream.read(buffer)
61+
62+
notifyObservers {
63+
onReceived(buffer)
64+
}
65+
} catch (ioException: IOException) {
66+
notifyObservers {
67+
onError(ioException)
68+
}
69+
}
70+
}
71+
}
72+
}
73+
74+
private inline fun notifyObservers(block: ConnectionListener.() -> Unit) {
75+
listenersMap.forEach {
76+
block(it.value)
77+
}
78+
}
79+
80+
}

common/bluetooth/src/commonMain/kotlin/ru/tetraquark/mpp/bluetooth/BluetoothAdapter.kt

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package ru.tetraquark.mpp.bluetooth
22

33
expect class BluetoothAdapter {
44

5+
val uuid: String
6+
val messagesBufferLength: Int
7+
58
fun isAvailable(): Boolean
69

710
fun isEnabled(): Boolean
@@ -16,4 +19,6 @@ expect class BluetoothAdapter {
1619

1720
fun getDeviceAddress(): String
1821

22+
fun connectTo(bluetoothRemoteDevice: BluetoothRemoteDevice): BluetoothConnection
23+
1924
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ru.tetraquark.mpp.bluetooth
2+
3+
expect class BluetoothConnection {
4+
5+
val remoteDevice: BluetoothRemoteDevice
6+
7+
fun isConnected(): Boolean
8+
9+
fun send(data: ByteArray)
10+
11+
fun close()
12+
13+
fun addConnectionListener(listener: ConnectionListener)
14+
15+
fun removeConnectionListener(listener: ConnectionListener)
16+
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ru.tetraquark.mpp.bluetooth
2+
3+
interface ConnectionListener {
4+
5+
fun onReceived(data: ByteArray)
6+
7+
fun onClose()
8+
9+
fun onError(exception: Throwable)
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,38 @@
11
package ru.tetraquark.bluetoothchatmpp.data
22

3-
class ChatMessageRepository {
3+
import kotlinx.coroutines.flow.Flow
4+
import ru.tetraquark.mpp.bluetooth.BluetoothAdapter
5+
import ru.tetraquark.mpp.bluetooth.BluetoothConnection
6+
import ru.tetraquark.mpp.bluetooth.BluetoothRemoteDevice
7+
import ru.tetraquark.mpp.bluetooth.ConnectionListener
8+
9+
class ChatMessageRepository(
10+
private val bluetoothAdapter: BluetoothAdapter
11+
) {
12+
13+
private val openConnectionsMap = mutableMapOf<BluetoothRemoteDevice, BluetoothConnection>()
14+
private val connectionObserversMap = mutableMapOf<BluetoothRemoteDevice, BluetoothConnectionObserver>()
15+
16+
suspend fun connectToDevice(device: BluetoothRemoteDevice) {
17+
// TODO("not implemented")
18+
val blConnection = bluetoothAdapter.connectTo(device)
19+
}
20+
21+
private inner class BluetoothConnectionObserver(
22+
23+
) : ConnectionListener {
24+
override fun onReceived(data: ByteArray) {
25+
// TODO("not implemented")
26+
}
27+
28+
override fun onClose() {
29+
// TODO("not implemented")
30+
}
31+
32+
override fun onError(exception: Throwable) {
33+
// TODO("not implemented")
34+
}
35+
36+
}
37+
438
}

mpp-library/data/src/commonMain/kotlin/ru/tetraquark/bluetoothchatmpp/data/RemoteDeviceRepository.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.callbackFlow
77
import ru.tetraquark.mpp.bluetooth.BluetoothAdapter
88
import ru.tetraquark.mpp.bluetooth.BluetoothRemoteDevice
99
import ru.tetraquark.mpp.bluetooth.DiscoveryListener
10-
import kotlin.coroutines.CoroutineContext
1110

1211
class RemoteDeviceRepository(
1312
private val bluetoothAdapter: BluetoothAdapter
@@ -34,4 +33,4 @@ class RemoteDeviceRepository(
3433
bluetoothAdapter.stopDeviceDiscovery()
3534
}
3635

37-
}
36+
}

mpp-library/domain/src/commonMain/kotlin/ru/tetraquark/bluetoothchatmpp/domain/DiscoveryBluetoothDevicesInteractor.kt

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class DiscoveryBluetoothDevicesInteractor(
2626
val flow = bluetoothDevicesRepository.startDeviceDiscovery()
2727
try {
2828
flow.onEach {
29+
// TODO: addition of lists
2930
_discoveredDeviceList.value = _discoveredDeviceList.value + it
3031
}.collect()
3132
} catch (cancelException: CancellationException) {

mpp-library/src/androidMain/kotlin/ru/tetraquark/bluetoothchatmpp/mpplibrary/PlatformInjector.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import kotlinx.coroutines.flow.Flow
2020
import kotlinx.coroutines.flow.map
2121

2222
actual class PlatformInjector(
23-
private val application: Application
23+
private val application: Application,
24+
actual val appUuid: String
2425
) : Application.ActivityLifecycleCallbacks {
2526

2627
actual fun injectPlatform() {
2728
application.registerActivityLifecycleCallbacks(this)
2829
}
2930

30-
val bluetoothAdapter = BluetoothAdapter()
31+
val bluetoothAdapter = BluetoothAdapter(appUuid)
3132

3233
val bluetoothDeviceRepository = RemoteDeviceRepository(bluetoothAdapter)
3334

mpp-library/src/commonMain/kotlin/ru/tetraquark/bluetoothchatmpp/mpplibrary/AppInjector.kt

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ class AppInjector(
66

77
init {
88
platformInjector.injectPlatform()
9-
109
}
1110

1211
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package ru.tetraquark.bluetoothchatmpp.mpplibrary
22

33
expect class PlatformInjector {
4+
5+
val appUuid: String
6+
47
fun injectPlatform()
58
}

0 commit comments

Comments
 (0)