สร้างแอปสื่อสำหรับรถยนต์

Android Auto และ Android Automotive OS ช่วยให้คุณนําเนื้อหาแอปสื่อไปยัง ในรถของผู้ใช้ แอปสื่อสำหรับรถยนต์ต้องให้บริการเบราว์เซอร์สื่อ เพื่อให้ Android Auto และ Android Automotive OS หรือแอปอื่นที่มีสื่อ สามารถค้นพบและแสดงเนื้อหาของคุณ

คู่มือนี้จะถือว่าคุณมีแอปสื่อที่เล่นเสียงใน โทรศัพท์และแอปสื่อของคุณสอดคล้องกับแอปสื่อของ Android สถาปัตยกรรม

คู่มือนี้จะอธิบายคอมโพเนนต์ที่จําเป็นของ MediaBrowserService และ MediaSessionที่แอปของคุณต้องใช้เพื่อทำงานบน Android Auto หรือ Android Automotive OS หลังจากที่คุณสร้างโครงสร้างพื้นฐานของสื่อหลักเสร็จสมบูรณ์แล้ว คุณจะสามารถ เพิ่มการสนับสนุนสำหรับ Android Auto และเพิ่มการสนับสนุนสำหรับ Android Automotive OS ไปยังสื่อของคุณ แอป

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

  1. อ่านเอกสารประกอบของ Android Media API
  2. อ่านหัวข้อสร้างแอปสื่อ เพื่อรับคำแนะนำในการออกแบบ
  3. ตรวจสอบคำศัพท์และแนวคิดสำคัญที่แสดงในส่วนนี้

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

บริการเบราว์เซอร์สื่อ
บริการ Android ที่ใช้งานโดยแอปสื่อซึ่งปฏิบัติตามข้อกำหนด MediaBrowserServiceCompat API แอปของคุณใช้บริการนี้เพื่อแสดงเนื้อหาของแอป
เบราว์เซอร์สื่อ
API ที่แอปสื่อใช้เพื่อค้นหาบริการเบราว์เซอร์สื่อและการแสดงผล เนื้อหาของตน Android Auto และ Android Automotive OS ใช้เบราว์เซอร์สื่อเพื่อ ค้นหาบริการเบราว์เซอร์สื่อของแอป
รายการสื่อ

เบราว์เซอร์สื่อจะจัดระเบียบเนื้อหาเป็นแผนผังของ MediaItem ออบเจ็กต์ รายการสื่ออาจมีแฟล็กต่อไปนี้อย่างใดอย่างหนึ่งหรือทั้ง 2 อย่าง

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

รายการสื่อที่ทั้งสามารถเลือกดูได้และเล่นได้ เช่น เพลย์ลิสต์ คุณสามารถ เลือกรายการด้วยตนเองเพื่อเล่นเด็กทั้งหมดของรายการนั้น หรือคุณสามารถเรียกดู

เพิ่มประสิทธิภาพยานพาหนะ

กิจกรรมสําหรับแอป Android Automotive OS ที่ปฏิบัติตาม หลักเกณฑ์การออกแบบของ Android Automotive OS อินเทอร์เฟซสำหรับกิจกรรมเหล่านี้ไม่ได้วาดโดย Android Automotive OS ดังนั้นคุณจึง ต้องตรวจสอบว่าแอปของคุณเป็นไปตามหลักเกณฑ์ด้านการออกแบบ โดยทั่วไปแล้ว ประกอบด้วยเป้าหมายการแตะและขนาดแบบอักษรที่ใหญ่ขึ้น การรองรับโหมดกลางวันและกลางคืน และ อัตราคอนทราสต์ที่สูงขึ้น

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

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

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

ก่อนที่คุณจะสร้างบริการเบราว์เซอร์สื่อ คุณจะต้องกำหนดค่า ไฟล์ Manifest ของแอป

ประกาศบริการเบราว์เซอร์สื่อของคุณ

ทั้ง Android Auto และ Android Automotive OS เชื่อมต่อกับแอปของคุณผ่าน บริการเบราว์เซอร์สื่อเพื่อเรียกดูรายการสื่อ ประกาศสื่อของคุณ บริการเบราว์เซอร์ในไฟล์ Manifest เพื่อให้ Android Auto และ Android Automotive OS ได้ สำรวจบริการและเชื่อมต่อกับแอปของคุณ

ข้อมูลโค้ดต่อไปนี้แสดงวิธีประกาศบริการเบราว์เซอร์สื่อใน ไฟล์ Manifest ใส่โค้ดนี้ในไฟล์ Manifest สำหรับ โมดูล Android Automotive OS และในไฟล์ Manifest สําหรับแอปโทรศัพท์

<application>
    ...
    <service android:name=".MyMediaBrowserService"
             android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
    ...
</application>

ระบุไอคอนแอป

คุณต้องระบุไอคอนแอปที่ Android Auto และ Android Automotive OS ทำได้ ใช้เพื่อแสดงแอปของคุณใน UI ของระบบ ต้องมีไอคอน 2 ประเภทดังนี้

  • ไอคอน Launcher
  • ไอคอนระบุแหล่งที่มา

ไอคอน Launcher

ไอคอน Launcher แสดงถึงแอปของคุณใน UI ของระบบ เช่น บน Launcher และในถาดไอคอน คุณระบุได้ว่าต้องการใช้ไอคอนจาก แอปบนอุปกรณ์เคลื่อนที่เพื่อแสดงแอปสื่อของรถยนต์โดยใช้ไฟล์ Manifest ต่อไปนี้ การประกาศ:

<application
    ...
    android:icon="@mipmap/ic_launcher"
    ...
/>

หากต้องการใช้ไอคอนอื่นที่ไม่ใช่ไอคอนของแอปบนอุปกรณ์เคลื่อนที่ ให้ตั้งค่าพร็อพเพอร์ตี้ android:icon ในองค์ประกอบ <service> ของบริการเบราว์เซอร์สื่อของคุณในไฟล์ Manifest

<application>
    ...
    <service
        ...
        android:icon="@mipmap/auto_launcher"
        ...
    />
</application>

ไอคอนระบุแหล่งที่มา

รูปที่ 1 ไอคอนระบุแหล่งที่มาบนการ์ดสื่อ

ไอคอนการแสดงที่มาจะใช้ในจุดที่เนื้อหาสื่อมีลำดับความสำคัญเหนือกว่า เช่น ในการ์ดสื่อ ลองนำไอคอนขนาดเล็กที่ใช้สำหรับการแจ้งเตือนมาใช้ซ้ำ ไอคอนนี้ต้องเป็นโมโนโครม คุณระบุไอคอนที่ใช้แสดงได้ แอปของคุณโดยใช้การประกาศต่อไปนี้ในไฟล์ Manifest

<application>
    ...
    <meta-data
        android:name="androidx.car.app.TintableAttributionIcon"
        android:resource="@drawable/ic_status_icon" />
    ...
</application>

สร้างบริการเบราว์เซอร์สื่อ

คุณสร้างบริการเบราว์เซอร์สื่อโดยการขยาย MediaBrowserServiceCompat จากนั้นทั้ง Android Auto และ Android Automotive OS ก็จะใช้บริการของคุณได้ ทำสิ่งต่อไปนี้ได้

  • เรียกดูลำดับชั้นเนื้อหาของแอปเพื่อนำเสนอเมนูให้แก่ผู้ใช้
  • รับโทเค็นสำหรับ MediaSessionCompat ของแอป เพื่อควบคุมการเล่นเสียง

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

เวิร์กโฟลว์บริการเบราว์เซอร์สื่อ

ส่วนนี้อธิบายวิธีที่ Android Automotive OS และ Android โต้ตอบกับบริการเบราว์เซอร์สื่อโดยอัตโนมัติในกระบวนการทำงานของผู้ใช้ทั่วไป

  1. ผู้ใช้เปิดแอปของคุณใน Android Automotive OS หรือ Android Auto
  2. Android Automotive OS หรือ Android Auto ติดต่อเบราว์เซอร์สื่อของแอป บริการที่ใช้ onCreate() ในการติดตั้งใช้งาน onCreate() คุณต้องสร้างและลงทะเบียน MediaSessionCompat และออบเจ็กต์ Callback ของออบเจ็กต์
  3. Android Automotive OS หรือ Android Auto เรียกใช้ onGetRoot() ของบริการ เพื่อรับรายการสื่อรูทในลำดับชั้นเนื้อหาของคุณ รายการสื่อรูท ไม่แสดง แต่จะใช้เพื่อดึงเนื้อหาเพิ่มเติมจากแอปของคุณแทน
  4. Android Automotive OS หรือ Android Auto โทรหาบริการของคุณ onLoadChildren() วิธีรับรายการย่อยของรายการสื่อราก Android Automotive OS และ Android Auto แสดงรายการสื่อเหล่านี้เป็นรายการเนื้อหาระดับบนสุด โปรดดู จัดโครงสร้างเมนูรากในหน้านี้สำหรับข้อมูลเพิ่มเติม เกี่ยวกับสิ่งที่ระบบคาดหวังในระดับนี้
  5. หากผู้ใช้เลือกรายการสื่อที่เรียกดูได้ บริการของคุณ onLoadChildren() เรียกเมธอดอีกครั้งเพื่อเรียกรายการย่อยของรายการในเมนูที่เลือก
  6. หากผู้ใช้เลือกรายการสื่อที่เล่นได้, Android Automotive OS หรือ Android อัตโนมัติจะเรียกใช้เมธอด Callback ของเซสชันสื่อที่เหมาะสมเพื่อดำเนินการนั้น
  7. หากแอปของคุณรองรับ ผู้ใช้จะค้นหาเนื้อหาของคุณได้เช่นกัน ด้วยวิธีนี้ Android Automotive OS หรือ Android Auto เรียกใช้บริการของคุณ onSearch()

