diff --git a/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
index 06436561d..0b4927562 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
@@ -230,6 +230,14 @@ fun AppSettingsScreen(
enabled = state.isPremium
)
+ StyledToggle(
+ label = stringResource(R.string.conversational_awareness_both_pods_only),
+ description = stringResource(R.string.conversational_awareness_both_pods_only_description),
+ checked = state.conversationalAwarenessBothPodsOnlyEnabled,
+ onCheckedChange = viewModel::setConversationalAwarenessBothPodsOnlyEnabled,
+ enabled = state.isPremium,
+ )
+
StyledToggle(
label = stringResource(R.string.relative_conversational_awareness_volume),
description = stringResource(R.string.relative_conversational_awareness_volume_description),
diff --git a/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt b/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
index 2c1716c33..606d7f0b2 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
@@ -18,6 +18,7 @@ import kotlin.math.roundToInt
data class AppSettingsUiState(
val showPhoneBatteryInWidget: Boolean = false,
val conversationalAwarenessPauseMusicEnabled: Boolean = false,
+ val conversationalAwarenessBothPodsOnlyEnabled: Boolean = false,
val relativeConversationalAwarenessVolumeEnabled: Boolean = true,
val disconnectWhenNotWearing: Boolean = false,
val takeoverWhenDisconnected: Boolean = false,
@@ -137,6 +138,7 @@ class AppSettingsViewModel(application: Application) : AndroidViewModel(applicat
currentState.copy(
showPhoneBatteryInWidget = sharedPreferences.getBoolean("show_phone_battery_in_widget", false),
conversationalAwarenessPauseMusicEnabled = sharedPreferences.getBoolean("conversational_awareness_pause_music", false),
+ conversationalAwarenessBothPodsOnlyEnabled = sharedPreferences.getBoolean("conversational_awareness_both_pods_only", false),
relativeConversationalAwarenessVolumeEnabled = sharedPreferences.getBoolean("relative_conversational_awareness_volume", true),
disconnectWhenNotWearing = sharedPreferences.getBoolean("disconnect_when_not_wearing", false),
takeoverWhenDisconnected = sharedPreferences.getBoolean("takeover_when_disconnected", false),
@@ -167,6 +169,11 @@ class AppSettingsViewModel(application: Application) : AndroidViewModel(applicat
_uiState.update { it.copy(conversationalAwarenessPauseMusicEnabled = enabled) }
}
+ fun setConversationalAwarenessBothPodsOnlyEnabled(enabled: Boolean) {
+ sharedPreferences.edit { putBoolean("conversational_awareness_both_pods_only", enabled) }
+ _uiState.update { it.copy(conversationalAwarenessBothPodsOnlyEnabled = enabled) }
+ }
+
fun setRelativeConversationalAwarenessVolumeEnabled(enabled: Boolean) {
sharedPreferences.edit { putBoolean("relative_conversational_awareness_volume", enabled) }
_uiState.update { it.copy(relativeConversationalAwarenessVolumeEnabled = enabled) }
diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
index 0cf08c11d..387ab6ba9 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
@@ -165,6 +165,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
var deviceName: String = "AirPods",
var earDetectionEnabled: Boolean = true,
var conversationalAwarenessPauseMusic: Boolean = false,
+ var conversationalAwarenessBothPodsOnly: Boolean = false,
var showPhoneBatteryInWidget: Boolean = true,
var relativeConversationalAwarenessVolume: Boolean = true,
var headGestures: Boolean = true,
@@ -440,6 +441,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
if (!contains("conversational_awareness_pause_music")) putBoolean(
"conversational_awareness_pause_music", false
)
+ if (!contains("conversational_awareness_both_pods_only")) putBoolean(
+ "conversational_awareness_both_pods_only", false
+ )
if (!contains("personalized_volume")) putBoolean("personalized_volume", false)
if (!contains("automatic_ear_detection")) putBoolean(
"automatic_ear_detection", true
@@ -912,10 +916,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
setPackage(packageName)
})
+ // Number of buds currently in the ear (0x00 == in ear).
+ val budsInEar = earDetectionNotification.status.count { it == 0x00.toByte() }
+ val blockedForSinglePod = config.conversationalAwarenessBothPodsOnly && budsInEar < 2
+
if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) {
- MediaController.startSpeaking()
- } else if (conversationAwarenessNotification.status == 6.toByte() ||conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) {
- MediaController.stopSpeaking()
+ if (!blockedForSinglePod) MediaController.startSpeaking()
+ } else if (conversationAwarenessNotification.status == 6.toByte() || conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) {
+ MediaController.stopSpeaking() // Never gate the "stop" side — volume must always be restored.
}
Log.d(
@@ -1360,6 +1368,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
conversationalAwarenessPauseMusic = sharedPreferences.getBoolean(
"conversational_awareness_pause_music", false
),
+ conversationalAwarenessBothPodsOnly = sharedPreferences.getBoolean(
+ "conversational_awareness_both_pods_only", false
+ ),
showPhoneBatteryInWidget = sharedPreferences.getBoolean(
"show_phone_battery_in_widget", true
),
@@ -1473,6 +1484,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
"conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic =
preferences.getBoolean(key, false)
+ "conversational_awareness_both_pods_only" -> config.conversationalAwarenessBothPodsOnly =
+ preferences.getBoolean(key, false)
+
"show_phone_battery_in_widget" -> {
config.showPhoneBatteryInWidget = preferences.getBoolean(key, true)
widgetMobileBatteryEnabled = config.showPhoneBatteryInWidget
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 3356edc4a..050f09fd3 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -39,6 +39,8 @@
Reduces to a percentage of the current volume instead of the maximum volume.
Pause Music
When you start speaking, music will be paused.
+ Only when both AirPods are worn
+ Only lowers the volume when you speak if both AirPods are in your ears.
EXAMPLE
Add widget
Control Noise Control Mode directly from your Home Screen.