如要實作語音互動應用程式 (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. Lifecycles
如先前所述,VoiceInteractionService
是 VIA 的進入點。這項服務的主要職責如下:
- 初始化任何程序,只要此 VIA 是有效的,就應持續執行。例如啟動字詞偵測。
- 回報支援的語音指令 (請參閱「Google 助理輕觸朗讀」)。
- 從螢幕鎖定畫面 (鎖定畫面) 啟動語音互動工作階段。
以最簡單的形式來說,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()
,才能處理 Google 助理輕觸朗讀功能。系統會使用 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
的完整清單,請參閱說明文件。
實作設定/登入流程
設定和登入作業可在以下情況發生:
- 在裝置加入期間 (設定精靈)。
- 在語音互動服務切換期間 (設定)。
- 選取應用程式後,首次啟動時。
如需建議的使用者體驗和視覺指南詳細資訊,請參閱「預先載入的助理:使用者體驗指南」。
在語音服務切換期間設定
使用者隨時可以選取未正確設定的 VIA。可能的原因如下:
- 使用者完全略過設定精靈,或略過語音互動設定步驟。
- 使用者選取的 VIA 與裝置新手上路期間設定的不同。
無論如何,VoiceInteractionService
有幾種方式可鼓勵使用者完成設定:
- 通知提醒。
- 使用者嘗試使用時,自動回覆語音。
注意:強烈建議您不要在沒有明確使用者要求的情況下,顯示 VIA 設定流程。也就是說,VIA 應避免在裝置啟動期間,或在使用者切換或解鎖時,自動在車用多媒體系統上顯示內容。
通知提醒
通知提醒是一種不具侵入性的方式,可用來指出設定需求,並為使用者提供導覽至 Google 助理設定流程的操作元素。
圖 2. 通知提醒
這個流程的運作方式如下:
圖 3. 通知提醒流程
語音回覆
這是最簡單的實作流程,可在 VoiceInteractionSession#onShow()
回呼上啟動語音,向使用者說明需要採取的步驟,然後詢問使用者是否要啟動設定流程 (如果允許設定,則會顯示使用者體驗限制狀態)。如果無法在當下完成設定,也請說明這個情況。
首次使用時設定
使用者一律可以觸發未正確設定的 VIA。在這種情況下:
- 口頭告知使用者這個情況 (例如:「為了讓系統正常運作,請完成幾個步驟……」)。
- 如果使用者體驗限制引擎允許 (請參閱 UX_RESTRICTIONS_NO_SETUP),請詢問使用者是否要開始設定程序,然後開啟 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。如需語音板導入作業的視覺和使用者體驗指南,請參閱「預先載入的 Google 助理:使用者體驗指南」。
圖 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()
時,工作階段必須能夠將這項要求傳遞至活動。
重要事項:在 Automotive 中,只有特別註解的活動或 UXR「許可清單」中列出的活動,才能在行車時顯示。這也適用於以 VoiceInteractionSession#startAssistantActivity()
啟動的活動。請記得使用 <meta-data
android:name="distractionOptimized" android:value="true"/>
為活動加上註解,或是在 /packages/services/Car/service/res/values/config.xml
檔案的 systemActivityWhitelist
鍵中加入此活動。詳情請參閱「駕駛人分心行為指南」。
實作語音辨識
在本節中,您將瞭解如何透過熱字詞的偵測和辨識功能,實作語音辨識功能。熱字詞是用來透過語音啟動新查詢或動作的觸發字詞。例如「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 必須使用以下方式存取音訊輸入:
- 音訊擷取功能必須使用 MediaRecorder.AudioSource.HOTWORD。
- 持有
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
權限。
這兩個常數都是 @hide
,且僅適用於已綁定的應用程式。
管理音訊輸入和語音辨識
音訊輸入會使用 MediaRecorder 類別實作。如要進一步瞭解如何使用此 API,請參閱「MediaRecorder 總覽」。語音互動服務也應為 RecognitionService
類別的實作項目。系統中任何需要語音辨識功能的應用程式,都會使用這個 API 存取這項功能。如要進行語音辨識並存取麥克風,VIA 必須持有 android.permission.RECORD_AUDIO
。存取 RecognitionService
實作項目的應用程式也應具備這項權限。
在 Android 10 之前,麥克風存取權一次只會授予一個應用程式 (除了熱字詞偵測,請參閱上文)。從 Android 10 開始,麥克風存取權可共用。詳情請參閱「共用音訊輸入」。
存取音訊輸出
當 VIA 準備好提供口頭回應時,請務必遵循下列指南:
- 要求音訊焦點或管理音訊輸出時,應用程式必須使用
AudioAttributes#USAGE_ASSISTANT
和AudioAttributes#CONTENT_TYPE_SPEECH
做為音訊屬性。 - 在語音辨識期間,您必須使用
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
要求音訊焦點。請注意,當部分媒體應用程式移除音訊焦點時,可能無法正確回應媒體指令 (請參閱「執行媒體指令」)。