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>(emptyList()) var pairedDevices: StateFlow> = _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 ) } } }