工程结构: - 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
118 lines
3.8 KiB
Kotlin
118 lines
3.8 KiB
Kotlin
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")
|
||
}
|
||
}
|