ใช้ไลบรารีแอป Android สำหรับรถยนต์

คลังแอป Android for Cars ช่วยให้คุณสามารถนำการนำทาง จุดที่น่าสนใจ (POI) และอินเทอร์เน็ตของสรรพสิ่ง (IOT) ของคุณ ลงในรถยนต์ โดยมอบชุดเทมเพลตที่ออกแบบมาเพื่อตอบสนองสิ่งรบกวนผู้ขับขี่ และดูแลรายละเอียดต่างๆ เช่น ปัจจัยต่างๆ ของหน้าจอรถ และวิธีการป้อนข้อมูล

คู่มือนี้จะให้ภาพรวมเกี่ยวกับฟีเจอร์และแนวคิดหลักของไลบรารีและ จะแนะนำขั้นตอนการตั้งค่าแอปพื้นฐาน

ก่อนเริ่มต้น

  1. ทบทวนการออกแบบสำหรับการขับรถ หน้าที่ครอบคลุมไลบรารีแอปรถยนต์
  2. อ่านคำและแนวคิดสำคัญในเรื่องต่อไปนี้
  3. ทำความคุ้นเคยกับระบบ Android Auto UI และ Android Automotive OS การออกแบบ
  4. อ่านบันทึกประจำรุ่น
  5. ดูตัวอย่าง

คำสำคัญและแนวคิด

โมเดลและเทมเพลต
อินเทอร์เฟซผู้ใช้จะแสดงเป็นกราฟของออบเจ็กต์โมเดลที่สามารถ จัดเรียงไว้ด้วยกันในรูปแบบต่างๆ ตามที่ได้รับอนุญาตโดยเทมเพลต เป็น เทมเพลตเป็นชุดย่อยของโมเดลซึ่งทำหน้าที่เป็นรูทของเทมเพลตเหล่านั้น กราฟ โมเดลประกอบด้วยข้อมูลที่แสดงต่อผู้ใช้ใน ข้อความและรูปภาพ รวมถึงแอตทริบิวต์เพื่อกำหนดค่าแง่มุมต่างๆ ลักษณะที่ปรากฏของข้อมูลดังกล่าว เช่น สีข้อความหรือรูปภาพ ขนาดต่างๆ โฮสต์จะแปลงโมเดลเป็นมุมมองที่ออกแบบมาเพื่อเป็นไปตาม มาตรฐานสิ่งรบกวนผู้ขับขี่และดูแลรายละเอียดต่างๆ เช่น ความหลากหลาย ของปัจจัยหน้าจอรถและวิธีป้อนข้อมูล
เป็นเจ้าภาพ
โฮสต์คือคอมโพเนนต์แบ็กเอนด์ที่ใช้ฟังก์ชันการทำงานที่มีให้บริการ API ของไลบรารีเพื่อให้แอปของคุณทำงานในรถได้ ซึ่งโฮสต์ก็มีตั้งแต่การค้นพบแอปและการจัดการ วงจรในการแปลงโมเดลเป็นข้อมูลพร็อพเพอร์ตี้และแจ้งเตือนแอปของคุณ การโต้ตอบของผู้ใช้ ในอุปกรณ์เคลื่อนที่ Android จะใช้โฮสต์นี้ อัตโนมัติ ใน Android Automotive OS โฮสต์นี้จะติดตั้งเป็นแอประบบ
ข้อจำกัดของเทมเพลต
เทมเพลตที่แตกต่างกันจะบังคับใช้ข้อจำกัดในเนื้อหาของโมเดล สำหรับ เช่น เทมเพลตรายการมีการจำกัดจำนวนรายการที่สามารถ ที่แสดงให้ผู้ใช้เห็น เทมเพลตยังมีข้อจำกัดในด้านวิธีดำเนินการ เพื่อสร้างโฟลว์ของงาน เช่น แอปสามารถพุช เทมเพลตลงในกองหน้าจอได้ถึง 5 เทมเพลต โปรดดู ดูรายละเอียดเพิ่มเติมได้ที่ข้อจำกัดของเทมเพลต
Screen
Screen คือชั้นเรียนที่ ที่แอปพลิเคชันนำมาใช้เพื่อจัดการอินเทอร์เฟซผู้ใช้ ผู้ใช้ Screen มี วงจร และให้กลไกสำหรับแอปในการ ส่งเทมเพลตเพื่อแสดงเมื่อหน้าจอปรากฏ สามารถพุชอินสแตนซ์ Screen รายการได้ด้วย และเปลี่ยนจาก Screen สแต็ก ซึ่ง เป็นไปตามข้อกำหนดของ ข้อจำกัดสำหรับขั้นตอนของเทมเพลต
CarAppService
CarAppService เป็น Abstract Service ที่แอปของคุณ ติดตั้งใช้งานและส่งออกเพื่อให้โฮสต์ค้นพบและจัดการ CarAppServiceของแอปคุณ ซึ่งมีหน้าที่ตรวจสอบว่าการเชื่อมต่อโฮสต์เชื่อถือได้โดยใช้ createHostValidator และมอบ Session ในภายหลัง อินสแตนซ์สำหรับการเชื่อมต่อแต่ละรายการโดยใช้ onCreateSession
Session

