feat(eq): name Bluetooth devices from the system pairing list
A Bluetooth device only reports its own name through AudioDeviceInfo.productName while it is enumerated as an active output, i.e. while it is connected. Paired-but-switched-off devices therefore had no name to fall back on, and the platform-name cache is in-memory only by design (bt-device-identity ADR-4), so it self-heals per session ONLY for whatever happens to be connected. Every other device showed its raw id. Android already knows those names: BluetoothAdapter.getBondedDevices() lists every pairing with its name and MAC, connected or not, and nothing in this app was asking. Read it and seed the platform-name cache from it, keyed bt_a2dp:<uppercase MAC> to match the ids the audio layer emits. Seeded BEFORE the active-device query so a live enumeration name, being the fresher of the two, still wins; a user's custom name outranks both. Re-read on refrescarDispositivoActual so pairing or renaming a device in system settings shows up as soon as the list becomes visible. Reading the bond list is gated by BLUETOOTH_CONNECT from API 31 and by the legacy BLUETOOTH permission below it, so declare the latter with maxSdkVersion 30. It is a normal permission: granted at install, no runtime prompt, no new friction. When the answer is unavailable — permission denied, no adapter, Bluetooth off — both layers return an empty map rather than throwing, and the row degrades to the id exactly as before. Does not help rows persisted under a bt_a2dp:name: placeholder id: those never had a MAC to match against.
This commit is contained in:
@@ -15,6 +15,14 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<!--
|
||||
Reading the paired-device list is gated by BLUETOOTH_CONNECT from API 31
|
||||
and by this legacy permission below it. Normal permission: granted at
|
||||
install, no runtime prompt.
|
||||
-->
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30"/>
|
||||
|
||||
<application
|
||||
android:label="PluriWave"
|
||||
|
||||
@@ -2,6 +2,7 @@ package es.freetimelab.pluriwave
|
||||
|
||||
import android.Manifest
|
||||
import android.app.NotificationManager
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.content.ActivityNotFoundException
|
||||
@@ -1080,6 +1081,11 @@ class MainActivity : AudioServiceActivity() {
|
||||
Log.d(tag, "audio_devices.requestBluetoothConnect")
|
||||
result.success(requestBluetoothConnect())
|
||||
}
|
||||
"getBondedDeviceNames" -> {
|
||||
val names = bondedDeviceNames()
|
||||
Log.d(tag, "audio_devices.getBondedDeviceNames count=${names.size}")
|
||||
result.success(names)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
@@ -1122,6 +1128,46 @@ class MainActivity : AudioServiceActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns MAC -> name for every PAIRED Bluetooth device, connected or not.
|
||||
*
|
||||
* `AudioDeviceInfo.productName` only exists while a device is enumerated as
|
||||
* an active output, so a paired-but-switched-off device can never report its
|
||||
* own name and its row falls back to the raw id. The bond list is the
|
||||
* system's own record and is the only source that survives disconnection.
|
||||
*
|
||||
* Returns an empty map instead of throwing when the answer is unavailable
|
||||
* (BLUETOOTH_CONNECT denied, no adapter, device with Bluetooth off): a
|
||||
* missing name must degrade to the id, never break device resolution.
|
||||
*/
|
||||
private fun bondedDeviceNames(): Map<String, String> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
|
||||
checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
Log.d(tag, "audio_devices.bondedDeviceNames BLUETOOTH_CONNECT not granted")
|
||||
return emptyMap()
|
||||
}
|
||||
return try {
|
||||
val manager = getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
|
||||
val adapter = manager?.adapter ?: return emptyMap()
|
||||
adapter.bondedDevices
|
||||
.orEmpty()
|
||||
.mapNotNull { device ->
|
||||
val address = device.address
|
||||
?.takeIf { it.isNotBlank() && it != bluetoothMacPlaceholder }
|
||||
?: return@mapNotNull null
|
||||
val name = device.name?.takeIf { it.isNotBlank() }
|
||||
?: return@mapNotNull null
|
||||
address.uppercase() to name
|
||||
}
|
||||
.toMap()
|
||||
} catch (error: Throwable) {
|
||||
Log.w(tag, "audio_devices.bondedDeviceNames failed", error)
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
|
||||
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
|
||||
Reference in New Issue
Block a user