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.