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

143 lines
5.2 KiB
Kotlin

package com.example.tvcontroller.services
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
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
class BluetoothService(private val context: Context) {
private var bluetoothManager: BluetoothManager =
getSystemService(context, BluetoothManager::class.java)!!;
private var bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
private var bluetoothStateChangedCallbacks: MutableList<(Int) -> Unit> = mutableListOf()
private val _scannedDevices = MutableStateFlow<List<BluetoothDevice>>(emptyList())
var scannedDevices: StateFlow<List<BluetoothDevice>> = _scannedDevices.asStateFlow()
private val _pairedDevices = MutableStateFlow<List<BluetoothDevice>>(emptyList())
var pairedDevices: StateFlow<List<BluetoothDevice>> = _pairedDevices.asStateFlow()
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) }
}
}
private var foundDeviceReceiver: BroadcastReceiver? = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(
android.bluetooth.BluetoothDevice.EXTRA_DEVICE,
android.bluetooth.BluetoothDevice::class.java
)
} else {
intent.getParcelableExtra(android.bluetooth.BluetoothDevice.EXTRA_DEVICE)
}
if (device != null) {
val newDevice = BluetoothDevice.fromBluetoothDevice(device)
Log.i("BluetoothService", "Found device: $newDevice")
_scannedDevices.update { devices -> if (newDevice in devices) devices else devices + newDevice }
}
}
}
init {
registerBluetoothStateReceiver()
updatePairedDevices()
}
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter.isEnabled ?: false
}
private fun registerBluetoothStateReceiver() {
context.registerReceiver(
bluetoothStateReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
)
}
fun onBluetoothStateChanged(callback: (Int) -> Unit) {
bluetoothStateChangedCallbacks.add(callback)
}
fun offBluetoothStateChanged(callback: (Int) -> Unit) {
bluetoothStateChangedCallbacks.remove(callback)
}
fun cleanUp() {
context.unregisterReceiver(bluetoothStateReceiver)
context.unregisterReceiver(foundDeviceReceiver)
}
fun updatePairedDevices() {
try {
_pairedDevices.update {
bluetoothAdapter.bondedDevices.toList()
.map { device -> BluetoothDevice.fromBluetoothDevice(device) }
}
} catch (e: SecurityException) {
println("Error updating paired devices: $e")
}
}
fun startDiscovery() {
try {
if (bluetoothAdapter.isDiscovering) return
Log.i("BluetoothService", "Starting discovery")
context.registerReceiver(
foundDeviceReceiver,
IntentFilter(android.bluetooth.BluetoothDevice.ACTION_FOUND)
)
updatePairedDevices()
bluetoothAdapter.startDiscovery()
} catch (e: SecurityException) {
println("Error starting discovery: $e")
Log.e("BluetoothService", "Error starting discovery: $e")
}
}
fun stopDiscovery() {
context.unregisterReceiver(foundDeviceReceiver)
try {
bluetoothAdapter.cancelDiscovery()
} catch (e: SecurityException) {
println("Error stopping discovery: $e")
}
}
fun hasRequiredPermissions(): Boolean {
return BLUETOOTH_PERMISSIONS.all {
context.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED
}
}
fun getBluetoothState(): Int {
return bluetoothAdapter.state
}
companion object {
val BLUETOOTH_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
android.Manifest.permission.BLUETOOTH,
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_SCAN,
android.Manifest.permission.BLUETOOTH_ADMIN
)
} else {
arrayOf(
android.Manifest.permission.BLUETOOTH,
android.Manifest.permission.BLUETOOTH_ADMIN
)
}
}
}