หน้านี้อธิบายวิธีดำเนินการตามคำสั่งด้วยการโต้ตอบด้วยเสียง
ดำเนินการตามคำสั่งเกี่ยวกับสื่อ
คำสั่งที่เกี่ยวข้องกับสื่อสามารถแบ่งออกเป็น 3 กลุ่มที่แตกต่างกัน ดังนี้
- แหล่งที่มาของสื่อภายนอก (เช่น Spotify ที่ติดตั้งใน AAOS)
- แหล่งที่มาของสื่อแบ็กเอนด์ (เช่น เพลงที่สตรีมผ่าน VIA)
- แหล่งที่มาของสื่อในเครื่อง (เช่น วิทยุในรถ)
จัดการคำสั่งแหล่งที่มาของสื่อภายนอก
แหล่งที่มาของสื่อภายนอกกำหนดไว้เป็นแอป Android ที่รองรับ MediaSessionCompat
และ MediaBrowseCompat
API (ดูคำอธิบายโดยละเอียดเกี่ยวกับการใช้ API เหล่านี้ได้ที่ สร้างแอปสื่อสำหรับ
รถยนต์)
สำคัญ: แอปผู้ช่วยต้องมีคุณสมบัติดังต่อไปนี้จึงจะเชื่อมต่อกับ MediaBrowseService ของแอปสื่อทั้งหมดที่ติดตั้งในระบบได้
- ติดตั้งเป็นแอปที่ลงชื่อโดยระบบ (ดูหลักเกณฑ์การพัฒนาแอปพลิเคชันสื่อสำหรับ AAOS และโค้ดตัวอย่าง
PackageValidator) - มีสิทธิ์
android.permission.MEDIA_CONTENT_CONTROLที่ได้รับสิทธิ์พิเศษจากระบบ (ดูหัวข้อให้สิทธิ์ ที่ได้รับสิทธิ์พิเศษจากระบบ)
นอกจาก MediaBrowserCompat
และ MediaControllerCompat,
แล้ว AAOS ยังมีสิ่งต่อไปนี้ด้วย
CarMediaServiceให้ข้อมูลแบบรวมศูนย์เกี่ยวกับแหล่งที่มาของสื่อที่เลือกในปัจจุบัน นอกจากนี้ยังใช้เพื่อกลับมาเล่นแหล่งที่มาของสื่อที่เล่นก่อนหน้านี้หลังจากปิดและรีสตาร์ทรถcar-media-commonมีเมธอดที่สะดวกในการแสดงรายการ เชื่อมต่อ และโต้ตอบกับแอปสื่อ
ด้านล่างนี้คือหลักเกณฑ์เฉพาะสำหรับการใช้คำสั่งการโต้ตอบด้วยเสียงทั่วไป
รับรายการแหล่งที่มาของสื่อที่ติดตั้ง
ระบบจะตรวจหาแหล่งที่มาของสื่อได้โดยใช้ PackageManager,
และกรองหาบริการที่ตรงกับ MediaBrowserService.SERVICE_INTERFACE
รถบางรุ่นอาจมีการใช้บริการ Media Browser แบบพิเศษ ซึ่งควรยกเว้น ตัวอย่างตรรกะนี้มีดังนี้
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; } … }
จัดการคำสั่งแหล่งที่มาของสื่อในเครื่อง (วิทยุ เครื่องเล่นซีดี บลูทูธ USB)
แหล่งที่มาของสื่อในเครื่องจะแสดงฟังก์ชันการทำงานต่อระบบโดยใช้ API MediaSession และ MediaBrowse เดียวกันกับที่อธิบายไว้ข้างต้น บริการ MediaBrowse เหล่านี้ใช้กฎเฉพาะเพื่อจัดระเบียบข้อมูลและคำสั่งสื่อเพื่อให้สอดคล้องกับลักษณะเฉพาะของฮาร์ดแวร์แต่ละประเภท
จัดการวิทยุ
คุณระบุ Radio MediaBrowseService ได้ด้วยตัวกรอง Intent ACTION_PLAY_BROADCASTRADIO
โดยคาดว่าจะปฏิบัติตามการควบคุมการเล่นและโครงสร้างการเรียกดูสื่อ
ที่อธิบายไว้ในหัวข้อใช้ฟังก์ชันวิทยุ AAOS มีไลบรารี
car-broadcastradio-support
ที่มีค่าคงที่และเมธอดเพื่อช่วยให้ OEM สร้างการใช้ MediaBrowseService
สำหรับบริการวิทยุของตนเองที่ปฏิบัติตามโปรโตคอลที่กำหนด
และรองรับแอปที่ใช้โครงสร้างการเรียกดู (เช่น VIA)
จัดการอินพุตเสริม เสียงจากซีดี และสื่อจาก USB
ไม่มีการใช้แหล่งที่มาของสื่อเหล่านี้เป็นค่าเริ่มต้นใน AOSP แนวทางที่แนะนำมีดังนี้
- ให้ OEM ใช้บริการสื่อสำหรับแหล่งที่มาของสื่อแต่ละรายการ ดูรายละเอียดได้ที่สร้างแอปสื่อสำหรับรถยนต์
- ระบบจะระบุและตอบสนองต่อการใช้ MediaBrowseService เหล่านี้ในการดำเนินการ Intent ที่กำหนดไว้ที่ Intent การเล่นทั่วไป
- บริการเหล่านี้จะแสดงโครงสร้างการเรียกดูตามหลักเกณฑ์ที่อธิบายไว้ ที่ ประเภทแหล่งที่มาอื่นๆ
จัดการบลูทูธ
เนื้อหาสื่อบลูทูธจะแสดงผ่านโปรไฟล์บลูทูธ AVRCP AAOS มีการใช้ MediaBrowserService และ MediaSession ที่สรุปรายละเอียดการสื่อสาร (ดู packages/apps/Bluetooth) เพื่อช่วยให้เข้าถึงฟังก์ชันการทำงานนี้ได้ง่ายขึ้น
โครงสร้างโครงสร้างการเรียกดูสื่อที่เกี่ยวข้องจะกำหนดไว้ที่ BrowseTree คลาส คุณสามารถส่งคำสั่งควบคุมการเล่นได้ในลักษณะเดียวกับแอปอื่นๆ โดยใช้การใช้ MediaSession ของแอป
จัดการคำสั่งสื่อสตรีมมิง
หากต้องการใช้การสตรีมสื่อฝั่งเซิร์ฟเวอร์ VIA ต้องกลายเป็นแหล่งที่มาของสื่อเอง โดยใช้ API MediaBrowse และ MediaSession ดูที่ สร้างแอปสื่อ สำหรับรถยนต์ การใช้ API เหล่านี้จะช่วยให้แอปควบคุมด้วยเสียงทำสิ่งต่อไปนี้ได้ (นอกเหนือจากสิ่งอื่นๆ)
- เข้าร่วมการเลือกแหล่งที่มาของสื่อได้อย่างราบรื่น
- กลับมาทำงานต่อโดยอัตโนมัติหลังจากรีสตาร์ทรถ
- ให้การควบคุมการเล่นและการเรียกดูโดยใช้ UI ของ Media Center
- รับเหตุการณ์ปุ่มสื่อฮาร์ดแวร์มาตรฐาน
ดำเนินการตามคำสั่งการนำทาง
ไม่มีวิธีมาตรฐานในการโต้ตอบกับแอปการนำทางทั้งหมด หากต้องการผสานรวมกับ Google Maps โปรดดู Google Maps สำหรับ Android Automotive Intent หากต้องการผสานรวมกับแอปอื่นๆ โปรดติดต่อนักพัฒนาแอปโดยตรง ก่อนที่จะเปิดใช้ Intent ไปยังแอปใดก็ตาม (รวมถึง Google Maps) ให้ตรวจสอบว่า Intent สามารถ แก้ไขได้ (ดู Intent ของ Google Maps) ซึ่งจะช่วยให้คุณแจ้งให้ผู้ใช้ทราบได้ในกรณีที่แอปเป้าหมายไม่พร้อมใช้งาน
ดำเนินการตามคำสั่งเกี่ยวกับรถ
คุณสามารถเข้าถึงพร็อพเพอร์ตี้ของรถเพื่ออ่านและเขียนได้ผ่าน
CarPropertyManager
ประเภทพร็อพเพอร์ตี้ของรถ การใช้พร็อพเพอร์ตี้ และรายละเอียดอื่นๆ อธิบายไว้
ใน หัวข้อการกำหนดค่า
พร็อพเพอร์ตี้ หากต้องการคำอธิบายที่ถูกต้องเกี่ยวกับพร็อพเพอร์ตี้ที่ Android รองรับ
คุณควรดู hardware/interfaces/automotive/vehicle/2.0/types.hal โดยตรง
Enum VehicleProperty
ที่กำหนดไว้ในนั้นมีทั้งพร็อพเพอร์ตี้มาตรฐานและพร็อพเพอร์ตี้เฉพาะของผู้ให้บริการ, ประเภทข้อมูล, โหมดการเปลี่ยนแปลง, หน่วย และคำจำกัดความการเข้าถึงแบบอ่าน/เขียน
หากต้องการเข้าถึงค่าคงที่เดียวกันนี้จาก Java คุณสามารถใช้ VehiclePropertyIds และคลาสที่เกี่ยวข้อง พร็อพเพอร์ตี้ต่างๆ มีสิทธิ์ของ Android ที่แตกต่างกันในการควบคุมการเข้าถึง สิทธิ์เหล่านี้ประกาศไว้ในไฟล์ Manifest ของ CarService และการแมประหว่างพร็อพเพอร์ตี้กับสิทธิ์อธิบายไว้ ใน Javadoc ของ VehiclePropertyIds และบังคับใช้ใน 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
VIA ควรใช้คอมโพเนนต์ของไลบรารี Car UI สำหรับองค์ประกอบ UI ส่วนใหญ่ เพื่อให้ปรับแต่งได้และสอดคล้องกับ การออกแบบส่วนอื่นๆ ของชุดอุปกรณ์บนแผงหน้าปัด (HU) ดูรายละเอียดได้ที่ การปรับแต่ง