สร้างลำดับชั้นเนื้อหา

Android Auto และ Android Automotive OS จะเรียกใช้บริการเบราว์เซอร์สื่อของแอปไปยัง ดูว่ามีเนื้อหาอะไรบ้าง คุณต้องใช้ 2 วิธีในบัญชี บริการเบราว์เซอร์สื่อเพื่อรองรับนี้: onGetRoot() และ onLoadChildren()

ใช้งาน onGetRoot

onGetRoot() ของบริการ จะแสดงข้อมูลเกี่ยวกับโหนดรากของลำดับชั้นเนื้อหาของคุณ Android Auto และ Android Automotive OS ใช้โหนดรูทนี้เพื่อขอส่วนที่เหลือของ เนื้อหาของคุณโดยใช้ onLoadChildren()

ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีการติดตั้งโค้ด เมธอด onGetRoot():

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? =
    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        null
    } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        return null;
    }

    return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
}

ดูตัวอย่างโดยละเอียดของวิธีการนี้ได้ที่ onGetRoot() ในแอปตัวอย่างของ Universal Android Music Player บน GitHub

เพิ่มการตรวจสอบแพ็กเกจสำหรับ onGetRoot()

เมื่อมีการโทรหา onGetRoot() ของบริการ แพ็กเกจการโทรจะส่งข้อมูลระบุตัวตนไปยังบริการของคุณ บัญชี บริการสามารถใช้ข้อมูลนี้เพื่อระบุว่าแพ็กเกจดังกล่าวสามารถเข้าถึง เนื้อหา เช่น คุณจำกัดการเข้าถึงเนื้อหาของแอปได้เฉพาะรายการ แพ็กเกจที่ได้รับการอนุมัติโดยการเปรียบเทียบ clientPackageName กับรายการที่อนุญาตและ ยืนยันใบรับรองที่ใช้ในการลงนาม APK ของแพ็กเกจ หากระบุแพ็กเกจไม่ได้ ได้รับการยืนยัน ให้กลับไปยัง null เพื่อปฏิเสธการเข้าถึงเนื้อหาของคุณ

เพื่อให้บริการแอประบบ เช่น Android Auto และ Android Automotive OS ที่เข้าถึงเนื้อหาของคุณได้ บริการของคุณจะต้องส่งคืนค่า BrowserRoot เมื่อแอประบบเหล่านี้เรียกใช้ onGetRoot() ลายเซ็นของแอประบบ Android Automotive OS อาจแตกต่างกันไปโดยขึ้นอยู่กับ จากยี่ห้อและรุ่นของรถ คุณจึงต้องอนุญาตให้การเชื่อมต่อ แอประบบเพื่อรองรับ Android Automotive OS อย่างมีประสิทธิภาพ

ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีที่บริการของคุณสามารถตรวจสอบได้ว่า แพ็กเกจการโทรเป็นแอประบบ

fun isKnownCaller(
    callingPackage: String,
    callingUid: Int
): Boolean {
    ...
    val isCallerKnown = when {
       // If the system is making the call, allow it.
       callingUid == Process.SYSTEM_UID -> true
       // If the app was signed by the same certificate as the platform
       // itself, also allow it.
       callerSignature == platformSignature -> true
       // ... more cases
    }
    return isCallerKnown
}

ข้อมูลโค้ดนี้เป็นข้อความที่ตัดตอนมาจาก PackageValidator ในแอปตัวอย่างของ Universal Android Music Player บน GitHub ดูชั้นเรียนนั้น เพื่อดูตัวอย่างโดยละเอียดเกี่ยวกับวิธีใช้การตรวจสอบแพ็กเกจสำหรับ onGetRoot()ของบริการ

นอกจากจะอนุญาตแอประบบแล้ว คุณต้องอนุญาตให้ Google Assistant เชื่อมต่อกับ MediaBrowserService โปรดทราบว่า Google Assistant มี ชื่อแพ็กเกจแยกกัน สำหรับโทรศัพท์ ซึ่งรวมถึง Android Auto และ Android Automotive OS

ใช้ onLoadChildren()

หลังจากได้รับออบเจ็กต์โหนดรูทแล้ว Android Auto และ Android Automotive OS สร้างเมนูระดับบนสุดโดยโทรหา onLoadChildren() บนออบเจ็กต์โหนดรูทเพื่อรับรายการย่อย เมนูย่อยของการสร้างแอปไคลเอ็นต์ตาม การเรียกเมธอดเดียวกันนี้โดยใช้ออบเจ็กต์โหนดย่อย

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

ข้อมูลโค้ดต่อไปนี้แสดงการใช้งาน onLoadChildren() แบบง่าย วิธีการ:

Kotlin

override fun onLoadChildren(
    parentMediaId: String,
    result: Result<List<MediaBrowserCompat.MediaItem>>
) {
    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check whether this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the children of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result<List<MediaBrowserCompat.MediaItem>> result) {

    // Assume for example that the music catalog is already loaded/cached.

    List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

    // Check whether this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the children of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

สำหรับตัวอย่างทั้งหมดของวิธีการนี้ โปรดดู onLoadChildren() ในแอปตัวอย่างของ Universal Android Music Player บน GitHub

จัดโครงสร้างเมนูรูท

รูปที่ 2 เนื้อหารูทที่แสดงเป็นแท็บการนําทาง

Android Auto และ Android Automotive OS มีข้อจำกัดเฉพาะเกี่ยวกับ ของเมนูราก ซึ่งระบบจะส่งเรื่องดังกล่าวให้ MediaBrowserService ผ่านคำแนะนำระดับรูท ซึ่งสามารถอ่านผ่านอาร์กิวเมนต์ Bundle ที่ส่งผ่าน onGetRoot() การทำตามคำแนะนำเหล่านี้จะช่วยให้ระบบแสดงเนื้อหาระดับรูทได้อย่างเหมาะสม เป็นแท็บนำทาง หากไม่ปฏิบัติตามคำแนะนำเหล่านี้ เนื้อหารูทบางส่วนอาจ ถูกทิ้งหรือทำให้ค้นพบได้น้อยลง ระบบจะส่งคำใบ้ไป 2 ฉบับ ได้แก่

ใช้รหัสต่อไปนี้เพื่ออ่านคำแนะนำรากที่เกี่ยวข้อง

Kotlin

import androidx.media.utils.MediaConstants

// Later, in your MediaBrowserServiceCompat.
override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}

Java

import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

คุณเลือกโยงตรรกะสำหรับโครงสร้างลำดับชั้นเนื้อหาได้ ตามค่าของคำแนะนำเหล่านี้ โดยเฉพาะหากลำดับชั้นของคุณ การผสานรวม MediaBrowser รายการภายนอก Android Auto และ Android Automotive OS เช่น หากปกติแล้วคุณแสดงรายการรูทที่เล่นได้ คุณอาจต้องการซ้อนรายการนั้น ภายใต้รายการที่เรียกดูได้ระดับรูทแทนเนื่องจากค่าของแฟล็กที่รองรับ คำใบ้

นอกจากคำแนะนำรูทแล้ว ยังมีหลักเกณฑ์เพิ่มเติมอีก 2-3 ข้อที่ควรทำตาม เพื่อช่วยให้แน่ใจว่าแท็บจะแสดงผลอย่างเหมาะสม

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

แสดงอาร์ตเวิร์กสื่อ

อาร์ตเวิร์กสำหรับรายการสื่อต้องส่งเป็น URI ในเครื่องโดยใช้ ContentResolver.SCHEME_CONTENT หรือ ContentResolver.SCHEME_ANDROID_RESOURCE URI ภายในนี้ต้องแปลค่าเป็นบิตแมปหรือเวกเตอร์ที่ถอนออกได้ใน แหล่งข้อมูลของแอปพลิเคชัน สำหรับ MediaDescriptionCompat ออบเจ็กต์ที่แสดงรายการใน ลำดับชั้นเนื้อหา แล้วส่ง URI ผ่าน setIconUri() สำหรับออบเจ็กต์ MediaMetadataCompat รายการที่แสดงถึงรายการที่กำลังเล่นอยู่ ให้ส่ง URI ผ่าน putString() โดยใช้คีย์ใดก็ได้ต่อไปนี้

