diff --git a/android/app/schemas/com.oppzippy.openscq30.room.AppDatabase/5.json b/android/app/schemas/com.oppzippy.openscq30.room.AppDatabase/5.json new file mode 100644 index 00000000..8b867416 --- /dev/null +++ b/android/app/schemas/com.oppzippy.openscq30.room.AppDatabase/5.json @@ -0,0 +1,112 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "3b07aa86fadee45ee085045a3eccae5b", + "entities": [ + { + "tableName": "equalizer_custom_profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `values` BLOB NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name" + ] + }, + "indices": [ + { + "name": "index_equalizer_custom_profile_values", + "unique": true, + "columnNames": [ + "values" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_equalizer_custom_profile_values` ON `${TABLE_NAME}` (`values`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "quick_preset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `ambientSoundMode` TEXT, `noiseCancelingMode` TEXT, `transparencyMode` TEXT, `customNoiseCanceling` INTEGER, `presetEqualizerProfile` TEXT, `customEqualizerProfileName` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ambientSoundMode", + "columnName": "ambientSoundMode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "noiseCancelingMode", + "columnName": "noiseCancelingMode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "transparencyMode", + "columnName": "transparencyMode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customNoiseCanceling", + "columnName": "customNoiseCanceling", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "presetEqualizerProfile", + "columnName": "presetEqualizerProfile", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customEqualizerProfileName", + "columnName": "customEqualizerProfileName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3b07aa86fadee45ee085045a3eccae5b')" + ] + } +} \ No newline at end of file diff --git a/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsQuickPresetsTest.kt b/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsQuickPresetsTest.kt index d7dad63e..f5086c6a 100644 --- a/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsQuickPresetsTest.kt +++ b/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsQuickPresetsTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasTextExactly import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput @@ -14,6 +15,7 @@ import com.oppzippy.openscq30.features.quickpresets.storage.QuickPresetDao import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode import com.oppzippy.openscq30.lib.bindings.PresetEqualizerProfile +import com.oppzippy.openscq30.lib.bindings.TransparencyMode import com.oppzippy.openscq30.ui.quickpresets.QuickPresetScreen import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -51,6 +53,8 @@ class DeviceSettingsQuickPresetsTest { private lateinit var name: SemanticsMatcher private lateinit var ambientSoundMode: SemanticsMatcher private lateinit var noiseCancelingMode: SemanticsMatcher + private lateinit var transparencyMode: SemanticsMatcher + private lateinit var customNoiseCanceling: SemanticsMatcher private lateinit var equalizer: SemanticsMatcher private lateinit var presetProfile: SemanticsMatcher private lateinit var customProfile: SemanticsMatcher @@ -64,6 +68,10 @@ class DeviceSettingsQuickPresetsTest { hasTextExactly(composeRule.activity.getString(R.string.ambient_sound_mode)) noiseCancelingMode = hasTextExactly(composeRule.activity.getString(R.string.noise_canceling_mode)) + transparencyMode = + hasTextExactly(composeRule.activity.getString(R.string.transparency_mode)) + customNoiseCanceling = + hasTextExactly(composeRule.activity.getString(R.string.custom_noise_canceling)) equalizer = hasTextExactly(composeRule.activity.getString(R.string.equalizer)) presetProfile = hasTestTag("quickPresetPresetEqualizerProfile") customProfile = hasTestTag("quickPresetCustomEqualizerProfile") @@ -95,6 +103,21 @@ class DeviceSettingsQuickPresetsTest { assertEquals(AmbientSoundMode.Transparency, quickPresetDao.get(0)?.ambientSoundMode) } + @Test + fun acceptsTransparencyMode() = runTest { + composeRule.setContent { + QuickPresetScreen() + } + assertEquals(null, quickPresetDao.get(0)?.transparencyMode) + + composeRule.onNode(transparencyMode).performClick() + assertEquals(TransparencyMode.VocalMode, quickPresetDao.get(0)?.transparencyMode) + + composeRule.onNodeWithText(composeRule.activity.getString(R.string.fully_transparent)) + .performClick() + assertEquals(TransparencyMode.FullyTransparent, quickPresetDao.get(0)?.transparencyMode) + } + @Test fun acceptsNoiseCancelingMode() = runTest { composeRule.setContent { @@ -109,6 +132,21 @@ class DeviceSettingsQuickPresetsTest { assertEquals(NoiseCancelingMode.Outdoor, quickPresetDao.get(0)?.noiseCancelingMode) } + @Test + fun acceptsCustomNoiseCanceling() = runTest { + composeRule.setContent { + QuickPresetScreen() + } + assertEquals(null, quickPresetDao.get(0)?.customNoiseCanceling) + + composeRule.onNode(customNoiseCanceling).performClick() + assertEquals(0.toShort(), quickPresetDao.get(0)?.customNoiseCanceling?.value()) + + composeRule.onNodeWithTag("customNoiseCancelingSlider").performClick() + // clicks in the middle of the 0-10 slider, which is 5 + assertEquals(5.toShort(), quickPresetDao.get(0)?.customNoiseCanceling?.value()) + } + @Test fun acceptsPresetEqualizerProfile() = runTest { composeRule.setContent { diff --git a/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsSoundModeTest.kt b/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsSoundModeTest.kt index 73761d3d..9c41cba3 100644 --- a/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsSoundModeTest.kt +++ b/android/app/src/androidTest/java/com/oppzippy/openscq30/DeviceSettingsSoundModeTest.kt @@ -3,6 +3,7 @@ package com.oppzippy.openscq30 import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.assertIsNotSelected import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasTextExactly import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick @@ -11,6 +12,7 @@ import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode import com.oppzippy.openscq30.lib.bindings.SoundModes import com.oppzippy.openscq30.lib.bindings.TransparencyMode +import com.oppzippy.openscq30.ui.soundmode.NoiseCancelingType import com.oppzippy.openscq30.ui.soundmode.SoundModeSettings import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -39,7 +41,11 @@ class DeviceSettingsSoundModeTest { private lateinit var noiseCancelingModes: List private lateinit var outdoor: SemanticsMatcher private lateinit var indoor: SemanticsMatcher + private lateinit var custom: SemanticsMatcher private lateinit var transport: SemanticsMatcher + private lateinit var fullyTransparent: SemanticsMatcher + private lateinit var vocalMode: SemanticsMatcher + private lateinit var customNoiseCanceling: SemanticsMatcher @Before fun setUp() { @@ -50,9 +56,14 @@ class DeviceSettingsSoundModeTest { noiseCanceling = hasTextExactly(composeRule.activity.getString(R.string.noise_canceling)) outdoor = hasTextExactly(composeRule.activity.getString(R.string.outdoor)) indoor = hasTextExactly(composeRule.activity.getString(R.string.indoor)) + custom = hasTextExactly(composeRule.activity.getString(R.string.custom)) transport = hasTextExactly(composeRule.activity.getString(R.string.transport)) + fullyTransparent = + hasTextExactly(composeRule.activity.getString(R.string.fully_transparent)) + vocalMode = hasTextExactly(composeRule.activity.getString(R.string.vocal_mode)) + customNoiseCanceling = hasTestTag("customNoiseCancelingSlider") ambientSoundModes = listOf(normal, transparency, noiseCanceling) - noiseCancelingModes = listOf(outdoor, indoor, transport) + noiseCancelingModes = listOf(outdoor, indoor, transport, custom) } @Test @@ -83,6 +94,8 @@ class DeviceSettingsSoundModeTest { CustomNoiseCanceling(0), ), onAmbientSoundModeChange = onAmbientSoundModeChange, + hasTransparencyModes = false, + noiseCancelingType = NoiseCancelingType.None, ) } composeRule.onNode(transparency).performClick() @@ -91,6 +104,30 @@ class DeviceSettingsSoundModeTest { } } + @Test + fun setsTransparencyMode() { + val onTransparencyModeChange = + mockk<(transparencyMode: TransparencyMode) -> Unit>(relaxed = true) + + composeRule.setContent { + SoundModeSettings( + soundModes = SoundModes( + AmbientSoundMode.Normal, + NoiseCancelingMode.Indoor, + TransparencyMode.VocalMode, + CustomNoiseCanceling(0), + ), + onTransparencyModeChange = onTransparencyModeChange, + hasTransparencyModes = true, + noiseCancelingType = NoiseCancelingType.None, + ) + } + composeRule.onNode(fullyTransparent).performClick() + verify(exactly = 1) { + onTransparencyModeChange(TransparencyMode.FullyTransparent) + } + } + @Test fun setsNoiseCancelingMode() { val onNoiseCancelingModeChange = @@ -105,6 +142,8 @@ class DeviceSettingsSoundModeTest { CustomNoiseCanceling(0), ), onNoiseCancelingModeChange = onNoiseCancelingModeChange, + hasTransparencyModes = true, + noiseCancelingType = NoiseCancelingType.Custom, ) } composeRule.onNode(outdoor).performClick() @@ -113,6 +152,31 @@ class DeviceSettingsSoundModeTest { } } + @Test + fun setsCustomNoiseCanceling() { + val onCustomNoiseCancelingChange = + mockk<(customNoiseCanceling: CustomNoiseCanceling) -> Unit>(relaxed = true) + + composeRule.setContent { + SoundModeSettings( + soundModes = SoundModes( + AmbientSoundMode.Normal, + NoiseCancelingMode.Custom, + TransparencyMode.VocalMode, + CustomNoiseCanceling(0), + ), + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, + hasTransparencyModes = false, + noiseCancelingType = NoiseCancelingType.Custom, + ) + } + // clicks in the middle of the 0-10 slider, so 5 + composeRule.onNode(customNoiseCanceling).performClick() + verify(exactly = 1) { + onCustomNoiseCancelingChange(any()) + } + } + private fun renderInitialSoundMode( ambientSoundMode: AmbientSoundMode, noiseCancelingMode: NoiseCancelingMode, @@ -125,6 +189,8 @@ class DeviceSettingsSoundModeTest { TransparencyMode.VocalMode, CustomNoiseCanceling(0), ), + hasTransparencyModes = true, + noiseCancelingType = NoiseCancelingType.Custom, ) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/features/quickpresets/storage/QuickPreset.kt b/android/app/src/main/java/com/oppzippy/openscq30/features/quickpresets/storage/QuickPreset.kt index a895eadd..81c2640b 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/features/quickpresets/storage/QuickPreset.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/features/quickpresets/storage/QuickPreset.kt @@ -3,8 +3,10 @@ package com.oppzippy.openscq30.features.quickpresets.storage import androidx.room.Entity import androidx.room.PrimaryKey import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode import com.oppzippy.openscq30.lib.bindings.PresetEqualizerProfile +import com.oppzippy.openscq30.lib.bindings.TransparencyMode @Entity( tableName = "quick_preset", @@ -14,6 +16,8 @@ data class QuickPreset( val name: String? = null, val ambientSoundMode: AmbientSoundMode? = null, val noiseCancelingMode: NoiseCancelingMode? = null, + val transparencyMode: TransparencyMode? = null, + val customNoiseCanceling: CustomNoiseCanceling? = null, val presetEqualizerProfile: PresetEqualizerProfile? = null, val customEqualizerProfileName: String? = null, ) diff --git a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/Packet.kt b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/Packet.kt index c32d6d7f..1de24d44 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/Packet.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/Packet.kt @@ -24,8 +24,8 @@ sealed class Packet { } } - class SoundModeUpdate(val packet: SoundModeUpdatePacket) : Packet() - class StateUpdate(val packet: StateUpdatePacket) : Packet() - class SetSoundModeOk(val packet: SetSoundModeOkPacket) : Packet() - class SetEqualizerOk(val packet: SetEqualizerOkPacket) : Packet() + class SoundModeUpdate(val inner: SoundModeUpdatePacket) : Packet() + class StateUpdate(val inner: StateUpdatePacket) : Packet() + class SetSoundModeOk(val inner: SetSoundModeOkPacket) : Packet() + class SetEqualizerOk(val inner: SetEqualizerOkPacket) : Packet() } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceFactoryImpl.kt b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceFactoryImpl.kt index ac0f711d..20552afd 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceFactoryImpl.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceFactoryImpl.kt @@ -30,6 +30,6 @@ class SoundcoreDeviceFactoryImpl(private val context: Context) : SoundcoreDevice } val packet = callbacks.packetsFlow.first { it is Packet.StateUpdate } as Packet.StateUpdate - return SoundcoreDeviceImpl(gatt, callbacks, scope, packet.packet.toSoundcoreDeviceState()) + return SoundcoreDeviceImpl(gatt, callbacks, scope, packet.inner.toSoundcoreDeviceState()) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceImpl.kt b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceImpl.kt index 36a87d4e..34bd5ce2 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceImpl.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/features/soundcoredevice/impl/SoundcoreDeviceImpl.kt @@ -10,12 +10,12 @@ import com.oppzippy.openscq30.lib.bindings.SetEqualizerPacket import com.oppzippy.openscq30.lib.bindings.SetSoundModePacket import com.oppzippy.openscq30.lib.bindings.SoundModes import com.oppzippy.openscq30.lib.wrapper.SoundcoreDeviceState -import com.oppzippy.openscq30.lib.wrapper.toSoundcoreDeviceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import kotlin.jvm.optionals.getOrNull @SuppressLint("MissingPermission") class SoundcoreDeviceImpl( @@ -37,25 +37,35 @@ class SoundcoreDeviceImpl( init { scope.launch { - callbacks.packetsFlow.collect { - when (it) { + callbacks.packetsFlow.collect { packet -> + when (packet) { is Packet.SoundModeUpdate -> { _stateFlow.value = - _stateFlow.value.let { state -> - state.copy( - soundModes = state.soundModes?.let { soundModes -> - SoundModes( - soundModes.ambientSoundMode(), - soundModes.noiseCancelingMode(), - soundModes.transparencyMode(), - soundModes.customNoiseCanceling(), - ) - }, - ) - } + _stateFlow.value.copy( + soundModes = + SoundModes( + packet.inner.ambientSoundMode(), + packet.inner.noiseCancelingMode(), + packet.inner.transparencyMode(), + packet.inner.customNoiseCanceling(), + ), + ) } - is Packet.StateUpdate -> _stateFlow.value = it.packet.toSoundcoreDeviceState() + is Packet.StateUpdate -> + _stateFlow.value = + _stateFlow.value.copy( + featureFlags = packet.inner.featureFlags(), + firmwareVersion = if (packet.inner.firmwareVersion().isPresent) { + packet.inner.firmwareVersion().asInt + } else { + null + }, + equalizerConfiguration = packet.inner.equalizerConfiguration(), + serialNumber = packet.inner.serialNumber().getOrNull(), + soundModes = packet.inner.soundModes().getOrNull(), + ) + is Packet.SetSoundModeOk -> {} is Packet.SetEqualizerOk -> {} } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/lib/bindings/SoundModeUpdatePacket.java b/android/app/src/main/java/com/oppzippy/openscq30/lib/bindings/SoundModeUpdatePacket.java index 6b255959..2206dad2 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/lib/bindings/SoundModeUpdatePacket.java +++ b/android/app/src/main/java/com/oppzippy/openscq30/lib/bindings/SoundModeUpdatePacket.java @@ -25,6 +25,22 @@ public final NoiseCancelingMode noiseCancelingMode() { } private static native int do_noiseCancelingMode(long self); + public final TransparencyMode transparencyMode() { + int ret = do_transparencyMode(mNativeObj); + TransparencyMode convRet = TransparencyMode.fromInt(ret); + + return convRet; + } + private static native int do_transparencyMode(long self); + + public final @NonNull CustomNoiseCanceling customNoiseCanceling() { + long ret = do_customNoiseCanceling(mNativeObj); + CustomNoiseCanceling convRet = new CustomNoiseCanceling(InternalPointerMarker.RAW_PTR, ret); + + return convRet; + } + private static native long do_customNoiseCanceling(long self); + public synchronized void delete() { if (mNativeObj != 0) { do_delete(mNativeObj); diff --git a/android/app/src/main/java/com/oppzippy/openscq30/room/AppDatabase.kt b/android/app/src/main/java/com/oppzippy/openscq30/room/AppDatabase.kt index bf3db362..bce6a08d 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/room/AppDatabase.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/room/AppDatabase.kt @@ -12,7 +12,7 @@ import com.oppzippy.openscq30.features.quickpresets.storage.QuickPreset import com.oppzippy.openscq30.features.quickpresets.storage.QuickPresetDao @Database( - version = 4, + version = 5, entities = [ CustomProfile::class, QuickPreset::class, @@ -25,6 +25,7 @@ import com.oppzippy.openscq30.features.quickpresets.storage.QuickPresetDao to = 4, spec = AppDatabase.CustomEqualizerProfileNameMigration::class, ), + AutoMigration(from = 4, to = 5), ], ) @TypeConverters(Converters::class) diff --git a/android/app/src/main/java/com/oppzippy/openscq30/room/Converters.kt b/android/app/src/main/java/com/oppzippy/openscq30/room/Converters.kt index 124def26..622207c5 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/room/Converters.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/room/Converters.kt @@ -1,6 +1,7 @@ package com.oppzippy.openscq30.room import androidx.room.TypeConverter +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling class Converters { @TypeConverter @@ -12,4 +13,14 @@ class Converters { fun toByteList(bytes: ByteArray): List { return bytes.asList() } + + @TypeConverter + fun fromCustomNoiseCanceling(customNoiseCanceling: CustomNoiseCanceling): Short { + return customNoiseCanceling.value() + } + + @TypeConverter + fun toCustomNoiseCanceling(value: Short): CustomNoiseCanceling { + return CustomNoiseCanceling(value) + } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/OpenSCQ30Root.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/OpenSCQ30Root.kt index 0b13ea23..59fecd74 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/OpenSCQ30Root.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/OpenSCQ30Root.kt @@ -71,6 +71,7 @@ fun OpenSCQ30Root( clip = false, ) }, + label = "Selection to Settings animation", ) { animationIsConnected -> if (animationIsConnected) { DeviceSettingsScreen( @@ -81,11 +82,21 @@ fun OpenSCQ30Root( viewModel.setSoundModes(ambientSoundMode = it) } }, + onTransparencyModeChange = { + withErrorToast(context) { + viewModel.setSoundModes(transparencyMode = it) + } + }, onNoiseCancelingModeChange = { withErrorToast(context) { viewModel.setSoundModes(noiseCancelingMode = it) } }, + onCustomNoiseCancelingChange = { + withErrorToast(context) { + viewModel.setSoundModes(customNoiseCanceling = it) + } + }, onEqualizerConfigurationChange = { withErrorToast(context) { viewModel.setEqualizerConfiguration(it) diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/DeviceSettingsScreen.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/DeviceSettingsScreen.kt index bfc43f75..4358db1f 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/DeviceSettingsScreen.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/DeviceSettingsScreen.kt @@ -2,8 +2,10 @@ package com.oppzippy.openscq30.ui.devicesettings import androidx.compose.runtime.Composable import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling import com.oppzippy.openscq30.lib.bindings.EqualizerConfiguration import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode +import com.oppzippy.openscq30.lib.bindings.TransparencyMode import com.oppzippy.openscq30.ui.devicesettings.composables.DeviceSettings import com.oppzippy.openscq30.ui.devicesettings.composables.Disconnected import com.oppzippy.openscq30.ui.devicesettings.models.UiDeviceState @@ -14,7 +16,9 @@ fun DeviceSettingsScreen( onBack: () -> Unit = {}, deviceState: UiDeviceState, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode) -> Unit = {}, + onTransparencyModeChange: (transparencyMode: TransparencyMode) -> Unit = {}, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode) -> Unit = {}, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling) -> Unit = {}, onEqualizerConfigurationChange: (equalizerConfiguration: EqualizerConfiguration) -> Unit = {}, ) { deviceState.let { uiDeviceState -> @@ -26,6 +30,8 @@ fun DeviceSettingsScreen( onAmbientSoundModeChange = onAmbientSoundModeChange, onNoiseCancelingModeChange = onNoiseCancelingModeChange, onEqualizerConfigurationChange = onEqualizerConfigurationChange, + onTransparencyModeChange = onTransparencyModeChange, + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, ) } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/composables/DeviceSettings.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/composables/DeviceSettings.kt index d9687140..97e81bac 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/composables/DeviceSettings.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/devicesettings/composables/DeviceSettings.kt @@ -23,6 +23,7 @@ import androidx.navigation.compose.rememberNavController import com.oppzippy.openscq30.R import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling +import com.oppzippy.openscq30.lib.bindings.DeviceFeatureFlags import com.oppzippy.openscq30.lib.bindings.EqualizerConfiguration import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode import com.oppzippy.openscq30.lib.bindings.PresetEqualizerProfile @@ -33,6 +34,7 @@ import com.oppzippy.openscq30.ui.devicesettings.Screen import com.oppzippy.openscq30.ui.devicesettings.models.UiDeviceState import com.oppzippy.openscq30.ui.equalizer.EqualizerSettings import com.oppzippy.openscq30.ui.quickpresets.QuickPresetScreen +import com.oppzippy.openscq30.ui.soundmode.NoiseCancelingType import com.oppzippy.openscq30.ui.soundmode.SoundModeSettings import com.oppzippy.openscq30.ui.theme.OpenSCQ30Theme @@ -42,7 +44,9 @@ fun DeviceSettings( uiState: UiDeviceState.Connected, onBack: () -> Unit = {}, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode) -> Unit = {}, + onTransparencyModeChange: (transparencyMode: TransparencyMode) -> Unit = {}, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode) -> Unit = {}, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling) -> Unit = {}, onEqualizerConfigurationChange: (equalizerConfiguration: EqualizerConfiguration) -> Unit = {}, ) { val navController = rememberNavController() @@ -93,8 +97,18 @@ fun DeviceSettings( composable(Screen.General.route) { SoundModeSettings( soundModes = soundModes, + hasTransparencyModes = uiState.deviceState.featureFlags and DeviceFeatureFlags.transparencyModes() != 0, + noiseCancelingType = if (uiState.deviceState.featureFlags and DeviceFeatureFlags.customNoiseCanceling() != 0) { + NoiseCancelingType.Custom + } else if (uiState.deviceState.featureFlags and DeviceFeatureFlags.noiseCancelingMode() != 0) { + NoiseCancelingType.Normal + } else { + NoiseCancelingType.None + }, onAmbientSoundModeChange = onAmbientSoundModeChange, + onTransparencyModeChange = onTransparencyModeChange, onNoiseCancelingModeChange = onNoiseCancelingModeChange, + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, ) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/QuickPresetScreen.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/QuickPresetScreen.kt index 0dad34e1..1466cec5 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/QuickPresetScreen.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/QuickPresetScreen.kt @@ -19,7 +19,9 @@ import com.oppzippy.openscq30.features.equalizer.storage.CustomProfile import com.oppzippy.openscq30.features.quickpresets.storage.QuickPreset import com.oppzippy.openscq30.features.soundcoredevice.service.SoundcoreDeviceNotification import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode +import com.oppzippy.openscq30.lib.bindings.TransparencyMode import com.oppzippy.openscq30.ui.quickpresets.composables.QuickPresetConfiguration import com.oppzippy.openscq30.ui.quickpresets.composables.QuickPresetSelection import com.oppzippy.openscq30.ui.quickpresets.models.QuickPresetEqualizerConfiguration @@ -71,6 +73,12 @@ fun QuickPresetScreen(viewModel: QuickPresetViewModel = hiltViewModel()) { preset.copy(noiseCancelingMode = it), ) }, + onTransparencyModeChange = { + viewModel.upsertQuickPreset(preset.copy(transparencyMode = it)) + }, + onCustomNoiseCancelingChange = { + viewModel.upsertQuickPreset(preset.copy(customNoiseCanceling = it)) + }, onEqualizerChange = { val presetEqualizerProfile = if (it is QuickPresetEqualizerConfiguration.PresetProfile) { @@ -106,6 +114,8 @@ private fun QuickPresetScreen( onSelectedIndexChange: (index: Int) -> Unit = {}, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode?) -> Unit = {}, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode?) -> Unit = {}, + onTransparencyModeChange: (transparencyMode: TransparencyMode?) -> Unit = {}, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling?) -> Unit = {}, onEqualizerChange: (config: QuickPresetEqualizerConfiguration?) -> Unit = {}, onNameChange: (name: String?) -> Unit = {}, ) { @@ -119,6 +129,8 @@ private fun QuickPresetScreen( defaultName = stringResource(R.string.quick_preset_number, preset.id + 1), ambientSoundMode = preset.ambientSoundMode, noiseCancelingMode = preset.noiseCancelingMode, + transparencyMode = preset.transparencyMode, + customNoiseCanceling = preset.customNoiseCanceling, equalizerConfiguration = if (preset.presetEqualizerProfile != null) { QuickPresetEqualizerConfiguration.PresetProfile(preset.presetEqualizerProfile) } else if (preset.customEqualizerProfileName != null) { @@ -129,6 +141,8 @@ private fun QuickPresetScreen( customEqualizerProfiles = customEqualizerProfiles, onAmbientSoundModeChange = onAmbientSoundModeChange, onNoiseCancelingModeChange = onNoiseCancelingModeChange, + onTransparencyModeChange = onTransparencyModeChange, + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, onEqualizerChange = onEqualizerChange, onNameChange = onNameChange, ) diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/composables/QuickPresetConfiguration.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/composables/QuickPresetConfiguration.kt index cbfd1b82..a89e71a4 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/composables/QuickPresetConfiguration.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/quickpresets/composables/QuickPresetConfiguration.kt @@ -20,13 +20,17 @@ import androidx.compose.ui.tooling.preview.Preview import com.oppzippy.openscq30.R import com.oppzippy.openscq30.features.equalizer.storage.CustomProfile import com.oppzippy.openscq30.lib.bindings.AmbientSoundMode +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling import com.oppzippy.openscq30.lib.bindings.NoiseCancelingMode import com.oppzippy.openscq30.lib.bindings.PresetEqualizerProfile +import com.oppzippy.openscq30.lib.bindings.TransparencyMode import com.oppzippy.openscq30.lib.extensions.resources.toStringResource import com.oppzippy.openscq30.ui.equalizer.composables.CustomProfileSelection import com.oppzippy.openscq30.ui.quickpresets.models.QuickPresetEqualizerConfiguration import com.oppzippy.openscq30.ui.soundmode.AmbientSoundModeSelection +import com.oppzippy.openscq30.ui.soundmode.CustomNoiseCancelingSelection import com.oppzippy.openscq30.ui.soundmode.NoiseCancelingModeSelection +import com.oppzippy.openscq30.ui.soundmode.TransparencyModeSelection import com.oppzippy.openscq30.ui.theme.OpenSCQ30Theme import com.oppzippy.openscq30.ui.utils.CheckboxWithLabel import com.oppzippy.openscq30.ui.utils.Dropdown @@ -38,10 +42,14 @@ fun QuickPresetConfiguration( defaultName: String, ambientSoundMode: AmbientSoundMode?, noiseCancelingMode: NoiseCancelingMode?, + transparencyMode: TransparencyMode?, + customNoiseCanceling: CustomNoiseCanceling?, equalizerConfiguration: QuickPresetEqualizerConfiguration?, customEqualizerProfiles: List, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode?) -> Unit = {}, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode?) -> Unit = {}, + onTransparencyModeChange: (transparencyMode: TransparencyMode?) -> Unit = {}, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling?) -> Unit = {}, onEqualizerChange: (config: QuickPresetEqualizerConfiguration?) -> Unit = {}, onNameChange: (name: String?) -> Unit = {}, ) { @@ -69,9 +77,26 @@ fun QuickPresetConfiguration( AmbientSoundModeSelection( ambientSoundMode = ambientSoundMode, onAmbientSoundModeChange = onAmbientSoundModeChange, + hasNoiseCanceling = true, ) } Divider() + + CheckboxWithLabel( + text = stringResource(R.string.transparency_mode), + isChecked = transparencyMode != null, + onCheckedChange = { + onTransparencyModeChange(if (it) TransparencyMode.VocalMode else null) + }, + ) + if (transparencyMode != null) { + TransparencyModeSelection( + transparencyMode = transparencyMode, + onTransparencyModeChange = onTransparencyModeChange, + ) + } + Divider() + CheckboxWithLabel( text = stringResource(R.string.noise_canceling_mode), isChecked = noiseCancelingMode != null, @@ -83,6 +108,22 @@ fun QuickPresetConfiguration( NoiseCancelingModeSelection( noiseCancelingMode = noiseCancelingMode, onNoiseCancelingModeChange = onNoiseCancelingModeChange, + hasCustomNoiseCanceling = true, + ) + } + Divider() + + CheckboxWithLabel( + text = stringResource(R.string.custom_noise_canceling), + isChecked = customNoiseCanceling != null, + onCheckedChange = { + onCustomNoiseCancelingChange(if (it) CustomNoiseCanceling(0) else null) + }, + ) + if (customNoiseCanceling != null) { + CustomNoiseCancelingSelection( + customNoiseCanceling = customNoiseCanceling, + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, ) } Divider() @@ -150,6 +191,8 @@ private fun PreviewQuickPresetConfiguration() { defaultName = "Quick Preset 1", ambientSoundMode = AmbientSoundMode.NoiseCanceling, noiseCancelingMode = NoiseCancelingMode.Transport, + transparencyMode = TransparencyMode.VocalMode, + customNoiseCanceling = CustomNoiseCanceling(5), customEqualizerProfiles = emptyList(), equalizerConfiguration = QuickPresetEqualizerConfiguration.PresetProfile( PresetEqualizerProfile.SoundcoreSignature, diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/AmbientSoundModeSelection.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/AmbientSoundModeSelection.kt index e4771428..2ff7b60e 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/AmbientSoundModeSelection.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/AmbientSoundModeSelection.kt @@ -12,14 +12,19 @@ import com.oppzippy.openscq30.ui.utils.LabeledRadioButtonGroup fun AmbientSoundModeSelection( ambientSoundMode: AmbientSoundMode, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode) -> Unit, + hasNoiseCanceling: Boolean, ) { + val values = linkedMapOf( + Pair(AmbientSoundMode.Normal, stringResource(R.string.normal)), + Pair(AmbientSoundMode.Transparency, stringResource(R.string.transparency)), + ) + if (hasNoiseCanceling) { + values[AmbientSoundMode.NoiseCanceling] = stringResource(R.string.noise_canceling) + } + LabeledRadioButtonGroup( selectedValue = ambientSoundMode, - values = linkedMapOf( - Pair(AmbientSoundMode.Normal, stringResource(R.string.normal)), - Pair(AmbientSoundMode.Transparency, stringResource(R.string.transparency)), - Pair(AmbientSoundMode.NoiseCanceling, stringResource(R.string.noise_canceling)), - ), + values = values, onValueChange = onAmbientSoundModeChange, ) } @@ -31,6 +36,7 @@ private fun PreviewAmbientSoundModeSelection() { AmbientSoundModeSelection( ambientSoundMode = AmbientSoundMode.Normal, onAmbientSoundModeChange = {}, + hasNoiseCanceling = true, ) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/CustomNoiseCancelingSelection.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/CustomNoiseCancelingSelection.kt new file mode 100644 index 00000000..8e25118e --- /dev/null +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/CustomNoiseCancelingSelection.kt @@ -0,0 +1,37 @@ +package com.oppzippy.openscq30.ui.soundmode + +import androidx.compose.material3.Slider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.tooling.preview.Preview +import com.oppzippy.openscq30.lib.bindings.CustomNoiseCanceling +import com.oppzippy.openscq30.ui.theme.OpenSCQ30Theme +import kotlin.math.roundToInt + +@Composable +fun CustomNoiseCancelingSelection( + customNoiseCanceling: CustomNoiseCanceling, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling) -> Unit, +) { + Slider( + value = customNoiseCanceling.value().toFloat(), + onValueChange = { + onCustomNoiseCancelingChange(CustomNoiseCanceling(it.roundToInt().toShort())) + }, + valueRange = 0f..10f, + steps = 11, + modifier = Modifier.testTag("customNoiseCancelingSlider"), + ) +} + +@Preview(showBackground = true) +@Composable +private fun PreviewAmbientSoundModeSelection() { + OpenSCQ30Theme { + CustomNoiseCancelingSelection( + customNoiseCanceling = CustomNoiseCanceling(1), + onCustomNoiseCancelingChange = {}, + ) + } +} diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingModeSelection.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingModeSelection.kt index 80b733da..564c16f0 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingModeSelection.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingModeSelection.kt @@ -12,14 +12,20 @@ import com.oppzippy.openscq30.ui.utils.LabeledRadioButtonGroup fun NoiseCancelingModeSelection( noiseCancelingMode: NoiseCancelingMode, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode) -> Unit, + hasCustomNoiseCanceling: Boolean, ) { + val values = linkedMapOf( + Pair(NoiseCancelingMode.Transport, stringResource(R.string.transport)), + Pair(NoiseCancelingMode.Indoor, stringResource(R.string.indoor)), + Pair(NoiseCancelingMode.Outdoor, stringResource(R.string.outdoor)), + ) + if (hasCustomNoiseCanceling) { + values[NoiseCancelingMode.Custom] = stringResource(R.string.custom) + } + LabeledRadioButtonGroup( selectedValue = noiseCancelingMode, - values = linkedMapOf( - Pair(NoiseCancelingMode.Transport, stringResource(R.string.transport)), - Pair(NoiseCancelingMode.Indoor, stringResource(R.string.indoor)), - Pair(NoiseCancelingMode.Outdoor, stringResource(R.string.outdoor)), - ), + values = values, onValueChange = onNoiseCancelingModeChange, ) } @@ -31,6 +37,7 @@ private fun PreviewNoiseCancelingModeSelection() { NoiseCancelingModeSelection( noiseCancelingMode = NoiseCancelingMode.Transport, onNoiseCancelingModeChange = {}, + hasCustomNoiseCanceling = true, ) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingType.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingType.kt new file mode 100644 index 00000000..4817a6e5 --- /dev/null +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/NoiseCancelingType.kt @@ -0,0 +1,7 @@ +package com.oppzippy.openscq30.ui.soundmode + +enum class NoiseCancelingType { + None, + Normal, + Custom, +} diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/SoundModeSettings.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/SoundModeSettings.kt index 4f0b15c6..8205cbb2 100644 --- a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/SoundModeSettings.kt +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/SoundModeSettings.kt @@ -22,21 +22,45 @@ import com.oppzippy.openscq30.ui.theme.OpenSCQ30Theme fun SoundModeSettings( modifier: Modifier = Modifier, soundModes: SoundModes, + hasTransparencyModes: Boolean, + noiseCancelingType: NoiseCancelingType, onAmbientSoundModeChange: (ambientSoundMode: AmbientSoundMode) -> Unit = {}, + onTransparencyModeChange: (transparencyMode: TransparencyMode) -> Unit = {}, onNoiseCancelingModeChange: (noiseCancelingMode: NoiseCancelingMode) -> Unit = {}, + onCustomNoiseCancelingChange: (customNoiseCanceling: CustomNoiseCanceling) -> Unit = {}, ) { Column(modifier = modifier) { GroupHeader(stringResource(R.string.ambient_sound_mode)) AmbientSoundModeSelection( ambientSoundMode = soundModes.ambientSoundMode(), onAmbientSoundModeChange = onAmbientSoundModeChange, + hasNoiseCanceling = noiseCancelingType != NoiseCancelingType.None, ) - Spacer(modifier = Modifier.padding(vertical = 8.dp)) - GroupHeader(stringResource(R.string.noise_canceling_mode)) - NoiseCancelingModeSelection( - noiseCancelingMode = soundModes.noiseCancelingMode(), - onNoiseCancelingModeChange = onNoiseCancelingModeChange, - ) + if (hasTransparencyModes) { + Spacer(modifier = Modifier.padding(vertical = 8.dp)) + GroupHeader(stringResource(R.string.transparency_mode)) + TransparencyModeSelection( + transparencyMode = soundModes.transparencyMode(), + onTransparencyModeChange = onTransparencyModeChange, + ) + } + if (noiseCancelingType != NoiseCancelingType.None) { + Spacer(modifier = Modifier.padding(vertical = 8.dp)) + GroupHeader(stringResource(R.string.noise_canceling_mode)) + NoiseCancelingModeSelection( + noiseCancelingMode = soundModes.noiseCancelingMode(), + onNoiseCancelingModeChange = onNoiseCancelingModeChange, + hasCustomNoiseCanceling = true, + ) + } + if (noiseCancelingType == NoiseCancelingType.Custom && soundModes.noiseCancelingMode() == NoiseCancelingMode.Custom) { + Spacer(modifier = Modifier.padding(vertical = 8.dp)) + GroupHeader(stringResource(R.string.custom_noise_canceling)) + CustomNoiseCancelingSelection( + customNoiseCanceling = soundModes.customNoiseCanceling(), + onCustomNoiseCancelingChange = onCustomNoiseCancelingChange, + ) + } } } @@ -60,6 +84,8 @@ private fun PreviewSoundModeSettings() { TransparencyMode.VocalMode, CustomNoiseCanceling(0), ), + hasTransparencyModes = true, + noiseCancelingType = NoiseCancelingType.Custom, ) } } diff --git a/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/TransparencyModeSelection.kt b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/TransparencyModeSelection.kt new file mode 100644 index 00000000..8744a3c9 --- /dev/null +++ b/android/app/src/main/java/com/oppzippy/openscq30/ui/soundmode/TransparencyModeSelection.kt @@ -0,0 +1,35 @@ +package com.oppzippy.openscq30.ui.soundmode + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.oppzippy.openscq30.R +import com.oppzippy.openscq30.lib.bindings.TransparencyMode +import com.oppzippy.openscq30.ui.theme.OpenSCQ30Theme +import com.oppzippy.openscq30.ui.utils.LabeledRadioButtonGroup + +@Composable +fun TransparencyModeSelection( + transparencyMode: TransparencyMode, + onTransparencyModeChange: (transparencyMode: TransparencyMode) -> Unit, +) { + LabeledRadioButtonGroup( + selectedValue = transparencyMode, + values = linkedMapOf( + Pair(TransparencyMode.FullyTransparent, stringResource(R.string.fully_transparent)), + Pair(TransparencyMode.VocalMode, stringResource(R.string.vocal_mode)), + ), + onValueChange = onTransparencyModeChange, + ) +} + +@Preview(showBackground = true) +@Composable +private fun PreviewAmbientSoundModeSelection() { + OpenSCQ30Theme { + TransparencyModeSelection( + transparencyMode = TransparencyMode.VocalMode, + onTransparencyModeChange = {}, + ) + } +} diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index c0b585bf..cbc1ff92 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -89,4 +89,8 @@ Quick Preset %d Notification Permission is Required Preset Profile + Vocal Mode + Fully Transparent + Custom Noise Canceling + Transparency Mode diff --git a/android/src/packets/inbound/sound_mode_update_packet.rs b/android/src/packets/inbound/sound_mode_update_packet.rs index a149cfa9..00cf62ac 100644 --- a/android/src/packets/inbound/sound_mode_update_packet.rs +++ b/android/src/packets/inbound/sound_mode_update_packet.rs @@ -1,4 +1,7 @@ -use crate::packets::structures::{AmbientSoundMode, NoiseCancelingMode}; +use crate::{ + packets::structures::{AmbientSoundMode, NoiseCancelingMode}, + CustomNoiseCanceling, TransparencyMode, +}; use openscq30_lib::packets::inbound::SoundModeUpdatePacket as LibSoundModeUpdatePacket; use rifgen::rifgen_attr::generate_interface; @@ -20,6 +23,16 @@ impl SoundModeUpdatePacket { pub fn noise_canceling_mode(&self) -> NoiseCancelingMode { self.0.noise_canceling_mode.into() } + + #[generate_interface] + pub fn transparency_mode(&self) -> TransparencyMode { + self.0.transparency_mode.into() + } + + #[generate_interface] + pub fn custom_noise_canceling(&self) -> CustomNoiseCanceling { + self.0.custom_noise_canceling.into() + } } impl From for SoundModeUpdatePacket {