要實現語音交互應用程序 (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()的實現是處理Voice Assistant Tap-to-Read所必需的。系統使用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有大量的回調方法,將在下面的部分中解釋。請參閱VoiceInterationSession的文檔完整列表。
實施設置/登錄流程
可以進行設置和登錄:
- 在設備載入期間(設置嚮導)。
 - 在語音交互服務交換期間(設置)。
 - 首次啟動時選擇應用程序。
 
有關推薦的用戶體驗和視覺指導的詳細信息,請參閱預加載助手:UX 指導。
語音業務交換期間的設置
用戶始終可以選擇未正確配置的 VIA。這可能是因為:
- 用戶完全跳過了設置嚮導或用戶跳過了語音交互配置步驟。
 - 用戶選擇的 VIA 與設備載入期間配置的 VIA 不同。
 
無論如何, VoiceInteractionService有幾種方法可以鼓勵用戶完成設置:
- 通知提醒。
 - 用戶嘗試使用時自動語音回复。
 
注意:強烈建議不要在沒有明確用戶請求的情況下提供 VIA 設置流程。這意味著 VIA 應避免在設備啟動期間或由於用戶切換或解鎖而在 HU 上自動顯示內容。
1. 通知提醒
通知提醒是一種非侵入式的方式來指示設置的需要,並為用戶提供導航到助手設置流程的提示。

圖 2.通知提醒
以下是此流程的工作方式:

圖 3.通知提醒流程
Note :根據用戶體驗限制規則(請參閱駕駛員分心指南),駕駛時可能不允許設置流程。當用戶單擊通知並且不允許設置時,應用程序應該使用話語向用戶解釋情況,而不是嘗試顯示將立即被阻止的 UI。
2.語音回复
這是最簡單的實現流程,在VoiceInteractionSession#onShow()回調上啟動話語,向用戶解釋需要做什麼,然後詢問他們(如果在 UX Restriction 狀態下允許設置)他們是否想要啟動設置流程。如果當時無法設置,也請說明這種情況。
首次使用時設置
用戶總是有可能觸發未正確配置的 VIA。在這種情況下:
- 口頭告知用戶這種情況(例如,“要正常工作,我需要您完成幾個步驟……”)。
 - 如果 UX 限制引擎允許(請參閱UX_RESTRICTIONS_NO_SETUP ),請詢問用戶是否要啟動設置過程,然後打開 VIA 的設置屏幕。
 - 否則(例如,如果用戶正在開車),請留下通知,讓用戶在安全的情況下單擊該選項。
 
構建語音交互設置屏幕
設置和登錄屏幕應作為常規活動開發。請參閱Preloaded Assistants: UX Guidance中的 UI 開發的 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()啟動 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 的處理委託給常規 Activity。使用此選項時, VoiceInteractionSession實現必須在onPrepareShow()回調中禁用其默認內容窗口的創建(請參閱使用 onCreateContentView() )。在VoiceInteractionSession#onShow() ,會話將使用VoiceInteractionSession#startAssistantActivity()啟動語音板活動。此方法使用正確的 Window 設置和 Activity 標誌啟動 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 啟動指令檢測可能不適用於所有設備。威盛開發人員應使用getDspModuleProperties()方法檢查硬件功能。有關顯示如何註冊聲音模型的示例代碼,請參閱VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java 。有關並發啟動指令識別,請參閱並發捕獲。
軟件熱詞檢測
如上所述,DSP 啟動指令檢測可能並非在所有設備中都可用(例如,Android 模擬器不提供 DSP 模擬)。在這種情況下,軟件語音識別是唯一的選擇。為避免干擾可能需要訪問麥克風的其他應用程序,VIA 必須使用以下方式訪問音頻輸入:
這兩個常量都是@hide並且僅對捆綁的應用程序可用。
管理音頻輸入和語音識別
音頻輸入將使用MediaRecorder API實現。有關如何使用此 API 的更多信息,請參閱MediaRecorder 概述。語音交互服務也有望成為RecognitionService實現。系統中任何需要語音識別的應用程序都將使用SpeechRecognizer API來訪問此功能。要進行語音識別並訪問麥克風,VIA 必須持有android.permission.RECORD_AUDIO 。訪問RecognitionService實現的應用程序也應持有此權限。
在 Android 10 之前,一次只能向一個應用程序授予麥克風訪問權限(啟動指令檢測除外,請參見上文)。從 Android 10 開始,可以共享麥克風訪問權限。有關詳細信息,請參閱共享音頻輸入。
訪問音頻輸出
當 VIA 準備好提供口頭答复時,請務必遵循下一組準則:
- 當請求音頻焦點或管理音頻輸出時,應用程序必須使用
AudioAttributes#USAGE_ASSISTANT和AudioAttributes#CONTENT_TYPE_SPEECH作為音頻屬性。 - 在語音識別期間,必須使用
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE請求音頻焦點。請注意,某些媒體應用程序在移除音頻焦點時可能無法正確響應媒體命令(請參閱實現媒體命令)。