> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Voice search

> Learn how to build Voice search experience with InstantSearch Android and Voice Overlay.

export const Records = () => <Tooltip tip="A record is a searchable object in an Algolia index. Each record consists of named attributes." cta="Algolia records" href="/doc/guides/sending-and-managing-data/prepare-your-data#algolia-records">
    records
  </Tooltip>;

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

This guide explains how to build step by step a voice search experience using the libraries provided by Algolia.
You'll build an Android app with a classic search box and a button that triggers the voice input.
To create this app, you'll use the [InstantSearch](https://github.com/algolia/instantsearch-android) and [Voice overlay](https://github.com/algolia/voice-overlay-android) libraries.

Building a voice search experience has three steps:

* **Input** using speech-to-text
* **Processing** using Algolia
* **Output** using speech synthesis

## Speech-to-text input layer

You must have a speech-to-text layer to convert your users' speech into something Algolia understands (Algolia can't process non-textual searches).

You can add a speech-to-text layer in two ways:

* Using the Chrome browser, iOS or Android native apps, or a voice platform tool like Alexa or Google Assistant with speech-to-text built-in.
* Using a third-party service. You send user speech to the service. When you receive it back, you then send it to Algolia as a search query. Some services include:

  * [Google Cloud Speech-to-Text](https://cloud.google.com/speech-to-text/)
  * [Azure Cognitive Services](https://azure.microsoft.com/en-us/services/cognitive-services/speech-to-text/)
  * [AssemblyAI](https://assemblyai.com)

## Process the query with Algolia

After converting speech to text, you need to process the query and return relevant results from your Algolia <Index />.

This step involves:

* Query time settings
* Index configuration

Both steps help improve the search experience, especially for voice input.

### Query time settings

The query time settings improve search results during query time.
For instance, [selecting a language](/doc/guides/managing-results/optimize-search-results/handling-natural-languages-nlp/how-to/how-to-set-an-index-query-language) for Algolia let you set certain features like ignoring "noise" words that users could enter in their search query.
If you choose English as the language, and you turn on the stop words feature, the search engine ignores words like 'a' and 'an' as they're not relevant to the search query.
This gives more exact search results.

* Set [`removeStopWords`](/doc/api-reference/api-parameters/removeStopWords) and ensure to select a supported language. For example, `en` for English.
  This setting removes stop words like "a", "an", or "the" before running the search query.
* Send the entire query string along as [`optionalWords`](/doc/api-reference/api-parameters/optionalWords).
  Speech often has words that aren't in any of your <Records />. With this setting, records don't need to match all the words. Records matching more words rank higher. For example, in the spoken query "Show me all blue dresses", only "blue dresses" may yield results for a clothing store: the other words should be optional.
* Set [`ignorePlurals`](/doc/api-reference/api-parameters/ignorePlurals) to `true` and ensure to [select a supported language](/doc/guides/managing-results/optimize-search-results/handling-natural-languages-nlp/how-to/how-to-set-an-index-query-language). For example, `en` for English.
  This setting marks words like "car" and "cars" as matching terms.
* Apply [`analyticsTags`](/doc/api-reference/api-parameters/analyticsTags) to the query, including voice queries.
  You can activate these settings using the [`naturalLanguages`](/doc/api-reference/api-parameters/naturalLanguages) parameter. These settings work well together when the query format is in natural language instead of keywords, for example, when your user performs a voice search.

### Index configuration

Similarly, you can apply some rules related to your index.
These rules are dynamic and apply depending on what users type in the search query.
[Detecting user intent](/doc/guides/managing-results/rules/detecting-intent) can help dynamically change the search results.

## Speech synthesis output

Not all voice platforms need speech synthesis or text-to-speech.
For example, a site that shows search results may be enough.

If your voice platform does need speech synthesis, your options are:

* A built-in system such as Alexa or Google Assistant.
* A third-party system. Most modern browsers support speech synthesis through the [SpeechSynthesis API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis). If you want a wider choice of voices, you have [Azure Cognitive Services](https://azure.microsoft.com/en-us/services/cognitive-services/speech-to-text/) or [Amazon Web Services' Polly](https://aws.amazon.com/polly/).

## Prepare your project

To use InstantSearch, you need an Algolia account.
You can [create a new account](https://dashboard.algolia.com/users/sign_up),
or use the following credentials:

* Application ID: `latency`
* Search API Key: `1f6fd3a6fb973cb08419fe7d288fa4db`
* Index name: `instant_search`

These credentials give access to a pre-loaded dataset of products appropriate for this guide.

### Create a new Android project

Start by creating a new Android project.
Open Android Studio, and select **Create New Project**.

<img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/project-creation.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=3afae1124425757dba798a3b0459543c" alt="Screenshot of the Android Studio welcome screen showing the 'Create New Project' option at the top of the list." width="1690" height="1112" data-path="images/guides/voice-search/android/project-creation.png" />

In the **Select a Project Template** window, select **Empty Activity** and click **Next**.

<img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/project-template-selection.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=4861a23e32d5046054049c0a4f2489a9" alt="Screenshot of the 'Select a Project Template' screen with activity options like 'Empty Activity' and 'Basic Activity' under 'Phone and Tablet.'" width="2024" height="1580" data-path="images/guides/voice-search/android/project-template-selection.png" />

Input the name of your app. Select `API 21` as minimum SDK version. Click **Finish**.

<img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/project-name-input.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=bd0e2b66ed85837729e8f75f925a1b98" alt="Screenshot of the 'Create New Project' dialog showing the 'Name' field with 'VoiceSearch' entered." width="2024" height="1580" data-path="images/guides/voice-search/android/project-name-input.png" />

[Build and run](https://developer.android.com/codelabs/basic-android-kotlin-compose-first-app#0) your app.
You should see your app with blank screen.

<img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/simulator-blank.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=8879c1e91acd744c7ba1724ec3d9b914" alt="Simulator blank" style={{ maxWidth: "300px" }} className="no-shaodow" width="1152" height="2465" data-path="images/guides/voice-search/android/simulator-blank.png" />

### Add project dependencies

In your `build.gradle` file at the project level, add `mavenCentral()` as a dependency repository in the `repositories` blocks:

```groovy groovy theme={"system"}
repositories {
    google()
    mavenCentral()
}
```

In your `build.gradle` file under `app` module, add the following in the `dependencies` block:

```groovy groovy theme={"system"}
implementation 'com.algolia:instantsearch-android:4.+'
implementation 'com.algolia.instantsearch:voice:1.+'
```

To perform network operations in your app, `AndroidManifest.xml` must include the following permissions:

```xml XML icon=code-xml theme={"system"}
<uses-permission android:name="android.permission.INTERNET" />
```

Setup [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) by adding the serialization plugin to your project's `build.gradle`:

```groovy groovy theme={"system"}
dependencies {
  //...
  classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
```

Add serialization plugin to the `plugins` block to your app's `build.gradle`:

```groovy Gradle theme={"system"}
plugins {
  // ...
  id 'kotlinx-serialization'
}
```

## Create a basic search experience

Start by creating a classic search interface with search box and results list.
First, create a layout file called `list_item.xml` under `res/layout/`.
Add a `TextView` to display a hit:

```xml XML icon=code-xml theme={"system"}
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="?attr/listPreferredItemHeightSmall"
    android:layout_marginBottom="0.5dp"
    app:cardCornerRadius="0dp"
    tools:layout_height="50dp">

    <TextView
        android:id="@+id/hit"
        style="@style/TextAppearance.MaterialComponents.Body1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginHorizontal="16dp"
        android:maxLines="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="@tools:sample/lorem/random" />

</com.google.android.material.card.MaterialCardView>
```

Update your layout file `activity_main.xml` to have a minimal set of components for a basic search experience:

* `SearchView`: view for textual query input
* `RecyclerView`: the list of search results

```xml XML icon=code-xml theme={"system"}
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.SearchView
        android:id="@+id/searchView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:elevation="6dp"
        app:iconifiedByDefault="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:queryBackground="@android:color/transparent"
        app:queryHint="Search for products"
        app:voiceIcon="@drawable/ic_microphone" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/hits"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layoutManager="LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/searchView"
        tools:itemCount="100"
        tools:listitem="@layout/list_item" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

The view is ready, it's time to work on the search logic. Create a new class `Product` to hold search results:

```kotlin Kotlin theme={"system"}
import com.algolia.instantsearch.core.Indexable

@Serializable
data class Product(override val objectID: String, val name: String) : Indexable
```

Create a new class `ProductAdapter` to render search results:

```kotlin Kotlin theme={"system"}
class ProductAdapter : ListAdapter<Product, ProductAdapter.ProductViewHolder>(ProductAdapter),
  HitsView<Product> {

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
      val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
      return ProductViewHolder(view)
  }

  override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
      val product = getItem(position)
      if (product != null) holder.bind(product)
  }

  override fun setHits(hits: List<Product>) {
      submitList(hits)
  }

  companion object : DiffUtil.ItemCallback<Product>() {
      override fun areItemsTheSame(oldItem: Product, newItem: Product): Boolean {
          return oldItem.objectID == newItem.objectID
      }

      override fun areContentsTheSame(oldItem: Product, newItem: Product): Boolean {
          return oldItem == newItem
      }
  }

  class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view) {

      private val hit = view.findViewById<TextView>(R.id.hit)

      fun bind(product: Product) {
          hit.text = product.name
      }
  }
}
```

In your `MainActivity` use the [`SearchBox`](/doc/api-reference/widgets/search-box/android) and [`Hits`](/doc/api-reference/widgets/hits/android) widgets:

```kotlin Kotlin theme={"system"}
class MainActivity : AppCompatActivity() {

    val searcher = HitsSearcher(
        applicationID = "latency",
        apiKey = "1f6fd3a6fb973cb08419fe7d288fa4db",
        indexName = "instant_search"
    )
    val searchBox = SearchBoxConnector(searcher, searchMode = SearchMode.AsYouType)
    val connection = ConnectionHandler(searchBox)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Setup SearchBox widget
        val searchView = findViewById<SearchView>(R.id.searchView)
        val searchBoxView = SearchBoxViewAppCompat(searchView)
        connection += searchBox.connectView(searchBoxView)
        // Setup Hits widget
        val hits = findViewById<RecyclerView>(R.id.hits)
        val productAdapter = ProductAdapter()
        hits.adapter = productAdapter
        connection += searcher.connectHitsView(productAdapter) { response ->
            response.hits.deserialize(Product.serializer())
        }
        // Initial search
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        searcher.cancel()
        connection.clear()
    }
}
```

Build and run your app. The basic search experience is ready:
you can type your search query and get instant results.

<img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/simulator-basic-search.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=eb8ea5aff3091cef5e0defdc7b5eb273" alt="Simulator basic search" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2465" data-path="images/guides/voice-search/android/simulator-basic-search.png" />

## Create a voice search experience

To integrate voice search:

<Steps>
  <Step title="Setup audio permissions">
    Add `RECORD_AUDIO` permission to `AndroidManifest.xml` file:

    ```xml XML icon=code-xml theme={"system"}
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    ```
  </Step>

  <Step title="Add voice input UI">
    Change `activity_main.xml` to add the new voice input button to the existing UI.
    The updated layout file should look as follows:

    ```xml XML icon=code-xml theme={"system"}
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.appcompat.widget.SearchView
            android:id="@+id/searchView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@color/white"
            android:elevation="6dp"
            android:focusable="false"
            app:iconifiedByDefault="false"
            app:layout_constraintEnd_toStartOf="@+id/microphone"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:queryBackground="@android:color/transparent"
            app:queryHint="Search for products"
            app:voiceIcon="@drawable/ic_microphone" />

        <ImageView
            android:id="@+id/microphone"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:background="@color/white"
            android:elevation="6dp"
            android:padding="4dp"
            app:layout_constraintBottom_toBottomOf="@+id/searchView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@+id/searchView"
            app:srcCompat="@drawable/ic_microphone"
            app:tint="@color/blue_dark" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/hits"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layoutManager="LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/searchView"
            tools:itemCount="100"
            tools:listitem="@layout/list_item" />

    </androidx.constraintlayout.widget.ConstraintLayout>
    ```
  </Step>

  <Step title="Add voice input logic">
    Add a basic implementation of [VoiceOverlay](https://github.com/algolia/voice-overlay-android):

    In the click listener, check if you have the audio permission and show the appropriate dialog:

    ```kotlin Kotlin theme={"system"}
    val microphone = findViewById<View>(R.id.microphone)
    microphone.setOnClickListener {
        when (isRecordAudioPermissionGranted()) {
            true -> VoiceInputDialogFragment().show(supportFragmentManager, "INPUT")
            false -> VoicePermissionDialogFragment().show(supportFragmentManager, "PERMISSION")
        }
    }
    ```

    Once the user speaks, you get their input back by implementing `VoiceSpeechRecognizer.ResultsListener`

    ```kotlin Kotlin theme={"system"}
    class MainActivity : AppCompatActivity(), VoiceSpeechRecognizer.ResultsListener {
        //...
        override fun onResults(possibleTexts: Array<out String>) {
            val searchView = findViewById<SearchView>(R.id.searchView)
            possibleTexts.firstOrNull()?.let { input -> searchView.setQuery(input, true) }
        }
    }
    ```
  </Step>
</Steps>

In the end, the code of your `MainActivity` should look as follows:

```kotlin Kotlin theme={"system"}
class MainActivity : AppCompatActivity(), VoiceSpeechRecognizer.ResultsListener {

    val searcher = HitsSearcher(
        applicationID = "latency",
        apiKey = "1f6fd3a6fb973cb08419fe7d288fa4db",
        indexName = "bestbuy"
    )
    val searchBox = SearchBoxConnector(searcher, searchMode = SearchMode.AsYouType)
    val connection = ConnectionHandler(searchBox)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Setup SearchBox widget
        val searchView = findViewById<SearchView>(R.id.searchView)
        val searchBoxView = SearchBoxViewAppCompat(searchView)
        connection += searchBox.connectView(searchBoxView)
        // Setup Hits widget
        val hits = findViewById<RecyclerView>(R.id.hits)
        val productAdapter = ProductAdapter()
        hits.adapter = productAdapter
        connection += searcher.connectHitsView(productAdapter) { response ->
            response.hits.deserialize(Product.serializer())
        }
        // Setup VoiceOverlay
        val microphone = findViewById<View>(R.id.microphone)
        microphone.setOnClickListener {
            when (isRecordAudioPermissionGranted()) {
                true -> VoiceInputDialogFragment().show(supportFragmentManager, "INPUT")
                false -> VoicePermissionDialogFragment().show(supportFragmentManager, "PERMISSION")
            }
        }
        // Initial search
        searcher.searchAsync()
    }

    override fun onResults(possibleTexts: Array<out String>) {
        val searchView = findViewById<SearchView>(R.id.searchView)
        possibleTexts.firstOrNull()?.let { input -> searchView.setQuery(input, true) }
    }

    override fun onDestroy() {
        super.onDestroy()
        searcher.cancel()
        connection.clear()
    }
}
```

Build and run your app to test your voice search. You should see the voice input button on the right of the search box.

<img src="https://mintcdn.com/algolia/PPYT_t3uPKSP6jma/images/guides/voice-search/android/simulator-voice-search.png?fit=max&auto=format&n=PPYT_t3uPKSP6jma&q=85&s=cdc38704c2c1aacfa9a0c746438bde54" alt="Simulator voice search" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2465" data-path="images/guides/voice-search/android/simulator-voice-search.png" />

The `VoiceOverlay` should appear when you tap the voice input button.
At the first launch, it asks for permission.

<Columns cols={2}>
  <img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/simulator-permission-request.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=4ff5442c7b9e53b120f07573e8b70946" alt="Simulator permission request" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2465" data-path="images/guides/voice-search/android/simulator-permission-request.png" />

  <img src="https://mintcdn.com/algolia/uAYFrBCMSmYQz381/images/guides/voice-search/android/simulator-os-permission-request.png?fit=max&auto=format&n=uAYFrBCMSmYQz381&q=85&s=d1172bb7c90a93969d896783f470d433" alt="Simulator os permission request" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2465" data-path="images/guides/voice-search/android/simulator-os-permission-request.png" />
</Columns>

Once you give all the authorizations, the voice input interface appears. Try to say something and get the instant search results.

<Columns cols={2}>
  <img src="https://mintcdn.com/algolia/PPYT_t3uPKSP6jma/images/guides/voice-search/android/simulator-voice-input.png?fit=max&auto=format&n=PPYT_t3uPKSP6jma&q=85&s=21bc9cdbb736438835e8337c8bc1301a" alt="Simulator voice input" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2525" data-path="images/guides/voice-search/android/simulator-voice-input.png" />

  <img src="https://mintcdn.com/algolia/PPYT_t3uPKSP6jma/images/guides/voice-search/android/simulator-voice-result.png?fit=max&auto=format&n=PPYT_t3uPKSP6jma&q=85&s=ba5030b8e047a0fd3123fe9157efb16e" alt="Simulator voice result" style={{maxWidth:"300px"}} className="no-shadow" width="1152" height="2525" data-path="images/guides/voice-search/android/simulator-voice-result.png" />
</Columns>

You can find a complete `VoiceOverlay` implementation on the git [repository](https://github.com/algolia/voice-overlay-android/blob/96e7d9c98fe40cbcd85afa0e8e1eeb33b374063f/app/src/main/kotlin/com/algolia/instantsearch/voice/demo/MainActivity.kt).

## Conclusion

With Algolia's libraries, you can build a voice search experience in less than a hundred lines of code.
You can customize your search experience and make it unique by modifying `InstantSearch` components, as well as the `VoiceOverlay` components.
