ใช้ Instrument Cluster API (Android API) เพื่อแสดงแอปการนำทาง รวมถึง Google Maps ในจอแสดงผลรองในรถยนต์ เช่น ด้านหลังพวงมาลัยบนแผงหน้าปัด หน้านี้จะอธิบายวิธีสร้างบริการเพื่อควบคุมจอแสดงผลรองดังกล่าวและผสานรวมบริการกับ CarService เพื่อให้แอปการนำทางแสดงอินเทอร์เฟซผู้ใช้ได้
คำศัพท์
เราใช้คำศัพท์ต่อไปนี้ในหน้านี้
CarManager ที่ช่วยให้แอปภายนอกเปิดใช้งานในแผงหน้าปัดและรับการเรียกกลับเมื่อแผงหน้าปัดพร้อมแสดงกิจกรรมandroid:singleUser ระบบ Android จะเรียกใช้อินสแตนซ์ของบริการอย่างน้อย 1 รายการในเวลาใดก็ตามสิ่งที่ต้องมีก่อน
โปรดตรวจสอบว่าคุณมีองค์ประกอบต่อไปนี้ก่อนดำเนินการต่อ
- สภาพแวดล้อมในการพัฒนาซอฟต์แวร์ Android ดูวิธีตั้งค่าสภาพแวดล้อมในการพัฒนาแอป Android ได้ที่ ข้อกำหนดในการสร้าง
- ดาวน์โหลดซอร์สโค้ด Android ดาวน์โหลดซอร์สโค้ด Android เวอร์ชันล่าสุดจากสาขา pi-car-release (หรือเวอร์ชันที่ใหม่กว่า) ที่ https://android.googlesource.com
- เครื่องเล่นวิทยุ (HU) อุปกรณ์ Android ที่สามารถใช้ Android 9 (หรือเวอร์ชันที่ใหม่กว่า) อุปกรณ์นี้ต้องมีจอแสดงผลของตัวเองและสามารถ แฟลชจอแสดงผลด้วยบิลด์ใหม่ของ Android ได้
- แผงหน้าปัด เป็นหนึ่งในรายการต่อไปนี้
- จอแสดงผลรองจริงที่แนบมากับ HU หากฮาร์ดแวร์และเคอร์เนลของอุปกรณ์รองรับการจัดการจอแสดงผลหลายจอ
- หน่วยอิสระ หน่วยประมวลผลใดก็ตามที่เชื่อมต่อกับ HU ผ่านการเชื่อมต่อเครือข่าย ซึ่งสามารถรับและแสดงสตรีมวิดีโอ ในจอแสดงผลของตัวเองได้
- จอแสดงผลจำลอง ในระหว่างการพัฒนา คุณสามารถใช้สภาพแวดล้อมจำลองอย่างใดอย่างหนึ่งต่อไปนี้
- จอแสดงผลรองจำลอง หากต้องการเปิดใช้จอแสดงผลรองจำลองในการเผยแพร่ AOSP Android ให้ไปที่การตั้งค่าตัวเลือกสำหรับนักพัฒนาแอป ในแอปการตั้งค่า ของระบบ แล้วเลือกจำลองจอแสดงผลรอง การกำหนดค่านี้เทียบเท่ากับการแนบจอแสดงผลรองจริง โดยมีข้อจำกัดว่าจอแสดงผลนี้จะซ้อนทับจอแสดงผลหลัก
- แผงหน้าปัดจำลอง โปรแกรมจำลอง Android ที่รวมอยู่ใน AAOS มีตัวเลือกในการแสดงแผงหน้าปัดด้วย ClusterRenderingService
สถาปัตยกรรมการผสานรวม
คอมโพเนนต์การผสานรวม
การผสานรวม Instrument Cluster API ประกอบด้วยคอมโพเนนต์ 3 รายการต่อไปนี้
CarService- แอปการนำทาง
- บริการแผงหน้าปัดของ OEM