Session เป็นคลาสนามธรรมที่ แอปของคุณต้องใช้และกลับมาใช้ CarAppService.onCreateSession ซึ่งทำหน้าที่เป็นจุดแรกเข้าในการแสดงข้อมูลบนหน้าจอของรถยนต์ ทั้งนี้ มีวงจรที่ให้ข้อมูล สถานะปัจจุบันของแอปบนหน้าจอของรถยนต์ เช่น เมื่อแอป มองเห็นได้หรือซ่อนอยู่

เมื่อเริ่มการทำงานของ Session เช่น เมื่อแอปเปิดขึ้นเป็นครั้งแรก โฮสต์จะขอ Screen เพื่อแสดงโดยใช้ onCreateScreen

ติดตั้งไลบรารีแอปรถยนต์

ดูไลบรารี Jetpack หน้าเผยแพร่สำหรับ วิธีการเพิ่มไลบรารีลงในแอปของคุณ

กำหนดค่าไฟล์ Manifest ของแอป

ก่อนจะสร้างแอปสำหรับรถยนต์ ให้กำหนดค่าแอป ไฟล์ Manifest ดังนี้

ประกาศ CarAppService ของคุณ

โฮสต์เชื่อมต่อกับแอปของคุณผ่าน CarAppService คุณ ประกาศบริการนี้ในไฟล์ Manifest เพื่อให้โฮสต์ค้นพบและเชื่อมต่อ กับแอปของคุณ

คุณต้องประกาศหมวดหมู่ของแอปใน <category> ขององค์ประกอบของแอป Intent ดูรายการ หมวดหมู่แอปที่รองรับสำหรับค่าที่อนุญาต องค์ประกอบนี้

ข้อมูลโค้ดต่อไปนี้แสดงวิธีประกาศบริการแอปรถยนต์สำหรับ แอปพลิเคชันความสนใจในไฟล์ 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 ตามที่ได้อธิบายไว้ ในส่วนก่อนหน้านี้

ดูคุณภาพแอป 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 ของตนเองเพื่อให้คุณรู้ว่า ฟีเจอร์คลังใช้ได้กับโฮสต์เทมเพลตในยานพาหนะ หากต้องการเรียกข้อมูลระดับ API ของแอปรถยนต์สูงสุดที่โฮสต์รองรับ ให้ใช้ getCarAppApiLevel()

ประกาศระดับ API ของแอปรถยนต์ขั้นต่ำที่แอปของคุณรองรับใน AndroidManifest.xml ไฟล์:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

โปรดดูเอกสารประกอบสำหรับ RequiresCarApi สำหรับรายละเอียดเกี่ยวกับวิธีการรักษาความเข้ากันได้แบบย้อนหลังและประกาศ ระดับ API ขั้นต่ำที่จำเป็นในการใช้ฟีเจอร์ ดูคำจำกัดความของ 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 เทมเพลตสำหรับ แสดงข้อความ "สวัสดีทุกคน" ง่ายๆ สตริง:

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 การทำความเข้าใจเนื้อหาที่เจาะจงในเทมเพลตที่ได้รับการอัปเดต ดังนั้นโฮสต์จะไม่นับเทมเพลตใหม่ในโควต้าเทมเพลต ดูรายละเอียดเพิ่มเติมได้ที่ส่วนข้อจำกัดของเทมเพลต

