Files
Cyrene-For-Android/devdocs/01-voice-assistant-system.md
T

8.1 KiB
Raw Blame History

01 — 系统语音助手集成规范

目标:让昔涟成为 Android 系统级默认语音助手,替换 Google Assistant / Bixby
核心 APIVoiceInteractionService + VoiceInteractionSession


1. 功能目标

  • 用户可在 系统设置 → 默认应用 → 数字助理 中选择昔涟
  • 长按 Home 键呼出昔涟(非全屏,悬浮覆盖层)
  • 屏幕底部两角向内滑动触发昔涟
  • 长按电源键可配置为呼出昔涟
  • 息屏状态下热词唤醒昔涟
  • 有线/蓝牙耳机按键呼出昔涟

2. AndroidManifest.xml 声明

<!-- VoiceInteractionService -->
<service
    android:name=".service.CyreneVoiceInteractionService"
    android:exported="true"
    android:permission="android.permission.BIND_VOICE_INTERACTION">

    <meta-data
        android:name="android.voice_interaction"
        android:resource="@xml/voice_interaction_config" />

    <intent-filter>
        <action android:name="android.service.voice.VoiceInteractionService" />
    </intent-filter>
</service>

<!-- AssistService (Android 14+) -->
<service
    android:name=".service.CyreneAssistService"
    android:exported="true"
    android:permission="android.permission.BIND_ASSIST">

    <intent-filter>
        <action android:name="android.service.voice.AssistService" />
    </intent-filter>
</service>

3. 配置文件

res/xml/voice_interaction_config.xml

<?xml version="1.0" encoding="utf-8"?>
<voice-interaction-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:sessionService=".service.CyreneVoiceInteractionSession"
    android:recognitionService=".service.CyreneRecognitionService"
    android:supportsAssist="true"
    android:supportsLaunchVoiceAssistFromKeyguard="true"
    android:supportsLocalRecognition="true"
    android:serviceIcon="@drawable/ic_cyrene"
    android:serviceLabel="@string/voice_assistant_name" />

4. VoiceInteractionService 实现

class CyreneVoiceInteractionService : VoiceInteractionService() {

    override fun onReady() {
        super.onReady()
        // 服务就绪,可在此初始化 TTS 引擎等
    }

    override fun onCreateSession(args: Bundle?): VoiceInteractionSession {
        return CyreneVoiceInteractionSession(this)
    }

    override fun onLaunchVoiceAssistFromKeyguard() {
        // 锁屏启动 → 进入简化模式,仅显示对话,IoT 控制等需先解锁
    }

    // Android 14+: AssistAction 回调
    override fun onHandleAssist(
        request: AssistRequest?,
        cancellationSignal: CancellationSignal?,
        callback: OutcomeCallback<AssistResult?>?
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            request?.let {
                val assistContent = it.assistContent
                // 提取当前屏幕上下文(可选,用于后续上下文感知)
                callback?.onResult(AssistResult(assistContent))
            }
        }
    }
}

5. VoiceInteractionSession 实现(悬浮窗界面)

class CyreneVoiceInteractionSession(context: Context) :
    VoiceInteractionSession(context) {

    override fun onCreateContentView(): View {
        // 返回 ComposeView 作为悬浮窗的内容
        return ComposeView(context).apply {
            setContent {
                CyreneTheme {
                    OverlayScreen(
                        viewModel = overlayViewModel,
                        onDismiss = { finish() }
                    )
                }
            }
        }
    }

    override fun onShow(args: Bundle?, showFlags: Int) {
        super.onShow(args, showFlags)
        // 设置窗口属性:透明背景 + 底部卡片式布局
        window?.apply {
            // 半透明遮罩
            setBackgroundDrawable(ColorDrawable(0x80000000.toInt()))
            // FLAG_DIM_BEHIND 可实现模糊效果
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                addSystemGestureExclusionRects(...)
            }
        }
    }

    override fun onComputeInsets(outInsets: Insets?) {
        super.onComputeInsets(outInsets)
        // 控制悬浮窗内容区域
    }

    override fun onHide() {
        super.onHide()
        // 悬浮窗隐藏时清理状态
    }
}

关键窗口属性

属性 说明
背景 ColorDrawable(0x80000000) 半透明黑色遮罩,透出底层 APP
内容区域 自适应高度 底部弹出,类似 Google Assistant
触摸外区域行为 关闭悬浮窗 用户点击遮罩区域关闭
键盘弹出 推高内容区域 文本输入时自动调整

6. 权限清单

<!-- 核心语音助手权限 -->
<uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
<uses-permission android:name="android.permission.BIND_ASSIST" />

<!-- 音频相关 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

<!-- 热词唤醒 -->
<uses-permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD" />

<!-- 后台服务 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />

<!-- 网络 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 推送 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<!-- 锁屏交互 -->
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

7. 引导用户设为默认助手

首次启动时检测并引导:

fun checkAndPromptDefaultAssistant(context: Context) {
    val isDefault = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val componentName = ComponentName(context, CyreneVoiceInteractionService::class.java)
        context.packageManager
            .queryIntentServices(
                Intent(VoiceInteractionService.SERVICE_INTERFACE),
                PackageManager.MATCH_DEFAULT_ONLY
            )
            .any { it.serviceInfo.packageName == context.packageName }
    } else {
        false
    }

    if (!isDefault) {
        // 显示引导 UI → 跳转到 Settings.ACTION_VOICE_INPUT_SETTINGS
        val intent = Intent(Settings.ACTION_VOICE_INPUT_SETTINGS)
        context.startActivity(intent)
    }
}

8. 热词唤醒检测

方案选型

方案 优点 缺点 适用场景
系统 Always-On Hotword API 低功耗、系统级支持 限 Android 8+,某些 ROM 不支持 首选
Porcupine (Picovoice) 跨平台、离线 商业许可,需额外集成 兜底
自建模型 (openWakeWord) 完全可控、低成本 需要本地推理能力 长期方案

唤醒词配置

优先级 唤醒词 说明
P0 "昔涟" (Xī Lián) 角色名,默认唤醒词
P1 "Hey 昔涟" 与 "Hey Google" 习惯对齐
P2 自定义 用户可在设置中自定义

息屏唤醒流程

用户说出唤醒词
    → HotwordDetector 识别成功(<800ms
    → 系统触发 VoiceInteractionService
    → CyreneVoiceInteractionSession.onCreateContentView()
    → Overlay 显示,播放连接提示音
    → 用户说话 → STT → AI-Core → TTS → 语音回复
    → 对话结束 → finish() → 息屏

9. Dismiss 时机

悬浮窗在以下情况关闭:

条件 行为
用户说"再见" / "退下" 自然对话结束,收起悬浮窗
用户点击遮罩区域 立即关闭
对话静默 10 秒 自动收起
用户主动滑动关闭 手势关闭,同 Google Assistant
收到系统电话等中断 暂停语音,进入后台等待

10. 降级策略

当系统不支持 VoiceInteractionService 或未设为默认助手时:

  • 保底方案:PWA(利用主项目已有的 PWA 支持)
  • WebView 封装:内嵌 H5 对话界面作为过渡
  • 通知栏常驻:提供快速对话入口,但功能受限