The VoiceInputIndicator is a Jetpack Compose Glimmer component that
displays sound activity to the user. You can use this component to accept voice
input or other audio and provide visual feedback to show that your app is
capturing audio.
This component is strictly a visual element. It doesn't record audio or process
microphone data on its own. Instead, it responds to an audio level provided
by your app, which shows a visual representation of the audio intensity.
Request projected permissions
To capture audio data to drive the indicator, your app needs the
Manifest.permission.RECORD_AUDIO permission.
When you build augmented experiences for display glasses, ensure that you
request permissions correctly across both the device and the glasses. Use
ProjectedPermissionsResultContract and
ProjectedPermissionsRequestParams to prompt the user.
For more information about requesting permissions in projected environments, see Request hardware permissions.
Capture and normalize audio
The VoiceInputIndicator requires a Float between 0.0f and 1.0f to
visually reflect audio intensity. Because it operates purely as a visual
component and doesn't capture audio, your app must actively monitor the
microphone and stream the volume to the indicator's level parameter.
While there are multiple ways to capture audio data, one approach is to
use the SpeechRecognizer API. When using SpeechRecognizer, you
can capture real-time volume updates using the
RecognitionListener.onRmsChanged callback, which provides the audio
level in decibels, rmsdB.
Because this raw value typically falls between 0 and 10, you must normalize the
output to the required 0.0f to 1.0f range. You can hold this normalized
level in a state variable to continuously update the indicator.
Here's an example using a RecognitionListener to update a state
variable:
// Example state variables to hold the normalized level val audioLevel = MutableStateFlow(0f) val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context) val listener = object : RecognitionListener { override fun onRmsChanged(rmsdB: Float) { // Normalize raw dB (~0–10) to 0.0-1.0 for the indicator audioLevel.value = ((rmsdB - 1f) / 9f).coerceIn(0f, 1f) } // ... Implement other required RecognitionListener methods ... override fun onReadyForSpeech(params: Bundle?) {} override fun onBeginningOfSpeech() {} override fun onEndOfSpeech() {} override fun onError(error: Int) {} override fun onResults(results: Bundle?) {} override fun onPartialResults(partialResults: Bundle?) {} override fun onEvent(eventType: Int, params: Bundle?) {} override fun onBufferReceived(buffer: ByteArray?) {} } // Attach the listener and start recognizing speech speechRecognizer.setRecognitionListener(listener) val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply { putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM) } speechRecognizer.startListening(intent)
Integrate the voice input indicator
Once your audio stream is properly normalized and exposed as state, you can pass
it directly into the VoiceInputIndicator.
@Composable fun VoiceInputExample() { // Collect the flow as Compose State so the UI reacts to changes val currentLevel by audioLevel.collectAsState() VoiceInputIndicator( // The component responds to the level provided, showing a visual representation of the audio intensity level = { currentLevel }, modifier = Modifier.size(64.dp) ) }
Customization parameters
The VoiceInputIndicator provides a few parameters to customize its
behavior and appearance:
level: A function returning aFloatthat represents the level of the voice input, ranging from0.0for silence to1.0for the loudest sound.indicatorColor: The color of the indicator bars. By default, this uses the primary color from the Glimmer theme; seeGlimmerTheme.colors.primary.modifier: The standard Composemodifierto be applied to the indicator.
Related components
If your UI requires the indicator to sit within a visible background container,
use the ContainedVoiceInputIndicator. This alternative ensures that the
indicator bars are transparent, allowing the background container to show
through.