本頁介紹如何透過語音互動完成指令。
完成媒體指令
與媒體相關的命令可以分為三個不同的群組:
- 外部媒體來源(例如 AAOS 中安裝的 Spotify)。
- 後端媒體來源(例如透過 VIA 傳輸的音樂)。
- 當地媒體來源(例如汽車廣播)。
處理外部媒體來源指令
外部媒體來源定義為支援MediaSessionCompat
和MediaBrowseCompat
API 的 Android 應用程式(有關使用這些 API 的詳細說明,請參閱建置汽車媒體應用程式)。
重要提示:對於要連接到系統中所有已安裝媒體應用程式的MediaBrowseService
的助理應用程序,它必須:
- 作為系統簽章安裝(請參閱 AAOS 的媒體應用程式開發指南和範例
PackageValidator
程式碼)。 - 持有
android.permission.MEDIA_CONTENT_CONTROL
系統特權權限(請參閱授予系統特權權限)。
除了MediaBrowserCompat
和MediaControllerCompat
之外,AAOS 還提供以下功能:
-
CarMediaService
提供有關目前所選媒體來源的集中資訊。這也用於在汽車關閉重啟後恢復之前播放的媒體源。 -
car-media-common
提供了列出、連接媒體應用程式並與之互動的便捷方法。
下面提供了特定於常見語音互動命令的實現的指南。
取得已安裝媒體來源的列表
可以使用PackageManager
偵測媒體來源,並過濾與MediaBrowserService.SERVICE_INTERFACE
相符的服務。在某些汽車中可能存在一些特殊的媒體瀏覽器服務實現,應將其排除。下面是這個邏輯的一個例子:
private Map<String, MediaSource> getAvailableMediaSources() { List<String> customMediaServices = Arrays.asList(mContext.getResources() .getStringArray(R.array.custom_media_packages)); List<ResolveInfo> mediaServices = mPackageManager.queryIntentServices( new Intent(MediaBrowserService.SERVICE_INTERFACE), PackageManager.GET_RESOLVED_FILTER); Map<String, MediaSource> result = new HashMap<>(); for (ResolveInfo info : mediaServices) { String packageName = info.serviceInfo.packageName; if (customMediaServices.contains(packageName)) { // Custom media sources should be ignored, as they might have a // specialized handling (e.g., radio). continue; } String className = info.serviceInfo.name; ComponentName componentName = new ComponentName(packageName, className); MediaSource source = MediaSource.create(mContext, componentName); result.put(source.getDisplayName().toString().toLowerCase(), source); } return result; }
請注意,媒體來源可能隨時安裝或解除安裝。為了維護準確的列表,建議為意圖操作ACTION_PACKAGE_ADDED
、 ACTION_PACKAGE_CHANGED
、 ACTION_PACKAGE_REPLACED
和ACTION_PACKAGE_REMOVED
實作BroadcastReceiver
實例。
連接到目前播放的媒體來源
CarMediaService
提供了獲取目前選擇的媒體來源以及此媒體來源何時變更的方法。這些變化可能是由於用戶直接與 UI 交互,或者由於使用汽車中的硬體按鈕而發生的。另一方面,car-media-common 庫提供了連接到給定媒體來源的便捷方法。以下是有關如何連接到目前所選媒體應用程式的簡化片段:
public class MediaActuator implements MediaBrowserConnector.onConnectedBrowserChanged { private final Car mCar; private CarMediaManager mCarMediaManager; private MediaBrowserConnector mBrowserConnector; … public void initialize(Context context) { mCar = Car.createCar(context); mBrowserConnector = new MediaBrowserConnector(context, this); mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE); mBrowserConnector.connectTo(mCarMediaManager.getMediaSource()); … } @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { // TODO: Handle connected/disconnected browser } … }
控制目前播放媒體來源的播放
透過連接的MediaBrowserCompat
,可以輕鬆地將傳輸控制命令傳送到目標應用程式。這是一個簡化的範例:
public class MediaActuator … { … private MediaControllerCompat mMediaController; @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { if (browser != null && browser.isConnected()) { mMediaController = new MediaControllerCompat(mContext, browser.getSessionToken()); } else { mMediaController = null; } } private boolean playSongOnCurrentSource(String song) { if (mMediaController == null) { // No source selected. return false; } MediaControllerCompat.TransportControls controls = mMediaController.getTransportControls(); PlaybackStateCompat state = controller.getPlaybackState(); if (state == null || ((state.getActions() & PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH) == 0)) { // Source can't play from search return false; } controls.playFromSearch(query, null); return true; } … }
處理本機媒體來源指令(收音機、CD 播放器、藍牙、USB)
本地媒體來源使用上面詳述的相同 MediaSession 和 MediaBrowse API 向系統公開其功能。為了適應每種類型硬體的特殊性,這些 MediaBrowse 服務使用特定的約定來組織其資訊和媒體命令。
手把收音機
Radio MediaBrowseService 可以透過ACTION_PLAY_BROADCASTRADIO
意圖過濾器來識別。他們應該遵循實現廣播中描述的播放控制和媒體瀏覽結構。 AAOS 提供car-broadcastradio-support
庫,其中包含常數和方法,可協助 OEM 為自己的遵循定義協定的無線電服務建立 MediaBrowseService 實現,並為使用其瀏覽樹(例如 VIA)的應用程式提供支援。
處理輔助輸入、CD 音訊和 USB 媒體
作為 AOSP 的一部分,這些媒體來源沒有預設實作。建議的方法是:
- 讓 OEM 為每個裝置實施媒體服務。有關詳細信息,請參閱構建汽車媒體應用程式。
- 這些 MediaBrowseService 實作將在常規播放意圖中定義的意圖操作中進行識別和回應。
- 這些服務將按照其他來源類型中所述的準則公開瀏覽樹。
手柄藍牙
藍牙媒體內容透過 AVRCP 藍牙設定檔公開。為了方便存取此功能,AAOS 包含 MediaBrowserService 和 MediaSession 實現,以抽象化通訊詳細資訊(請參閱packages/apps/Bluetooth )。
對應的媒體瀏覽器樹結構在BrowseTree類別中定義。透過使用其 MediaSession 實現,可以像任何其他應用程式一樣傳遞播放控制命令。
處理串流媒體命令
要實現伺服器端媒體串流,VIA 本身必須成為媒體來源,實作 MediaBrowse 和 MediaSession API。請參閱建立汽車媒體應用程式。透過實現這些 API,語音控制應用程式將能夠(除其他外):
- 無縫參與媒體源選擇
- 汽車重啟後自動恢復
- 使用 Media Center UI 提供播放和瀏覽控制
- 接收標準硬體媒體按鈕事件
完成導航命令
沒有與所有導航應用程式互動的標準化方式。有關與 Google 地圖的集成,請參閱Google Maps for Android Automotive Intents 。如需與其他應用程式集成,請直接聯絡應用程式開發人員。在向任何應用程式(包括 Google 地圖)啟動意圖之前,請先驗證該意圖是否可以解析(請參閱意圖請求)。這創造了在目標應用程式不可用時通知用戶的機會。
執行車輛命令
透過CarPropertyManager提供車輛屬性的讀寫存取。車輛屬性類型、其實現和其他詳細資訊在屬性配置中進行了解釋。要準確描述 Android 支援的屬性,最好直接參考hardware/interfaces/automotive/vehicle/2.0/types.hal
。此處定義的VehicleProperty列舉包含標準和供應商特定的屬性、資料類型、變更模式、單位和讀取/寫入存取定義。
若要從 Java 存取這些相同的常數,您可以使用VehiclePropertyIds及其伴隨類別。不同的屬性有不同的 Android 權限來控制其存取。這些權限在CarService 清單中聲明,屬性和權限之間的對應在VehiclePropertyIds Javadoc 中描述並在PropertyHalServiceIds中強制執行。
讀取車輛屬性
以下舉例說明如何讀取車速:
public class CarActuator ... { private final Car mCar; private final CarPropertyManager mCarPropertyManager; private final TextToSpeech mTTS; /** Global VHAL area id */ public static final int GLOBAL_AREA_ID = 0; public CarActuator(Context context, TextToSpeech tts) { mCar = Car.createCar(context); mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE); mTTS = tts; ... } @Nullable private void getSpeedInMetersPerSecond() { if (!mCarPropertyManager.isPropertyAvailable(VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID)) { mTTS.speak("I'm sorry, but I can't read the speed of this vehicle"); return; } // Data type and unit can be found in // automotive/vehicle/2.0/types.hal float speedInMps = mCarPropertyManager.getFloatProperty( VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID); int speedInMph = (int)(speedInMetersPerSecond * 2.23694f); mTTS.speak(String.format("Sure. Your current speed is %d miles " + "per hour", speedInUserUnit); } ... }
設定車輛屬性
以下範例展示如何開啟和關閉前置空調。
public class CarActuator … { … private void changeFrontAC(boolean turnOn) { List<CarPropertyConfig> configs = mCarPropertyManager .getPropertyList(new ArraySet<>(Arrays.asList( VehiclePropertyIds.HVAC_AC_ON))); if (configs == null || configs.size() != 1) { mTTS.speak("I'm sorry, but I can't control the AC of your vehicle"); return; } // Find the front area Ids for the AC property. int[] areaIds = configs.get(0).getAreaIds(); List<Integer> areasToChange = new ArrayList<>(); for (int areaId : areaIds) { if ((areaId & (VehicleAreaSeat.SEAT_ROW_1_CENTER | VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_RIGHT)) == 0) { continue; } boolean isACInAreaAlreadyOn = mCarPropertyManager .getBooleanProperty(VehiclePropertyIds.HVAC_AC_ON, areaId); if ((!isACInAreaAlreadyOn && turnOn) || (isACInAreaAlreadyOn && !turnOn)) { areasToChange.add(areaId); } } if (areasToChange.isEmpty()) { mTTS.speak(String.format("The AC is already %s", turnOn ? "on" : "off")); return; } for (int areaId : areasToChange) { mCarPropertyManager.setBooleanProperty( VehiclePropertyIds.HVAC_AC_ON, areaId, turnOn); } mTTS.speak(String.format("Okay, I'm turning your front AC %s", turnOn ? "on" : "off")); } … }
完成通訊命令
處理訊息命令
VIA 必須按照語音助理點擊閱讀中所述的「點擊閱讀」流程處理傳入訊息,該流程可以選擇處理將回覆發送回傳入訊息發送者。此外,VIA 還可以使用SmsManager
( android.telephony
套件的一部分)直接從汽車或透過藍牙編寫和發送 SMS 訊息。
處理呼叫命令
類似地,VIA 可以使用TelephonyManager
撥打電話並呼叫使用者的語音郵件號碼。在這些情況下,VIA 將直接與電話堆疊或汽車撥號器應用程式互動。無論如何,汽車撥號器應用程式應該是向用戶顯示語音呼叫相關 UI 的應用程式。
完成其他指令
有關 VIA 和系統之間其他可能的整合點的列表,請查看眾所周知的Android 意圖列表。許多使用者命令可以在伺服器端解析(例如,閱讀使用者電子郵件和日曆事件),並且除了語音互動本身之外不需要與系統進行任何互動。
沉浸式動作(顯示視覺內容)
在增強用戶操作或理解的地方,VIA 可以在汽車螢幕上提供補充視覺內容。為了最大限度地減少駕駛員分心,請保持此類內容簡單、簡短且可操作。有關沉浸式操作的 UI/UX 指南的詳細信息,請參閱預加載助手:UX 指南。
為了實現自訂並與主機 (HU) 設計的其餘部分保持一致,VIA 應為大多數 UI 元素使用汽車 UI 庫元件。詳情請參閱定制。