要實現語音互動應用程式 (VIA),您需要完成以下步驟:
- 建立 VIA 骨架。
- (可選)實施設定/登入流程。
- (可選)實施設定畫面。
- 在清單檔案中聲明所需的權限。
- 實現語音板 UI。
- 實現語音辨識(必須包含 RecognitionService API 實作)。
- 實作話語(您可以選擇實作 TextToSpeech API)。
- 執行命令執行。請參閱執行命令中的此內容。
以下部分說明如何完成上述每個步驟。
建立 VIA 骨架
艙單
當清單中包含以下內容時,應用程式將被偵測為具有語音互動功能的應用程式:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myvoicecontrol"> ... <application ... > <service android:name=".MyInteractionService" android:label="@string/app_name" android:permission="android.permission.BIND_VOICE_INTERACTION" android:process=":interactor"> <meta-data android:name="android.voice_interaction" android:resource="@xml/interaction_service" /> <intent-filter> <action android:name= "android.service.voice.VoiceInteractionService" /> </intent-filter> </service> </application> </manifest>
在這個例子中:
- VIA 必須公開擴展
VoiceInteractionService
服務,並附有用於操作VoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService")
意圖過濾器。 - 本服務必須持有
BIND_VOICE_INTERACTION
系統簽章權限。 - 該服務應包含
android.voice_interaction
資料檔案以包含以下內容:res/xml/interaction_service.xml
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" android:sessionService= "com.example.MyInteractionSessionService" android:recognitionService= "com.example.MyRecognitionService" android:settingsActivity= "com.example.MySettingsActivity" android:supportsAssist="true" android:supportsLaunchVoiceAssistFromKeyguard="true" android:supportsLocalInteraction="true" />
有關每個欄位的詳細信息,請參閱R.styleable#VoiceInteractionService
。鑑於所有 VIA 也是語音辨識器服務,您還必須在清單中包含以下內容:
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <application ...> ... <service android:name=".RecognitionService" ...> <intent-filter> <action android:name="android.speech.RecognitionService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.speech" android:resource="@xml/recognition_service" /> </service> </application> </manifest>
語音辨識服務還需要以下元資料:
res/xml/recognition_service.xml
<recognition-service xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.example.MyRecognizerSettingsActivity" />
VoiceInteractionService、VoiceInteractionSessionService 和 VoiceInteractionSession
下圖描述了每個實體的生命週期:
圖 1.生命週期
如前所述, VoiceInteractionService
是 VIA 的入口點。該服務的主要職責是:
- 初始化只要該 VIA 處於活動狀態就應保持執行的任何進程。例如,熱詞檢測。
- 報告支援的語音操作(請參閱語音助理點擊閱讀)。
- 從鎖定畫面(鍵盤鎖定)啟動語音互動會話。
最簡單的形式是,VoiceInteractionService 實作如下所示:
public class MyVoiceInteractionService extends VoiceInteractionService { private static final List<String> 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: Setup hotword detector } @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 InteractionSession(Context context) { super(context); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); // TODO: Unhide UI and update UI state // TODO: Start processing audio input } ... }
VoiceInteractionSession
有大量回呼方法,以下各節將對此進行說明。請參閱VoiceInteractionSession
的文檔完整清單。
實施設定/登入流程
設定和登入可能發生:
- 在設備啟動期間(設定精靈)。
- 語音互動服務交換期間(設定)。
- 首次啟動時選擇應用程式時。
有關推薦的用戶體驗和視覺指導的詳細信息,請參閱預加載助手:UX 指導。
語音服務交換期間的設置
使用者總是有可能選擇尚未正確配置的 VIA。發生這種情況的原因是:
- 使用者完全跳過了設定嚮導,或者使用者跳過了語音互動配置步驟。
- 使用者選擇的 VIA 與設備載入期間配置的 VIA 不同。
無論如何, VoiceInteractionService
有多種方法來鼓勵使用者完成設定:
- 通知提醒。
- 當用戶嘗試使用它時自動語音回覆。
注意:強烈建議不要在沒有明確使用者要求的情況下提供 VIA 設定流程。這表示 VIA 應避免在裝置啟動期間或因使用者切換或解鎖而在 HU 上自動顯示內容。
通知提醒
通知提醒是一種非侵入式的方式來指示設定的需要,並為使用者提供導航到助理設定流程的功能。
圖2.通知提醒
該流程的工作原理如下:
圖 3.通知提醒流程
語音回覆
這是最簡單的實作流程,在VoiceInteractionSession#onShow()
回呼上啟動話語,向使用者解釋需要做什麼,然後詢問他們(如果在 UX 限制狀態下允許設定)是否要啟動設定流程。如果當時無法進行設置,也請解釋一下這種情況。
首次使用時設定
使用者總是有可能觸發尚未正確配置的 VIA。在這種情況下:
- 口頭告知使用者這種情況(例如,「為了正常工作,我需要您完成幾個步驟…」)。
- 如果 UX 限制引擎允許(請參閱UX_RESTRICTIONS_NO_SETUP ),請詢問使用者是否要啟動設定過程,然後開啟 VIA 的「設定」畫面。
- 否則(例如,如果使用者正在開車),請留下通知,以便使用者在安全時按一下該選項。
建構語音互動設定畫面
設定和登入畫面應作為常規活動進行開發。請參閱預先載入助理中的 UI 開發的 UX 和視覺指南:UX 指南。
一般準則:
- VIA 應允許使用者隨時中斷和恢復設定。
- 如果
UX_RESTRICTIONS_NO_SETUP
限制有效,則不應允許設定。有關詳細信息,請參閱駕駛員分心指南。 - 設定畫面應與每輛車的設計系統相符。一般螢幕佈局、圖示、顏色和其他方面應與 UI 的其餘部分保持一致。有關詳細信息,請參閱定制。
實施設定畫面
圖 4.設定集成
設定畫面是常規的 Android 活動。如果實現,它們的入口點必須在res/xml/interaction_service.xml
中聲明為 VIA 清單的一部分(請參閱清單)。 「設定」部分是繼續設定和登入(如果使用者未完成)或根據需要提供登出或切換使用者選項的好地方。與上述設定畫面類似,這些畫面應該:
在清單檔案中聲明所需的權限
VIA 所需的權限可分為三類:
- 系統簽名權限。這些權限僅授予預先安裝的系統簽署 APK。使用者無法授予這些權限,只有 OEM 可以在建置系統映像時授予這些權限。有關獲取簽名權限的更多信息,請參閱授予系統特權權限。
- 危險的權限。這些是使用者必須使用 PermissionsController 對話方塊授予的權限。 OEM 可以將其中一些權限預先授予預設的 VoiceInteractionService。但考慮到此預設值可能會因裝置而異,應用程式應該能夠在需要時請求這些權限。
- 其他權限。這些都是不需要使用者乾預的其他權限。這些權限是系統自動授予的。
鑑於上述情況,以下部分僅重點討論請求危險權限。僅當使用者處於登入或設定畫面時才應請求權限。
如果應用程式沒有操作所需的權限,建議的流程是使用語音向使用者解釋情況,並提供通知以供使用者導航回 VIA 設定畫面。詳情請參閱1.通知提醒。
作為設定畫面的一部分請求權限
使用常規ActivityCompat#requestPermission()
方法(或等效方法)請求危險權限。如何申請權限,請參考申請應用程式權限。
圖 5.請求權限
通知監聽者權限
若要實現 TTR 流程,必須將 VIA 指定為通知偵聽器。這本身不是權限,而是允許系統向註冊的偵聽器發送通知的配置。要了解 VIA 是否有權訪問此信息,應用程式可以:
- (可選)使用
CarAssistUtils#assistantIsNotificationListener()
提前檢查是否有通知偵聽器。例如,這可以在設定流程期間完成。 - (強制)使用操作
VOICE_ACTION_HANDLE_EXCEPTION
和異常EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
來回應處理CarVoiceInteractionSession#onShow()
。
如果未預先授予此存取權限,VIA 應結合使用語音和通知,將使用者引導至「汽車設定」的「通知存取」部分。以下程式碼可用於開啟設定應用程式的相應部分:
private void requestNotificationListenerAccess() { Intent intent = new Intent(Settings .ACTION_NOTIFICATION_LISTENER_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); startActivity(intent); }
實現一個語音板UI
當VoiceInteractionSession
收到onShow()
回呼時,它可以呈現一個語音板 UI。有關語音板實施的視覺和 UX 指南,請參閱預先載入助理:UX 指南。
圖 6.顯示語音板
關於如何實現此 UI,有兩個選擇:
- 覆寫
VoiceInteractionSession#onCreateContentView()
- 使用
VoiceInteractionSession#startAssistantActivity()
啟動活動
使用 onCreateContentView()
這是呈現音板的預設方式。 VoiceInteractionSession
基類別建立一個窗口,並在語音會話處於活動狀態時管理其生命週期。應用程式必須重寫VoiceInteractionSession#onCreateContentView()
並在會話建立後立即傳回附加到該視窗的視圖。該視圖最初應該是不可見的。當語音互動開始時,該視圖應該在VoiceInteractionSession#onShow()
上可見,然後在VoiceInteractionSession#onHide()
上再次不可見。
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { private View mVoicePlate; … @Override public View onCreateContentView() { mVoicePlate = inflater.inflate(R.layout.voice_plate, null); … } @Override protected void onShow(String action, Bundle args, int showFlags) { // TODO: Update UI state to "listening" mVoicePlate.setVisibility(View.VISIBLE); } @Override public void onHide() { mVoicePlate.setVisibility(View.GONE); } … }
使用此方法時,您可能需要調整VoiceInteractionSession#onComputeInsets()
以考慮 UI 的模糊區域。
使用startAssistantActivity()
在這種情況下, VoiceInteractionSession
將語音板 UI 的處理委託給常規活動。使用此選項時, VoiceInteractionSession
實作必須禁止在onPrepareShow()
回呼上建立其預設內容視窗(請參閱使用 onCreateContentView() )。在VoiceInteractionSession#onShow()
處,會話將使用VoiceInteractionSession#startAssistantActivity()
啟動語音板活動。此方法使用正確的視窗設定和活動標誌啟動 UI。
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); } … }
為了維持此活動和VoiceInteractionSession
之間的通信,可能需要一組內部 Intent 或服務綁定。例如,當呼叫VoiceInteractionSession#onHide()
時,會話必須能夠將此請求傳遞給活動。
重要的。在汽車領域,只有特別註釋的活動或 UXR「允許清單」中列出的活動才能在駕駛時顯示。這也適用於使用VoiceInteractionSession#startAssistantActivity()
啟動的活動。請記得使用<meta-data android:name="distractionOptimized" android:value="true"/>
註解您的活動,或將此活動包含在/packages/services/Car/service/res/values/config.xml
的systemActivityWhitelist
鍵中/packages/services/Car/service/res/values/config.xml
檔。有關更多信息,請參閱駕駛員分心指南。
實施語音識別
在本節中,您將學習如何透過熱詞的檢測和識別來實現語音識別。啟動詞是用來透過語音啟動新查詢或操作的觸發詞。例如,「OK Google」或「Hey Google」。
DSP 熱詞偵測
Android 透過AlwaysOnHotwordDetector
提供對 DSP 等級始終在線的熱詞偵測器的存取。一種在低 CPU 情況下實現熱詞檢測的方法。此功能的使用分為兩部分:
-
AlwaysOnHotwordDetector
的實例化。 - 註冊熱詞檢測聲音模型。
VoiceInteractionService 實作可以使用VoiceInteractionService#createAlwaysOnHotwordDetector()
建立熱詞偵測器,傳遞他們希望用於偵測的關鍵字短語和區域設定。因此,應用程式會收到一個onAvailabilityChanged()
回調,其中包含以下可能值之一:
-
STATE_HARDWARE_UNAVAILABLE
。該設備不提供 DSP 功能。在這種情況下,使用軟體熱詞檢測。 -
STATE_HARDWARE_UNSUPPORTED
。通常不提供 DSP 支持,但 DSP 不支援給定的關鍵字短語和區域設定組合。該應用程式可以選擇使用軟體熱詞檢測。 -
STATE_HARDWARE_ENROLLED
。熱詞偵測已準備就緒,可以透過呼叫startRecognition()
方法啟動。 -
STATE_HARDWARE_UNENROLLED
。所要求的關鍵字詞的聲音模型不可用,但可以註冊。
可以使用IVoiceInteractionManagerService#updateKeyphraseSoundModel()
來註冊熱詞偵測聲音模型。給定時間可以在系統中註冊多個模型,但只有一個模型與AlwaysOnHotwordDetector
關聯。 DSP 熱詞偵測可能不適用於所有裝置。 VIA 開發人員應使用getDspModuleProperties()
方法檢查硬體功能。有關如何註冊聲音模型的範例程式碼,請參閱VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
。請參閱有關並發熱詞識別的並發捕獲。
軟體熱詞偵測
如上所述,DSP 熱詞偵測可能不適用於所有裝置(例如,Android 模擬器不提供 DSP 類比)。在這種情況下,軟體語音辨識是唯一的選擇。為了避免干擾其他可能需要存取麥克風的應用程序,VIA 必須使用以下方式存取音訊輸入:
這兩個常數都是@hide
並且僅適用於捆綁應用程式。
管理音訊輸入和語音識別
音訊輸入將使用MediaRecorder 類別來實現。有關如何使用此 API 的更多信息,請參閱MediaRecorder 概述。語音互動服務也有望成為RecognitionService
類別的實作。系統中任何需要語音辨識的應用程式都可以使用此功能。若要進行語音辨識並存取麥克風,VIA 必須持有android.permission.RECORD_AUDIO
。存取RecognitionService
實現的應用程式也應持有此權限。
在 Android 10 之前,一次只能向一個應用程式授予麥克風存取權限(熱詞偵測除外,請參閱上文)。從 Android 10 開始,可以共享麥克風存取權限。有關詳細信息,請參閱共享音訊輸入。
存取音訊輸出
當 VIA 準備好提供口頭答案時,請遵循下一組指南非常重要:
- 請求音訊焦點或管理音訊輸出時,應用程式必須使用
AudioAttributes#USAGE_ASSISTANT
和AudioAttributes#CONTENT_TYPE_SPEECH
作為音訊屬性。 - 在語音辨識期間,必須使用
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
請求音訊焦點。請注意,某些媒體應用程式在音訊焦點移除時可能無法對媒體命令做出正確反應(請參閱實現媒體命令)。