Rdzleo 128f7ca02e init: KWS 软件唤醒 APK 工程骨架(基于 sherpa-onnx 改造)
工程结构:
- com.lila.wakeup 包名,基于 sherpa-onnx v1.13.0 SherpaOnnxKws demo 改造
- 9 个 Kotlin 模块: WakeupForegroundService / KwsStateMachine / AudioCapture
  / KwsEngine / BootReceiver / ProtocolReceiver / BroadcastSender / Config
  / MainActivity
- 5 个 Lila 中文唤醒词(你好Lila/hello Lila/Lila同学/Lila你好/小Lila)
- 仅 arm64-v8a(OrangePi CM5 / RK3588)
- 协议 v2.1: 双向 setPackage / silence_ms 3000 / 2min 兜底
- 数字人 APP 包名: com.qy.lila

构建通过项:
- Gradle Sync OK(腾讯云 Gradle 镜像 + 阿里云 Maven 镜像)
- APK 编译成功 55MB,含 25MB onnxruntime + 24MB 模型 + 9 个 Kotlin .dex
- adb install OK,Service onCreate 被调用,sherpa-onnx 加载模型

已知问题(下一 commit 修复):
- KwsEngine.kt 的 OnlineModelConfig modelType 设为 'zipformer'
  实际应为 'zipformer2',native 加载在 InitEncoder 后崩溃
- 修复方法: 用 sherpa-onnx 官方 getKwsModelConfig(0) 工厂函数

参考文档:
- 协议: ~/OrangePi_CM5_Project/docs/.../KWS唤醒协议-V2.md (v2.1)
- 设计: ~/OrangePi_CM5_Project/docs/.../KWS服务设计.md
- 评估: ~/OrangePi_CM5_Project/docs/.../KWS唤醒方案适配评估_Unity.md
2026-04-30 09:55:49 +08:00

118 lines
3.8 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.lila.wakeup
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
/**
* 状态查看 Activity开发期辅助 + 首次启动权限申请入口)。
*
* 职责:
* 1. 首次启动时弹麦克风权限申请
* 2. 启动 [WakeupForegroundService]
* 3. 显示当前状态(开发期可选)
*
* 量产时(方案 B改为 launcher 隐藏(删 LAUNCHER intent-filter
* 用户不用进 APP 也能开机自启。
*/
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "KwsService.Main"
private const val PERMISSION_REQUEST_CODE = 1001
}
private lateinit var statusText: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 简化:用一个 TextView 直接显示状态,不用 layout xml
statusText = TextView(this).apply {
text = "Lila 语音唤醒服务\n\n初始化中..."
textSize = 18f
setPadding(60, 60, 60, 60)
}
setContentView(statusText)
checkAndRequestPermissions()
}
private fun checkAndRequestPermissions() {
val needRequestList = mutableListOf<String>()
// 麦克风权限(核心)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED
) {
needRequestList.add(Manifest.permission.RECORD_AUDIO)
}
// 通知权限Android 13+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED
) {
needRequestList.add(Manifest.permission.POST_NOTIFICATIONS)
}
}
if (needRequestList.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
needRequestList.toTypedArray(),
PERMISSION_REQUEST_CODE
)
} else {
startWakeupService()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != PERMISSION_REQUEST_CODE) return
val recordIdx = permissions.indexOf(Manifest.permission.RECORD_AUDIO)
if (recordIdx >= 0 && grantResults[recordIdx] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "RECORD_AUDIO granted")
startWakeupService()
} else {
statusText.text = "Lila 语音唤醒服务\n\n❌ 麦克风权限被拒绝,无法启动唤醒"
Log.e(TAG, "RECORD_AUDIO denied, service NOT started")
}
}
private fun startWakeupService() {
val intent = Intent(this, WakeupForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
statusText.text = """
Lila 语音唤醒服务
✅ 服务已启动
✅ 监听唤醒词中
可关闭本界面,服务会在后台持续运行。
通知栏可看到运行状态。
日志adb logcat -s KwsService
""".trimIndent()
Log.i(TAG, "WakeupForegroundService start requested")
}
}