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

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

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

คู่มือนี้ถือว่าคุณมีแอปสื่อที่เล่นเสียงในโทรศัพท์อยู่แล้ว และแอปสื่อของคุณเป็นไปตามสถาปัตยกรรมแอปสื่อของ Android Car App Library ช่วยให้คุณแทนที่ประสบการณ์ในแอปด้วย เทมเพลตแทนที่จะใช้เทมเพลตที่สร้างขึ้นโดยใช้โครงสร้างข้อมูลสร้างแอปสื่อสำหรับรถยนต์ 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 เดียวสำหรับ Car App Library และแอปเวอร์ชัน MediaBrowserService หรือ MediaLibraryService คุณต้องตั้งค่า android.software.car.templates_host.media เป็น android:required="false"

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

จากนั้นทำตามหลักเกณฑ์ของ Car App Library สำหรับ 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) และส่งโทเค็นความเข้ากันได้นี้ ไปยังไลบรารีแอปสำหรับรถยนต์ใน .registerMediaPlaybackToken()

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

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

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

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

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

รูปที่ 3: อาร์เรย์ SectionedItemTemplate ที่มี Chips, Condensed Items, ออบเจ็กต์ Interactive Header, Grid Items และ Minimized Control Panel

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

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

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

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

หากใช้ SectionedItemTemplate คุณจะทำได้โดยการเพิ่มปุ่มการดำเนินการที่นำทางคุณไปยังหน้าจอการเล่นสื่อ ใช้Action.MEDIA_PLAYBACKการดำเนินการมาตรฐานของ Car App Library แอปสื่อจะแสดงการดำเนินการนี้เป็นแผงควบคุมที่ย่อเล็กสุด ซึ่งจำเป็นต่อการปฏิบัติตามข้อกำหนดด้านคุณภาพ 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 หรือ Trampoline 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();
    }
}