สร้างแอปสื่อที่ใช้เทมเพลต

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

แอปสื่อที่ใช้เทมเพลต Car App Library สามารถปรับแต่งประสบการณ์การเรียกดูและการเล่นสื่อได้ พร้อมทั้งมั่นใจได้ว่าประสบการณ์การใช้งานจะได้รับการเพิ่มประสิทธิภาพสำหรับหน้าจอรถยนต์และลดสิ่งรบกวนขณะขับรถ

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

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

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

ประกาศการรองรับหมวดหมู่ในไฟล์ Manifest

แอปของคุณต้องประกาศandroidx.car.app.category.MEDIA หมวดหมู่แอปในรถยนต์ในตัวกรอง Intent ของ CarAppService

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.MEDIA"/>
      </intent-filter>
    </service>
    ...
<application>

หากต้องการเข้าถึง MediaPlaybackTemplate แอปของคุณต้องประกาศสิทธิ์ androidx.car.app.MEDIA_TEMPLATES ในไฟล์ Manifest ด้วย

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.MEDIA_TEMPLATES"/>
  ...
</manifest>

ตั้งค่าระดับ API ขั้นต่ำของแอปในรถยนต์

แอปสื่อที่ใช้ MediaPlaybackTemplate รองรับเฉพาะใน CAL API 8 ขึ้นไป โปรดตรวจสอบว่าได้ตั้งค่า Car App API level ขั้นต่ำเป็น 8

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

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

อย่าลืมเพิ่มไอคอนการระบุแหล่งที่มาสำหรับแอปสื่อที่สร้างโดยใช้ Car App Library

ประกาศการรองรับ Android Auto

ตรวจสอบว่าไฟล์ Manifest ของแอปมีข้อมูลต่อไปนี้

<application>
  ...
  <meta-data android:name="com.google.android.gms.car.application"
      android:resource="@xml/automotive_app_desc"/>
  ...
</application>

จากนั้นเพิ่มการประกาศเทมเพลตไปยัง automotive_app_desc.xml ใน xml resources โดยควรมีลักษณะดังนี้

<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
 <uses name="media"/>
 <uses name="template"/>
</automotiveApp>

ประกาศการรองรับ Android Automotive OS

คุณเผยแพร่แอปสื่อที่เปิดใช้ Car App Library ใน Android Automotive OS ได้ 2 วิธี ได้แก่ เป็น APK เดียวหรือเป็น APK 2 รายการแยกกัน หากคุณ จัดจำหน่าย APK เดียว APK นั้นจะรองรับยานพาหนะที่เปิดใช้ Android Automotive OS ด้วยโฮสต์ไลบรารีแอปในรถยนต์ และจะกลับไปใช้แอปพลิเคชัน MediaBrowserService หรือ MediaLibraryService หากไม่รองรับ แม้แต่สำหรับ Android เวอร์ชันเก่ากว่า (Android 10 - Android 13) หากเลือกที่จะจัดจำหน่าย APK แยกกัน 2 รายการ คุณจะอัปเดตฟีเจอร์ใหม่ๆ ใน Car App Library เวอร์ชันได้ง่ายขึ้นโดยไม่ต้องกังวลว่าจะส่งผลต่อแอปเวอร์ชัน MediaBrowserService หรือ MediaLibraryService

การจัดจำหน่าย APK เดียว

เมื่อเผยแพร่ APK เดียวสำหรับไลบรารีแอปในรถยนต์และแอปเวอร์ชัน MediaBrowserService หรือ MediaLibraryService คุณต้องตั้งค่า "" เป็น android:required="false"

<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>

จากนั้นทําตามหลักเกณฑ์ของไลบรารีแอปในรถยนต์สําหรับ AAOS และ แนะนํา CarAppActivity ที่เปิดใช้ได้ (หรือกิจกรรมแทรมโพลีน) คุณต้องตั้งค่า กิจกรรมเป็น android:enabled="false" ในไฟล์ Manifest จากนั้นเพิ่มแท็กข้อมูลเมตา ลงในประกาศMediaBrowserServiceที่ระบุCarAppActivity คอมโพเนนต์เป็นการแทนที่ ดูตัวอย่างไฟล์ Manifest ด้านล่าง

