หน้านี้จะอธิบายวิธีดำเนินการตามคำสั่งด้วยการโต้ตอบด้วยเสียง
ทำตามคำสั่งสื่อ
คำสั่งเกี่ยวกับสื่อจะแบ่งออกเป็น 3 กลุ่มดังนี้
- แหล่งที่มาสื่อภายนอก (เช่น Spotify ที่ติดตั้งใน AAOS)
- แหล่งที่มาของสื่อแบ็กเอนด์ (เช่น เพลงที่สตรีมผ่าน VIA)
- แหล่งที่มาสื่อในท้องถิ่น (เช่น วิทยุในรถยนต์)
จัดการคำสั่งของแหล่งที่มาสื่อภายนอก
แหล่งที่มาสื่อภายนอกหมายถึงแอป Android ที่รองรับ MediaSessionCompat
และ MediaBrowseCompat
API (โปรดดูสร้างแอปสื่อสำหรับ
เพื่อดูคำอธิบายโดยละเอียดเกี่ยวกับการใช้ API เหล่านี้)
ข้อสำคัญ: เพื่อให้แอป Assistant เชื่อมต่อกับ
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; }
โปรดทราบว่าอาจมีการติดตั้งหรือถอนการติดตั้งแหล่งที่มาของสื่อได้ทุกเมื่อ ใน
เราขอแนะนำให้ใช้ BroadcastReceiver
เพื่อรักษารายการให้ถูกต้องอยู่เสมอ
สำหรับการดำเนินการ Intent ACTION_PACKAGE_ADDED
ACTION_PACKAGE_CHANGED
,
ACTION_PACKAGE_REPLACED
และ ACTION_PACKAGE_REMOVED
เชื่อมต่อกับแหล่งที่มาของสื่อที่เล่นอยู่
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
Intent การควบคุมการเล่นและการเรียกดูสื่อจะเป็นไปตามการควบคุมการเล่นและการเรียกดูสื่อ
ที่อธิบายไว้ในหัวข้อติดตั้งวิทยุ AAOS เสนอ
car-broadcastradio-support
ไลบรารีที่มีค่าคงที่และวิธีการเพื่อช่วย OEM สร้าง MediaBrowseService
การใช้งานบริการวิทยุของตนเองที่เป็นไปตามโปรโตคอลที่ระบุไว้
และให้การสนับสนุนสำหรับแอปที่ใช้โครงสร้างการเรียกดู (เช่น VIA)
จัดการอินพุตเสริม, เสียง CD และสื่อ USB
ไม่มีการติดตั้งใช้งานเริ่มต้นของแหล่งที่มาสื่อเหล่านี้ในฐานะที่เป็นส่วนหนึ่งของ AOSP แนวทางที่แนะนำมีดังนี้
- ให้ OEM ใช้บริการสื่อสำหรับแต่ละราย โปรดดูรายละเอียดที่หัวข้อสร้างแอปสื่อสำหรับรถยนต์
- การใช้งาน MediaBrowseService เหล่านี้จะได้รับการระบุและตอบสนองด้วยความตั้งใจ การดำเนินการที่ระบุไว้ใน General Play Intents
- บริการเหล่านี้จะแสดงโครงสร้างการเรียกดูตามหลักเกณฑ์ที่อธิบายไว้ ที่แหล่งข้อมูลประเภทอื่นๆ
จัดการบลูทูธ
เนื้อหาสื่อบลูทูธจะแสดงผ่านโปรไฟล์บลูทูธ AVRCP ใน AAOS ประกอบด้วย การใช้งาน MediaBrowserService และ MediaSession ที่แยก รายละเอียดการสื่อสาร (ดูแพ็กเกจ/แอป/บลูทูธ)
โครงสร้างแผนผังสื่อของเบราว์เซอร์ที่เกี่ยวข้องจะกำหนดไว้ที่ BrowseTree คุณจะส่งคำสั่งควบคุมการเล่นได้เช่นเดียวกับแอปอื่นๆ โดยใช้การใช้งาน MediaSession
จัดการคำสั่งสตรีมมิงสื่อ
ในการใช้สตรีมมิงสื่อฝั่งเซิร์ฟเวอร์ VIA ต้องเป็นตัวเอง แหล่งที่มาของสื่อ โดยใช้ MediaBrowse และ MediaSession API โปรดดู สร้างแอปสื่อ สำหรับรถยนต์ การใช้ API เหล่านี้จะทำให้แอปควบคุมด้วยเสียงทำสิ่งต่อไปนี้ได้ (และอื่นๆ):
- มีส่วนร่วมในการเลือกแหล่งที่มาของสื่อได้อย่างราบรื่น
- กลับมาทำงานอีกครั้งโดยอัตโนมัติหลังจากรีสตาร์ทรถ
- ให้การควบคุมการเล่นและการท่องเว็บโดยใช้ UI ของ Media Center
- รับเหตุการณ์เกี่ยวกับปุ่มสื่อฮาร์ดแวร์มาตรฐาน
ดำเนินการตามคำสั่งการนำทาง
ไม่มีวิธีมาตรฐานในการโต้ตอบกับแอปนำทางทั้งหมด สำหรับการผสานรวมกับ Google Maps โปรดดูที่ Google Maps for Android Automotive Intent สำหรับการผสานรวมกับ โปรดติดต่อนักพัฒนาแอปโดยตรง ก่อนการเปิดตัว แอป (รวมถึง Google Maps) เพื่อยืนยันว่า Intent นั้น ได้รับการแก้ไขแล้ว (ดูความตั้งใจ คำขอ) ซึ่งเป็นโอกาสในการแจ้งให้ผู้ใช้ทราบในกรณีที่ แอปเป้าหมายไม่พร้อมใช้งาน
ดำเนินการตามคำสั่งของรถ
คุณสามารถเข้าถึงพร็อพเพอร์ตี้ยานพาหนะสำหรับทั้งการอ่านและเขียนได้ผ่านทาง
CarPropertyManager
อธิบายประเภทพร็อพเพอร์ตี้ของยานพาหนะ การใช้งาน และรายละเอียดอื่นๆ
ในพร็อพเพอร์ตี้
และการกำหนดค่าของคุณ หากต้องการคำอธิบายที่ถูกต้องเกี่ยวกับที่พักที่รองรับ
โดย Android คุณควรอ้างอิง hardware/interfaces/automotive/vehicle/2.0/types.hal
โดยตรง
VehicleProperty
enum ที่กำหนดไว้มีทั้งคุณสมบัติ ข้อมูล ทั้งแบบมาตรฐานและเฉพาะผู้ให้บริการ
ประเภท โหมดเปลี่ยน หน่วย และคำจำกัดความการเข้าถึงแบบอ่าน/เขียน
หากต้องการเข้าถึงค่าคงที่เหล่านี้จาก Java คุณสามารถใช้ VehiclePropertyIds และคลาสที่ใช้ร่วมกัน พร็อพเพอร์ตี้แต่ละรายการมีสิทธิ์ Android ที่แตกต่างกันซึ่งควบคุมสิทธิ์ของตน สิทธิ์การเข้าถึง มีการประกาศสิทธิ์เหล่านี้ใน CarService ไฟล์ Manifest และการแมประหว่างพร็อพเพอร์ตี้และสิทธิ์ที่อธิบายไว้ ใน 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 และ ให้ตรวจสอบรายการ Intent ของ Android ที่รู้จักกันดี หลายหน้า คำสั่งของผู้ใช้จะแก้ไขได้จากฝั่งเซิร์ฟเวอร์ (เช่น การอ่าน อีเมลของผู้ใช้และกิจกรรมในปฏิทิน) และไม่ต้องมีการโต้ตอบกับระบบ นอกเหนือจากการโต้ตอบด้วยเสียงเอง
การดำเนินการแบบสมจริง (แสดงเนื้อหาภาพ)
ที่ช่วยส่งเสริมการกระทำหรือความเข้าใจของผู้ใช้ VIA สามารถมอบ เนื้อหาเสริมที่มองเห็นได้บนหน้าจอรถ เพื่อลดสิ่งรบกวนผู้ขับขี่ ทำให้เนื้อหานั้นเรียบง่าย กระชับ และนำไปใช้ได้จริง สำหรับรายละเอียดเกี่ยวกับหลักเกณฑ์ UI/UX เกี่ยวกับการดำเนินการที่สมจริง โปรดดู ผู้ช่วยที่โหลดไว้ล่วงหน้า: คำแนะนำสำหรับ UX
เพื่อให้สามารถปรับแต่งได้และมีความสอดคล้องกับ ส่วนที่เหลือของการออกแบบระบบเครื่องเสียง (HU) VIA ควรใช้ รถยนต์ UI Library สำหรับองค์ประกอบ UI ส่วนใหญ่ โปรดดูรายละเอียดที่หัวข้อ การปรับแต่ง