DaVoice Integration Guides

Step-by-step guides to integrate wake word detection into your applications across all major platforms

New to voice activation? Read what a wake word is and how wake word detection works before choosing an integration path.

Android (Kotlin) Integration Guide

📦 Repository

GitHub: https://github.com/frymanofer/Android_Native_Wake_Word

Native Android SDK with NNAPI hardware acceleration support.

Step 1: Add Dependency

Add to app/build.gradle:

dependencies {
    implementation 'com.davoice:wake-word-sdk:1.0.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
}

Step 2: Manifest Configuration

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application>
    <service
        android:name=".WakeWordService"
        android:foreground ServiceType="microphone"
        android:exported="false" />
</application>

Step 3: Kotlin Implementation

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.davoice.wakeword.DaVoiceDetector
import com.davoice.wakeword.DetectorConfig
import com.davoice.wakeword.DetectionCallback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private var detector: DaVoiceDetector? = null
    private var isListening = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        checkPermissions()
    }

    private fun checkPermissions() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.RECORD_AUDIO),
                PERMISSION_REQUEST_CODE
            )
        } else {
            initializeDetector()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSION_REQUEST_CODE
            && grantResults.isNotEmpty()
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initializeDetector()
        }
    }

    private fun initializeDetector() {
        val config = DetectorConfig.Builder()
            .setModelPath("models/hey_google.onnx")
            .setThreshold(0.97f)
            .setBufferCount(3)
            .enableNoiseReduction(true)
            .build()

        detector = DaVoiceDetector(this, config)
        detector?.setCallback(object : DetectionCallback {
            override fun onWakeWordDetected(confidence: Float, timestamp: Long) {
                runOnUiThread {
                    Toast.makeText(
                        this@MainActivity,
                        "Wake word detected! Confidence: $confidence",
                        Toast.LENGTH_SHORT
                    ).show()
                    handleWakeWordDetection()
                }
            }

            override fun onError(error: String) {
                Log.e("WakeWord", "Detection error: $error")
            }
        })
    }

    fun toggleListening(view: View) {
        if (isListening) {
            stopListening()
        } else {
            startListening()
        }
    }

    private fun startListening() {
        CoroutineScope(Dispatchers.Main).launch {
            try {
                detector?.start()
                isListening = true
                Log.d("WakeWord", "Started listening")
            } catch (e: Exception) {
                Log.e("WakeWord", "Failed to start: ${e.message}")
            }
        }
    }

    private fun stopListening() {
        detector?.stop()
        isListening = false
        Log.d("WakeWord", "Stopped listening")
    }

    private fun handleWakeWordDetection() {
        // Navigate to voice command screen
        // or trigger speech recognition
    }

    override fun onDestroy() {
        super.onDestroy()
        detector?.release()
    }

    companion object {
        private const val PERMISSION_REQUEST_CODE = 100
    }
}

Step 4: Foreground Service (Always-On)

class WakeWordService : Service() {
    private var detector: DaVoiceDetector? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(NOTIFICATION_ID, createNotification())
        initializeAndStart()
        return START_STICKY
    }

    private fun createNotification(): Notification {
        val channelId = "wake_word_channel"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "Wake Word Detection",
                NotificationManager.IMPORTANCE_LOW
            )
            getSystemService(NotificationManager::class.java)
                .createNotificationChannel(channel)
        }

        return NotificationCompat.Builder(this, channelId)
            .setContentTitle("Wake Word Active")
            .setContentText("Listening for voice commands")
            .setSmallIcon(R.drawable.ic_mic)
            .build()
    }

    companion object {
        private const val NOTIFICATION_ID = 1001
    }
}

✅ Performance Tips

  • • Enable NNAPI for hardware acceleration
  • • Use foreground service for reliable detection
  • • Implement proper lifecycle management
  • • Test on devices with different chipsets

Need Help Getting Started?

Our team provides free integration support and custom wake word training for all platforms.

Free Development Support • Code Examples • Community Discord • 1-2 Week Custom Model Turnaround