<service android:name=".media.MyMediaService"
    android:exported="true"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
    </intent-filter>

    <!-- Link to Car App Library Activity -->
    <meta-data
        android:name="androidx.car.app.media.CalMediaActivityComponent" 
        android:value="com.example.mediaapp.LaunchableTrampoline"/>
</service>

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false"> <!-- Set to false -->

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

การเผยแพร่ใน Play

APK ที่มี Car App Library และ MediaBrowserService หรือ MediaLibraryService ควรเปิดใช้ด้วยรหัสเวอร์ชันที่สูงกว่าและ minSdk ที่กำหนดเป้าหมายเป็น Android 14 (34)

การเผยแพร่ด้วย APK 2 รายการ

หากต้องการเผยแพร่ APK 2 รายการแยกกัน โดยรายการหนึ่งใช้ Car App Library และอีกรายการใช้ MediaBrowserService หรือ MediaLibraryService ให้ทำตามขั้นตอนต่อไปนี้เพื่อให้แน่ใจว่าระบบกำหนดเป้าหมายความสามารถของรถยนต์ที่ถูกต้องอย่างถูกต้อง

เมื่อสร้าง APK แยกต่างหากสำหรับแอปเวอร์ชัน Car App Library คุณต้องตั้งค่า android.software.car.templates_host.media เป็น android:required=true วิธีนี้ช่วยให้มั่นใจได้ว่าแอปจะเผยแพร่เฉพาะในบิลด์ Android Automotive OS ที่ได้รับการรับรองว่ารองรับโฮสต์ไลบรารีแอปในรถยนต์

<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>

นอกเหนือจากการใช้ android.software.car.templates_host.media และตั้งค่าเป็น android:required=true ด้านบนแล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อเปิดใช้ Android Automotive OS สำหรับกิจกรรมไลบรารีแอปในรถยนต์ที่เปิดใช้ได้

การเผยแพร่ใน Play

ควรเผยแพร่ APK ที่ใช้ไลบรารีแอปในรถยนต์ในแทร็กเฉพาะของ Automotive OS

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

เปิดใช้เสียงในแอปเพื่อให้ผู้ใช้ดำเนินการทั่วไปแบบแฮนด์ฟรีได้ ดูวิธีการติดตั้งใช้งานโดยละเอียดเพิ่มเติมได้ที่รองรับการสั่งงานด้วยเสียงสำหรับสื่อ หากใช้แอปสื่อที่ใช้เทมเพลตและได้รับคำสั่งเสียง คุณไม่จำเป็นต้องอัปเดต MediaBrowserService หรือ MediaLibraryService ด้วยผลการค้นหา แต่คุณอาจเพิ่มการดำเนินการในเทมเพลตการเล่นสื่อ เพื่อให้ผู้ใช้ค้นหาเนื้อหาเพิ่มเติมตามการเล่นหรือคำค้นหา นั้นได้ การรองรับคำสั่งเสียงเป็นข้อกำหนดในการปฏิบัติตามหลักเกณฑ์ด้านคุณภาพของ VC-1

สร้างเทมเพลตการเล่น

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

มิวสิกเพลเยอร์แสดงเพลง Sounds of Spring โดย Summer Fielding พร้อมภาพบุคคลสี่เหลี่ยม
     ของหญิงสาวที่เล่นกีตาร์

รูปที่ 1: MediaPlaybackTemplate พร้อมการดำเนินการส่วนหัวเพื่อเปิดคิว ที่ด้านบน

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