เราขอแนะนำให้คุณกำหนดโครงสร้างหน้าจอ เพื่อให้เป็นแบบ 1 ต่อ 1 การแมประหว่าง 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

ให้ผู้ใช้สามารถโต้ตอบกับแผนที่ของคุณ

เมื่อใช้เทมเพลตต่อไปนี้ คุณจะสามารถเพิ่มการสนับสนุนเพื่อให้ผู้ใช้โต้ตอบ กับแผนที่ที่คุณวาด เช่น ทำให้พวกเขาเห็นส่วนต่างๆ ของแผนที่ รวมถึงการซูมเข้าและเลื่อน

เทมเพลต รองรับการโต้ตอบตั้งแต่ระดับ API ของแอปรถยนต์
NavigationTemplate 2
PlaceListNavigationTemplate (เลิกใช้งานแล้ว) 4
RoutePreviewNavigationTemplate (เลิกใช้งานแล้ว) 4
MapTemplate (เลิกใช้งานแล้ว) 5 (การแนะนำเทมเพลต)
MapWithContentTemplate 7 (แนะนำเทมเพลต)

ใช้ Callback ของการโต้ตอบ

อินเทอร์เฟซ SurfaceCallback มีวิธี Callback หลายวิธีที่คุณสามารถใช้เพื่อเพิ่มการโต้ตอบให้กับแผนที่ที่สร้างขึ้น กับเทมเพลตในส่วนก่อนหน้านี้

การโต้ตอบ SurfaceCallback วิธี รองรับตั้งแต่ระดับ API ของแอปรถยนต์
แตะ onClick 5
บีบและกางนิ้วเพื่อซูม onScale 2
ลากด้วยการแตะเพียงครั้งเดียว onScroll 2
การสะบัดด้วยการแตะเพียงครั้งเดียว onFling 2
แตะสองครั้ง onScale (ด้วยค่าตัวคูณมาตราส่วนที่กำหนดโดยโฮสต์เทมเพลต) 2
การกระตุ้นเตือนแบบหมุนในโหมดเลื่อน onScroll (โดยมีปัจจัยระยะทางที่กำหนดโดยโฮสต์ของเทมเพลต) 2

เพิ่มแถบการดำเนินการบนแผนที่

เทมเพลตเหล่านี้มีแถบการทำงานของแผนที่สำหรับการดำเนินการที่เกี่ยวข้องกับแผนที่ได้ เช่น การย่อและขยาย ตั้งจุดศูนย์กลาง แสดงเข็มทิศ และการทำงานอื่นๆ ที่คุณ เลือกที่จะแสดง แถบการทำงานบนแผนที่มีปุ่มเฉพาะไอคอนได้สูงสุด 4 ปุ่ม ซึ่งสามารถรีเฟรชได้โดยไม่ส่งผลกระทบต่อจำนวนงาน ระบบจะซ่อนระหว่างสภาวะไม่มีการใช้งาน และปรากฏขึ้นอีกครั้งในสถานะทำงานอยู่

หากต้องการรับการเรียกกลับของการโต้ตอบบนแผนที่ ให้ทำดังนี้ ต้องเพิ่มปุ่ม Action.PAN ในแถบการทำงานของแผนที่ เมื่อผู้ใช้ ให้กดปุ่มเลื่อน โฮสต์จะเข้าสู่โหมดเลื่อนตามที่อธิบายไว้ใน

หากแอปละเว้น Action.PAN ในแถบการดำเนินการบนแผนที่ ไม่ได้รับข้อมูลจากผู้ใช้ SurfaceCallback วิธี และโฮสต์จะออกจากเมธอดที่เปิดใช้งานก่อนหน้านี้ โหมดเลื่อน

บนหน้าจอสัมผัส ปุ่มเลื่อนจะไม่แสดง

ทำความเข้าใจโหมดเลื่อน