ขั้นตอนต่อไปนี้อธิบายวิธีดาวน์โหลดอาร์ตเวิร์กจาก URI ของเว็บและแสดง ผ่าน URI ในเครื่อง ดูตัวอย่างที่สมบูรณ์มากขึ้นได้ที่ การใช้งาน ของ openFile() และวิธีการโดยรอบใน Universal Android Music แอปตัวอย่างโปรแกรมเล่น

  1. สร้าง URI content:// ที่สอดคล้องกับ URI ของเว็บ เบราว์เซอร์สื่อ บริการและสื่อจะส่ง URI เนื้อหานี้ไปยัง Android Auto และ Android Automotive OS

    Kotlin

    fun Uri.asAlbumArtContentURI(): Uri {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(this.getPath()) // Make sure you trust the URI
        .build()
    }
    

    Java

    public static Uri asAlbumArtContentURI(Uri webUri) {
      return new Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(webUri.getPath()) // Make sure you trust the URI!
        .build();
    }
    
  2. ในการใช้งาน ContentProvider.openFile() ให้ตรวจสอบว่าไฟล์ สำหรับ URI ที่เกี่ยวข้อง หากไม่มี ให้ดาวน์โหลดและแคชไฟล์ภาพ ข้อมูลโค้ดต่อไปนี้ใช้ Glide

    Kotlin

    override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
      val context = this.context ?: return null
      val file = File(context.cacheDir, uri.path)
      if (!file.exists()) {
        val remoteUri = Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.path)
            .build()
        val cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
    
        cacheFile.renameTo(file)
        file = cacheFile
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
    }
    

    Java

    @Nullable
    @Override
    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
        throws FileNotFoundException {
      Context context = this.getContext();
      File file = new File(context.getCacheDir(), uri.getPath());
      if (!file.exists()) {
        Uri remoteUri = new Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.getPath())
            .build();
        File cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    
        cacheFile.renameTo(file);
        file = cacheFile;
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    }
    

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับผู้ให้บริการเนื้อหา โปรดดูที่การสร้างเนื้อหา ผู้ให้ทุน

ใช้รูปแบบเนื้อหา

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

คุณใช้รูปแบบเนื้อหาต่อไปนี้ได้

ทีละรายการ

รูปแบบเนื้อหานี้ให้ความสำคัญกับชื่อและข้อมูลเมตามากกว่ารูปภาพ

รายการตารางกริด

รูปแบบเนื้อหานี้ให้ความสำคัญกับรูปภาพมากกว่าชื่อและข้อมูลเมตา

ตั้งค่ารูปแบบเนื้อหาเริ่มต้น

คุณสามารถตั้งค่าเริ่มต้นส่วนกลางสำหรับวิธีแสดงรายการสื่อของคุณโดยใส่ ค่าคงที่บางอย่างในแพ็กเกจพิเศษ BrowserRoot ของบริการ onGetRoot() Android Auto และ Android Automotive OS จะอ่านแพ็กเกจนี้และมองหา ค่าคงที่เหล่านั้นเพื่อระบุรูปแบบที่เหมาะสม

ส่วนเสริมต่อไปนี้สามารถใช้เป็นคีย์ในแพ็กเกจได้

  • DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE: ระบุคำแนะนำในการนำเสนอสำหรับรายการที่เรียกดูได้ทั้งหมดภายในโครงสร้างการเรียกดู
  • DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE: บ่งชี้ถึงคำแนะนำการนำเสนอสำหรับรายการที่เล่นได้ทั้งหมดภายในโครงสร้างการเรียกดู

คีย์สามารถจับคู่กับค่าคงที่ที่เป็นจำนวนเต็มต่อไปนี้เพื่อกำหนด การนำเสนอรายการเหล่านั้น

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM: ระบบจะแสดงรายการที่เกี่ยวข้องเป็นลิสต์รายการ
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM: ระบบจะแสดงรายการที่เกี่ยวข้องเป็นตารางกริด
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM: รายการที่เกี่ยวข้องจะแสดงเป็น "หมวดหมู่" รายการ สิ่งเหล่านี้คือ เหมือนกับรายการทั่วไป เว้นแต่ว่าจะมีการใช้ส่วนต่างกำไร the items' เนื่องจากไอคอนจะดูดีขึ้นเมื่อมีขนาดเล็ก ไอคอน ต้องเป็นเวกเตอร์ที่ถอนออกได้ คำแนะนำนี้ควรได้มีเพียง สำหรับรายการที่เรียกดูได้
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM: รายการที่เกี่ยวข้องจะแสดงเป็น "หมวดหมู่" รายการในตาราง สิ่งเหล่านี้คือ เหมือนรายการตารางกริดทั่วไป เว้นแต่ว่าจะมีการใช้ขอบ the items' เนื่องจากไอคอนจะดูดีขึ้นเมื่อมีขนาดเล็ก ไอคอน ต้องเป็นเวกเตอร์ที่ถอนออกได้ คำแนะนำนี้ควรได้มีเพียง สำหรับรายการที่เรียกดูได้

ข้อมูลโค้ดต่อไปนี้แสดงวิธีตั้งค่ารูปแบบเนื้อหาเริ่มต้นสำหรับ รายการที่เรียกดูได้ในตารางและรายการที่เล่นได้ลงในรายการ:

Kotlin

import androidx.media.utils.MediaConstants

@Nullable
override fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    return BrowserRoot(ROOT_ID, extras)
}

Java

import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    return new BrowserRoot(ROOT_ID, extras);
}

กำหนดรูปแบบเนื้อหาต่อรายการ

Content Style API ช่วยให้คุณสามารถลบล้างรูปแบบเนื้อหาเริ่มต้นได้ รายการสื่อย่อยที่เรียกดูได้ ตลอดจนรายการสื่อทั้งหมด

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

หากต้องการลบล้างค่าเริ่มต้นสำหรับรายการสื่อบางรายการด้วยตัวเอง ไม่ใช่สำหรับรายการนั้น เด็ก ให้สร้างกลุ่มรายการเพิ่มเติมไว้ใน MediaDescription ของรายการสื่อ และเพิ่มคำแนะนำด้วยคีย์ DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM ใช้ค่าเดียวกันกับที่อธิบายก่อนหน้านี้เพื่อระบุการนำเสนอของรายการนั้นๆ

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

Kotlin

import androidx.media.utils.MediaConstants

private fun createBrowsableMediaItem(
    mediaId: String,
    folderName: String,
    iconUri: Uri
): MediaBrowser.MediaItem {
    val mediaDescriptionBuilder = MediaDescription.Builder()
    mediaDescriptionBuilder.setMediaId(mediaId)
    mediaDescriptionBuilder.setTitle(folderName)
    mediaDescriptionBuilder.setIconUri(iconUri)
    val extras = Bundle()
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)
}

Java

import androidx.media.utils.MediaConstants;

private MediaBrowser.MediaItem createBrowsableMediaItem(
    String mediaId,
    String folderName,
    Uri iconUri) {
    MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
    mediaDescriptionBuilder.setMediaId(mediaId);
    mediaDescriptionBuilder.setTitle(folderName);
    mediaDescriptionBuilder.setIconUri(iconUri);
    Bundle extras = new Bundle();
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    mediaDescriptionBuilder.setExtras(extras);
    return new MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
}

จัดกลุ่มรายการโดยใช้คำแนะนำชื่อ

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

ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้าง MediaItem กับกลุ่มย่อย ส่วนหัวของ"Songs":

Kotlin

import androidx.media.utils.MediaConstants

private fun createMediaItem(
    mediaId: String,
    folderName: String,
    iconUri: Uri
): MediaBrowser.MediaItem {
    val mediaDescriptionBuilder = MediaDescription.Builder()
    mediaDescriptionBuilder.setMediaId(mediaId)
    mediaDescriptionBuilder.setTitle(folderName)
    mediaDescriptionBuilder.setIconUri(iconUri)
    val extras = Bundle()
    extras.putString(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
        "Songs")
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), /* playable or browsable flag*/)
}

Java

import androidx.media.utils.MediaConstants;

private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) {
   MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
   mediaDescriptionBuilder.setMediaId(mediaId);
   mediaDescriptionBuilder.setTitle(folderName);
   mediaDescriptionBuilder.setIconUri(iconUri);
   Bundle extras = new Bundle();
   extras.putString(
       MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
       "Songs");
   mediaDescriptionBuilder.setExtras(extras);
   return new MediaBrowser.MediaItem(
       mediaDescriptionBuilder.build(), /* playable or browsable flag*/);
}