val playbackTemplate = MediaPlaybackTemplate.Builder()
      .setHeader(
        Header.Builder()
          .setStartHeaderAction(Action.BACK)
          .addEndHeaderAction(
                Action.Builder()
                  .setTitle(model.context.getString(R.string.queue_button_title))
                  .setIcon(
                    CarIcon.Builder(
                        IconCompat.createWithResource(
                          model.context,
                          R.drawable.gs_queue_music_vd_theme_24,
                        ))
                      .build())
                  .setOnClickListener(showQueueScreen())
                  .build())
          .setTitle(model.context.getString(R.string.media_playback_view_title))
          .build())
      .build()

เมื่อใช้ MediaPlaybackTemplate ให้ลงทะเบียนโทเค็น MediaSession โดยใช้ MediaPlaybackManager ใน CarAppService หากไม่ดำเนินการดังกล่าว ระบบจะแสดงข้อผิดพลาดเมื่อส่ง MediaPlaybackTemplate ไปยังโฮสต์

import androidx.car.app.media.MediaPlaybackManager


override fun onCreateSession(sessionInfo: SessionInfo): Session {
    return object : Session() {
        

        init {
          lifecycle.addObserver(
            LifecycleEventObserver { _, event ->
              if (event == ON_CREATE) {
                val token = ... // MediaSessionCompat.Token
                (carContext.getCarService(CarContext.MEDIA_PLAYBACK_SERVICE) as MediaPlaybackManager)
                  .registerMediaPlaybackToken(token)
              }
              ...
            }
          )
        }
    }
}

.registerMediaPlaybackToken จำเป็นสำหรับการแสดงข้อมูลและการควบคุมการเล่นสื่อต่อ Android Auto นอกจากนี้ยังมีความสำคัญต่อโฮสต์ในการ สร้างการแจ้งเตือนเฉพาะสื่อด้วย

สำหรับแอปที่ใช้ไลบรารี Media3 ซึ่งใช้ PlatformToken แทน MediaSessionCompat.Token มาตรฐาน คุณจะต้องติดตั้งใช้งาน SessionCommand ที่กำหนดเองใน MediaLibrarySession.Callback ซึ่งจะแสดงผลโทเค็นแพลตฟอร์มพื้นฐานของเซสชัน: session.platformToken ใน CarAppService ให้ส่งคำสั่งที่กำหนดเองนี้ไปยังเซสชัน เมื่อได้รับโทเค็นแพลตฟอร์มแล้ว ให้แปลงโทเค็นโดยใช้ MediaSessionCompat.Token.fromToken(platformToken) แล้วส่งโทเค็นความเข้ากันได้นี้ ไปยัง Car App Library ใน .registerMediaPlaybackToken()

จัดระเบียบสื่อโดยใช้เทมเพลต

หากต้องการจัดระเบียบสื่อเพื่อการเรียกดู เช่น เพลงหรืออัลบั้ม เราขอแนะนำให้ใช้ SectionedItemTemplate ซึ่งช่วยให้คุณใช้ GridSection และ RowSection ร่วมกันเพื่อสร้างเลย์เอาต์ที่ผสมรายการรูปภาพ และรายการข้อความ

อินเทอร์เฟซแอปเพลงแสดงเพลงและอัลบั้มที่เล่นล่าสุด รวมถึงแถวแนวตั้ง 2 แถวและภาพปกอัลบั้มแนวนอน 3 ภาพ

รูปที่ 2: A SectionedItemTemplate ที่มี RowSection ตามด้วย GridSection

การใช้ SectionedItemTemplate ภายใน TabTemplate

วิธีที่สะดวกในการจัดหมวดหมู่สื่อภายในแอปคือการใช้ SectionedItemTemplate ภายใน TabTemplate

val template =
      SectionedItemTemplate.Builder()...build();
val tabTemplate = 
      TabTemplate.Builder(tabCallback)
          .setTabContents(TabContents.Builder(template).build)
          .setHeaderAction(Action.APP_ICON)
          
          .build();

