要实现语音交互应用(VIA),请按照以下步骤进行:
- 创建VIA框架。
- (Option)实现首次设置/登录流程。
- (Option)实现“设置”界面。
- 在清单文件中声明所需权限。
- 实现语音控制面板界面。
- 实现语音识别(需包含 RecognitionService API 的实现)。
- 实现语音内容(可以选择实现 TextToSpeech API)。
- 实现命令执行。
下面的各部分将详细介绍如何完成这些步骤。
创建VIA框架
清单
当应用的清单文件包含特定内容时,系统将识别该应用具有语音交互功能。
AndroidManifest.xml
…
在这个示例中:
- VIA 必须公开一个服务,用于扩展 VoiceInteractionService,并为 VoiceInteractionService.SERVICE_INTERFACE(即 “android.service.voice.VoiceInteractionService”)提供 intent 过滤器。
- 该服务需要拥有 BIND_VOICE_INTERACTION 系统签名权限,以确保与系统的正确交互。
- 此外,该服务应包含一个元数据文件,该文件定义为 android.voice_interaction,并存放在 res/xml/interaction_service.xml 中。
如需详细了解各字段,请参阅 R.styleable#VoiceInteractionService。 由于所有的 VIA 都属于语音识别服务,您需要在清单文件中添加以下内容:
AndroidManifest.xml
…
此外,语音识别服务还需要以下元数据:
res/xml/recognition_service.xml
接下来,VoiceInteractionService、VoiceInteractionSessionService 和 VoiceInteractionSession 的生命周期如图所示:
VoiceInteraction相关组件生命周期
如前所述,VoiceInteractionService 是 VIA 的核心入口,主要职责包括:
- 初始化在 VIA 活跃状态时需要保持运行的进程,如热词检测。
- 报告支持的语音操作 。
- 从锁定屏幕启动语音交互会话。
下面是一个简单的 VoiceInteractionService 实现示例:
public class MyVoiceInteractionService extends VoiceInteractionService {
private static final List SUPPORTED_VOICE_ACTIONS =
Arrays.asList(
CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION,
CarVoiceInteractionSession.VOICE_ACTION_REPLY_NOTIFICATION,
CarVoiceInteractionSession.VOICE_ACTION_HANDLE_EXCEPTION
);
@Override
public void onReady() {
super.onReady();
// TODO: 设置热词检测器
}
@NonNull
@Override
public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
Set<String> result = new HashSet<>(voiceActions);
result.retainAll(SUPPORTED_VOICE_ACTIONS);
return result;
}
...
}
要处理语音助手的功能,您需要实现 VoiceInteractionService#onGetSupportedVoiceActions()。系统会使用 VoiceInteractionSessionService 创建 VoiceInteractionSession 并进行交互,该服务的主要职责是在收到请求时启动新的会话。
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService {
@Override
public VoiceInteractionSession onNewSession(Bundle args) {
return new MyVoiceInteractionSession(this);
}
}
大部分逻辑将在 VoiceInteractionSession 中处理。会话实例可以被重复利用,以处理多个用户交互。在 AAOS 中,还提供了一个辅助的 CarVoiceInteractionSession,以实现汽车特定的功能。
public class MyVoiceInteractionSession extends CarVoiceInteractionSession {
public MyVoiceInteractionSession(Context context) {
super(context);
}
@Override
protected void onShow(String action, Bundle args, int showFlags) {
closeSystemDialogs();
// TODO: 显示 UI 并更新状态
// TODO: 开始处理音频输入
}
...
}
VoiceInteractionSession 包含多个回调方法,详细介绍可参考相关文档。
实现设置/登录流程
首次设置和登录可在以下情况下进行:设备初始配置期间、语音交互服务交换期间,或应用首次启动时。
在语音服务交换期间,用户可能选择未正确配置的 VIA,原因包括跳过设置向导或选择不同的 VIA。VoiceInteractionService 可通过以下方式鼓励用户完成设置:
- 通知提醒:以不干扰用户的方式提示需要设置,并提供导航。注意,设置流程在驾驶时可能受到限制,此时应通过语音说明。
- 语音回复:通过 VoiceInteractionSession#onShow() 发起语音提示,告知用户所需操作,并询问是否要启动设置流程。
- 首次使用时设置:当 VIA 未配置时,语音告知用户,并询问是否启动设置流程。如果受限,应留通知供用户安全点击。
首次设置和登录界面应作为常规 Activity 开发,确保用户随时能中断和恢复设置。同时,界面设计应与汽车设计系统保持一致,以确保用户体验的一致性。
通知提醒用户完成设置流程
实现“设置”界面
设置界面应作为常规 Android activity 实现。如果已开发该界面,需在 res/xml/interaction_service.xml 中将其入口点声明为 VIA 清单的一部分。这一部分非常适合用于首次设置和登录(若用户尚未完成)或在需要时提供退出账号和切换用户的选项。
与首次设置屏幕相似,这些设置屏幕应具备以下特性:
- 提供返回到上一个屏幕(如“汽车设置”)的选项。
- 在驾驶时禁止使用,以确保安全。
- 符合各类车辆设计系统,保持一致的用户体验。
在清单文件中声明必需的权限
VIA 需要的权限分为三类:
- 系统签名权限:仅授予系统签名的预装 APK,用户无法授予。这类权限需由原始设备制造商 (OEM) 在构建时授予,详情请参见授予系统特许权限。
- 危险权限:用户必须通过 PermissionsController 对话框授予。这些权限可以预先授予默认的 VoiceInteractionService,但应用应在需要时请求权限。
- 其他权限:这些权限不需用户干预,由系统自动授予。
接下来重点介绍危险权限的请求。只有在用户登录或设置屏幕时,才能请求这些权限。
注意:驾车时不允许显示权限请求对话框。如果缺少权限,建议用语音提示说明情况,并通过通知引导用户返回 VIA 设置屏幕。
在“设置”屏幕中,应使用 ActivityCompat#requestPermission() 方法请求危险权限。
通知监听器权限
要实现 TTR 流程,VIA 必须指定为通知监听器。应用可通过以下方式检查获取权限:
- (可选)使用 CarAssistUtils#assistantIsNotificationListener() 检查是否为通知监听器。
- (必需)响应 VOICE_ACTION_HANDLE_EXCEPTION 和 EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING。
如果未预先授予访问权限,VIA 应用语音提示和通知,引导用户到“汽车设置”的通知权限部分。可以使用以下代码打开设置应用:
private void requestNotificationListenerAccess() {
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(intent);
}
实现语音控制面板界面
在 VoiceInteractionSession 收到 onShow() 回调时,可以显示语音控制面板界面。实现方式有两种:
- 覆盖 VoiceInteractionSession#onCreateContentView()
- 使用 VoiceInteractionSession#startAssistantActivity() 启动 Activity
使用 onCreateContentView()
这是显示语音控制面板的默认方式。应用需覆盖 VoiceInteractionSession#onCreateContentView() 并返回一个初始不可见的视图。在语音互动开始时,视图在 VoiceInteractionSession#onShow() 上变为可见,并在 onHide() 时再次隐藏。
public class MyVoiceInteractionSession extends CarVoiceInteractionSession {
private View mVoicePlate;
@Override
public View onCreateContentView() {
mVoicePlate = inflater.inflate(R.layout.voice_plate, null);
return mVoicePlate;
}
@Override
protected void onShow(String action, Bundle args, int showFlags) {
mVoicePlate.setVisibility(View.VISIBLE); // 更新UI状态为“监听”
}
@Override
public void onHide() {
mVoicePlate.setVisibility(View.GONE);
}
}
建议在 onComputeInsets() 中调整被遮盖区域的处理。
使用 startAssistantActivity()
此方法将处理委托给一个常规 Activity。需在 onPrepareShow() 回调中禁用默认窗口创建,并在 onShow() 时使用 startAssistantActivity() 启动界面。
public class MyVoiceInteractionSession extends CarVoiceInteractionSession {
@Override
public void onPrepareShow(Bundle args, int showFlags) {
super.onPrepareShow(args, showFlags);
setUiEnabled(false);
}
@Override
protected void onShow(String action, Bundle args, int showFlags) {
closeSystemDialogs();
Intent intent = new Intent(getContext(), VoicePlateActivity.class);
intent.putExtra(VoicePlateActivity.EXTRA_ACTION, action);
intent.putExtra(VoicePlateActivity.EXTRA_ARGS, args);
startAssistantActivity(intent);
}
}
确保 Activity 与 VoiceInteractionSession 之间保持通信,必要时使用内部 Intent 或服务绑定。注意,在 Automotive 中,驾车期间只能显示特殊注解或许可名单中的 Activity,请为 Activity 添加 。
实现语音识别
本节介绍如何通过检测和识别启动指令来实现语音识别。启动指令是用于通过语音启动查询或操作的热词,如“Ok Google”。
DSP 启动指令检测
Android 提供了 AlwaysOnHotwordDetector,可以在 DSP 级别实现始终开启的启动指令检测,具有低 CPU 开销。实现步骤包括:
- 实例化 AlwaysOnHotwordDetector。
- 注册热词声音模型。
使用 VoiceInteractionService#createAlwaysOnHotwordDetector() 创建检测器,并传递所需的启动指令和语言区域。应用会接收 onAvailabilityChanged() 回调,可能的状态有:
- STATE_HARDWARE_UNAVAILABLE: DSP 功能不可用。
- STATE_HARDWARE_UNSUPPORTED: 不支持给定的指令和语言。
- STATE_HARDWARE_ENROLLED: 启动指令检测就绪。
- STATE_HARDWARE_UNENROLLED: 声音模型不可用,但可注册。
可用 IVoiceInteractionManagerService#updateKeyphraseSoundModel() 注册声音模型。
软件启动指令检测
若 DSP 检测不可用,使用软件语音识别。为避免干扰其他应用,VIA 必须:
- 使用 MediaRecorder.AudioSource.HOTWORD 捕获音频。
- 拥有 android.Manifest.permission.CAPTURE_AUDIO_HOTWORD 权限。
这两个常量仅适用于捆绑应用。
管理音频输入和语音识别
音频输入通过 MediaRecorder 实现,语音交互服务需实现 RecognitionService 类。为使用麦克风,VIA 必须拥有 android.permission.RECORD_AUDIO 权限。
在 Android 10 之前,麦克风权限一次只能授予一个应用。从 Android 10 开始,权限可共享。
访问音频输出
在提供语音回复时,必须遵循以下准则:
- 请求音频焦点时使用 AudioAttributes#USAGE_ASSISTANT 和 AudioAttributes#CONTENT_TYPE_SPEECH。
- 在语音识别期间请求 AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 音频焦点。
命令执行
语音识别完成后,相关回调接口会返回结构化数据结构,如json、xml等,通过解析相应字段即可区分业务调用类型及参数,通过相应接口调用或者Intent发起调用,从而将命令词转换成业务调用,完成一次语音交互操作。
示例代码
AOSP源码下提供了创建VIA的示例代码,请参考如下链接: /frameworks/base/tests/VoiceInteraction
参考链接
Android语音互动简介[1] Android语音互动集成流程[2] 语音互动开发[3] Android 标准语音识别框架:SpeechRecognizer 的封装、调用和原理[4] 如何打造车机语音交互:Google Voice Interaction 给你答案[5] 直面原理:5 张图彻底了解 Android TextToSpeech 机制[6]
引用链接
[1] Android语音互动简介: https://source.android.google.cn/docs/automotive/voice/voice_interaction_guide?hl=zh-cn
[2] Android语音互动集成流程: https://source.android.google.cn/docs/automotive/voice/voice_interaction_guide/integration_flows?hl=zh-cn
[3] 语音互动开发: https://source.android.google.cn/docs/automotive/voice/voice_interaction_guide/app_development?hl=zh-cn
[4] Android 标准语音识别框架:SpeechRecognizer 的封装、调用和原理: https://lovecodeboy.blog.csdn.net/article/details/142461117
[5] 如何打造车机语音交互:Google Voice Interaction 给你答案: https://lovecodeboy.blog.csdn.net/article/details/142458948
[6] 直面原理:5 张图彻底了解 Android TextToSpeech 机制: https://lovecodeboy.blog.csdn.net/article/details/142461117
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/421972.html