แอปของคุณต้องส่งผ่านรายการสื่อทั้งหมดที่คุณต้องการจัดกลุ่มเข้าด้วยกันเป็น บล็อกที่ต่อเนื่องกัน ตัวอย่างเช่น สมมติว่าคุณต้องการแสดง 2 กลุ่ม ของรายการสื่อ, "เพลง" และ "อัลบั้ม" ตามลำดับดังกล่าว และแอปของคุณ รายการสื่อ 5 รายการตามลำดับต่อไปนี้

  1. รายการสื่อ A ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. รายการสื่อ B ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. รายการสื่อ C ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. รายการสื่อ D ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. รายการสื่อ E ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

เนื่องจากรายการสื่อสำหรับ "เพลง" กลุ่มและ "อัลบั้ม" ไม่เก็บกลุ่มไว้ บล็อกติดกัน, Android Auto และ Android Automotive OS ระบบจะตีความข้อมูลนี้เป็น 4 กลุ่มต่อไปนี้

  • กลุ่ม 1 ชื่อว่า "เพลง" ที่มีรายการสื่อ A
  • กลุ่ม 2 ชื่อ "อัลบั้ม" ที่มีรายการสื่อ B
  • กลุ่ม 3 ชื่อว่า "เพลง" ที่มีรายการสื่อ C และ D
  • กลุ่ม 4 ชื่อ "อัลบั้ม" ที่มีรายการสื่อ E

หากต้องการแสดงรายการเหล่านี้ใน 2 กลุ่ม แอปของคุณจะต้องส่งรายการสื่อใน ต่อไปนี้แทน:

  1. รายการสื่อ A ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. รายการสื่อ C ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. รายการสื่อ D ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. รายการสื่อ B ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. รายการสื่อ E ที่มี extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

แสดงตัวบ่งชี้ข้อมูลเมตาเพิ่มเติม

คุณสามารถระบุตัวบ่งชี้ข้อมูลเมตาเพิ่มเติมเพื่อแสดงข้อมูลโดยย่อ สำหรับเนื้อหาในโครงสร้างเบราว์เซอร์สื่อและระหว่างการเล่น ภายใน แผนผังเรียกดู, Android Auto และ Android Automotive OS อ่านข้อมูลเพิ่มเติมที่เกี่ยวข้อง โดยใช้รายการหนึ่ง และมองหาค่าคงที่เพื่อระบุว่าควรใช้ตัวบ่งชี้ใด จอแสดงผล ระหว่างการเล่นสื่อ Android Auto และ Android Automotive OS จะอ่าน สำหรับเซสชันสื่อและมองหาค่าคงที่บางอย่างเพื่อระบุ สัญญาณที่จะแสดง

รูปที่ 3 มุมมองการเล่นที่มีข้อมูลเมตาที่ระบุเพลง และศิลปิน รวมถึงไอคอนที่ระบุเนื้อหาที่อาจไม่เหมาะสม

รูปที่ 4 มุมมองแบบเรียกดูที่มีจุดสำหรับเนื้อหาที่ยังไม่ได้เล่นเปิดอยู่ รายการแรกและแถบความคืบหน้าสำหรับเนื้อหาที่มีการเล่นบางส่วนบน รายการที่ 2

ค่าคงที่ต่อไปนี้ใช้ได้ในทั้งส่วนเพิ่มเติมของคำอธิบาย MediaItem และ เพิ่มเติม MediaMetadata รายการ:

  • EXTRA_DOWNLOAD_STATUS: ระบุสถานะการดาวน์โหลดของรายการ ใช้ค่าคงที่นี้เป็นค่า คีย์; ค่าคงที่ยาวต่อไปนี้คือค่าที่เป็นไปได้
  • METADATA_KEY_IS_EXPLICIT: ระบุว่ารายการนั้นมีเนื้อหาที่อาจไม่เหมาะสมหรือไม่ ในการระบุว่ารายการ ให้ใช้ค่าคงที่นี้เป็นคีย์และ METADATA_VALUE_ATTRIBUTE_PRESENT เป็นค่า

ค่าคงที่ต่อไปนี้ใช้เฉพาะในคำอธิบายเพิ่มเติมของ MediaItem ได้

  • DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS: ระบุสถานะความสำเร็จของคอนเทนต์แบบยาว เช่น ตอนของพอดแคสต์หรือ หนังสือเสียง ใช้ค่าคงที่นี้เป็นกุญแจสำคัญ จำนวนเต็มต่อไปนี้ คงตัวเป็นค่าที่เป็นไปได้:
  • DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE: บอกถึงจำนวนความคืบหน้าในการเล่นเนื้อหาแบบยาวเป็นเลขทศนิยมอย่างละเอียด ตั้งแต่ 0.0 ถึง 1.0 ข้อมูลเพิ่มเติมนี้จะให้ข้อมูลเพิ่มเติมเกี่ยวกับ PARTIALLY_PLAYING ทำให้ Android Auto หรือ Android Automotive OS จะแสดงตัวบ่งชี้ความคืบหน้าที่มีความหมายมากขึ้น เช่น แถบความคืบหน้า หากคุณใช้ส่วนเสริมนี้ โปรดดูส่วนเกี่ยวกับ การอัปเดตแถบความคืบหน้าในมุมมองแบบเรียกดูขณะที่เนื้อหากำลังเล่น ในคู่มือนี้เพื่อเรียนรู้วิธีทำให้ตัวบ่งชี้นี้อัปเดตอยู่เสมอหลังจาก การแสดงผลครั้งแรก

เพื่อแสดงสัญญาณบอกสถานะที่ปรากฏเมื่อผู้ใช้เรียกดูสื่อ ต้นไม้ ให้สร้างชุดข้อมูลพิเศษที่รวมค่าคงที่เหล่านี้อย่างน้อย 1 ค่าและ ส่ง Bundle นั้นไปยังเมธอด MediaDescription.Builder.setExtras()

ข้อมูลโค้ดต่อไปนี้แสดงวิธีแสดงตัวบ่งชี้สำหรับสื่อที่อาจไม่เหมาะสม รายการที่เสร็จสมบูรณ์ 70%

Kotlin

import androidx.media.utils.MediaConstants

val extras = Bundle()
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED)
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)

Java

import androidx.media.utils.MediaConstants;

Bundle extras = new Bundle();
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);

หากต้องการแสดงสัญญาณบอกสถานะสำหรับรายการสื่อที่กำลังเล่นอยู่ คุณสามารถดำเนินการได้ดังนี้ ประกาศค่า Long สำหรับ METADATA_KEY_IS_EXPLICIT หรือ EXTRA_DOWNLOAD_STATUS ใน MediaMetadataCompat ของ mediaSession ไม่สามารถแสดง DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS หรือ สัญญาณบอกสถานะ DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE ในมุมมองการเล่น

ข้อมูลโค้ดต่อไปนี้แสดงวิธีระบุว่าเพลงปัจจุบันใน มุมมองการเล่นอาจไม่เหมาะสมและจะดาวน์โหลด

Kotlin

import androidx.media.utils.MediaConstants

mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build())

Java

import androidx.media.utils.MediaConstants;

mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build());

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

ดังที่กล่าวไว้ก่อนหน้านี้ คุณสามารถใช้ DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE เพิ่มเติมเพื่อแสดงแถบความคืบหน้าสำหรับเนื้อหาที่เล่นบางส่วนใน มุมมองแบบเรียกดู อย่างไรก็ตาม หากผู้ใช้เล่นเนื้อหาที่เล่นบางส่วนต่อ จาก Android Auto หรือ Android Automotive OS ตัวบ่งชี้นั้นจะ ไม่แม่นยำเมื่อเวลาผ่านไป

สำหรับ Android Auto และ Android Automotive OS เพื่อให้แถบความคืบหน้าอัปเดตอยู่เสมอ คุณสามารถให้ข้อมูลเพิ่มเติมใน MediaMetadataCompat และ PlaybackStateCompat เพื่อลิงก์เนื้อหาต่อเนื่อง รายการสื่อในมุมมองการเรียกดู ต้องเป็นไปตามข้อกำหนดต่อไปนี้สำหรับ รายการสื่อให้มีแถบความคืบหน้าการอัปเดตโดยอัตโนมัติ:

ข้อมูลโค้ดต่อไปนี้แสดงวิธีระบุว่ารายการที่กำลังเล่น ถูกเชื่อมโยงกับรายการในมุมมองการเรียกดู:

Kotlin

import androidx.media.utils.MediaConstants

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
val mediaItemExtras = Bundle()
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build())

val playbackStateExtras = Bundle()
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id")
mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build())

Java

