Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ fun DeviceSettingsScreen(
level = adaptiveNoise.level,
onLevelChange = onAdaptiveAudioNoiseChange,
enabled = enabled,
isAdaptiveMode = ancMode.current == AapSetting.AncMode.Value.ADAPTIVE,
)
}
if (features.hasNcOneAirpod && ncOneAirpod != null) {
Expand Down Expand Up @@ -722,18 +723,24 @@ private fun AdaptiveNoiseSlider(
level: Int,
onLevelChange: (Int) -> Unit,
enabled: Boolean,
isAdaptiveMode: Boolean,
) {
var sliderValue by remember(level) { mutableIntStateOf(level) }
val subtitleRes = if (isAdaptiveMode) {
R.string.device_settings_adaptive_noise_description
} else {
R.string.device_settings_adaptive_noise_requires_adaptive
}
SettingsSliderItem(
icon = Icons.TwoTone.GraphicEq,
title = stringResource(R.string.device_settings_adaptive_noise_label),
subtitle = stringResource(R.string.device_settings_adaptive_noise_description),
subtitle = stringResource(subtitleRes),
value = sliderValue.toFloat(),
onValueChange = { sliderValue = it.toInt() },
onValueChangeFinished = { onLevelChange(sliderValue) },
valueRange = 0f..100f,
steps = 99,
enabled = enabled,
enabled = enabled && isAdaptiveMode,
valueLabel = { "${it.toInt()}%" },
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ sealed class AapSetting {
val enabled: Boolean,
) : AapSetting()

// UI-space 0..100 (100 = max noise reduction). Wire value is inverted — conversion lives in
// the device profile. Pro 3 silently accepts writes (no echo) but the value persists.
data class AdaptiveAudioNoise(
val level: Int,
) : AapSetting()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class DefaultAapDeviceProfile(
override fun encodeInitExt(): ByteArray? {
if (!model.features.needsInitExt) return null
return byteArrayOf(
0x04, 0x00, 0x04, 0x00, 0x4d, 0x00, 0x0e, 0x00,
0x04, 0x00, 0x04, 0x00, 0x4d, 0x00, 0xd7.toByte(), 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
)
}
Expand All @@ -101,7 +101,9 @@ class DefaultAapDeviceProfile(
is AapCommand.SetVolumeSwipeLength -> buildSettingsMessage(SETTING_VOLUME_SWIPE_LENGTH, command.value.wireValue)
is AapCommand.SetVolumeSwipe -> buildSettingsMessage(SETTING_VOLUME_SWIPE, encodeAppleBool(command.enabled))
is AapCommand.SetPersonalizedVolume -> buildSettingsMessage(SETTING_PERSONALIZED_VOLUME, encodeAppleBool(command.enabled))
is AapCommand.SetAdaptiveAudioNoise -> buildSettingsMessage(SETTING_ADAPTIVE_AUDIO_NOISE, command.level.coerceIn(0, 100))
// Wire semantics are inverted: wire 0 = max noise reduction, wire 100 = min (transparency-like).
// UI value 0..100 follows user intuition (100 = max NC), so flip on write/read.
is AapCommand.SetAdaptiveAudioNoise -> buildSettingsMessage(SETTING_ADAPTIVE_AUDIO_NOISE, 100 - command.level.coerceIn(0, 100))
is AapCommand.SetEndCallMuteMic -> buildEndCallMuteMicMessage(command.muteMic, command.endCall)
is AapCommand.SetMicrophoneMode -> buildSettingsMessage(SETTING_MICROPHONE_MODE, command.mode.wireValue)
is AapCommand.SetEarDetectionEnabled -> buildSettingsMessage(SETTING_EAR_DETECTION_ENABLED, encodeAppleBool(command.enabled))
Expand Down Expand Up @@ -242,7 +244,7 @@ class DefaultAapDeviceProfile(
AapSetting.PersonalizedVolume::class to AapSetting.PersonalizedVolume(enabled)
}
SETTING_ADAPTIVE_AUDIO_NOISE -> {
AapSetting.AdaptiveAudioNoise::class to AapSetting.AdaptiveAudioNoise(level = value)
AapSetting.AdaptiveAudioNoise::class to AapSetting.AdaptiveAudioNoise(level = 100 - value.coerceIn(0, 100))
}
SETTING_MICROPHONE_MODE -> {
val mode = AapSetting.MicrophoneMode.Mode.fromWire(value) ?: return null
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@
<string name="device_settings_tone_volume_description">Volume of confirmation sounds and ringtones</string>
<string name="device_settings_adaptive_noise_label">Adaptive Audio Noise</string>
<string name="device_settings_adaptive_noise_description">How much environmental noise is allowed through</string>
<string name="device_settings_adaptive_noise_requires_adaptive">Requires Adaptive noise control</string>
<string name="device_settings_press_speed_label">Press Speed</string>
<string name="device_settings_press_speed_description">How quickly you need to press for multi-press gestures</string>
<string name="device_settings_press_speed_default">Default</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,17 @@ class DefaultAapDeviceProfileTest : BaseAapSessionTest() {

@Nested
inner class AdaptiveAudioNoiseTests {
// UI level is inverted on the wire: UI 0 = max NC → wire 100, UI 100 = min NC → wire 0.
@Test fun `encode level 50`() { profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(50))[7] shouldBe 50.toByte() }
@Test fun `encode clamps to 0`() { profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(-5))[7] shouldBe 0x00.toByte() }
@Test fun `encode clamps to 100`() { profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(150))[7] shouldBe 0x64.toByte() }
@Test fun `decode level`() { decodeSetting<AapSetting.AdaptiveAudioNoise>(settingsMessage(0x2E, 64)).level shouldBe 64 }
@Test fun `encode clamps to 0`() { profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(-5))[7] shouldBe 0x64.toByte() }
@Test fun `encode clamps to 100`() { profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(150))[7] shouldBe 0x00.toByte() }
@Test fun `decode level`() { decodeSetting<AapSetting.AdaptiveAudioNoise>(settingsMessage(0x2E, 64)).level shouldBe 36 }
@Test fun `encode decode round-trip`() {
for (ui in listOf(0, 25, 50, 75, 100)) {
val encoded = profile.encodeCommand(AapCommand.SetAdaptiveAudioNoise(ui))
decodeSetting<AapSetting.AdaptiveAudioNoise>(AapMessage.Companion.parse(encoded)!!).level shouldBe ui
}
}
}

// ── EndCall / MuteMic ────────────────────────────────────
Expand Down