CarService
CarService ทำหน้าที่เป็นสื่อกลางระหว่างแอปการนำทางกับรถยนต์ โดยตรวจสอบว่ามีแอปการนำทางเพียงแอปเดียวที่ทำงานอยู่ตลอดเวลา และมีเพียงแอปที่มีสิทธิ์ android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL เท่านั้นที่ส่งข้อมูลไปยังรถยนต์ได้
CarService จะเริ่มต้นบริการทั้งหมดที่เฉพาะเจาะจงสำหรับรถยนต์และให้สิทธิ์เข้าถึงบริการเหล่านี้ผ่านตัวจัดการต่างๆ แอปที่ทำงานในรถยนต์สามารถเข้าถึงตัวจัดการเหล่านี้เพื่อโต้ตอบกับบริการได้
สำหรับการติดตั้งใช้งานแผงหน้าปัด OEM ยานยนต์ต้องสร้างการติดตั้งใช้งาน InstrumentClusterRendererService ที่กำหนดเองและอัปเดต ClusterRenderingService
เมื่อแสดงผลแผงหน้าปัด ในระหว่างกระบวนการบูต
CarService จะอ่านคีย์ InstrumentClusterRendererService ของ
ClusterRenderingService
เพื่อค้นหาการติดตั้งใช้งาน InstrumentClusterService ใน AOSP จุดเริ่มต้นนี้จะชี้ไปยังบริการแสดงผลการติดตั้งใช้งานคลัสเตอร์ตัวอย่างของ Navigation State API
<string name="instrumentClusterRendererService"> android.car.cluster/.ClusterRenderingService </string>
ระบบจะเริ่มต้นและผูกบริการที่อ้างถึงในรายการนี้กับ CarService เมื่อแอปการนำทาง เช่น Google Maps ขอ CarInstrumentClusterManager CarService จะจัดเตรียมตัวจัดการที่อัปเดตสถานะแผงหน้าปัดจาก InstrumentClusterRenderingService ที่ผูกไว้
(ในกรณีนี้ ผูก หมายถึง
บริการ
Android)
บริการแผงหน้าปัด
OEM ต้องสร้างแพ็กเกจ Android (APK) ที่มีคลาสย่อยของ ClusterRenderingService
คลาสนี้มีวัตถุประสงค์ 2 ประการดังนี้
- จัดเตรียมอินเทอร์เฟซ Android และอุปกรณ์แสดงผลแผงหน้าปัด (วัตถุประสงค์ของหน้านี้)
- รับและแสดงผลข้อมูลอัปเดตสถานะการนำทาง เช่น คำแนะนำการนำทางแบบเลี้ยวต่อเลี้ยว
สำหรับวัตถุประสงค์แรก การติดตั้งใช้งาน InstrumentClusterRendererService
ต้องเริ่มต้นจอแสดงผลรองที่ใช้เพื่อแสดงข้อมูลบนหน้าจอในห้องโดยสารของรถยนต์ และ
สื่อสารข้อมูลนี้ไปยัง CarService โดยเรียกใช้เมธอด
InstrumentClusterRendererService.setClusterActivityOptions() และ
InstrumentClusterRendererService.setClusterActivityState()
สำหรับฟังก์ชันที่ 2 บริการแผงหน้าปัดต้องมีการติดตั้งใช้งานอินเทอร์เฟซ
ClusterRenderingService
ที่รับข้อมูลอัปเดตสถานะการนำทาง เหตุการณ์ ซึ่งเข้ารหัสเป็น
eventType และข้อมูลเหตุการณ์ที่เข้ารหัสในชุด
ลำดับการผสานรวม
แผนภาพต่อไปนี้แสดงการติดตั้งใช้งานสถานะการนำทางที่แสดงผลข้อมูลอัปเดต
ในภาพประกอบนี้ สีต่างๆ หมายถึงสิ่งต่อไปนี้
- สีเหลือง
CarServiceและCarNavigationStatusManagerที่จัดเตรียมโดยแพลตฟอร์ม Android ดูข้อมูลเพิ่มเติมได้ที่ Car และ CAR_NAVIGATION_SERVICE - สีฟ้า
InstrumentClusterRendererServiceติดตั้งใช้งาน โดย OEM - สีม่วง แอปการนำทางที่ติดตั้งใช้งานโดย Google และนักพัฒนาแอปบุคคลที่สาม
- สีเขียว
CarAppFocusManagerดูข้อมูลเพิ่มเติมได้ที่ การใช้ CarAppFocusManager API ด้านล่างและ CarAppFocusManager
โฟลว์ข้อมูลสถานะการนำทางเป็นไปตามลำดับต่อไปนี้
CarServiceเริ่มต้นInstrumentClusterRenderingService- ในระหว่างการเริ่มต้น
InstrumentClusterRenderingServiceจะอัปเดตCarServiceด้วย:- พร็อพเพอร์ตี้การแสดงผลแผงหน้าปัด เช่น ขอบเขตที่ไม่ถูกบดบัง (ดูรายละเอียดเพิ่มเติมเกี่ยวกับขอบเขตที่ไม่ถูกบดบังในภายหลัง)
- ตัวเลือกกิจกรรมที่จำเป็นในการเปิดใช้งานภายในจอแสดงผลแผงหน้าปัด ดูข้อมูลเพิ่มเติมได้ที่ ActivityOptions.
- แอปการนำทาง (เช่น Google Maps สำหรับ Android Automotive หรือแอป Maps ใดก็ตามที่มีสิทธิ์ที่จำเป็น)
จะดำเนินการต่อไปนี้:
- รับ
CarAppFocusManagerโดยใช้คลาส Car จาก car-lib - ก่อนที่คำแนะนำแบบเลี้ยวต่อเลี้ยวจะเริ่มขึ้น ให้เรียกใช้
CarAppFocusManager.requestFocus()เพื่อส่งผ่านCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATIONเป็นappTypeพารามิเตอร์
- รับ
CarAppFocusManagerจะสื่อสารคำขอนี้ไปยังCarServiceหากได้รับอนุญาตCarServiceจะตรวจสอบแพ็กเกจแอปการนำทางและค้นหากิจกรรมที่ทำเครื่องหมายด้วยหมวดหมู่android.car.cluster.NAVIGATION- หากพบ แอปการนำทางจะใช้
ActivityOptionsที่รายงานโดยInstrumentClusterRenderingServiceเพื่อเปิดใช้งานและรวมพร็อพเพอร์ตี้การแสดงผลแผงหน้าปัดเป็นข้อมูลเพิ่มเติมใน Intent
ผสานรวม API
การติดตั้งใช้งาน InstrumentClusterRenderingService ต้องมีลักษณะดังนี้
- กำหนดให้เป็นบริการ Singleton โดยเพิ่มค่าต่อไปนี้ลงใน
AndroidManifest.xml ซึ่งจำเป็นเพื่อให้แน่ใจว่าบริการแผงหน้าปัดจะทำงานเพียงสำเนาเดียว แม้ในระหว่างการเริ่มต้นและการสลับผู้ใช้:
android:singleUser="true" - มีสิทธิ์ของระบบ
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICEซึ่ง รับประกันว่า `CarService` จะผูกเฉพาะบริการแสดงผลแผงหน้าปัดที่รวมเป็นส่วนหนึ่ง ของอิมเมจระบบ Android เท่านั้นที่ผูกโดยCarService<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
ติดตั้งใช้งาน InstrumentClusterRenderingService
วิธีสร้างบริการ
- เขียนคลาสที่ขยายจาก
ClusterRenderingService
แล้วเพิ่มรายการที่เกี่ยวข้องลงในไฟล์
AndroidManifest.xmlคลาสนี้ จะควบคุมจอแสดงผลแผงหน้าปัดและสามารถแสดงผลข้อมูล Navigation State API ได้ (ไม่บังคับ) - ในระหว่าง
onCreate()ให้ใช้บริการนี้เพื่อเริ่มต้นการสื่อสารกับ ฮาร์ดแวร์การแสดงผล ตัวเลือกต่างๆ ได้แก่- กำหนดจอแสดงผลรองที่จะใช้สำหรับแผงหน้าปัด
- สร้างจอแสดงผลเสมือนเพื่อให้แอปแผงหน้าปัดแสดงผลและส่งภาพที่แสดงผลไปยังหน่วยภายนอก (โดยใช้รูปแบบการสตรีมวิดีโอ เช่น H.264)
- เมื่อจอแสดงผลที่ระบุไว้ข้างต้นพร้อมแล้ว บริการนี้ต้องเรียกใช้
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()เพื่อกำหนดActivityOptionsที่แน่นอนซึ่งต้องใช้เพื่อแสดงกิจกรรมใน แผงหน้าปัด ใช้พารามิเตอร์ต่อไปนี้category.ClusterRenderingService.ActivityOptions.อินสแตนซ์ActivityOptionsที่ใช้เปิดใช้งานในแผงหน้าปัดได้ ตัวอย่างเช่น จากการติดตั้งใช้งานแผงหน้าปัดตัวอย่างใน AOSP:getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- เมื่อแผงหน้าปัดพร้อมแสดงกิจกรรมแล้ว บริการนี้ต้องเรียกใช้
InstrumentClusterRenderingService#setClusterActivityState()ใช้พารามิเตอร์ต่อไปนี้ พารามิเตอร์:categoryClusterRenderingServicestateชุดที่สร้างด้วย ClusterRenderingService. ตรวจสอบว่าได้ระบุข้อมูลต่อไปนี้visibleระบุว่าแผงหน้าปัดมองเห็นได้และพร้อมที่จะ แสดงเนื้อหาunobscuredBoundsสี่เหลี่ยมผืนผ้าที่กำหนดพื้นที่ภายในจอแสดงผลแผงหน้าปัดซึ่งแสดงเนื้อหาได้อย่างปลอดภัย เช่น พื้นที่ ที่ครอบคลุมโดยหน้าปัดและเกจ
- ลบล้างเมธอด
Service#dump()และรายงานข้อมูลสถานะที่เป็นประโยชน์สำหรับการแก้ไขข้อบกพร่อง (ดูข้อมูลเพิ่มเติมได้ที่ dumpsys)
ตัวอย่างการติดตั้งใช้งาน InstrumentClusterRenderingService
ตัวอย่างต่อไปนี้แสดงการติดตั้งใช้งาน InstrumentClusterRenderingService ซึ่งสร้าง VirtualDisplay เพื่อนำเสนอเนื้อหาแผงหน้าปัดในจอแสดงผลจริงระยะไกล
หรือโค้ดนี้อาจส่งผ่าน displayId ของจอแสดงผลรองจริงที่เชื่อมต่อกับ HU หากทราบว่ามีจอแสดงผลดังกล่าว
/** * Sample {@link InstrumentClusterRenderingService} implementation */ public class SampleClusterServiceImpl extends InstrumentClusterRenderingService { // Used to retrieve or create displays private final DisplayManager mDisplayManager; // Unique identifier for the display to be used for instrument // cluster private final String mUniqueId = UUID.randomUUID().toString(); // Format of the instrument cluster display private static final int DISPLAY_WIDTH = 1280; private static final int DISPLAY_HEIGHT = 720; private static final int DISPLAY_DPI = 320; // Area not covered by instruments private static final int DISPLAY_UNOBSCURED_LEFT = 40; private static final int DISPLAY_UNOBSCURED_TOP = 0; private static final int DISPLAY_UNOBSCURED_RIGHT = 1200; private static final int DISPLAY_UNOBSCURED_BOTTOM = 680; @Override public void onCreate() { super.onCreate(); // Create a virtual display to render instrument cluster activities on mDisplayManager = getSystemService(DisplayManager.class); VirtualDisplay display = mDisplayManager.createVirtualDisplay( mUniqueId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_DPI, null, 0 /* flags */, null, null); // Do any additional initialization (e.g.: start a video stream // based on this virtual display to present activities on a remote // display). onDisplayReady(display.getDisplay()); } private void onDisplayReady(Display display) { // Report activity options that should be used to launch activities on // the instrument cluster. String category = CarInstrumentClusterManager.CATEGORY_NAVIGATION; ActionOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(display.getDisplayId()); setClusterActivityOptions(category, options); // Report instrument cluster state. Rect unobscuredBounds = new Rect(DISPLAY_UNOBSCURED_LEFT, DISPLAY_UNOBSCURED_TOP, DISPLAY_UNOBSCURED_RIGHT, DISPLAY_UNOBSCURED_BOTTOM); boolean visible = true; ClusterActivityState state = ClusterActivityState.create(visible, unobscuredBounds); setClusterActivityState(category, options); } }
ใช้ CarAppFocusManager API
CarAppFocusManager API มีเมธอดชื่อ getAppTypeOwner() ซึ่งช่วยให้ บริการคลัสเตอร์ที่เขียนโดย OEM ทราบว่าแอปการนำทางใดมีโฟกัสการไปยังส่วนต่างๆ ในเวลาใดก็ตาม OEM สามารถใช้เมธอด CarAppFocusManager#addFocusListener() ที่มีอยู่ และ
แล้วใช้ getAppTypeOwner() เพื่อดูว่าแอปใดมีโฟกัส ข้อมูลนี้ช่วยให้ OEM ทำสิ่งต่อไปนี้ได้
- เปลี่ยนกิจกรรมที่แสดงในคลัสเตอร์เป็นกิจกรรมคลัสเตอร์ที่จัดเตรียมโดยแอปการนำทางที่มีโฟกัส
- ตรวจหาว่าแอปการนำทางที่มีโฟกัสมีกิจกรรมคลัสเตอร์หรือไม่ หากแอปการนำทางที่มีโฟกัส ไม่มีกิจกรรมคลัสเตอร์ (หรือหากปิดใช้งานกิจกรรมดังกล่าว) OEM สามารถ ส่งสัญญาณนี้ไปยัง DIM ของรถยนต์เพื่อให้ข้ามส่วนการนำทางของคลัสเตอร์ไปเลย
ใช้ CarAppFocusManager เพื่อตั้งค่าและฟังโฟกัสแอปปัจจุบัน เช่น การนำทางที่ใช้งานอยู่หรือคำสั่งเสียง โดยปกติจะมีเพียงอินสแตนซ์เดียวของแอปดังกล่าวที่ทำงานอยู่ (หรือมีโฟกัส) ในระบบ
ใช้เมธอด CarAppFocusManager#addFocusListener(..) เพื่อฟังการเปลี่ยนแปลงโฟกัสแอป
import android.car.CarAppFocusManager; ... Car car = Car.createCar(this); mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE); mAppFocusManager.addFocusListener(this, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); ... public void onAppFocusChanged(int appType, boolean active) { // Use the CarAppFocusManager#getAppTypeOwner(appType) method call // to retrieve a list of active package names }
ใช้เมธอด CarAppFocusManager#getAppTypeOwner(..) เพื่อดึงข้อมูลชื่อแพ็กเกจของเจ้าของแอปประเภทหนึ่งๆ ที่มีโฟกัสในปัจจุบัน เมธอดนี้อาจแสดงชื่อแพ็กเกจมากกว่า 1 รายการหากเจ้าของปัจจุบันใช้ฟีเจอร์ android:sharedUserId
import android.car.CarAppFocusManager; ... Car car = Car.createCar(this); mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE); List<String> focusOwnerPackageNames = mAppFocusManager.getAppTypeOwner( CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); if (focusOwnerPackageNames == null || focusOwnerPackageNames.isEmpty()) { // No Navigation app has focus // OEM may choose to show their default cluster view } else { // focusOwnerPackageNames // Use the PackageManager to retrieve the cluster activity for the package(s) // returned in focusOwnerPackageNames } ...
ระบุแอปเทมเพลต
สำหรับแอปการนำทางที่อิงตามเทมเพลตซึ่งใช้ไลบรารีแอปในรถยนต์ CarAppFocusManager#getAppTypeOwner() จะแสดงชื่อแพ็กเกจของโฮสต์ (เช่น com.google.android.apps.automotive.templates.host) เนื่องจากโฮสต์มีโฟกัสของระบบในนามของแอปไคลเอ็นต์
หากต้องการระบุแอปไคลเอ็นต์ที่นำทาง OEM สามารถแยกชื่อแพ็กเกจออกจากชุดสถานะการนำทางที่ส่งด้วย CarNavigationStatusManager ระบบจะจัดเก็บชื่อแพ็กเกจไว้ภายใต้คีย์ active_app_package_name ในชุดที่ได้รับจาก NavigationRenderer#onNavigationStateChanged(Bundle)
// In your NavigationRenderer implementation @Override public void onNavigationStateChanged(Bundle bundle) { if (bundle.containsKey("active_app_package_name")) { String activeAppPackage = bundle.getString("active_app_package_name"); // Use the package name to identify the navigating app (e.g., com.waze) } }
ภาคผนวก: ใช้แอปตัวอย่าง
AOSP มีแอปตัวอย่างที่ติดตั้งใช้งาน Navigation State API
วิธีเรียกใช้แอปตัวอย่างนี้
- สร้างและแฟลช Android Auto ใน HU ที่รองรับ ใช้คำแนะนำในการสร้างและแฟลช Android ที่เฉพาะเจาะจงสำหรับอุปกรณ์ของคุณ ดูคำแนะนำได้ที่ การใช้บอร์ดอ้างอิง
- เชื่อมต่อจอแสดงผลรองจริงกับ HU (หากรองรับ) หรือเปิดจอแสดงผลรองเสมือนของ HU:
- เลือกโหมดนักพัฒนาแอป ในแอปการตั้งค่า
- ไปที่การตั้งค่า > ระบบ > ขั้นสูง > ตัวเลือกสำหรับนักพัฒนาแอป > จำลองจอแสดงผลรอง
- รีบูต HU
- วิธีเปิดแอป KitchenSink
- เปิดลิ้นชัก
- ไปที่แผงหน้าปัด
- คลิกเริ่มข้อมูลเมตา
KitchenSink ขอโฟกัสการนำทาง ซึ่งจะสั่งให้บริการ DirectRenderingCluster แสดงอินเทอร์เฟซผู้ใช้จำลองในแผงหน้าปัด