應用程式開發

要實現語音互動應用程式 (VIA),您需要完成以下步驟:

  1. 建立 VIA 骨架。
  2. 可選)實施設定/登入流程。
  3. 可選)實施設定畫面。
  4. 在清單檔案中聲明所需的權限。
  5. 實現語音板 UI。
  6. 實現語音辨識(必須包含 RecognitionService API 實作)。
  7. 實作話語(您可以選擇實作 TextToSpeech API)。
  8. 執行命令執行。請參閱執行命令中的此內容。

以下部分說明如何完成上述每個步驟。

建立 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。在這種情況下:

  1. 口頭告知使用者這種情況(例如,「為了正常工作,我需要您完成幾個步驟…」)。
  2. 如果 UX 限制引擎允許(請參閱UX_RESTRICTIONS_NO_SETUP ),請詢問使用者是否要啟動設定過程,然後開啟 VIA 的「設定」畫面。
  3. 否則(例如,如果使用者正在開車),請留下通知,以便使用者在安全時按一下該選項。

建構語音互動設定畫面

設定和登入畫面應作為常規活動進行開發。請參閱預先載入助理中的 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 是否有權訪問此信息,應用程式可以:

如果未預先授予此存取權限,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.xmlsystemActivityWhitelist鍵中/packages/services/Car/service/res/values/config.xml檔。有關更多信息,請參閱駕駛員分心指南

實施語音識別

在本節中,您將學習如何透過熱詞的檢測和識別來實現語音識別。啟動是用來透過語音啟動新查詢或操作的觸發詞。例如,「OK Google」或「Hey Google」。

DSP 熱詞偵測

Android 透過AlwaysOnHotwordDetector提供對 DSP 等級始終在線的熱詞偵測器的存取。一種在低 CPU 情況下實現熱詞檢測的方法。此功能的使用分為兩部分:

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 準備好提供口頭答案時,請遵循下一組指南非常重要: