diff --git a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt
index 6abada80..13fba17c 100644
--- a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt
+++ b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt
@@ -273,12 +273,20 @@ fun DeviceSettingsScreen(
// Not connected info — BLE live but no AAP connection
if (device != null && device.ble != null && !device.isAapConnected && device.address != null) {
- item("not_connected_info") {
- NotConnectedCard(
- isNudgeAvailable = state.isNudgeAvailable,
- isForceConnecting = state.isForceConnecting,
- onConnect = onForceConnect,
- )
+ if (state.isClassicallyConnected) {
+ // Device is connected for audio but AAP isn't available — show passive info
+ item("aap_unavailable_info") {
+ AapUnavailableCard()
+ }
+ } else {
+ // Device is nearby but not connected — prompt user to connect
+ item("not_connected_info") {
+ NotConnectedCard(
+ isNudgeAvailable = state.isNudgeAvailable,
+ isForceConnecting = state.isForceConnecting,
+ onConnect = onForceConnect,
+ )
+ }
}
}
@@ -827,6 +835,29 @@ private fun NotConnectedCard(
}
}
+@Composable
+private fun AapUnavailableCard() {
+ ElevatedCard(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ elevation = CardDefaults.elevatedCardElevation(defaultElevation = 1.dp),
+ ) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(
+ text = stringResource(R.string.device_settings_aap_unavailable_label),
+ style = MaterialTheme.typography.titleMedium,
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.device_settings_aap_unavailable_description),
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
+}
+
@Composable
private fun InfoRow(label: String, value: String, modifier: Modifier = Modifier) {
Column(
diff --git a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModel.kt b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModel.kt
index 9677eb7c..0511c161 100644
--- a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModel.kt
+++ b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModel.kt
@@ -80,13 +80,16 @@ class DeviceSettingsViewModel @Inject constructor(
deviceForProfile(profileId),
upgradeRepo.upgradeInfo,
isForceConnecting,
- ) { _, device, upgrade, forcing ->
+ bluetoothManager.connectedDevices,
+ ) { _, device, upgrade, forcing, connectedDevices ->
+ val connectedAddresses = connectedDevices.map { it.address }.toSet()
State(
device = device,
now = Instant.now(),
isPro = upgrade.isPro,
isNudgeAvailable = bluetoothManager.isNudgeAvailable,
isForceConnecting = forcing,
+ isClassicallyConnected = device?.address?.let { it in connectedAddresses } == true,
)
}
}.asLiveState()
@@ -108,6 +111,7 @@ class DeviceSettingsViewModel @Inject constructor(
val isPro: Boolean = false,
val isNudgeAvailable: Boolean = true,
val isForceConnecting: Boolean = false,
+ val isClassicallyConnected: Boolean = false,
) {
val reactions: ReactionConfig get() = device?.reactions ?: ReactionConfig()
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2ed64fa2..af09f293 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -437,6 +437,8 @@
This device is nearby but not connected to this phone. Connect to access settings and controls.
Connect
Open Bluetooth Settings
+ Advanced settings unavailable
+ This phone\'s Bluetooth does not support the connection required for advanced AirPods settings.
Sound
Controls
Noise Cancellation with One AirPod
diff --git a/app/src/test/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModelTest.kt b/app/src/test/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModelTest.kt
index ca8bbf88..2df3ce66 100644
--- a/app/src/test/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModelTest.kt
+++ b/app/src/test/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsViewModelTest.kt
@@ -53,6 +53,7 @@ class DeviceSettingsViewModelTest : BaseTest() {
private lateinit var devicesFlow: MutableStateFlow>
private lateinit var upgradeInfoFlow: MutableStateFlow
+ private lateinit var connectedDevicesFlow: MutableStateFlow>
private fun mockBondedDevice(address: BluetoothAddress): BluetoothDevice2 = mockk {
every { this@mockk.address } returns address
@@ -80,9 +81,11 @@ class DeviceSettingsViewModelTest : BaseTest() {
upgradeRepo = mockk().also {
every { it.upgradeInfo } returns upgradeInfoFlow
}
+ connectedDevicesFlow = MutableStateFlow(emptyList())
bluetoothManager = mockk(relaxed = true) {
every { isNudgeAvailable } returns true
every { bondedDevices() } returns flowOf(emptySet())
+ every { connectedDevices } returns connectedDevicesFlow
}
profilesRepo = mockk(relaxed = true)
generalSettings = mockk(relaxed = true)
@@ -275,4 +278,24 @@ class DeviceSettingsViewModelTest : BaseTest() {
sendFailed.command shouldBe AapCommand.SetDeviceName("NewName")
sendFailed.message shouldBe "socket closed"
}
+
+ @Test
+ fun `isClassicallyConnected is true when device address is in connected devices`() = runTest(testDispatcher) {
+ connectedDevicesFlow.value = listOf(mockBondedDevice(testAddress))
+
+ val vm = createViewModel()
+ vm.initialize(testAddress)
+
+ vm.state.first().isClassicallyConnected shouldBe true
+ }
+
+ @Test
+ fun `isClassicallyConnected is false when device address is not in connected devices`() = runTest(testDispatcher) {
+ connectedDevicesFlow.value = listOf(mockBondedDevice("XX:XX:XX:XX:XX:XX"))
+
+ val vm = createViewModel()
+ vm.initialize(testAddress)
+
+ vm.state.first().isClassicallyConnected shouldBe false
+ }
}
diff --git a/app/src/test/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectLogicTest.kt b/app/src/test/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectLogicTest.kt
index ad43f9ed..fc6de09c 100644
--- a/app/src/test/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectLogicTest.kt
+++ b/app/src/test/java/eu/darken/capod/reaction/core/autoconnect/AutoConnectLogicTest.kt
@@ -17,7 +17,6 @@ class AutoConnectLogicTest : BaseTest() {
autoConnect = AutoConnect(
bluetoothManager = mockk(relaxed = true),
deviceMonitor = mockk(relaxed = true),
- generalSettings = mockk(relaxed = true),
)
}