ในโหมดเลื่อน โฮสต์เทมเพลตจะแปลข้อมูลที่ผู้ใช้ป้อนจากการป้อนข้อมูลที่ไม่ใช่แบบสัมผัส เช่น ตัวควบคุมแบบหมุน และทัชแพด SurfaceCallback วิธี ตอบสนองต่อการดำเนินการของผู้ใช้เพื่อเข้าหรือออกจากโหมดเลื่อน พร้อมด้วย setPanModeListener ใน NavigationTemplate.Builder โฮสต์สามารถซ่อน UI อื่นได้ ในเทมเพลตขณะที่ผู้ใช้อยู่ในโหมดเลื่อน

โต้ตอบกับผู้ใช้

แอปของคุณสามารถโต้ตอบกับผู้ใช้โดยใช้รูปแบบที่คล้ายกับแอปบนอุปกรณ์เคลื่อนที่

จัดการข้อมูลจากผู้ใช้

แอปของคุณสามารถตอบสนองต่อข้อมูลจากผู้ใช้ได้โดยส่ง Listener ที่เหมาะสมไปยัง รุ่นที่รองรับ ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีสร้าง โมเดล 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);
}

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีเริ่มต้นใช้งานแอป รวมถึงรูปแบบของ ACTION_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 เกี่ยวกับ การแจ้งเตือน

แสดงข้อความโทสต์

แอปของคุณสามารถแสดงข้อความโทสต์โดยใช้ 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 เนื้อหาที่มี PendingIntent การรวมข้อความที่ไม่เหมาะสม ความตั้งใจในการทำงานของแอป

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 รายการขณะอยู่ในหน้าจอ A จากนั้นพุชหน้าจอ ข จะสามารถส่งเทมเพลตได้อีก 3 รายการ หรือถ้าหน้าจอแต่ละหน้ามีโครงสร้าง ส่งเทมเพลตเดียว แอปจะสามารถพุชอินสแตนซ์หน้าจอ 5 รายการไปยัง กอง ScreenManager

ข้อจำกัดเหล่านี้มีกรณีพิเศษ ได้แก่ การรีเฟรชเทมเพลต การย้อนกลับ และ การรีเซ็ต

การรีเฟรชเทมเพลต

การอัปเดตเนื้อหาบางอย่างจะไม่นับรวมในขีดจำกัดของเทมเพลต โดยทั่วไปแล้ว หากแอปพุชเทมเพลตใหม่ที่เป็นประเภทเดียวกันและมี เนื้อหาหลักเดียวกับเทมเพลตก่อนหน้า เทมเพลตใหม่จะ นับรวมในโควต้า ตัวอย่างเช่น การอัปเดตสถานะการสลับแถวใน ไม่นับ ListTemplate เทียบกับโควต้า ดูข้อมูลเพิ่มเติมในเอกสารประกอบของเทมเพลตแต่ละรายการ เกี่ยวกับประเภทการอัปเดตเนื้อหาที่ถือว่าเป็นการรีเฟรช

การทำงานส่วนหลัง

หากต้องการเปิดใช้ขั้นตอนย่อยภายในงาน โฮสต์จะตรวจพบเมื่อแอปแสดงขั้นตอน Screen จากกลุ่ม ScreenManager และการอัปเดต โควต้าที่เหลือขึ้นอยู่กับจำนวนเทมเพลตที่แอปใช้ ย้อนกลับ

เช่น หากแอปส่งเทมเพลต 2 รายการขณะอยู่ในหน้าจอ A ก็จะพุช หน้าจอ B และส่งเทมเพลตอีก 2 รายการ แอปมีโควต้าเหลือเพียง 1 รายการ ถ้า แล้วแอปก็จะแสดงกลับไปที่หน้าจอ A โฮสต์จะรีเซ็ตโควต้าเป็น 3 เนื่องจาก แอปได้ย้อนกลับไป 2 เทมเพลต

