androidandroid-jetpack-composeandroid-jetpackandroid-tvandroid-jetpack-compose-tv

Enabling System Sounds on Android TV with Jetpack Compose


Hello Stack Overflow community,

I am currently working on an Android TV app using Jetpack Compose and am facing a problem that I can't seem to find a solution for.

In traditional Android development, I could easily use the AudioManager class to handle system sounds. However, with the transition to Jetpack Compose, I am not sure how to do the same.

Here is the typical approach that I would use in the traditional View system:

AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.playSoundEffect(Sounds.DISMISS);

How can I accomplish the equivalent in Jetpack Compose? Any assistance would be greatly appreciated. If any additional information is needed, I'm more than happy to provide it.

Thank you in advance.

(it didn't work:)

<resources>
    <style name="Theme.xxx" parent="android:Theme.Material.Light.NoActionBar">
        <item name="android:soundEffectsEnabled">true</item>
    </style>
</resources>

Solution

  • You will mostly be needing sound effects when an item gets focus. So, you can create a custom modifier like the following to do that.

    @Composable
    fun Modifier.playSoundEffectOnFocus(
        effectType: Int = AudioManager.FX_FOCUS_NAVIGATION_UP
    ): Modifier {
        val context = LocalContext.current
        val audioManager = remember {
            context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        }
        return this
            .onFocusChanged {
                if (it.isFocused) {
                    audioManager.playSoundEffect(effectType)
                }
            }
    }
    

    Now, you can add it to any focusable item.

    Example usage with Jetpack Compose TV Material 3 Clickable Surface:

    Surface(
      onClick = { },
      modifier = Modifier.playSoundEffectOnFocus(),
    ) {
      // ...
    }
    

    You might also want to play the sound effect when the item is clicked. For that, you can create a composable function to play the audio on demand and use that in the onClick method.

    @Composable
    fun getPlaySoundEffect(
      effectType: Int = AudioManager.FX_FOCUS_NAVIGATION_UP
    ): () -> Unit {
        val context = LocalContext.current
        val audioManager = remember {
            context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        }
        
        return { audioManager.playSoundEffect(effectType) }
    }
    

    Now, you can use it as following:

    val playSoundEffect = getPlaySoundEffect()
    
    Surface(
      onClick = { playSoundEffect() },
      modifier = Modifier.playSoundEffectOnFocus(),
    ) {
      // ...
    }