本頁面說明如何透過語音互動執行指令。
執行媒體指令
媒體相關指令可分為三個不同的群組:
- 外部媒體來源 (例如在 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 服務會使用特定慣例來整理資訊和媒體指令。
處理無線電
可透過 ACTION_PLAY_BROADCASTRADIO
意圖篩選器識別 Radio MediaBrowseService。這些控制項應遵循「實作廣播電台」一文所述的播放控制項和媒體瀏覽結構。AAOS 提供 car-broadcastradio-support
程式庫,其中包含常數和方法,可協助原始設備製造商為自家電台服務建立 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 地圖,請參閱「適用於 Android Automotive 意圖的 Google 地圖」。如要整合其他應用程式,請直接與應用程式開發人員聯絡。向任何應用程式 (包括 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 可使用 SmsManager
(android.telephony
套件的一部分),直接透過車輛或藍牙撰寫及傳送簡訊。
處理通話指令
同樣地,VIA 可使用 TelephonyManager
撥打電話,並撥打至使用者的語音信箱號碼。在這些情況下,VIA 會直接與電話服務堆疊或 Car Dialer 應用程式互動。無論如何,Car Dialer 應用程式都應向使用者顯示與語音通話相關的使用者介面。
執行其他指令
如需 VIA 與系統之間其他可能的整合點清單,請查看知名的 Android 意圖清單。許多使用者指令可在伺服器端解析 (例如讀取使用者的電子郵件和日曆活動),而且除了語音互動本身,不需要與系統進行任何互動。
沉浸式動作 (顯示視覺內容)
在可提升使用者操作或理解的情況下,VIA 可在車輛螢幕上提供輔助視覺內容。為了盡量減少駕駛人分心,請確保這類內容簡單、簡短且可採取行動。如要進一步瞭解沉浸式動作的 UI/UX 指南,請參閱「預先載入的 Google 助理:使用者體驗指南」。
為讓自訂內容與其他車用主機 (HU) 設計保持一致,VIA 應在大部分的 UI 元素中使用 Car UI Library 元件。詳情請參閱「自訂」。