如要實作語音互動應用程式 (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 處於啟用狀態即可。例如啟動字詞偵測。
- 回報支援的語音動作 (請參閱「語音助理輕觸朗讀」)。
- 從螢幕鎖定畫面 (keyguard) 啟動語音互動工作階段。
最簡單的 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 中完成。單一工作階段例項可能會重複使用,以完成多項使用者互動。在 Android Automotive OS 中,有一個 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 的說明文件。
實作設定/登入流程
設定和登入方式如下:
- 在裝置開機期間 (設定精靈)。
- 在語音互動服務切換期間 (「設定」)。
- 首次啟動應用程式時。
如需建議的使用者體驗和視覺化指南詳細資料,請參閱「預先載入的 Google 助理:使用者體驗指南」。
更換語音服務時的設定
使用者隨時可以選取未正確設定的 VIA。可能原因如下:
- 使用者完全略過設定精靈,或略過語音互動設定步驟。
- 使用者選取的 VIA 與裝置新手上路期間設定的 VIA 不同。
無論如何,VoiceInteractionService 可以透過多種方式鼓勵使用者完成設定:
- 通知提醒。
- 使用者嘗試使用時的自動語音回覆。
注意:強烈建議您不要在沒有明確使用者要求的情況下,顯示 VIA 設定流程。也就是說,VIA 應避免在裝置啟動時,或因使用者切換或解鎖而自動在 HU 上顯示內容。
通知提醒
通知提醒不會干擾使用者,可指出需要設定,並提供使用者前往助理設定流程的途徑。

圖 2. 通知提醒
這個流程的運作方式如下:

圖 3. 通知提醒流程
語音回覆
這是最簡單的實作流程,在 VoiceInteractionSession#onShow() 回呼中啟動語音指令,向使用者說明需要執行的動作,然後詢問使用者 (如果 UX 限制狀態允許設定),是否要啟動設定流程。如果當時無法設定,也請說明情況。
首次使用時設定
使用者隨時可能觸發未正確設定的 VIA。在這種情況下:
- 口頭告知使用者這個情況 (例如:「如要正常運作,請完成幾個步驟…」)。
- 如果 UX 限制引擎允許 (請參閱 UX_RESTRICTIONS_NO_SETUP),請詢問使用者是否要啟動設定程序,然後開啟 VIA 的「設定」畫面。
- 否則 (例如使用者正在開車),請留下通知,讓使用者在安全無虞時點選選項。
建構語音互動設定畫面
設定和登入畫面應開發為一般活動。如需預先載入的 Assistant 使用者介面開發 UX 和視覺設計指南,請參閱這篇文章。
一般指南:
- VIA 應允許使用者隨時中斷及繼續設定。
- 如果
UX_RESTRICTIONS_NO_SETUP限制生效,則不應允許設定。詳情請參閱「駕駛人分心指南」。 - 設定畫面應符合各車輛的設計系統。一般畫面版面配置、圖示、顏色和其他方面應與其餘 UI 一致。詳情請參閱「自訂」一節。
實作設定畫面

圖 4. 設定整合
設定畫面是一般的 Android 活動。如果實作,必須在 VIA 資訊清單中,將進入點宣告為 res/xml/interaction_service.xml 的一部分 (請參閱「資訊清單」)。如果使用者尚未完成設定和登入程序,您可以在「設定」部分繼續進行,或視需要提供「登出」或「切換使用者」選項。與上述「設定」畫面類似,這些畫面應具備下列特點:
在資訊清單檔案中宣告必要權限
VIA 要求的權限可分為三類:
- 系統簽章權限。這些權限只會授予預先安裝的系統簽署 APK。使用者無法授予這些權限,只有原始設備製造商 (OEM) 可以在建構系統映像檔時授予這些權限。如要進一步瞭解如何取得簽章權限,請參閱「授予系統專屬權限」。
- 危險權限。使用者必須透過 PermissionsController 對話方塊授予這些權限。原始設備製造商可以預先將部分權限授予預設的 VoiceInteractionService。但由於這項預設值可能會因裝置而異,應用程式應能在需要時要求這些權限。
- 其他權限。這些是所有其他不需要使用者介入的權限。系統會自動授予這些權限。
鑑於上述情況,以下章節僅著重於要求危險權限。只有在使用者位於登入或設定畫面時,才應要求權限。
如果應用程式沒有運作所需的權限,建議的流程是使用語音指令向使用者說明情況,並透過通知提供使用者可返回 VIA 設定畫面的功能。詳情請參閱「1. 通知提醒。
在設定畫面中要求權限
危險權限是使用一般 ActivityCompat#requestPermission() 方法 (或同等方法) 要求。如要瞭解如何要求權限,請參閱「要求應用程式權限」。

圖 5. 要求權限
通知接聽器權限
如要導入 TTR 流程,VIA 必須指定為通知監聽器。這並非權限,而是允許系統將通知傳送給已註冊監聽器的設定。如要瞭解 VIA 是否已取得這項資訊的存取權,應用程式可以:
- (選用) 使用
CarAssistUtils#assistantIsNotificationListener()預先檢查是否有通知監聽器。舉例來說,您可以在設定流程中執行這項操作。 - (必要) 對處理
CarVoiceInteractionSession#onShow()的動作做出反應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);
}實作語音面板 UI
VoiceInteractionSession 收到 onShow() 回呼時,可以顯示語音面板 UI。如需語音面板導入作業的視覺和使用者體驗指南,請參閱「預先載入的 Google 助理:使用者體驗指南」。

圖 6. 顯示語音板
您可以透過下列兩種方式實作這個 UI:
- 覆寫
VoiceInteractionSession#onCreateContentView() - 使用
VoiceInteractionSession#startAssistantActivity()啟動 Activity
使用 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」。
偵測啟動字詞
Android 提供 DSP 層級的全時啟動字詞偵測器存取權,方法是透過 AlwaysOnHotwordDetector。以低 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
類別實作項目。系統中需要語音辨識的應用程式會使用
存取這項功能。如要進行語音辨識及存取麥克風,VIA 必須持有 android.permission.RECORD_AUDIO。存取 RecognitionService 實作項目的應用程式也應持有這項權限。
在 Android 10 之前,麥克風存取權一次只會授予一個應用程式 (但熱字詞偵測除外,詳情請參閱上文)。從 Android 10 開始,麥克風存取權可以共用。詳情請參閱「共用音訊輸入」。
存取音訊輸出
當 VIA 準備好提供口頭回覆時,請務必遵守下列規範:
- 要求音訊焦點或管理音訊輸出時,應用程式必須使用
AudioAttributes#USAGE_ASSISTANT和AudioAttributes#CONTENT_TYPE_SPEECH做為音訊屬性。 - 語音辨識期間,必須使用
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE要求音訊焦點。請注意,音訊焦點移除後,部分媒體應用程式可能無法正確回應媒體指令 (請參閱「滿足媒體指令」)。