import androidx.media.utils.MediaConstants;

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
Bundle mediaItemExtras = new Bundle();
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build();
return MediaBrowserCompat.MediaItem(description, /* flags */);

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build());

Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id");
mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build());

รูปที่ 5 มุมมองการเล่นที่มีตัวเลือก "ผลการค้นหา" สำหรับ ดูรายการสื่อที่เกี่ยวข้องกับการค้นหาด้วยเสียงของผู้ใช้

แอปสามารถให้ผลการค้นหาตามบริบทที่แสดงต่อผู้ใช้เมื่อ ผู้ใช้เริ่มใช้คำค้นหา รายการ Android Auto และ Android Automotive OS ผลลัพธ์เหล่านี้ผ่านอินเทอร์เฟซคำค้นหาหรือผ่านจำนวนเงินที่เปลี่ยนได้ สำหรับการค้นหาที่เกิดขึ้นก่อนหน้านี้ในเซสชัน ดูข้อมูลเพิ่มเติมได้ที่ ส่วนรองรับการสั่งงานด้วยเสียงในคู่มือนี้

หากต้องการแสดงผลการค้นหาที่เรียกดูได้ ให้ใส่แป้นคงที่ BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED ในแพ็กเกจเพิ่มเติมของ onGetRoot() ของบริการ การแมปกับ true แบบบูลีน

ข้อมูลโค้ดต่อไปนี้แสดงวิธีเปิดใช้การสนับสนุนใน onGetRoot() วิธีการ:

Kotlin

import androidx.media.utils.MediaConstants

@Nullable
fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true)
    return BrowserRoot(ROOT_ID, extras)
}

Java

import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
    return new BrowserRoot(ROOT_ID, extras);
}

หากต้องการเริ่มแสดงผลการค้นหา ให้ลบล้าง onSearch() ในบริการเบราว์เซอร์สื่อของคุณ Android Auto และ Android Automotive OS ส่งต่อข้อความค้นหาของผู้ใช้ไปยังวิธีการนี้เมื่อใดก็ตามที่ผู้ใช้ทำการค้นหา อินเทอร์เฟซคำค้นหาหรือ "ผลการค้นหา"

คุณสามารถจัดระเบียบการค้นหา ผลลัพธ์จากเมธอด onSearch() ของบริการที่ใช้ title items เพื่อให้ผู้ใช้เลือกดูได้ง่ายขึ้น ตัวอย่างเช่น หากแอปเปิดเพลง คุณอาจ จัดระเบียบผลการค้นหาตามอัลบั้ม ศิลปิน และเพลง

ข้อมูลโค้ดต่อไปนี้แสดงการใช้งาน onSearch() แบบง่ายๆ วิธีการ:

Kotlin

fun onSearch(query: String, extras: Bundle) {
  // Detach from results to unblock the caller (if a search is expensive).
  result.detach()
  object:AsyncTask() {
    internal var searchResponse:ArrayList
    internal var succeeded = false
    protected fun doInBackground(vararg params:Void):Void {
      searchResponse = ArrayList()
      if (doSearch(query, extras, searchResponse))
      {
        succeeded = true
      }
      return null
    }
    protected fun onPostExecute(param:Void) {
      if (succeeded)
      {
        // Sending an empty List informs the caller that there were no results.
        result.sendResult(searchResponse)
      }
      else
      {
        // This invokes onError() on the search callback.
        result.sendResult(null)
      }
      return null
    }
  }.execute()
}
// Populates resultsToFill with search results. Returns true on success or false on error.
private fun doSearch(
    query: String,
    extras: Bundle,
    resultsToFill: ArrayList
): Boolean {
  // Implement this method.
}

Java

@Override
public void onSearch(final String query, final Bundle extras,
                        Result<List<MediaItem>> result) {

  // Detach from results to unblock the caller (if a search is expensive).
  result.detach();

  new AsyncTask<Void, Void, Void>() {
    List<MediaItem> searchResponse;
    boolean succeeded = false;
    @Override
    protected Void doInBackground(Void... params) {
      searchResponse = new ArrayList<MediaItem>();
      if (doSearch(query, extras, searchResponse)) {
        succeeded = true;
      }
      return null;
    }

    @Override
    protected void onPostExecute(Void param) {
      if (succeeded) {
       // Sending an empty List informs the caller that there were no results.
       result.sendResult(searchResponse);
      } else {
        // This invokes onError() on the search callback.
        result.sendResult(null);
      }
    }
  }.execute()
}

/** Populates resultsToFill with search results. Returns true on success or false on error. */
private boolean doSearch(String query, Bundle extras, ArrayList<MediaItem> resultsToFill) {
    // Implement this method.
}

การดำเนินการเรียกดูที่กำหนดเอง

การดำเนินการเรียกดูที่กำหนดเอง 1 รายการ

รูปที่ 6 การเรียกดูที่กำหนดเองรายการเดียว

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

เมนูรายการเพิ่มเติมของการดำเนินการเรียกดูที่กำหนดเอง

รูปที่ 7 การดำเนินการเพิ่มเติมของการเรียกดูที่กำหนดเอง

หากมีการดำเนินการที่กำหนดเองมากกว่าที่ OEM อนุญาตให้แสดงได้ เมนูรายการเพิ่มเติมจะแสดงให้ผู้ใช้เห็น

วิธีการทำงาน

การทำงานเรียกดูที่กำหนดเองแต่ละรายการจะกำหนดด้วย:

  • รหัสการดำเนินการ (ตัวระบุสตริงที่ไม่ซ้ำกัน)
  • ป้ายกำกับการดำเนินการ (ข้อความที่แสดงต่อผู้ใช้)
  • URI ไอคอนการทำงาน (เวกเตอร์ที่ถอนออกได้ที่สามารถปรับสีได้)

คุณกำหนดรายการการดำเนินการเรียกดูที่กำหนดเองทั่วโลกโดยเป็นส่วนหนึ่งของ BrowseRoot จากนั้นคุณสามารถแนบชุดย่อยของการดำเนินการเหล่านี้กับ MediaItem.

เมื่อผู้ใช้โต้ตอบกับการดำเนินการเรียกดูที่กำหนดเอง แอปจะได้รับการติดต่อกลับ ใน onCustomAction() จากนั้นคุณสามารถจัดการการทำงานและอัปเดตรายการของ การดำเนินการสำหรับ MediaItem หากจำเป็น การดำเนินการนี้มีประโยชน์สำหรับการดำเนินการแบบเก็บสถานะ เช่น "รายการโปรด" และ "ดาวน์โหลด" สำหรับการดำเนินการที่ไม่ต้องอัปเดต เช่น "เปิด Radio" คุณไม่จำเป็นต้องอัปเดตรายการการดำเนินการ

การดำเนินการเรียกดูที่กำหนดเองในรูทของโหนดการเรียกดู

รูปที่ 8 แถบเครื่องมือการดำเนินการเรียกดูที่กำหนดเอง

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

วิธีใช้งานการดำเนินการเรียกดูที่กำหนดเอง

ขั้นตอนในการเพิ่มการดำเนินการเรียกดูที่กำหนดเองลงในโปรเจ็กต์มีดังนี้

  1. ลบล้าง 2 วิธีใน MediaBrowserServiceCompat การใช้งาน:
  2. วิธีแยกวิเคราะห์ขีดจำกัดการดำเนินการระหว่างรันไทม์
    • ใน onGetRoot() รับจำนวนการดำเนินการสูงสุดที่อนุญาตสำหรับแต่ละรายการ MediaItem โดยใช้คีย์ BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT ในBundle rootHints ขีดจำกัดเป็น 0 หมายความว่าฟีเจอร์นั้น ไม่รองรับระบบดังกล่าว
  3. สร้างรายการการดำเนินการเรียกดูที่กำหนดเองโดยรวมดังนี้
    • สำหรับการดำเนินการแต่ละรายการ ให้สร้างออบเจ็กต์ Bundle ด้วยคีย์ต่อไปนี้ * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID: รหัสการดำเนินการ * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL: ป้ายกำกับการดำเนินการ * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI: URI ไอคอนการทำงาน * เพิ่มออบเจ็กต์ Bundle การดำเนินการทั้งหมดลงในรายการ
  4. เพิ่มรายการส่วนกลางลงใน BrowseRoot:
  5. เพิ่มการดำเนินการลงในออบเจ็กต์ MediaItem ดังนี้
    • คุณเพิ่มการดำเนินการให้กับออบเจ็กต์ MediaItem แต่ละรายการได้โดยรวม รายการรหัสการดำเนินการในส่วนเพิ่มเติมของ MediaDescriptionCompat ที่ใช้คีย์ DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST รายการนี้ต้องเป็นชุดย่อยของการดำเนินการทั้งหมดที่คุณกำหนดใน BrowseRoot
  6. จัดการการดำเนินการและแสดงผลความคืบหน้าหรือผลลัพธ์
    • ใน onCustomAction ให้จัดการการดำเนินการตามรหัสการดำเนินการ ข้อมูลอื่นๆ ที่คุณต้องการ คุณสามารถรับรหัสของ MediaItem ที่ ทริกเกอร์การดำเนินการจากส่วนเสริมโดยใช้คีย์ EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID.
    • คุณสามารถอัปเดตรายการการดำเนินการสำหรับ MediaItem โดยใส่ แป้น EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM กำลังดำเนินการหรือกลุ่มผลลัพธ์