คอมโพเนนต์และฟีเจอร์ของไลบรารีแอปในรถยนต์ 1.9

Car App Library API เวอร์ชัน 1.9 มีการเปิดตัวคอมโพเนนต์ที่ปรับแต่งแล้ว เพื่อความสามารถในการเรียกดูที่ไม่เหมือนใคร เช่น ชิป แถบความคืบหน้า รายการแบบย่อ ส่วนหัวแบบอินเทอร์แอกทีฟและแบบขยาย ส่วนสปอตไลท์ และแบนเนอร์

อินเทอร์เฟซแอปเพลงแสดงเพลงและอัลบั้มที่เล่นล่าสุด รวมถึงแถวแนวตั้ง 2 แถวและภาพปกอัลบั้มแนวนอน 3 ภาพ

รูปที่ 3: A SectionedItemTemplate ที่มี Chips, Condensed Items, Interactive Header, Grid Items และ Minimized Control Panel

อินเทอร์เฟซแอปเพลงแสดงเพลงและอัลบั้มที่เล่นล่าสุด รวมถึงแถวแนวตั้ง 2 แถวและภาพปกอัลบั้มแนวนอน 3 ภาพ

รูปที่ 4: หน้าจอการเรียกดูสื่อ 2 หน้าจอที่มีExpanded Header Spotlight Sections และ Progress Bars

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

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

หากใช้ SectionedItemTemplate คุณสามารถทำได้โดยเพิ่มปุ่มการดำเนินการที่นำทางคุณไปยังหน้าจอการเล่นสื่อ ใช้การดำเนินการ ไลบรารีแอปในรถยนต์มาตรฐาน Action.MEDIA_PLAYBACK แอปสื่อจะแสดงการดำเนินการนี้เป็นแผงควบคุมที่ย่อเล็กสุด ซึ่งจำเป็นต่อการปฏิบัติตามข้อกำหนดด้านคุณภาพของ MFT-1 หากคุณใช้ Car App Library API 1.9 ขึ้นไป สำหรับเทมเพลตอื่นๆ การดำเนินการส่วนหัวเป็นอีกวิธีหนึ่ง ในการดำเนินการนี้

จัดการ Intent การเล่นสื่อของระบบ

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

เพิ่มandroidx.car.app.media.action.SHOW_MEDIA_PLAYBACKลงใน intent-filter ของคอมโพเนนต์ Car App Library (CarAppActivity หรือ Activity)

ตรวจสอบว่ากิจกรรมใช้ launchMode ของ singleTask หรือ singleTop เพื่อให้เรียกใช้ onNewIntent() ได้

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false">

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

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

@Override
public void onNewIntent(@NonNull Intent intent) {
    super.onNewIntent(intent);
    if (SHOW_MEDIA_PLAYBACK.equals(intent.getAction())) {
        ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
        // Avoid redundant navigation if already on the playing screen
        if (screenManager.getTop() instanceof MyMediaPlayScreen) {
            return;
        }
        screenManager.push(MyMediaPlayScreen.createScreenFromPlaying(
                getCarContext(), mMediaSessionController));
    }
}

หากคุณใช้กิจกรรมแทรมโพลีน ให้ตรวจสอบการดำเนินการผ่าน Intent ภายใน onCreate() ส่งการดำเนินการนี้ไปยังCarAppActivityเจตนาในการสร้างก่อน เรียกใช้ finish()

public class LaunchableTrampoline extends AppCompatActivity {
    private static final String SHOW_MEDIA_PLAYBACK = "androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent receivedIntent = getIntent();
        String action;

        if (SHOW_MEDIA_PLAYBACK.equals(receivedIntent.getAction())) {
            action = SHOW_MEDIA_PLAYBACK;
        } else {
            action = Intent.ACTION_MAIN;
        }

        Intent intent = new Intent(action);
        intent.setClassName(getPackageName(), "androidx.car.app.activity.CarAppActivity");
        startActivity(intent);
        finish();
    }
}