Crear una aplicación para Android que permita recibir mensajes por Bluetooth

Código de la aplicación

AndroidManifest.xml

[crayon-6a616ce5cf98c009786486/]

MainActivity.kt

[crayon-6a616ce5cf993831633967/]

build.gradle.kts

[crayon-6a616ce5cf99a487814098/]

Resultado de la ejecución de la aplicación

Explicación del código

1. Definición de UUIDs y Variables

private val SERVICE_UUID = UUID.fromString("12345678-1234-5678-1234-56789abcdef0")
private val CHARACTERISTIC_UUID = UUID.fromString("abcdef01-2345-6789-abcd-ef0123456789")

  • SERVICE_UUID: UUID para el servicio BLE.
  • CHARACTERISTIC_UUID: UUID para la característica BLE que permite leer y escribir datos.

2. Declaración de Variables

private lateinit var bluetoothManager: BluetoothManager
private lateinit var bluetoothAdapter: BluetoothAdapter
private var bluetoothGattServer: BluetoothGattServer? = null
private var bluetoothLeAdvertiser: BluetoothLeAdvertiser? = null

private var receivedData by mutableStateOf("No data received")
private var connectionInfo by mutableStateOf("No device connected")

  • bluetoothManager y bluetoothAdapter: Para manejar Bluetooth.
  • bluetoothGattServer: El servidor GATT que manejará las solicitudes de otros dispositivos.
  • bluetoothLeAdvertiser: Para la publicidad BLE.
  • receivedData y connectionInfo: Estados que almacenan la información de los datos recibidos y la conexión actual.

3. Método onCreate

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BluetoothPeripheralApp(receivedData, connectionInfo)
}

checkPermissions()
setupBluetooth()
startGattServer()
startAdvertising()
}

  • setContent: Configura la interfaz de usuario usando Jetpack Compose.
  • checkPermissions: Verifica y solicita permisos necesarios para Bluetooth.
  • setupBluetooth: Inicializa el BluetoothManager y BluetoothAdapter.
  • startGattServer: Inicia el servidor GATT para manejar solicitudes.
  • startAdvertising: Comienza a anunciar el dispositivo para que otros puedan encontrarlo.

4. Método onDestroy

override fun onDestroy() {
super.onDestroy()
bluetoothGattServer?.close()
}

  • Cierra el servidor GATT cuando la actividad se destruye.

5. Método checkPermissions

private fun checkPermissions() {
val permissions = arrayOf(
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_ADVERTISE
)
if (permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) {
ActivityCompat.requestPermissions(this, permissions, 1)
}
}

  • Verifica si se tienen los permisos necesarios para usar Bluetooth. Si no, solicita permisos al usuario.

6. Método setupBluetooth

private fun setupBluetooth() {
bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
}

  • Inicializa el BluetoothManager y el BluetoothAdapter, que son necesarios para la operación de Bluetooth.

7. Método startGattServer

@SuppressLint("MissingPermission")
private fun startGattServer() {
bluetoothGattServer = bluetoothManager.openGattServer(this, object : BluetoothGattServerCallback() {
override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
connectionInfo = "Connected to ${device.name ?: "Unknown"} (${device.address})"
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
connectionInfo = "Disconnected from device"
}
}

override fun onServiceAdded(status: Int, service: BluetoothGattService) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Handle service added
}
}

override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) {
if (characteristic.uuid == CHARACTERISTIC_UUID) {
bluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, characteristic.value)
}
}

override fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) {
if (characteristic.uuid == CHARACTERISTIC_UUID) {
receivedData = String(value, Charsets.UTF_8)
if (responseNeeded) {
bluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
}
}
}
})

val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
val characteristic = BluetoothGattCharacteristic(
CHARACTERISTIC_UUID,
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ or BluetoothGattCharacteristic.PERMISSION_WRITE
)
service.addCharacteristic(characteristic)
bluetoothGattServer?.addService(service)
}

  • onConnectionStateChange: Actualiza la información de conexión cuando un dispositivo se conecta o desconecta.
  • onServiceAdded: Maneja el estado cuando un servicio se agrega.
  • onCharacteristicReadRequest: Responde a las solicitudes de lectura de la característica.
  • onCharacteristicWriteRequest: Actualiza receivedData con el valor recibido cuando se escribe en la característica.

8. Método startAdvertising

@SuppressLint("MissingPermission")
private fun startAdvertising() {
bluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser
if (bluetoothLeAdvertiser == null) {
println("Bluetooth LE Advertiser not available.")
return
}

val advertisingSettings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setConnectable(true)
.build()

val advertisingData = AdvertiseData.Builder()
.setIncludeDeviceName(true)
.addServiceUuid(ParcelUuid(SERVICE_UUID))
.build()

bluetoothLeAdvertiser?.startAdvertising(advertisingSettings, advertisingData, object : AdvertiseCallback() {
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
super.onStartSuccess(settingsInEffect)
println("Advertising started successfully")
}

override fun onStartFailure(errorCode: Int) {
super.onStartFailure(errorCode)
println("Advertising failed with error code $errorCode")
}
})
}

  • Configura y comienza a anunciar el servicio BLE. Esto hace que el dispositivo sea detectable y accesible para otros dispositivos BLE.

9. Método BluetoothPeripheralApp

@Composable
fun BluetoothPeripheralApp(receivedData: String, connectionInfo: String) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Received Messages:")
Spacer(modifier = Modifier.height(8.dp))
Text(receivedData)
Spacer(modifier = Modifier.height(16.dp))
Text("Connection Info:")
Spacer(modifier = Modifier.height(8.dp))
Text(connectionInfo)
}
}

  • Define la interfaz de usuario usando Jetpack Compose. Muestra los datos recibidos y la información de conexión.