การเปลี่ยนแปลงที่คุณทำได้ในBrowserServiceCompatเพื่อเริ่มต้นใช้งานมีดังนี้ ด้วยการดำเนินการเรียกดูที่กำหนดเอง

ลบล้าง BrowserServiceCompat

คุณต้องลบล้างเมธอดต่อไปนี้ใน MediaBrowserServiceCompat

public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)

public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)

การแยกวิเคราะห์ขีดจำกัด

คุณควรตรวจสอบเพื่อดูจำนวนการดำเนินการเรียกดูที่กำหนดเองที่รองรับ

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
    rootHints.getInt(
            MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0)
}

สร้างการดำเนินการเรียกดูที่กำหนดเอง

การดำเนินการแต่ละรายการจะต้องรวมอยู่ใน Bundle แยกกัน

  • รหัสการดำเนินการ
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
    
  • ป้ายกำกับการทำงาน
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
    
  • URI ไอคอนการทำงาน
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")
    

เพิ่มการดำเนินการเรียกดูที่กำหนดเองลงใน Parceable ArrayList

เพิ่มออบเจ็กต์การดำเนินการเรียกดูที่กำหนดเอง Bundle ทั้งหมดลงใน ArrayList

private ArrayList<Bundle> createCustomActionsList(
                                        CustomBrowseAction browseActions) {
    ArrayList<Bundle> browseActionsBundle = new ArrayList<>();
    for (CustomBrowseAction browseAction : browseActions) {
        Bundle action = new Bundle();
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                browseAction.mId);
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                getString(browseAction.mLabelResId));
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                browseAction.mIcon);
        browseActionsBundle.add(action);
    }
    return browseActionsBundle;
}

เพิ่มรายการการดำเนินการเรียกดูที่กำหนดเองลงในรูทการเรียกดู

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {
    Bundle browserRootExtras = new Bundle();
    browserRootExtras.putParcelableArrayList(
            BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST,
            createCustomActionsList()));
    mRoot = new BrowserRoot(ROOT_ID, browserRootExtras);
    return mRoot;
}

เพิ่มการดำเนินการไปยัง MediaItem

MediaDescriptionCompat buildDescription (long id, String title, String subtitle,
                String description, Uri iconUri, Uri mediaUri,
                ArrayList<String> browseActionIds) {

    MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
    bob.setMediaId(id);
    bob.setTitle(title);
    bob.setSubtitle(subtitle);
    bob.setDescription(description);
    bob.setIconUri(iconUri);
    bob.setMediaUri(mediaUri);

    Bundle extras = new Bundle();
    extras.putStringArrayList(
          DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST,
          browseActionIds);

    bob.setExtras(extras);
    return bob.build();
}
MediaItem mediaItem = new MediaItem(buildDescription(...), flags);

สร้างผลลัพธ์ onCustomAction รายการ

  • แยกวิเคราะห์รหัสสื่อจาก Bundle extras: วันที่
    @Override
    public void onCustomAction(
              @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){
      String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID);
    }
    
  • สำหรับผลลัพธ์แบบอะซิงโครนัส ให้ปลดผลลัพธ์ออก result.detach()
  • กลุ่มผลการค้นหา
    • ข้อความถึงผู้ใช้
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
    • อัปเดตรายการ(ใช้เพื่ออัปเดตการดำเนินการในรายการ)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
      
    • เปิดมุมมองการเล่น
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
      
    • อัปเดตการเรียกดูโหนด
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  • หากเกิดข้อผิดพลาด โปรดโทรติดต่อ result.sendError(resultBundle).
  • หากมีการอัปเดตความคืบหน้า โปรดโทรติดต่อ result.sendProgressUpdate(resultBundle)
  • ให้เสร็จสิ้นโดยการโทรหา result.sendResult(resultBundle)

อัปเดตสถานะการดำเนินการ

โดยใช้เมธอด result.sendProgressUpdate(resultBundle) กับเมธอด EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM คุณสามารถอัปเดต MediaItem เพื่อแสดงสถานะใหม่ของการดำเนินการได้ ช่วงเวลานี้ ช่วยให้คุณแสดงความคิดเห็นแบบเรียลไทม์เกี่ยวกับความคืบหน้าและ ของการกระทำ

ตัวอย่าง: การดาวน์โหลด

ตัวอย่างวิธีใช้ฟีเจอร์นี้เพื่อใช้การดาวน์โหลด ซึ่งมี 3 สถานะ ได้แก่

  1. ดาวน์โหลด: นี่คือสถานะเริ่มต้นของการดำเนินการ เมื่อผู้ใช้เลือก การดำเนินการนี้ คุณสามารถเปลี่ยนการตั้งค่าเป็น "กำลังดาวน์โหลด" และโทร sendProgressUpdateเพื่ออัปเดต UI
  2. กำลังดาวน์โหลด: สถานะนี้บ่งบอกว่ากำลังดาวน์โหลดอยู่ คุณสามารถ ใช้สถานะนี้เพื่อแสดงแถบความคืบหน้าหรือสัญญาณบอกสถานะอื่นแก่ผู้ใช้
  3. ดาวน์โหลดแล้ว: สถานะนี้บ่งบอกว่าการดาวน์โหลดเสร็จสมบูรณ์ เมื่อ การดาวน์โหลดเสร็จสิ้นแล้ว คุณสามารถสลับปุ่ม "ดาวน์โหลด" พร้อม "ดาวน์โหลดแล้ว" และโทร sendResult ที่มี EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM เพื่อระบุว่าควรรีเฟรชรายการ นอกจากนี้ คุณยังสามารถใช้ เวลา EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE เพื่อแสดงข้อความดำเนินการสำเร็จแก่ผู้ใช้

วิธีนี้ช่วยให้คุณแสดงความคิดเห็นที่ชัดเจนแก่ผู้ใช้เกี่ยวกับการดาวน์โหลดได้ และสถานะปัจจุบันของกระบวนการ คุณเพิ่มรายละเอียดได้มากยิ่งขึ้นด้วยไอคอนที่จะแสดง สถานะการดาวน์โหลด 25%, 50%, 75%

ตัวอย่าง: การดำเนินการโปรด

อีกตัวอย่างหนึ่งคือการดำเนินการโปรดที่มี 2 สถานะ ดังนี้

  1. รายการโปรด: การดำเนินการนี้จะแสดงสำหรับรายการที่ไม่ได้อยู่ใน รายการโปรดของผู้ใช้ เมื่อผู้ใช้เลือกการดำเนินการนี้ คุณจะสลับการกระทำได้ กับ "รายการโปรด" และโทรหา sendResult ด้วย EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM เพื่ออัปเดต UI
  2. รายการโปรด: การดําเนินการนี้จะแสดงสำหรับรายการที่อยู่ใน รายการโปรด เมื่อผู้ใช้เลือกการดำเนินการนี้ คุณสามารถสลับกับ "รายการโปรด" และโทรหา sendResult ด้วย EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM เพื่ออัปเดต UI

แนวทางนี้จะให้วิธีการที่ชัดเจนและสอดคล้องกันสำหรับผู้ใช้ในการจัดการ รายการโปรด

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

หากต้องการดูตัวอย่างที่สมบูรณ์ของฟีเจอร์นี้ โปรดดู TestMediaApp โปรเจ็กต์ของคุณ

เปิดใช้ตัวควบคุมการเล่น

Android Auto และ Android Automotive OS ส่งคําสั่งการควบคุมการเล่นผ่าน MediaSessionCompat ของบริการ คุณต้องลงทะเบียนเซสชันและใช้วิธีการเรียกกลับที่เกี่ยวข้อง

ลงทะเบียนเซสชันสื่อ

ใน onCreate() ของบริการเบราว์เซอร์สื่อของคุณ ให้สร้าง MediaSessionCompat จากนั้นลงทะเบียนเซสชันสื่อโดยโทรไปที่ setSessionToken()

ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างและลงทะเบียนเซสชันสื่อ

Kotlin

override fun onCreate() {
    super.onCreate()
    ...
    // Start a new MediaSession.
    val session = MediaSessionCompat(this, "session tag").apply {
        // Set a callback object that implements MediaSession.Callback
        // to handle play control requests.
        setCallback(MyMediaSessionCallback())
    }
    sessionToken = session.sessionToken
    ...
}

