tv-controller-android/app/src/main/java/com/example/tvcontroller/services/BluetoothService.kt

129 lines
4.5 KiB
Kotlin

package com.example.tvcontroller.services
import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.content.Intent
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat.getSystemService
import com.example.tvcontroller.data.BluetoothDevice
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.io.IOException
import java.util.UUID
private const val SERIAL_PORT_SERVICE_UUID = "00001101-0000-1000-8000-00805F9B34FB"
private const val TAG = "BluetoothService"
class BluetoothService(private val context: Context) {
private var bluetoothManager: BluetoothManager =
getSystemService(context, BluetoothManager::class.java)!!
private var bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
private val _pairedDevices = MutableStateFlow<List<BluetoothDevice>>(emptyList())
var pairedDevices: StateFlow<List<BluetoothDevice>> = _pairedDevices.asStateFlow()
private var clientSocket: BluetoothSocket? = null
private var bluetoothStateChangedCallbacks: MutableList<(Int) -> Unit> = mutableListOf()
private var bluetoothStateReceiver: BroadcastReceiver? = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
bluetoothStateChangedCallbacks.forEach { it(state) }
}
}
init {
context.registerReceiver(
bluetoothStateReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
)
updatePairedDevices()
}
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter.isEnabled
}
fun onBluetoothStateChanged(callback: (Int) -> Unit) {
bluetoothStateChangedCallbacks.add(callback)
}
fun offBluetoothStateChanged(callback: (Int) -> Unit) {
bluetoothStateChangedCallbacks.remove(callback)
}
fun cleanUp() {
context.unregisterReceiver(bluetoothStateReceiver)
bluetoothStateChangedCallbacks.clear()
closeBluetoothConnection()
}
fun updatePairedDevices() {
try {
_pairedDevices.update {
bluetoothAdapter.bondedDevices.toList()
.map { device -> BluetoothDevice.fromBluetoothDevice(device) }
}
} catch (e: SecurityException) {
println("Error updating paired devices: $e")
}
}
fun connectToDevice(device: BluetoothDevice) {
Log.i(TAG, "Initiating connection process")
if (context.checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
Log.e(TAG, "Bluetooth permission not granted")
return
}
Log.i(TAG, "Connecting to device: $device")
try {
var androidBluetoothDevice = bluetoothAdapter.getRemoteDevice(device.getAddress())
Log.i(TAG, "Creating socket to device: $device")
clientSocket = androidBluetoothDevice.createRfcommSocketToServiceRecord(
UUID.fromString(SERIAL_PORT_SERVICE_UUID)
)
Log.i(TAG, "Connecting to socket")
clientSocket?.connect()
Log.i(TAG, "Connected to device: $device")
} catch (e: IOException) {
Log.e(TAG, "Error connecting to device: $e")
}
}
fun hasRequiredPermissions(): Boolean {
return BLUETOOTH_PERMISSIONS.all {
context.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED
}
}
fun getBluetoothState(): Int {
return bluetoothAdapter.state
}
fun closeBluetoothConnection() {
clientSocket?.close()
clientSocket = null
}
companion object {
val BLUETOOTH_PERMISSIONS = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
arrayOf(
Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN
)
} else {
arrayOf(
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_ADMIN
)
}
}
}