ไลบรารีแอป Android สำหรับรถยนต์ช่วยให้คุณนำแอปการนำทาง จุดที่น่าสนใจ (POI) และอินเทอร์เน็ตของสรรพสิ่ง (IoT) มาใช้ในรถได้ โดยให้บริการชุดเทมเพลตที่ออกแบบมาเพื่อให้เป็นไปตามมาตรฐานความรบกวนของผู้ขับขี่ รวมถึงดูแลรายละเอียดต่างๆ เช่น ปัจจัยต่างๆ ของหน้าจอรถยนต์และรูปแบบอินพุต
คู่มือนี้จะให้ภาพรวมเกี่ยวกับฟีเจอร์และแนวคิดหลักของไลบรารี รวมถึงแนะนำขั้นตอนการตั้งค่าแอปพื้นฐาน
ก่อนเริ่มต้น
- ดูหน้าการออกแบบสำหรับการขับรถ
ที่ครอบคลุมไลบรารีแอปรถยนต์
- แอปการนำทาง และแอปอื่นๆ ที่เกี่ยวข้องกับการขับรถ ภาพรวมหมวดหมู่
- ภาพรวมสร้างแอปด้วยเทมเพลต
- องค์ประกอบพื้นฐานซึ่งครอบคลุมเทมเพลตและคอมโพเนนต์ของเทมเพลต
- ตัวอย่างขั้นตอนที่แสดงรูปแบบ UX ทั่วไป
- ข้อกำหนดของแอปที่เป็นเทมเพลต
- อ่านคำสำคัญและแนวคิดในหัวข้อต่อไปนี้
- ทำความคุ้นเคยกับUI ของระบบ Android Auto และการออกแบบ Android Automotive OS
- โปรดอ่านบันทึกประจำรุ่น
- ตรวจสอบตัวอย่าง
คําศัพท์และแนวคิดสําคัญ
- โมเดลและเทมเพลต
- อินเทอร์เฟซผู้ใช้จะแสดงด้วยกราฟของออบเจ็กต์โมเดลที่สามารถจัดเรียงร่วมกันได้หลายวิธีตามที่เทมเพลตที่เป็นเจ้าของอนุญาต เทมเพลตคือชุดย่อยของโมเดลที่ทําหน้าที่เป็นรูทในกราฟเหล่านั้นได้ โมเดลประกอบด้วยข้อมูลที่แสดงต่อผู้ใช้ในรูปแบบข้อความและรูปภาพ รวมถึงแอตทริบิวต์ในการกำหนดค่าแง่มุมต่างๆ ของลักษณะที่ปรากฏของข้อมูล เช่น สีข้อความหรือขนาดรูปภาพ โฮสต์จะแปลงโมเดลเป็นมุมมองที่ออกแบบมาเพื่อปฏิบัติตามมาตรฐานความรบกวนของผู้ขับขี่ และคำนึงถึงรายละเอียดต่างๆ เช่น ปัจจัยด้านหน้าจอรถยนต์และรูปแบบอินพุตที่หลากหลาย
- เป็นเจ้าภาพ
- โฮสต์คือคอมโพเนนต์แบ็กเอนด์ที่ใช้ฟังก์ชันการทำงานที่ API ของไลบรารีนำเสนอเพื่อให้แอปทำงานในรถได้ ความรับผิดชอบของโฮสต์มีตั้งแต่การค้นพบแอปและจัดการวงจรการทํางานของแอปไปจนถึงการเปลี่ยนรูปแบบเป็นมุมมองและการแจ้งเตือนแอปเกี่ยวกับการโต้ตอบของผู้ใช้ บนอุปกรณ์เคลื่อนที่ Android Auto จะใช้โฮสต์นี้ ใน Android Automotive OS ระบบจะติดตั้งโฮสต์นี้เป็นแอประบบ
- ข้อจำกัดของเทมเพลต
- เทมเพลตที่แตกต่างกันจะบังคับใช้ข้อจำกัดในเนื้อหาของโมเดล เช่น เทมเพลตรายการมีการจํากัดจํานวนรายการที่แสดงต่อผู้ใช้ เทมเพลตยังมีข้อจำกัดในการเชื่อมต่อ เพื่อสร้างโฟลว์ของงาน เช่น แอปสามารถพุชเทมเพลตไปยังกองหน้าจอได้สูงสุด 5 รายการเท่านั้น ดูรายละเอียดเพิ่มเติมได้ในข้อจำกัดของเทมเพลต
Screen
Screen
คือคลาสที่มาจากไลบรารีที่แอปนำมาใช้เพื่อจัดการอินเทอร์เฟซผู้ใช้ที่แสดงต่อผู้ใช้Screen
มีวงจรและให้กลไกสําหรับแอปในการส่งเทมเพลตเพื่อแสดงเมื่อหน้าจอมองเห็นได้ นอกจากนี้ คุณยังพุชและป๊อปอินสแตนซ์Screen
ไปยังและจากกองScreen
ได้ด้วย ซึ่งช่วยให้มั่นใจว่าอินสแตนซ์จะเป็นไปตามข้อจำกัดของโฟลว์เทมเพลตCarAppService
CarAppService
เป็นคลาสService
นามธรรมที่แอปของคุณต้องใช้และส่งออกเพื่อให้โฮสต์ค้นพบและจัดการได้CarAppService
ของแอปคุณมีหน้าที่รับผิดชอบในการตรวจสอบว่าการเชื่อมต่อโฮสต์เชื่อถือได้โดยใช้createHostValidator
และหลังจากนั้นให้อินสแตนซ์Session
สำหรับการเชื่อมต่อแต่ละรายการโดยใช้onCreateSession
Session
Session
เป็นคลาสนามธรรมที่แอปของคุณต้องใช้และแสดงผลโดยใช้CarAppService.onCreateSession
ซึ่งทำหน้าที่เป็นจุดแรกเข้าเพื่อแสดงข้อมูลบนหน้าจอของรถยนต์ โดยจะมีวงจรที่บอกสถานะปัจจุบันของแอปบนหน้าจอรถยนต์ เช่น เมื่อแอปแสดงหรือซ่อนอยู่เมื่อ
Session
เริ่มทำงาน เช่น เมื่อเปิดแอปครั้งแรก โฮสต์จะส่งคำขอสำหรับScreen
เริ่มต้นแสดงผลโดยใช้เมธอดonCreateScreen
ติดตั้งไลบรารีแอปในรถ
ดูวิธีการเพิ่มไลบรารีลงในแอปได้จากหน้ารุ่นของไลบรารี Jetpack
กำหนดค่าไฟล์ Manifest ของแอป
ก่อนที่จะสร้างแอปรถยนต์ ให้กําหนดค่าไฟล์ Manifest ของแอปดังนี้
ประกาศ CarAppService
โฮสต์จะเชื่อมต่อกับแอปของคุณผ่านการใช้งาน CarAppService
คุณต้องประกาศบริการนี้ในไฟล์ Manifest เพื่อให้โฮสต์ค้นพบและเชื่อมต่อกับแอปของคุณ
คุณยังต้องประกาศหมวดหมู่ของแอปในองค์ประกอบ <category>
ของตัวกรองความตั้งใจของแอปด้วย ดูรายการหมวดหมู่แอปที่รองรับสำหรับค่าที่อนุญาตสำหรับองค์ประกอบนี้
ข้อมูลโค้ดต่อไปนี้แสดงวิธีประกาศบริการแอปรถยนต์สําหรับแอปจุดที่น่าสนใจในไฟล์ Manifest
<application>
...
<service
...
android:name=".MyCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.POI"/>
</intent-filter>
</service>
...
<application>
หมวดหมู่แอปที่รองรับ
ประกาศหมวดหมู่ของแอปโดยเพิ่มค่าหมวดหมู่ต่อไปนี้อย่างน้อย 1 รายการในตัวกรอง Intent เมื่อคุณประกาศ CarAppService
ตามที่อธิบายไว้ในส่วนก่อนหน้า
androidx.car.app.category.NAVIGATION
: แอปที่ให้เส้นทางแบบเลี้ยวต่อเลี้ยว ดูเอกสารประกอบเพิ่มเติมเกี่ยวกับหมวดหมู่นี้ได้ที่สร้างแอปการนำทางสำหรับรถยนต์androidx.car.app.category.POI
: แอปซึ่งมีฟังก์ชันการทำงานที่เกี่ยวข้องกับการค้นหาจุดที่น่าสนใจ เช่น จุดจอดรถ สถานีชาร์จ และปั๊มน้ำมัน ดูเอกสารประกอบเพิ่มเติมเกี่ยวกับหมวดหมู่นี้ได้ที่หัวข้อสร้างแอปจุดที่น่าสนใจสำหรับรถยนต์androidx.car.app.category.IOT
: แอปที่ช่วยให้ผู้ใช้ดำเนินการที่เกี่ยวข้องในอุปกรณ์ที่เชื่อมต่อได้จากภายในรถ ดูเอกสารประกอบเพิ่มเติมเกี่ยวกับหมวดหมู่นี้ได้ที่หัวข้อสร้างแอปอินเทอร์เน็ตในทุกสิ่งสำหรับรถยนต์
ดูคุณภาพแอป Android สำหรับรถยนต์เพื่อดูคำอธิบายโดยละเอียดของแต่ละหมวดหมู่และเกณฑ์ของแอปที่จะจัดอยู่ในหมวดหมู่นั้นๆ
ระบุชื่อและไอคอนของแอป
คุณต้องระบุชื่อและไอคอนแอปที่โฮสต์ใช้แสดงแอปของคุณใน UI ของระบบได้
คุณระบุชื่อแอปและไอคอนที่ใช้แสดงแอปได้โดยใช้แอตทริบิวต์ label
และ icon
ของ CarAppService
ดังนี้
...
<service
android:name=".MyCarAppService"
android:exported="true"
android:label="@string/my_app_name"
android:icon="@drawable/my_app_icon">
...
</service>
...
หากไม่ได้ประกาศป้ายกำกับหรือไอคอนในองค์ประกอบ <service>
โฮสต์จะใช้ค่าที่ระบุสำหรับองค์ประกอบ <application>
ตั้งค่าธีมที่กำหนดเอง
หากต้องการตั้งค่าธีมที่กำหนดเองสำหรับแอปสำหรับรถยนต์ ให้เพิ่มองค์ประกอบ <meta-data>
ในไฟล์ Manifest ดังนี้
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
จากนั้นประกาศทรัพยากรสไตล์เพื่อกำหนดแอตทริบิวต์ต่อไปนี้สำหรับธีมแอปรถยนต์ที่กำหนดเอง
<resources> <style name="MyCarAppTheme"> <item name="carColorPrimary">@layout/my_primary_car_color</item> <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item> <item name="carColorSecondary">@layout/my_secondary_car_color</item> <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
ระดับ API ของแอปรถยนต์
ไลบรารีแอปรถยนต์จะกำหนดระดับ API ของตนเองเพื่อให้คุณทราบว่าโฮสต์เทมเพลตในรถยนต์รองรับฟีเจอร์ใดของไลบรารี
หากต้องการเรียกข้อมูลระดับ Car App API สูงสุดที่โฮสต์รองรับ ให้ใช้วิธี getCarAppApiLevel()
ประกาศระดับ API ของแอปรถยนต์ขั้นต่ำที่แอปของคุณรองรับในไฟล์ AndroidManifest.xml
ดังนี้
<manifest ...>
<application ...>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>
</application>
</manifest>
ดูรายละเอียดเกี่ยวกับวิธีรักษาความเข้ากันได้แบบย้อนหลังและประกาศระดับ API ขั้นต่ำที่จำเป็นในการใช้ฟีเจอร์ได้ในเอกสารประกอบสำหรับคำอธิบายประกอบ RequiresCarApi
ดูคำจำกัดความของระดับ API ที่จำเป็นต้องใช้เพื่อใช้ฟีเจอร์บางอย่างของไลบรารีแอปสำหรับรถยนต์ได้ในเอกสารอ้างอิงสำหรับ CarAppApiLevels
สร้าง CarAppService และเซสชัน
แอปของคุณต้องขยายคลาส CarAppService
และติดตั้งใช้งานเมธอด onCreateSession
ซึ่งจะแสดงผลอินสแตนซ์ Session
ที่สอดคล้องกับการเชื่อมต่อปัจจุบันกับโฮสต์
Kotlin
class HelloWorldService : CarAppService() { ... override fun onCreateSession(): Session { return HelloWorldSession() } ... }
Java
public final class HelloWorldService extends CarAppService { ... @Override @NonNull public Session onCreateSession() { return new HelloWorldSession(); } ... }
อินสแตนซ์ Session
มีหน้าที่รับผิดชอบในการส่งคืนอินสแตนซ์ Screen
เพื่อใช้ในครั้งแรกที่แอปเริ่มต้น ดังนี้
Kotlin
class HelloWorldSession : Session() { ... override fun onCreateScreen(intent: Intent): Screen { return HelloWorldScreen(carContext) } ... }
Java
public final class HelloWorldSession extends Session { ... @Override @NonNull public Screen onCreateScreen(@NonNull Intent intent) { return new HelloWorldScreen(getCarContext()); } ... }
หากต้องการจัดการสถานการณ์ที่แอปในรถต้องเริ่มต้นจากหน้าจอที่ไม่ใช่หน้าจอหลักหรือหน้าจอ Landing Page ของแอป เช่น การจัดการ Deep Link คุณสามารถกำหนดสแต็กหน้าจอที่ซ้อนกันไว้ล่วงหน้าได้โดยใช้ ScreenManager.push
ก่อนกลับมาจาก onCreateScreen
การจัดเตรียมข้อมูลล่วงหน้าช่วยให้ผู้ใช้ไปยังหน้าจอก่อนหน้าได้จากหน้าจอแรกที่แอปแสดง
สร้างหน้าจอเริ่มต้น
คุณสร้างหน้าจอที่แอปแสดงโดยกำหนดคลาสที่ขยายคลาส Screen
และใช้เมธอด onGetTemplate
ของคลาส ซึ่งจะแสดงผลอินสแตนซ์ Template
ที่แสดงสถานะของ UI เพื่อแสดงในหน้าจอรถยนต์
ข้อมูลโค้ดต่อไปนี้แสดงวิธีประกาศ Screen
ที่ใช้เทมเพลต PaneTemplate
เพื่อแสดงสตริง "Hello world" ง่ายๆ
Kotlin
class HelloWorldScreen(carContext: CarContext) : Screen(carContext) { override fun onGetTemplate(): Template { val row = Row.Builder().setTitle("Hello world!").build() val pane = Pane.Builder().addRow(row).build() return PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build() } }
Java
public class HelloWorldScreen extends Screen { @NonNull @Override public Template onGetTemplate() { Row row = new Row.Builder().setTitle("Hello world!").build(); Pane pane = new Pane.Builder().addRow(row).build(); return new PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build(); } }
คลาส CarContext
คลาส CarContext
เป็นคลาสย่อย ContextWrapper
ที่เข้าถึงอินสแตนซ์ Session
และ Screen
ได้ ซึ่งจะให้สิทธิ์เข้าถึงบริการต่างๆ ในรถยนต์ เช่น ScreenManager
สำหรับการจัดการกองซ้อนหน้าจอ, AppManager
สำหรับฟังก์ชันการทำงานทั่วไปที่เกี่ยวข้องกับแอป เช่น การเข้าถึงออบเจ็กต์ Surface
สำหรับการวาดแผนที่ และ NavigationManager
ที่แอปเบนดิงแบบเลี้ยวต่อเลี้ยวใช้เพื่อสื่อสารข้อมูลเมตาการนำทางและเหตุการณ์อื่นๆ ที่เกี่ยวข้องกับการนำทางกับโฮสต์
ดูรายการฟังก์ชันการทำงานของคลังที่มีให้ใช้งานในแอปการนำทางอย่างครอบคลุมได้ที่เข้าถึงเทมเพลตการนำทาง
CarContext
ยังมีฟังก์ชันอื่นๆ เช่น ให้คุณโหลดทรัพยากรที่ถอนออกได้โดยใช้การกำหนดค่าจากหน้าจอของรถยนต์ การเริ่มแอปในรถโดยใช้ Intent และการส่งสัญญาณว่าแอปควรแสดงแผนที่ในธีมมืดหรือไม่
ใช้การไปยังส่วนต่างๆ ของหน้าจอ
แอปมักจะแสดงหน้าจอต่างๆ หลายหน้าจอ โดยแต่ละหน้าจออาจใช้เทมเพลตที่แตกต่างกัน ซึ่งผู้ใช้สามารถไปยังส่วนต่างๆ ได้ขณะโต้ตอบกับอินเทอร์เฟซที่แสดงในหน้าจอ
คลาส ScreenManager
ประกอบด้วยกลุ่มหน้าจอที่คุณใช้เพื่อดันหน้าจอที่จะปรากฏขึ้นโดยอัตโนมัติเมื่อผู้ใช้เลือกปุ่มย้อนกลับในหน้าจอของรถยนต์หรือใช้ปุ่มย้อนกลับที่เป็นฮาร์ดแวร์ซึ่งมีให้ใช้งานในรถบางรุ่น
ข้อมูลโค้ดต่อไปนี้แสดงวิธีเพิ่มการดำเนินการย้อนกลับไปยังเทมเพลตข้อความ รวมถึงการดำเนินการที่พุชหน้าจอใหม่เมื่อผู้ใช้เลือก
Kotlin
val template = MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( Action.Builder() .setTitle("Next screen") .setOnClickListener { screenManager.push(NextScreen(carContext)) } .build()) .build()
Java
MessageTemplate template = new MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( new Action.Builder() .setTitle("Next screen") .setOnClickListener( () -> getScreenManager().push(new NextScreen(getCarContext()))) .build()) .build();
ออบเจ็กต์ Action.BACK
เป็น Action
มาตรฐานที่เรียกใช้ ScreenManager.pop
โดยอัตโนมัติ
คุณสามารถลบล้างลักษณะการทำงานนี้ได้โดยใช้อินสแตนซ์ OnBackPressedDispatcher
ที่มีอยู่ใน CarContext
กองหน้าจอมีความลึกสูงสุด 5 หน้าจอเพื่อดูแลให้แอปใช้งานขณะขับรถได้อย่างปลอดภัย ดูรายละเอียดเพิ่มเติมได้ที่ส่วนข้อจำกัดของเทมเพลต
รีเฟรชเนื้อหาของเทมเพลต
แอปของคุณขอให้เนื้อหาของ Screen
เป็นโมฆะได้ด้วยการเรียกใช้เมธอด Screen.invalidate
จากนั้นโฮสต์จะเรียกใช้เมธอด Screen.onGetTemplate
ของแอปเพื่อเรียกข้อมูลเทมเพลตที่มีเนื้อหาใหม่
เมื่อรีเฟรช Screen
คุณต้องทำความเข้าใจเนื้อหาที่เจาะจงในเทมเพลตที่อัปเดตได้ เพื่อโฮสต์จะไม่นับเทมเพลตใหม่ในโควต้าของเทมเพลต
ดูรายละเอียดเพิ่มเติมได้ในส่วนข้อจำกัดของเทมเพลต
เราขอแนะนำให้คุณจัดโครงสร้างหน้าจอเพื่อให้มีการแมปแบบหนึ่งต่อหนึ่งระหว่าง Screen
กับประเภทของเทมเพลตที่แสดงผ่านการใช้งาน onGetTemplate
วาดแผนที่
แอปการนำทางและจุดที่น่าสนใจ (POI) ที่ใช้เทมเพลตต่อไปนี้จะวาดแผนที่ได้โดยเข้าถึง Surface
เทมเพลต | สิทธิ์ของเทมเพลต | คำแนะนำเกี่ยวกับหมวดหมู่ |
---|---|---|
NavigationTemplate |
androidx.car.app.NAVIGATION_TEMPLATES |
การนำทาง |
MapWithContentTemplate |
androidx.car.app.NAVIGATION_TEMPLATES หรือ androidx.car.app.MAP_TEMPLATES |
การนำทาง, จุดที่น่าสนใจ |
MapTemplate (เลิกใช้งานแล้ว) |
androidx.car.app.NAVIGATION_TEMPLATES |
การนำทาง |
PlaceListNavigationTemplate (เลิกใช้งานแล้ว) |
androidx.car.app.NAVIGATION_TEMPLATES |
การนำทาง |
RoutePreviewNavigationTemplate (เลิกใช้งานแล้ว) |
androidx.car.app.NAVIGATION_TEMPLATES |
การนำทาง |
ประกาศสิทธิ์ระดับแพลตฟอร์ม
นอกเหนือจากสิทธิ์ที่จำเป็นสำหรับเทมเพลตที่แอปใช้แล้ว แอปของคุณต้องประกาศสิทธิ์ androidx.car.app.ACCESS_SURFACE
ในไฟล์ AndroidManifest.xml
ด้วย เพื่อเข้าถึงแพลตฟอร์มด้วย
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
เข้าถึงแพลตฟอร์ม
หากต้องการเข้าถึง Surface
ที่โฮสต์ให้ คุณต้องติดตั้งใช้งาน SurfaceCallback
และระบุการติดตั้งใช้งานนั้นให้กับบริการรถ AppManager
ระบบจะส่ง Surface
ปัจจุบันไปยัง SurfaceCallback
ในพารามิเตอร์ SurfaceContainer
ของ Callback onSurfaceAvailable()
และ onSurfaceDestroyed()
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
ทําความเข้าใจพื้นที่ที่มองเห็นของพื้นผิว
โดยสามารถวาดองค์ประกอบอินเทอร์เฟซผู้ใช้สำหรับเทมเพลตบนแผนที่ได้ โฮสต์จะสื่อสารพื้นที่ของพื้นผิวที่รับประกันว่าไม่มีสิ่งกีดขวางและผู้ใช้มองเห็นได้อย่างเต็มที่โดยการเรียกใช้เมธอด SurfaceCallback.onVisibleAreaChanged
นอกจากนี้ โฮสต์จะเรียกใช้เมธอด SurfaceCallback.onStableAreaChanged
ที่มีสี่เหลี่ยมผืนผ้าที่เล็กที่สุดด้วย เพื่อลดจำนวนการเปลี่ยนแปลง ซึ่งจะแสดงตามเทมเพลตปัจจุบันเสมอ
ตัวอย่างเช่น เมื่อแอปการนำทางใช้ NavigationTemplate
ที่มีแถบการดำเนินการด้านบน แถบการดำเนินการจะซ่อนตัวเมื่อผู้ใช้ไม่ได้โต้ตอบกับหน้าจอเป็นเวลานานเพื่อให้มีพื้นที่มากขึ้นสำหรับแผนที่ ในกรณีนี้ มีการเรียกกลับไปยัง onStableAreaChanged
และ
onVisibleAreaChanged
ที่มีสี่เหลี่ยมผืนผ้าเดียวกัน เมื่อแถบการทำงานซ่อนอยู่
ระบบจะเรียกใช้เฉพาะ onVisibleAreaChanged
ด้วยพื้นที่ที่ใหญ่กว่า หากผู้ใช้โต้ตอบกับหน้าจอ ระบบจะเรียกใช้เฉพาะ onVisibleAreaChanged
กับสี่เหลี่ยมผืนผ้าแรกอีกครั้ง
รองรับธีมมืด
แอปต้องวาดแผนที่ซ้ำลงในอินสแตนซ์ Surface
ด้วยสีเข้มที่เหมาะสมเมื่อโฮสต์พิจารณาว่าเงื่อนไขควรเป็นไปตามนั้น ตามที่อธิบายไว้ในคุณภาพแอป Android สำหรับรถยนต์
หากต้องการตัดสินใจว่าจะวาดแผนที่แบบมืดหรือไม่ ให้ใช้วิธีการ CarContext.isDarkMode
เมื่อสถานะธีมมืดเปลี่ยนแปลง คุณจะได้รับสายเรียกเข้าที่หมายเลข Session.onCarConfigurationChanged
อนุญาตให้ผู้ใช้โต้ตอบกับแผนที่
เมื่อใช้เทมเพลตต่อไปนี้ คุณสามารถเพิ่มการรองรับให้ผู้ใช้โต้ตอบกับแผนที่ที่คุณวาดได้ เช่น อนุญาตให้ผู้ใช้ดูส่วนต่างๆ ของแผนที่ด้วยการซูมและเลื่อน
เทมเพลต | รองรับการโต้ตอบตั้งแต่ระดับ Car App API |
---|---|
NavigationTemplate |
2 |
PlaceListNavigationTemplate (เลิกใช้งานแล้ว) |
4 |
RoutePreviewNavigationTemplate (เลิกใช้งานแล้ว) |
4 |
MapTemplate (เลิกใช้งานแล้ว) |
5 (introduction of template) |
MapWithContentTemplate |
7 (introduction of template) |
ใช้ Callback ของการโต้ตอบ
อินเทอร์เฟซ SurfaceCallback
มีเมธอดการเรียกกลับหลายรายการที่คุณนำไปใช้เพื่อเพิ่มความโต้ตอบให้กับแผนที่ที่สร้างด้วยเทมเพลตในส่วนก่อนหน้าได้ ดังนี้
การโต้ตอบ | SurfaceCallback วิธี |
รองรับตั้งแต่ระดับ Car App API |
---|---|---|
แตะ | onClick |
5 |
บีบและกางนิ้วเพื่อซูม | onScale |
2 |
ลากด้วยการแตะเพียงครั้งเดียว | onScroll |
2 |
การเลื่อนด้วยนิ้วเดียว | onFling |
2 |
แตะสองครั้ง | onScale (ที่มีตัวคูณมาตราส่วนซึ่งกำหนดโดยโฮสต์ของเทมเพลต) |
2 |
การเลื่อนแบบหมุนในโหมดเลื่อน | onScroll (โดยมีปัจจัยระยะทางที่โฮสต์เทมเพลตกำหนด) |
2 |
เพิ่มแถบการดำเนินการบนแผนที่
เทมเพลตเหล่านี้อาจมีแถบการดำเนินการของแผนที่สําหรับการดําเนินการที่เกี่ยวข้องกับแผนที่ เช่น การซูมเข้าและออก การปรับศูนย์ใหม่ การแสดงเข็มทิศ และการดําเนินการอื่นๆ ที่คุณเลือกแสดง แถบการดำเนินการของแผนที่จะมีปุ่มที่เป็นไอคอนเท่านั้นได้สูงสุด 4 ปุ่ม ซึ่งจะรีเฟรชได้โดยไม่ส่งผลต่อระดับความลึกของงาน โดยจะซ่อนตัวเมื่ออยู่ในสถานะ "ไม่มีการใช้งาน" และปรากฏขึ้นอีกครั้งเมื่ออยู่ในสถานะ "ใช้งานอยู่"
หากต้องการรับการเรียกกลับแบบอินเทอร์แอกทีฟของแผนที่ คุณต้องเพิ่มปุ่ม Action.PAN
ในแถบการดำเนินการของแผนที่ เมื่อผู้ใช้กดปุ่มแพน โฮสต์จะเข้าสู่โหมดแพนตามที่อธิบายไว้ในส่วนต่อไปนี้
หากแอปไม่มีปุ่ม Action.PAN
ในแถบการดำเนินการบนแผนที่ แอปจะไม่รับอินพุตจากผู้ใช้จากเมธอด SurfaceCallback
และโฮสต์จะออกจากโหมดการแพนที่เคยเปิดใช้งานไว้ก่อนหน้านี้
ปุ่มเลื่อนจะไม่แสดงบนหน้าจอสัมผัส
ทำความเข้าใจโหมดเลื่อน
ในโหมดเลื่อน โฮสต์ของเทมเพลตจะแปลอินพุตของผู้ใช้จากอุปกรณ์อินพุตแบบไม่สัมผัส เช่น ตัวควบคุมแบบหมุนและทัชแพด เป็นSurfaceCallback
เมธอดที่เหมาะสม ตอบสนองการดําเนินการของผู้ใช้เพื่อเข้าสู่หรือออกจากโหมดแพนด้วยวิธี setPanModeListener
ใน NavigationTemplate.Builder
โฮสต์สามารถซ่อนคอมโพเนนต์ UI อื่นๆ ในเทมเพลตขณะที่ผู้ใช้อยู่ในโหมดเลื่อน
โต้ตอบกับผู้ใช้
แอปสามารถโต้ตอบกับผู้ใช้โดยใช้รูปแบบที่คล้ายกับแอปบนอุปกรณ์เคลื่อนที่
จัดการอินพุตของผู้ใช้
แอปจะตอบสนองต่ออินพุตของผู้ใช้ได้โดยส่ง Listeners ที่เหมาะสมไปยังโมเดลที่รองรับ ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างรูปแบบ Action
ที่ตั้งค่า OnClickListener
ซึ่งเรียกกลับไปยังเมธอดที่กําหนดโดยโค้ดของแอป
Kotlin
val action = Action.Builder() .setTitle("Navigate") .setOnClickListener(::onClickNavigate) .build()
Java
Action action = new Action.Builder() .setTitle("Navigate") .setOnClickListener(this::onClickNavigate) .build();
จากนั้นเมธอด onClickNavigate
จะเริ่มต้นแอปนำทางรถยนต์เริ่มต้นได้โดยใช้เมธอด CarContext.startCarApp
ดังนี้
Kotlin
private fun onClickNavigate() { val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)) carContext.startCarApp(intent) }
Java
private void onClickNavigate() { Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)); getCarContext().startCarApp(intent); }
ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีเริ่มแอป รวมถึงรูปแบบของ IntentACTION_NAVIGATE
ได้ที่ส่วนเริ่มแอปรถยนต์ด้วย Intent
การดำเนินการบางอย่าง เช่น การดำเนินการที่ต้องนำผู้ใช้ไปยังส่วนต่อประสานบนอุปกรณ์เคลื่อนที่เพื่อดำเนินการต่อ จะอนุญาตก็ต่อเมื่อรถจอดอยู่เท่านั้น
คุณจะใช้ ParkedOnlyOnClickListener
เพื่อดำเนินการดังกล่าวได้ หากรถไม่ได้จอดอยู่ โฮสต์จะแสดงข้อความแจ้งให้ผู้ใช้ทราบว่าไม่ได้รับอนุญาตในกรณีนี้ หากรถจอดอยู่ โค้ดจะทำงานตามปกติ ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ ParkedOnlyOnClickListener
เพื่อเปิดหน้าจอการตั้งค่าในอุปกรณ์เคลื่อนที่
Kotlin
val row = Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone)) .build()
Java
Row row = new Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)) .build();
แสดงการแจ้งเตือน
การแจ้งเตือนที่ส่งไปยังอุปกรณ์เคลื่อนที่จะปรากฏบนหน้าจอของรถก็ต่อเมื่อมีการขยายการแจ้งเตือนด้วย CarAppExtender
คุณตั้งค่าแอตทริบิวต์ของการแจ้งเตือนบางอย่างได้ เช่น ชื่อเนื้อหา ข้อความ ไอคอน และการดำเนินการใน CarAppExtender
โดยจะลบล้างแอตทริบิวต์ของการแจ้งเตือนเมื่อปรากฏในหน้าจอของรถ
ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่งการแจ้งเตือนไปยังหน้าจอรถยนต์ซึ่งแสดงชื่อที่แตกต่างจากที่แสดงในอุปกรณ์เคลื่อนที่
Kotlin
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(titleOnThePhone) .extend( CarAppExtender.Builder() .setContentTitle(titleOnTheCar) ... .build()) .build()
Java
Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(titleOnThePhone) .extend( new CarAppExtender.Builder() .setContentTitle(titleOnTheCar) ... .build()) .build();
การแจ้งเตือนอาจส่งผลต่อส่วนต่างๆ ของอินเทอร์เฟซผู้ใช้ต่อไปนี้
- ระบบอาจแสดงการแจ้งเตือนล่วงหน้า (HUN) ต่อผู้ใช้
- คุณสามารถเพิ่มข้อความลงในศูนย์การแจ้งเตือนได้ (ไม่บังคับ) พร้อมป้ายที่มองเห็นได้บนแถบ
- สําหรับแอปการนําทาง การแจ้งเตือนอาจแสดงในวิดเจ็ตแถบข้างตามที่อธิบายไว้ในการแจ้งเตือนแบบเลี้ยวต่อเลี้ยว
คุณเลือกวิธีกำหนดค่าการแจ้งเตือนของแอปให้ส่งผลต่อองค์ประกอบอินเทอร์เฟซผู้ใช้แต่ละรายการได้โดยใช้ลําดับความสําคัญของการแจ้งเตือน ตามที่อธิบายไว้ในเอกสารประกอบของ CarAppExtender
หากมีการเรียก NotificationCompat.Builder.setOnlyAlertOnce
ด้วยค่า true
การแจ้งเตือนที่มีลำดับความสำคัญสูงจะแสดงเป็น HUN เพียงครั้งเดียว
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีออกแบบการแจ้งเตือนของแอปในรถได้ที่คู่มือการแจ้งเตือนของ Google Design for Driving
แสดงข้อความโทสต์
แอปสามารถแสดงข้อความ Toast โดยใช้ CarToast
ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
Kotlin
CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()
Java
CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();
ขอสิทธิ์
หากแอปของคุณจำเป็นต้องเข้าถึงข้อมูลหรือการดําเนินการที่จํากัด เช่น ตำแหน่ง กฎมาตรฐานของสิทธิ์ Android จะมีผล หากต้องการขอสิทธิ์ ให้ใช้วิธี CarContext.requestPermissions()
ประโยชน์ของการใช้ CarContext.requestPermissions()
ต่างจากการใช้ Android API มาตรฐานคือคุณไม่ต้องเปิด Activity
ของคุณเองเพื่อสร้างกล่องโต้ตอบสิทธิ์ นอกจากนี้ คุณยังใช้โค้ดเดียวกันทั้งใน Android Auto และ Android Automotive OS ได้โดยไม่ต้องสร้างขั้นตอนที่ขึ้นอยู่กับแพลตฟอร์ม
จัดรูปแบบกล่องโต้ตอบสิทธิ์ใน Android Auto
ใน Android Auto กล่องโต้ตอบสิทธิ์สำหรับผู้ใช้จะปรากฏในโทรศัพท์
โดยค่าเริ่มต้น จะไม่มีพื้นหลังด้านหลังกล่องโต้ตอบ หากต้องการตั้งค่าพื้นหลังที่กำหนดเอง ให้ประกาศธีมแอปรถยนต์ในไฟล์ AndroidManifest.xml
และตั้งค่าแอตทริบิวต์ carPermissionActivityLayout
สำหรับธีมแอปรถยนต์
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
จากนั้นตั้งค่าแอตทริบิวต์ carPermissionActivityLayout
สำหรับธีมแอปรถยนต์ โดยทำดังนี้
<resources> <style name="MyCarAppTheme"> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
เริ่มแอปรถยนต์ด้วย Intent
คุณสามารถเรียกใช้วิธี
CarContext.startCarApp
เพื่อดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้
- เปิดแป้นโทรศัพท์เพื่อโทรออก
- เริ่มการนำทางแบบเลี้ยวต่อเลี้ยวไปยังสถานที่ด้วยแอปนำทางสำหรับรถยนต์เริ่มต้น
- เริ่มแอปของคุณเองด้วย Intent
ตัวอย่างต่อไปนี้แสดงวิธีสร้างการแจ้งเตือนที่มีการดำเนินการซึ่งเปิดแอปด้วยหน้าจอที่แสดงรายละเอียดการจองที่จอดรถ
คุณขยายอินสแตนซ์การแจ้งเตือนด้วย Intent เนื้อหาที่มี PendingIntent
ซึ่งรวม Intent ที่ชัดเจนไว้ในการดําเนินการของแอป ดังนี้
Kotlin
val notification = notificationBuilder ... .extend( CarAppExtender.Builder() .setContentIntent( PendingIntent.getBroadcast( context, ACTION_VIEW_PARKING_RESERVATION.hashCode(), Intent(ACTION_VIEW_PARKING_RESERVATION) .setComponent(ComponentName(context, MyNotificationReceiver::class.java)), 0)) .build())
Java
Notification notification = notificationBuilder ... .extend( new CarAppExtender.Builder() .setContentIntent( PendingIntent.getBroadcast( context, ACTION_VIEW_PARKING_RESERVATION.hashCode(), new Intent(ACTION_VIEW_PARKING_RESERVATION) .setComponent(new ComponentName(context, MyNotificationReceiver.class)), 0)) .build());
นอกจากนี้ แอปของคุณยังต้องประกาศ BroadcastReceiver
ที่เรียกใช้เพื่อประมวลผล Intent เมื่อผู้ใช้เลือกการดำเนินการในอินเทอร์เฟซการแจ้งเตือนและเรียกใช้ CarContext.startCarApp
ด้วย Intent ที่มี URI ข้อมูล ดังนี้
Kotlin
class MyNotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val intentAction = intent.action if (ACTION_VIEW_PARKING_RESERVATION == intentAction) { CarContext.startCarApp( intent, Intent(Intent.ACTION_VIEW) .setComponent(ComponentName(context, MyCarAppService::class.java)) .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction))) } } }
Java
public class MyNotificationReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String intentAction = intent.getAction(); if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) { CarContext.startCarApp( intent, new Intent(Intent.ACTION_VIEW) .setComponent(new ComponentName(context, MyCarAppService.class)) .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction))); } } }
สุดท้าย วิธีการ Session.onNewIntent
ในแอปทํางานกับ Intent นี้โดยการพุชหน้าจอการจองที่จอดรถลงในกองซ้อน หากไม่ได้อยู่ด้านบนอยู่แล้ว
Kotlin
override fun onNewIntent(intent: Intent) { val screenManager = carContext.getCarService(ScreenManager::class.java) val uri = intent.data if (uri != null && MY_URI_SCHEME == uri.scheme && MY_URI_HOST == uri.schemeSpecificPart && ACTION_VIEW_PARKING_RESERVATION == uri.fragment ) { val top = screenManager.top if (top !is ParkingReservationScreen) { screenManager.push(ParkingReservationScreen(carContext)) } } }
Java
@Override public void onNewIntent(@NonNull Intent intent) { ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class); Uri uri = intent.getData(); if (uri != null && MY_URI_SCHEME.equals(uri.getScheme()) && MY_URI_HOST.equals(uri.getSchemeSpecificPart()) && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment()) ) { Screen top = screenManager.getTop(); if (!(top instanceof ParkingReservationScreen)) { screenManager.push(new ParkingReservationScreen(getCarContext())); } } }
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีจัดการการแจ้งเตือนสำหรับแอปรถยนต์ได้ที่ส่วนแสดงการแจ้งเตือน
ข้อจำกัดของเทมเพลต
โฮสต์จะจำกัดจำนวนเทมเพลตที่จะแสดงสำหรับงานหนึ่งๆ ไว้ที่ไม่เกิน 5 รายการ โดยเทมเพลตสุดท้ายต้องเป็นเทมเพลตประเภทใดประเภทหนึ่งต่อไปนี้
โปรดทราบว่าขีดจํากัดนี้มีผลกับจํานวนเทมเพลต ไม่ใช่จํานวนScreen
อินสแตนซ์ในกอง ตัวอย่างเช่น หากแอปส่งเทมเพลต 2 รายการขณะอยู่ในหน้าจอ ก แล้วส่งหน้าจอ ข ตามมา ก็จะส่งเทมเพลตได้อีก 3 รายการ หรือหากแต่ละหน้าจอมีโครงสร้างเพื่อส่งเทมเพลตเดียว แอปจะดันอินสแตนซ์หน้าจอ 5 รายการลงในกองScreenManager
ได้
มีกรณีพิเศษสําหรับข้อจํากัดเหล่านี้ ได้แก่ การรีเฟรชเทมเพลต การย้อนกลับ และการรีเซ็ต
การรีเฟรชเทมเพลต
การอัปเดตเนื้อหาบางอย่างจะไม่นับรวมในขีดจํากัดของเทมเพลต โดยทั่วไปแล้ว หากแอปพุชเทมเพลตใหม่ที่เป็นประเภทเดียวกันและมีเนื้อหาหลักเหมือนกับเทมเพลตก่อนหน้า ระบบจะไม่นับเทมเพลตใหม่รวมอยู่ในโควต้า เช่น การอัปเดตสถานะเปิด/ปิดของแถวใน ListTemplate
จะไม่นับรวมในโควต้า โปรดดูเอกสารของแต่ละเทมเพลตเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับประเภทของการอัปเดตเนื้อหาที่ถือว่าเป็นการรีเฟรชได้
การทำงานส่วนหลัง
หากต้องการเปิดใช้โฟลว์ย่อยภายในงาน โฮสต์จะตรวจจับเมื่อแอปแสดงScreen
จากกอง ScreenManager
และอัปเดตโควต้าที่เหลือตามจํานวนเทมเพลตที่แอปจะย้อนกลับ
ตัวอย่างเช่น หากแอปส่งเทมเพลต 2 รายการขณะอยู่ในหน้าจอ ก จากนั้นส่งเทมเพลตอีก 2 รายการในหน้าจอ ข แอปจะมีโควต้าเหลืออยู่ 1 รายการ หากแอปแสดงกลับไปที่หน้าจอ A โฮสต์จะรีเซ็ตโควต้าเป็น 3 เนื่องจากแอปมีเทมเพลตย้อนกลับไป 2 เทมเพลต
โปรดทราบว่า เมื่อกลับมาที่หน้าจออีกครั้ง แอปต้องส่งเทมเพลตที่เป็นประเภทเดียวกันกับเทมเพลตที่ส่งโดยหน้าจอล่าสุด การส่งเทมเพลตประเภทอื่นๆ จะทำให้เกิดข้อผิดพลาด อย่างไรก็ตาม ตราบใดที่ประเภทยังคงเหมือนเดิมระหว่างการดำเนินการย้อนกลับ แอปจะแก้ไขเนื้อหาของเทมเพลตได้อย่างอิสระโดยไม่ส่งผลกระทบต่อโควต้า
รีเซ็ตการดำเนินการ
บางเทมเพลตจะมีความหมายพิเศษซึ่งแสดงถึงจุดสิ้นสุดของงาน ตัวอย่างเช่น NavigationTemplate
เป็นมุมมองที่คาดว่าจะแสดงบนหน้าจอและรีเฟรชด้วยวิธีการแบบเลี้ยวต่อเลี้ยวใหม่เพื่อให้ผู้ใช้ใช้งาน เมื่อถึงเทมเพลตใดเทมเพลตหนึ่งเหล่านี้ โฮสต์จะรีเซ็ตโควต้าของเทมเพลต โดยถือว่าเทมเพลตนั้นเป็นขั้นตอนแรกของงานใหม่ ซึ่งจะช่วยให้แอปเริ่มงานใหม่ได้
โปรดดูเอกสารประกอบของเทมเพลตแต่ละรายการเพื่อดูว่าเทมเพลตใดทริกเกอร์การรีเซ็ตโฮสต์
หากโฮสต์ได้รับ Intent เพื่อเริ่มแอปจากการดำเนินการแจ้งเตือนหรือจากตัวเปิด โควต้าก็จะรีเซ็ตด้วย กลไกนี้ช่วยให้แอปเริ่มเวิร์กโฟลว์งานใหม่จากการแจ้งเตือนได้ และยังคงทำงานต่อไปได้แม้ว่าแอปจะเชื่อมโยงและทำงานอยู่เบื้องหน้าอยู่แล้วก็ตาม
ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีแสดงการแจ้งเตือนของแอปในหน้าจอของรถได้ที่ส่วนแสดงการแจ้งเตือน ดูข้อมูลเกี่ยวกับวิธีเริ่มแอปจากการดําเนินการของการแจ้งเตือนได้ในส่วนเริ่มแอปรถยนต์ด้วย Intent
Connection API
คุณสามารถระบุว่าแอปของคุณทำงานบน Android Auto หรือ Android Automotive OS โดยใช้ CarConnection
API เพื่อดึงข้อมูลการเชื่อมต่อขณะรันไทม์
เช่น ใน Session
ของแอปรถยนต์ ให้เริ่มต้น CarConnection
และสมัครรับการอัปเดต LiveData
ดังนี้
Kotlin
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
Java
new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);
ในผู้สังเกตการณ์ คุณสามารถตอบสนองต่อการเปลี่ยนแปลงสถานะการเชื่อมต่อได้ ดังนี้
Kotlin
fun onConnectionStateUpdated(connectionState: Int) { val message = when(connectionState) { CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit" CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS" CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto" else -> "Unknown car connection type" } CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show() }
Java
private void onConnectionStateUpdated(int connectionState) { String message; switch(connectionState) { case CarConnection.CONNECTION_TYPE_NOT_CONNECTED: message = "Not connected to a head unit"; break; case CarConnection.CONNECTION_TYPE_NATIVE: message = "Connected to Android Automotive OS"; break; case CarConnection.CONNECTION_TYPE_PROJECTION: message = "Connected to Android Auto"; break; default: message = "Unknown car connection type"; break; } CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show(); }
Constraints API
รถยนต์แต่ละรุ่นอาจอนุญาตให้แสดงอินสแตนซ์ Item
จํานวนต่างกันต่อผู้ใช้ในแต่ละครั้ง ใช้ ConstraintManager
เพื่อตรวจสอบขีดจํากัดของเนื้อหาที่รันไทม์และตั้งค่าจํานวนรายการที่เหมาะสมในเทมเพลต
เริ่มต้นด้วยการรับ ConstraintManager
จาก CarContext
โดยทำดังนี้
Kotlin
val manager = carContext.getCarService(ConstraintManager::class.java)
Java
ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
จากนั้นคุณสามารถค้นหาออบเจ็กต์ ConstraintManager
ที่ดึงข้อมูลเพื่อดูขีดจํากัดของเนื้อหาที่เกี่ยวข้อง ตัวอย่างเช่น หากต้องการดูจํานวนรายการที่แสดงในตารางกริด ให้เรียกใช้ getContentLimit
ด้วย CONTENT_LIMIT_TYPE_GRID
ดังนี้
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
เพิ่มขั้นตอนการลงชื่อเข้าใช้
หากแอปของคุณมอบประสบการณ์การลงชื่อเข้าใช้สำหรับผู้ใช้ คุณสามารถใช้เทมเพลตอย่าง SignInTemplate
และ LongMessageTemplate
กับ Car App API ระดับ 2 ขึ้นไปเพื่อจัดการการลงชื่อเข้าใช้แอปในจอภาพหลักของรถยนต์
หากต้องการสร้าง SignInTemplate
ให้กําหนด SignInMethod
ขณะนี้ไลบรารีแอปรถยนต์รองรับวิธีการลงชื่อเข้าใช้ต่อไปนี้
InputSignInMethod
สำหรับการลงชื่อเข้าใช้ด้วยชื่อผู้ใช้/รหัสผ่านPinSignInMethod
สำหรับการลงชื่อเข้าใช้ด้วย PIN ซึ่งผู้ใช้ลิงก์บัญชีจากโทรศัพท์โดยใช้ PIN ที่แสดงบนเครื่องเล่นวิทยุProviderSignInMethod
สำหรับการลงชื่อเข้าใช้ของผู้ให้บริการ เช่น Google Sign-In และ One TapQRCodeSignInMethod
สําหรับการลงชื่อเข้าใช้ด้วยคิวอาร์โค้ด ซึ่งผู้ใช้จะสแกนคิวอาร์โค้ดเพื่อลงชื่อเข้าใช้ในโทรศัพท์ ซึ่งใช้ได้กับ Car API ระดับ 4 ขึ้นไป
ตัวอย่างเช่น หากต้องการใช้เทมเพลตที่รวบรวมรหัสผ่านของผู้ใช้ ให้เริ่มต้นด้วยการสร้าง InputCallback
เพื่อประมวลผลและตรวจสอบข้อมูลที่ผู้ใช้ป้อน ดังนี้
Kotlin
val callback = object : InputCallback { override fun onInputSubmitted(text: String) { // You will receive this callback when the user presses Enter on the keyboard. } override fun onInputTextChanged(text: String) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } }
Java
InputCallback callback = new InputCallback() { @Override public void onInputSubmitted(@NonNull String text) { // You will receive this callback when the user presses Enter on the keyboard. } @Override public void onInputTextChanged(@NonNull String text) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } };
Builder
ของ InputSignInMethod
ต้องมี InputCallback
Kotlin
val passwordInput = InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build()
Java
InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build();
สุดท้าย ให้ใช้ InputSignInMethod
ใหม่เพื่อสร้าง SignInTemplate
Kotlin
SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build()
Java
new SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build();
ใช้ AccountManager
แอป Android Automotive OS ที่มีการตรวจสอบสิทธิ์ต้องใช้ AccountManager เนื่องจากเหตุผลต่อไปนี้
- UX ที่ดีขึ้นและการจัดการบัญชีที่ง่าย: ผู้ใช้สามารถจัดการบัญชีทั้งหมดของตนได้อย่างง่ายดายจากเมนูบัญชีในการตั้งค่าระบบ รวมถึงการลงชื่อเข้าใช้และออกจากระบบ
- ประสบการณ์การใช้งานแบบ"ผู้มาเยือน": เนื่องจากรถยนต์เป็นอุปกรณ์ที่แชร์กัน OEM จึงเปิดใช้ประสบการณ์การใช้งานแบบผู้มาเยือนในรถได้ ซึ่งจะเพิ่มบัญชีไม่ได้
เพิ่มตัวแปรสตริงข้อความ
หน้าจอรถยนต์ขนาดต่างๆ อาจแสดงข้อความในจำนวนที่แตกต่างกัน เมื่อใช้ Car App API ระดับ 2 ขึ้นไป คุณจะระบุสตริงข้อความหลายรูปแบบเพื่อให้พอดีกับหน้าจอมากที่สุดได้ หากต้องการดูตําแหน่งที่ยอมรับตัวแปรข้อความ ให้มองหาเทมเพลตและคอมโพเนนต์ที่ใช้ CarText
คุณเพิ่มตัวแปรสตริงข้อความลงใน CarText
ได้โดยใช้เมธอด CarText.Builder.addVariant()
ดังนี้
Kotlin
val itemTitle = CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build()
Java
CarText itemTitle = new CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build();
จากนั้นคุณจะใช้ CarText
นี้ เช่น เป็นข้อความหลักของ GridItem
ได้
Kotlin
GridItem.Builder() .addTitle(itemTitle) ... .build()
Java
new GridItem.Builder() .addTitle(itemTitle) ... build();
เพิ่มสตริงตามลำดับจากที่ต้องการมากที่สุดไปจนถึงน้อยที่สุด เช่น จากยาวที่สุดไปจนถึงสั้นที่สุด โฮสต์จะเลือกสตริงที่มีความยาวที่เหมาะสมโดยขึ้นอยู่กับพื้นที่ว่างบนหน้าจอของรถ
เพิ่ม CarIcons ในบรรทัดสำหรับแถว
คุณเพิ่มไอคอนในบรรทัดข้อความเพื่อเพิ่มความน่าสนใจให้กับแอปได้โดยใช้ CarIconSpan
ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้างสแปนเหล่านี้ได้ในเอกสารประกอบสำหรับ CarIconSpan.create
ดูภาพรวมของวิธีการทำงานของการจัดรูปแบบข้อความด้วยช่วงได้ที่หัวข้อ
Spantastic
การจัดรูปแบบข้อความด้วยช่วง
Kotlin
val rating = SpannableString("Rating: 4.5 stars") rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val row = Row.Builder() ... .addText(rating) .build()
Java
SpannableString rating = new SpannableString("Rating: 4.5 stars"); rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars new CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ); Row row = new Row.Builder() ... .addText(rating) .build();
Car Hardware API
ตั้งแต่ Car App API ระดับ 3 เป็นต้นไป ไลบรารีแอปรถยนต์จะมี API ที่คุณสามารถใช้เพื่อเข้าถึงพร็อพเพอร์ตี้และเซ็นเซอร์ของยานพาหนะ
ข้อกำหนด
หากต้องการใช้ API กับ Android Auto ให้เริ่มด้วยการเพิ่ม Dependency ของ androidx.car.app:app-projected
ลงในไฟล์ build.gradle
สำหรับโมดูล Android Auto สำหรับ Android Automotive OS ให้เพิ่มทรัพยากร Dependency ของ androidx.car.app:app-automotive
ลงในไฟล์ build.gradle
ของโมดูล Android Automotive OS
นอกจากนี้ ในไฟล์ AndroidManifest.xml
คุณจะต้องประกาศสิทธิ์ที่เกี่ยวข้องที่จำเป็นต่อการขอข้อมูลรถที่ต้องการใช้ โปรดทราบว่าผู้ใช้ต้องให้สิทธิ์เหล่านี้แก่คุณด้วย คุณสามารถใช้โค้ดเดียวกันทั้งใน Android Auto และ Android Automotive OS ได้โดยไม่ต้องสร้างขั้นตอนที่ขึ้นอยู่กับแพลตฟอร์ม อย่างไรก็ตาม สิทธิ์ที่จำเป็น
จะแตกต่างออกไป
CarInfo
ตารางนี้อธิบายพร็อพเพอร์ตี้ที่แสดงโดย API ของ CarInfo
และสิทธิ์ที่คุณต้องขอเพื่อใช้
วิธีการ | คุณสมบัติ | สิทธิ์ของ Android Auto | สิทธิ์ของ Android Automotive OS | รองรับตั้งแต่ระดับ Car App API |
---|---|---|---|---|
fetchModel |
ยี่ห้อ รุ่น ปี | android.car.permission.CAR_INFO |
3 | |
fetchEnergyProfile |
ประเภทหัวชาร์จไฟฟ้า EV, ประเภทเชื้อเพลิง | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
3 |
fetchExteriorDimensions
ข้อมูลนี้ใช้ได้กับรถยนต์ Android Automotive OS บางรุ่นที่ใช้ API ระดับ 30 ขึ้นไปเท่านั้น |
ขนาดภายนอก | ไม่มี | android.car.permission.CAR_INFO |
7 |
addTollListener
removeTollListener |
สถานะบัตรค่าผ่านทาง ประเภทบัตรค่าผ่านทาง | 3 | ||
addEnergyLevelListener
removeEnergyLevelListener |
ระดับแบตเตอรี่ ระดับน้ำมัน น้ำมันเหลือน้อย ระยะทางที่เหลือ | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_ENERGY android.car.permission.CAR_ENERGY_PORTS android.car.permission.READ_CAR_DISPLAY_UNITS
|
3 |
addSpeedListener
removeSpeedListener |
ความเร็วดิบ, ความเร็วในการแสดงผล (แสดงในจอแสดงผลคลัสเตอร์ของรถยนต์) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED android.car.permission.READ_CAR_DISPLAY_UNITS |
3 |
addMileageListener
removeMileageListener |
ระยะทางจากเครื่องวัดระยะทาง | com.google.android.gms.permission.CAR_MILEAGE |
ข้อมูลนี้ไม่พร้อมใช้งานใน Android Automotive OS สำหรับแอปที่ติดตั้งจาก Play Store | 3 |
เช่น หากต้องการช่วงที่เหลือ ให้สร้างอินสแตนซ์ของออบเจ็กต์ CarInfo
จากนั้นสร้างและลงทะเบียน OnCarDataAvailableListener
Kotlin
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo val listener = OnCarDataAvailableListener<EnergyLevel> { data -> if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) { val rangeRemaining = data.rangeRemainingMeters.value } else { // Handle error } } carInfo.addEnergyLevelListener(carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener)
Java
CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo(); OnCarDataAvailableListener<EnergyLevel> listener = (data) -> { if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) { float rangeRemaining = data.getRangeRemainingMeters().getValue(); } else { // Handle error } }; carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener);
อย่าคิดว่าข้อมูลจากรถจะพร้อมใช้งานตลอดเวลา
หากพบข้อผิดพลาด ให้ตรวจสอบสถานะของค่าที่ขอเพื่อทําความเข้าใจสาเหตุที่ดึงข้อมูลที่คุณขอไม่ได้ได้ดียิ่งขึ้น ดูคำจำกัดความของคลาส CarInfo
ฉบับเต็มได้ในเอกสารอ้างอิง
CarSensors
คลาส CarSensors
ช่วยให้คุณเข้าถึงข้อมูลตัวตรวจวัดความเร่ง เครื่องวัดการหมุน เข็มทิศ และตำแหน่งของยานพาหนะ ความพร้อมใช้งานของค่าเหล่านี้อาจขึ้นอยู่กับ OEM รูปแบบของข้อมูลจากตัวตรวจวัดความเร่ง เครื่องวัดการหมุน และเข็มทิศจะเหมือนกับที่คุณได้รับจาก SensorManager
API ตัวอย่างเช่น หากต้องการตรวจสอบทิศทางของยานพาหนะ ให้ทำดังนี้
Kotlin
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors val listener = OnCarDataAvailableListener<Compass> { data -> if (data.orientations.status == CarValue.STATUS_SUCCESS) { val orientation = data.orientations.value } else { // Data not available, handle error } } carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener)
Java
CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors(); OnCarDataAvailableListener<Compass> listener = (data) -> { if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) { List<Float> orientations = data.getOrientations().getValue(); } else { // Data not available, handle error } }; carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener);
หากต้องการเข้าถึงข้อมูลตำแหน่งจากรถยนต์ คุณจะต้องประกาศและขอสิทธิ์ android.permission.ACCESS_FINE_LOCATION
ด้วย
การทดสอบ
หากต้องการจำลองข้อมูลเซ็นเซอร์เมื่อทดสอบใน Android Auto โปรดดูส่วนเซ็นเซอร์และการกำหนดค่าเซ็นเซอร์ของคู่มือเกี่ยวกับจอแสดงผลบนเดสก์ท็อป หากต้องการจําลองข้อมูลเซ็นเซอร์เมื่อทดสอบใน Android Automotive OS โปรดดูส่วนจําลองสถานะฮาร์ดแวร์ในคู่มือโปรแกรมจําลอง Android Automotive OS
วงจรชีวิตของ CarAppService, เซสชัน และหน้าจอ
คลาส Session
และ Screen
ใช้อินเทอร์เฟซ LifecycleOwner
เมื่อผู้ใช้โต้ตอบกับแอป ระบบจะเรียกใช้การเรียกกลับของวงจรชีวิตของออบเจ็กต์ Session
และ Screen
ตามที่อธิบายไว้ในแผนภาพต่อไปนี้
วงจรชีวิตของ CarAppService และเซสชัน
ดูรายละเอียดทั้งหมดได้ในเอกสารประกอบของวิธี Session.getLifecycle
วงจรชีวิตของหน้าจอ
ดูรายละเอียดทั้งหมดได้ในเอกสารประกอบของเมธอด Screen.getLifecycle
บันทึกจากไมโครโฟนของรถยนต์
คุณสามารถใช้แอป CarAppService
และ CarAudioRecord
API ของแอปเพื่ออนุญาตให้แอปเข้าถึงไมโครโฟนของรถยนต์ของผู้ใช้ ผู้ใช้ต้องให้สิทธิ์แอปของคุณในการเข้าถึงไมโครโฟนรถยนต์ แอปสามารถบันทึกและประมวลผลอินพุตของผู้ใช้ภายในแอป
สิทธิ์ในการบันทึก
ก่อนที่จะบันทึกเสียง คุณต้องประกาศสิทธิ์ในการบันทึกใน AndroidManifest.xml
ก่อน แล้วขอให้ผู้ใช้ให้สิทธิ์
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
...
</manifest>
คุณต้องขอสิทธิ์ในการบันทึกขณะรันไทม์ ดูรายละเอียดเกี่ยวกับวิธีขอสิทธิ์ในแอปรถยนต์ได้ที่ส่วนขอสิทธิ์
บันทึกเสียง
หลังจากที่ผู้ใช้ให้สิทธิ์ในการบันทึกแล้ว คุณจะบันทึกเสียงและประมวลผลการบันทึกได้
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) carAudioRecord.startRecording() val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording()
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); carAudioRecord.startRecording(); byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE]; while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording();
โฟกัสอัตโนมัติ
เมื่อบันทึกจากไมโครโฟนรถยนต์ ก่อนอื่นให้ซื้อโฟกัสเสียง เพื่อให้แน่ใจว่าสื่อที่ดำเนินอยู่หยุดลง หากเสียโฟกัสเสียง ให้หยุดบันทึก
ตัวอย่างวิธีรับโฟกัสเสียงมีดังนี้
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) // Take audio focus so that user's media is not recorded val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build() val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener { state: Int -> if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording() } } .build() if (carContext.getSystemService(AudioManager::class.java) .requestAudioFocus(audioFocusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) { // Don't record if the focus isn't granted return } carAudioRecord.startRecording() // Process the audio and abandon the AudioFocusRequest when done
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); // Take audio focus so that user's media is not recorded AudioAttributes audioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build(); AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener(state -> { if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording(); } }) .build(); if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest) != AUDIOFOCUS_REQUEST_GRANTED) { // Don't record if the focus isn't granted return; } carAudioRecord.startRecording(); // Process the audio and abandon the AudioFocusRequest when done
ไลบรารีการทดสอบ
ไลบรารีการทดสอบของ Android สำหรับรถยนต์มีคลาสเสริมที่คุณสามารถใช้เพื่อตรวจสอบลักษณะการทํางานของแอปในสภาพแวดล้อมการทดสอบ
ตัวอย่างเช่น SessionController
ช่วยให้คุณจำลองการเชื่อมต่อกับโฮสต์และยืนยันว่ามีการสร้างและแสดงผล Screen
และ Template
ที่ถูกต้อง
ดูตัวอย่างการใช้งานได้ที่ส่วนตัวอย่าง
รายงานปัญหาเกี่ยวกับไลบรารีแอป Android สำหรับรถยนต์
หากคุณพบปัญหาในไลบรารี ให้รายงานปัญหาโดยใช้เครื่องมือติดตามปัญหาของ Google ตรวจสอบว่าได้กรอกข้อมูลที่ขอทั้งหมดในเทมเพลตของปัญหา
ก่อนจะยื่นปัญหาใหม่ โปรดตรวจสอบว่าปัญหาแสดงอยู่ในรายการบันทึกประจำรุ่นของไลบรารีหรือรายงานในรายการปัญหาหรือไม่ คุณสามารถติดตามและโหวตปัญหาได้โดยคลิกดาวสำหรับปัญหาในเครื่องมือติดตาม ดูข้อมูลเพิ่มเติมได้ที่การติดตามปัญหา