Java

public void onCreate() {
    super.onCreate();
    ...
    // Start a new MediaSession.
    MediaSessionCompat session = new MediaSessionCompat(this, "session tag");
    setSessionToken(session.getSessionToken());

    // Set a callback object that implements MediaSession.Callback
    // to handle play control requests.
    session.setCallback(new MyMediaSessionCallback());
    ...
}

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

ใช้คำสั่งการเล่น

เมื่อผู้ใช้ขอให้เล่นรายการสื่อจากแอป Android Automotive ระบบปฏิบัติการและ Android Auto ใช้MediaSessionCompat.Callback จากแอปพลิเคชัน MediaSessionCompat ที่ได้รับจากบริการเบราว์เซอร์สื่อของแอป เมื่อผู้ใช้ ต้องการควบคุมการเล่นเนื้อหา เช่น การหยุดเล่นชั่วคราวหรือการข้ามไปยัง แทร็กถัดไป Android Auto และ Android Automotive OS เรียกใช้หนึ่งใน Callback ของออบเจ็กต์

แอปต้องขยาย MediaSessionCompat.Callback สมมติจึงจะจัดการการเล่นเนื้อหาได้ และนำวิธีการที่แอปของคุณรองรับไปใช้

ใช้วิธีการเรียกกลับทั้งหมดต่อไปนี้ตามความเหมาะสม ประเภทของเนื้อหาที่แอปของคุณนำเสนอ ได้แก่

onPrepare()
เรียกใช้เมื่อเปลี่ยนแหล่งที่มาของสื่อ Android Automotive OS ยังเรียกใช้ วิธีนี้ทันทีหลังจากเปิดเครื่อง แอปสื่อต้องใช้ฟีเจอร์นี้
onPlay()
เรียกใช้หากผู้ใช้เลือกเล่นโดยไม่เลือกรายการที่เฉพาะเจาะจง บัญชี แอปต้องเล่นเนื้อหาเริ่มต้น หรือหากหยุดเล่นชั่วคราวโดยมี onPause() กลับมาเล่นต่อ

หมายเหตุ: แอปไม่ควรเริ่มเปิดเพลงโดยอัตโนมัติ เมื่อ Android Automotive OS หรือ Android Auto เชื่อมต่อกับเบราว์เซอร์สื่อ service. สำหรับข้อมูลเพิ่มเติม โปรดดูหัวข้อเกี่ยวกับ การตั้งค่าสถานะการเล่นเริ่มต้น

onPlayFromMediaId()
เรียกใช้เมื่อผู้ใช้เลือกเล่นรายการที่เจาะจง ผ่านวิธีแล้ว รหัสที่บริการเบราว์เซอร์สื่อของคุณกำหนด ไปยังรายการสื่อในลำดับชั้นเนื้อหา
onPlayFromSearch()
เรียกใช้เมื่อผู้ใช้เลือกที่จะเล่นจากคำค้นหา แอปจะต้อง เลือกตัวเลือกที่เหมาะสมตามสตริงการค้นหาที่ส่งเข้ามา
onPause()
เรียกใช้เมื่อผู้ใช้เลือกหยุดเล่นชั่วคราว
onSkipToNext()
มีการเรียกใช้เมื่อผู้ใช้เลือกที่จะข้ามไปยังรายการถัดไป
onSkipToPrevious()
มีการเรียกใช้เมื่อผู้ใช้เลือกที่จะข้ามไปยังรายการก่อนหน้า
onStop()
เรียกใช้เมื่อผู้ใช้เลือกที่จะหยุดเล่น

ลบล้างวิธีการเหล่านี้ในแอปเพื่อให้มีฟังก์ชันการทำงานที่ต้องการ คุณ คุณไม่จำเป็นต้องใช้วิธีใดหากแอปของคุณไม่รองรับฟังก์ชันการทำงานของวิธีการดังกล่าว สำหรับ ตัวอย่างเช่น หากแอปเล่นสตรีมแบบสด เช่น การออกอากาศการแข่งขันกีฬา คุณ คุณไม่จำเป็นต้องใช้เมธอด onSkipToNext() คุณสามารถใช้ค่าเริ่มต้น การใช้ onSkipToNext() แทน

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเล่นเนื้อหาเสียง โปรดดู ภาพรวมของ MediaPlayer ภาพรวมแอปเสียง, และภาพรวม ExoPlayer

ตั้งค่าการทำงานมาตรฐานในการเล่น

การควบคุมการเล่นจอแสดงผล Android Auto และ Android Automotive OS ตาม การดำเนินการที่เปิดใช้ใน PlaybackStateCompat ออบเจ็กต์

โดยค่าเริ่มต้น แอปของคุณต้องรองรับการดำเนินการต่อไปนี้

แอปรองรับการดำเนินการต่อไปนี้เพิ่มเติมได้ด้วย หากเกี่ยวข้องกับ เนื้อหาของแอป

นอกจากนี้ คุณยังมีตัวเลือกในการสร้างคิวการเล่นที่สามารถแสดงผลสำหรับ ผู้ใช้ แต่ไม่จำเป็น ในการดำเนินการนี้ ให้เรียก setQueue() และ setQueueTitle() ให้เปิดใช้ ACTION_SKIP_TO_QUEUE_ITEM การทำงาน และกำหนด Callback onSkipToQueueItem()

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

ปุ่มแสดง Android Auto และ Android Automotive OS สำหรับการดำเนินการที่เปิดใช้แต่ละรายการ และคิวการเล่น เมื่อปุ่ม ระบบจะเรียกใช้ Callback ที่เกี่ยวข้องจาก MediaSessionCompat.Callback

จองพื้นที่ที่ไม่ได้ใช้

พื้นที่สงวนของ Android Auto และ Android Automotive OS ใน UI สําหรับ การดำเนินการ ACTION_SKIP_TO_PREVIOUS และ ACTION_SKIP_TO_NEXT รายการ หากแอปของคุณ ไม่รองรับฟังก์ชันเหล่านี้ การใช้ Android Auto และ Android Automotive OS พื้นที่ทำงานเพื่อแสดงการดำเนินการที่กำหนดเองที่คุณสร้างขึ้น

หากคุณไม่ต้องการเติมพื้นที่ทำงานเหล่านั้นด้วยการดำเนินการที่กำหนดเอง คุณสามารถจอง เพื่อให้ Android Auto และ Android Automotive OS เว้นช่องว่างไว้ เมื่อใดก็ตามที่แอปไม่รองรับฟังก์ชันที่เกี่ยวข้อง โดยโทร setExtras() ด้วยกลุ่มพิเศษที่มีค่าคงที่ที่สอดคล้องกับ ฟังก์ชันที่สงวนไว้ SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT สอดคล้องกับ ACTION_SKIP_TO_NEXT และ SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV สอดคล้องกับ ACTION_SKIP_TO_PREVIOUS ใช้ค่าคงที่เหล่านี้เป็นคีย์ใน และใช้บูลีน true สำหรับค่า

ตั้งค่า PlaybackState เริ่มต้น

เมื่อ Android Auto และ Android Automotive OS สื่อสารกับเบราว์เซอร์สื่อของคุณ เซสชันสื่อของคุณจะสื่อสารสถานะการเล่นเนื้อหาโดยใช้ PlaybackStateCompat แอปของคุณไม่ควรเริ่มเล่นเพลงโดยอัตโนมัติเมื่อ Android Automotive OS หรือ Android Auto เชื่อมต่อกับบริการเบราว์เซอร์สื่อของคุณ โปรดใช้ Android แทน Auto และ Android Automotive OS เพื่อกลับมาเล่นต่อหรือเริ่มเล่นตามการใช้งานของรถ สถานะหรือการดำเนินการของผู้ใช้

หากต้องการดำเนินการนี้ ให้ตั้งค่า PlaybackStateCompat เริ่มต้น ของเซสชันสื่อไปยัง STATE_STOPPED, STATE_PAUSED STATE_NONE หรือ STATE_ERROR

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

เพิ่มการทำงานที่กำหนดเองในการเล่น

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

ใช้การกระทำที่กำหนดเองเพื่อสร้างลักษณะการทำงานที่แตกต่างจากมาตรฐาน การดำเนินการ อย่าใช้เพื่อแทนที่หรือทำสำเนามาตรฐาน การดำเนินการ

คุณเพิ่มการดำเนินการที่กำหนดเองได้โดยใช้addCustomAction() ในPlaybackStateCompat.Builder

ข้อมูลโค้ดต่อไปนี้แสดงวิธีเพิ่ม "เริ่มช่องวิทยุ" ที่กำหนดเอง การดำเนินการ:

Kotlin

stateBuilder.addCustomAction(
    PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon
    ).run {
        setExtras(customActionExtras)
        build()
    }
)

Java