โปรดทราบว่าเมื่อกลับมาที่หน้าจอ แอปต้องส่งเทมเพลตที่ ของประเภทเดียวกับที่ส่งครั้งสุดท้ายมาจากหน้าจอนั้น การส่งอื่นๆ ประเภทเทมเพลตทำให้เกิดข้อผิดพลาด อย่างไรก็ตาม ตราบใดที่ประเภทดังกล่าวยังคง ขณะดำเนินการย้อนกลับ แอปสามารถแก้ไขเนื้อหาของ เทมเพลตโดยไม่กระทบโควต้า

รีเซ็ตการดำเนินการ

บางเทมเพลตจะมีความหมายพิเศษซึ่งแสดงถึงจุดสิ้นสุดของงาน สำหรับ ตัวอย่างเช่น NavigationTemplate เป็นมุมมองที่คาดว่าจะอยู่บนหน้าจอและได้รับการรีเฟรชด้วย คำแนะนำแบบเลี้ยวต่อเลี้ยวเพื่อดูการใช้งานของผู้ใช้ เมื่อมาถึงจุดใดจุดหนึ่งเหล่านี้ โฮสต์ โฮสต์จะรีเซ็ตโควต้าเทมเพลต โดยจะถือว่าเทมเพลตนั้น ให้เป็นขั้นตอนแรกของงานใหม่ การดำเนินการนี้จะช่วยให้แอปเริ่มงานใหม่ได้ ดูเอกสารประกอบของเทมเพลตแต่ละรายการเพื่อดูว่าเทมเพลตใดเรียกใช้การรีเซ็ต ในโฮสต์

หากโฮสต์ได้รับ Intent ในการเริ่มแอปจากการดำเนินการแจ้งเตือน หรือ จาก Launcher ระบบจะรีเซ็ตโควต้าด้วย กลไกนี้ช่วยให้แอป เริ่มขั้นตอนงานใหม่จากการแจ้งเตือน ซึ่งจะเป็น "จริง" แม้ว่าแอป อยู่แล้วและอยู่เบื้องหน้า

ดูรายละเอียดเพิ่มเติมในส่วนแสดงการแจ้งเตือน เกี่ยวกับวิธีแสดงการแจ้งเตือนของแอปในหน้าจอของรถยนต์ โปรดดู เริ่มต้นแอปรถยนต์ด้วย Intent เพื่อดูข้อมูลเกี่ยวกับวิธี เพื่อเริ่มแอปจากการดำเนินการแจ้งเตือน

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();
}

ข้อจำกัด 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 Tap
  • QRCodeSignInMethod สำหรับการลงชื่อเข้าใช้ด้วยคิวอาร์โค้ด ซึ่งผู้ใช้จะสแกนคิวอาร์โค้ดเพื่อลงชื่อเข้าใช้ โทรศัพท์ของตน ซึ่งใช้ได้กับ 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();

ใช้ Account Manager

แอป Android Automotive OS ที่มีการตรวจสอบสิทธิ์ต้องใช้ AccountManager เนื่องจากเหตุผลต่อไปนี้

  • UX ที่ดีขึ้นและการจัดการบัญชีที่ง่ายยิ่งขึ้น: ผู้ใช้สามารถจัดการทั้งหมดได้อย่างง่ายดาย บัญชีของตนจากเมนูบัญชีในการตั้งค่าระบบ รวมถึงการลงชื่อเข้าใช้ และออกจากระบบ
  • "ผู้มาเยือน" : เนื่องจากรถยนต์เป็นอุปกรณ์ที่แชร์กัน OEM จึงสามารถเปิดใช้ ประสบการณ์ของแขกในยานพาหนะที่ไม่สามารถเพิ่มบัญชีได้

เพิ่มตัวแปรสตริงข้อความ

หน้าจอของรถยนต์ขนาดต่างๆ อาจแสดงจำนวนข้อความแตกต่างกัน พร้อม 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 สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการสร้างระยะเวลาเหล่านี้ โปรดดู สเปนต่อเนื่อง การจัดรูปแบบข้อความด้วย Spans เพื่อดูภาพรวมวิธีการทำงานของการจัดรูปแบบข้อความด้วยระยะเวลา

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();
  
  

API ฮาร์ดแวร์รถยนต์

ตั้งแต่ API แอปรถระดับที่ 3 ไลบรารีแอปรถยนต์มี API ที่คุณ สามารถใช้เพื่อเข้าถึงคุณสมบัติและเซ็นเซอร์ของยานพาหนะ

ข้อกำหนด

หากต้องการใช้ API กับ Android Auto ให้เริ่มด้วยการเพิ่มทรัพยากร Dependency androidx.car.app:app-projected ไปยังไฟล์ build.gradle สำหรับ Android ของคุณ โมดูลอัตโนมัติ สำหรับ Android Automotive OS ให้เพิ่มทรัพยากร Dependency androidx.car.app:app-automotive ไปยังไฟล์ build.gradle สำหรับ Android ของคุณ โมดูล Automotive OS

นอกจากนี้ ในไฟล์ AndroidManifest.xml คุณจะต้อง ประกาศสิทธิ์ที่เกี่ยวข้องที่จำเป็นต่อการ ขอข้อมูลรถที่คุณต้องการใช้ โปรดทราบว่าสิทธิ์เหล่านี้ต้อง ให้สิทธิ์แก่คุณ คุณสามารถใช้ รหัสเดียวกันทั้งใน Android Auto และ Android Automotive OS แทนที่จะต้องสร้างขั้นตอน ที่ขึ้นอยู่กับแพลตฟอร์ม แต่สิทธิ์ที่จำเป็น แตกต่างกัน

ข้อมูลรถยนต์

ตารางนี้อธิบายพร็อพเพอร์ตี้ที่แสดงโดย CarInfo API และ สิทธิ์ที่คุณต้องขอเพื่อใช้

วิธีการ คุณสมบัติ สิทธิ์ของ Android Auto สิทธิ์ของ Android Automotive OS รองรับตั้งแต่ระดับ 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 จะช่วยให้คุณเข้าถึงตัวตรวจวัดความเร่ง เครื่องวัดการหมุน เข็มทิศ ของรถ และ ข้อมูลสถานที่ ความพร้อมใช้งานของค่าเหล่านี้อาจขึ้นอยู่กับ 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 ของคุณ วงจร ระบบจะเรียกใช้ Callback ตามที่อธิบายไว้ในแผนภาพต่อไปนี้

วงจรชีวิตของ CarAppService และเซสชัน

วันที่
รูปที่ 1 วงจรการใช้งาน Session

โปรดดูรายละเอียดทั้งหมดในเอกสารประกอบเกี่ยวกับ Session.getLifecycle

วงจรชีวิตของหน้าจอ

วันที่
รูปที่ 2 วงจรการใช้งาน Screen

โปรดดูรายละเอียดทั้งหมดในเอกสารประกอบเกี่ยวกับ 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 for Cars ห้องสมุดจะให้ความช่วยเหลือ คลาสที่คุณสามารถใช้เพื่อตรวจสอบลักษณะการทำงานของแอปในสภาพแวดล้อมการทดสอบ ตัวอย่างเช่น พารามิเตอร์ SessionController จะช่วยให้คุณสามารถจำลองการเชื่อมต่อกับโฮสต์และยืนยันว่า Screen และ สร้าง Template และ ส่งคืนแล้ว

โปรดดู ลองฟัง เพื่อดูตัวอย่างการใช้งาน

รายงานปัญหาเกี่ยวกับคลังแอป Android สำหรับรถยนต์

หากคุณพบปัญหากับไลบรารี ให้รายงานปัญหาโดยใช้ เครื่องมือติดตามปัญหาของ Google ตรวจสอบว่าได้กรอกข้อมูลที่ขอทั้งหมดในเทมเพลตของปัญหา

สร้างรายการใหม่

ก่อนยื่นฉบับใหม่ โปรดตรวจสอบว่าฉบับแสดงไว้ในรุ่นของไลบรารีแล้วหรือไม่ หมายเหตุหรือที่รายงานในรายการปัญหา คุณสามารถสมัครรับข้อมูลและโหวตปัญหาได้โดย คลิกดาวที่ปัญหาในเครื่องมือติดตาม สำหรับข้อมูลเพิ่มเติม โปรดดู การสมัครรับปัญหา