stateBuilder.addCustomAction(
    new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon)
    .setExtras(customActionExtras)
    .build());

ดูตัวอย่างโดยละเอียดของวิธีการนี้ได้ที่ setCustomAction() ในแอปตัวอย่างของ Universal Android Music Player บน GitHub

หลังจากสร้างการกระทำที่กำหนดเองแล้ว เซสชันสื่อจะตอบสนองต่อการดำเนินการได้ โดยการลบล้าง onCustomAction()

ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีที่แอปของคุณอาจตอบสนองต่อ การดำเนินการ "เริ่มช่องวิทยุ"

Kotlin

override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> {
            ...
        }
    }
}

Java

@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) {
        ...
    }
}

ดูตัวอย่างโดยละเอียดของวิธีการนี้ได้ที่ onCustomAction ในแอปตัวอย่างของ Universal Android Music Player บน GitHub

ไอคอนสำหรับการกระทำที่กำหนดเอง

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

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

ระบุรูปแบบไอคอนอื่นสำหรับการดำเนินการที่ปิดใช้

เมื่อการดำเนินการที่กำหนดเองไม่พร้อมใช้งานกับบริบทปัจจุบัน ให้สลับ ไอคอนการดำเนินการที่กำหนดเอง ซึ่งมีไอคอนสำรองที่แสดงว่าการดำเนินการดังกล่าว ปิดใช้อยู่

รูปที่ 6 ตัวอย่างไอคอนการดำเนินการแบบกำหนดเองนอกสไตล์

ระบุรูปแบบเสียง

หากต้องการระบุว่าสื่อที่กำลังเล่นอยู่ใช้รูปแบบเสียงพิเศษ คุณสามารถระบุไอคอนที่แสดงผลในรถยนต์ที่รองรับฟีเจอร์นี้ได้ คุณ สามารถตั้งค่า KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI และ KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI ในแพ็กเกจเพิ่มเติมของรายการสื่อที่เล่นอยู่ (ส่งไปยัง MediaSession.setMetadata()) อย่าลืมตั้งค่าทั้งคู่ เพื่อให้เหมาะกับเลย์เอาต์แบบต่างๆ

นอกจากนี้ คุณยังสามารถตั้งค่า KEY_IMMERSIVE_AUDIO เพิ่มเติม เพื่อบอก OEM รถยนต์ว่านี่เป็นเสียงที่สมจริง และพวกเขาควรระมัดระวังให้มาก เมื่อตัดสินใจว่าจะใช้เอฟเฟกต์เสียงที่อาจรบกวน เนื้อหาแบบสมจริง

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

หากต้องการเพิ่มลิงก์ ให้กำหนดค่า ข้อมูลเมตา KEY_SUBTITLE_LINK_MEDIA_ID (เพื่อลิงก์จากคำบรรยาย) หรือ KEY_DESCRIPTION_LINK_MEDIA_ID (เพื่อลิงก์จาก คำอธิบาย) โปรดดูรายละเอียดในเอกสารอ้างอิงสําหรับ ช่องข้อมูลเมตา

รองรับการสั่งงานด้วยเสียง

แอปสื่อต้องรองรับการสั่งงานด้วยเสียงเพื่อช่วยให้ผู้ขับขี่ปลอดภัย และประสบการณ์การใช้งานแสนสะดวก ที่ช่วยลดสิ่งรบกวนสมาธิ ตัวอย่างเช่น หากแอปของคุณ กำลังเล่นรายการสื่อ 1 รายการ ผู้ใช้สามารถพูดว่า "เปิด [ชื่อเพลง]" เพื่อบอกแอปของคุณให้เล่นเพลงอื่นโดยไม่ต้องดูหรือแตะเพลง จอแสดงผล ผู้ใช้สามารถเริ่มการค้นหาได้โดยคลิกปุ่มที่เหมาะสม พวงมาลัยหรือพูดคำสั่งให้ดำเนินการ "OK Google"

เมื่อ Android Auto หรือ Android Automotive OS ตรวจพบและตีความเสียง การดำเนินการดังกล่าว คือการสั่งงานด้วยเสียงผ่าน onPlayFromSearch() เมื่อได้รับการติดต่อกลับนี้ แอปพบเนื้อหาที่ตรงกับ query และเริ่มเล่น

ผู้ใช้สามารถระบุหมวดหมู่ของคำต่างๆ ในการค้นหา ได้แก่ แนวเพลง ศิลปิน อัลบั้ม ชื่อเพลง สถานีวิทยุ หรือเพลย์ลิสต์ และอื่นๆ เมื่อสร้าง รองรับการค้นหา รวมถึงหมวดหมู่ทั้งหมดที่เหมาะกับแอปของคุณ หาก Android Auto หรือ Android Automotive OS ตรวจพบว่าคําค้นหาที่ระบุตรงกับ ในบางหมวดหมู่ ตำแหน่งนี้จะต่อท้ายในพารามิเตอร์ extras สามารถส่งเพิ่มเติมต่อไปนี้:

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

หากดำเนินการค้นหาไม่ได้อย่างรวดเร็ว โปรดอย่าบล็อกใน onPlayFromSearch() แต่ให้ตั้งสถานะการเล่นเป็น STATE_CONNECTING แทน และค้นหาชุดข้อความอะซิงโครนัส

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

นอกเหนือจาก "play" การค้นหา, Android Auto และ Android Automotive OS จดจำคำสั่งเสียงเพื่อควบคุมการเล่น เช่น "หยุดเพลงชั่วคราว" และ "ถัดไป "เพลง" และจับคู่คำสั่งเหล่านี้กับ Callback ของเซสชันสื่อที่เหมาะสม เช่น onPause() และ onSkipToNext()

สำหรับตัวอย่างโดยละเอียดเกี่ยวกับวิธีใช้การเล่นเสียงที่สามารถเล่นได้ใน ดู Google Assistant และแอปสื่อ

ใช้การป้องกันสิ่งรบกวน

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

ลดเสียงสัญญาณเตือนในรถ

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

เพื่อให้เป็นไปตามข้อกำหนดนี้ แอปของคุณ สามารถใช้ CarConnection เป็นสัญญาณก่อนเล่นเสียงใดๆ แอปจะตรวจสอบได้ว่าโทรศัพท์ ฉายภาพไปยังหน้าจอรถโดยสังเกต LiveData สำหรับการเชื่อมต่อรถ ประเภท และตรวจสอบว่าค่าเท่ากับ CONNECTION_TYPE_PROJECTION

ถ้าโทรศัพท์ของผู้ใช้กำลังฉายภาพ แอปสื่อที่รองรับการปลุกจะต้องแสดง ดังนี้

  • ปิดการปลุก
  • เล่นการปลุกเวลา STREAM_ALARM และแสดง UI ในหน้าจอโทรศัพท์เพื่อปิดใช้การปลุก

จัดการโฆษณาสื่อ

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

Kotlin

import androidx.media.utils.MediaConstants

override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) {
    MediaMetadataCompat.Builder().apply {
        if (isAd(mediaId)) {
            putLong(
                MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
                MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        }
        // ...add any other properties you normally would.
        mediaSession.setMetadata(build())
    }
}

Java

import androidx.media.utils.MediaConstants;

@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
    MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
    if (isAd(mediaId)) {
        builder.putLong(
            MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
    }
    // ...add any other properties you normally would.
    mediaSession.setMetadata(builder.build());
}

จัดการกับข้อผิดพลาดทั่วไป

เมื่อแอปพบข้อผิดพลาด ให้ตั้งค่าสถานะการเล่นเป็น STATE_ERROR และแสดงข้อความแสดงข้อผิดพลาดโดยใช้ setErrorMessage() โปรดดู PlaybackStateCompat สำหรับรายการรหัสข้อผิดพลาดที่คุณใช้เมื่อตั้งค่าข้อความแสดงข้อผิดพลาดได้ ข้อความแสดงข้อผิดพลาดจะต้องแสดงต่อผู้ใช้และแปลเป็นภาษาปัจจุบันของผู้ใช้ ภาษา Android Auto และ Android Automotive OS จะแสดงข้อผิดพลาดได้ ข้อความถึงผู้ใช้

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

Kotlin

mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build())

Java

mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build());

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสถานะข้อผิดพลาด โปรดดูการใช้เซสชันสื่อ: สถานะ และข้อผิดพลาด

หากผู้ใช้ Android Auto ต้องการเปิดแอปโทรศัพท์เพื่อแก้ไขข้อผิดพลาด ให้ทำดังนี้ ให้ข้อมูลนั้นแก่ผู้ใช้ในข้อความ เช่น ข้อผิดพลาด อาจแสดงข้อความว่า "ลงชื่อเข้าใช้ [ชื่อแอปของคุณ]" แทน "โปรดลงชื่อเข้าใช้"

ทรัพยากรอื่นๆ