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

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

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

คู่มือนี้จะถือว่าคุณมีแอปสื่อที่เล่นเสียงในโทรศัพท์อยู่แล้ว และแอปสื่อของคุณเป็นไปตามสถาปัตยกรรมแอปสื่อของ 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>

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

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

ประกาศการรองรับ 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 ซึ่งควรมีลักษณะดังนี้

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

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

คุณสามารถเผยแพร่แอปสื่อที่เปิดใช้คลังแอปในรถใน Android Automotive OS ได้ 2 วิธี ได้แก่ เป็น APK เดียวหรือเป็น APK 2 รายการแยกกัน หากเผยแพร่ APK เดียว แอปจะรองรับรถยนต์ที่เปิดใช้ Android Automotive OS ที่มีโฮสต์คลังแอปในรถ และจะกลับไปใช้แอปพลิเคชัน MediaBrowserService หรือ MediaLibraryService หากไม่รองรับ แม้แต่ใน Android เวอร์ชันเก่า (Android 10 - Android 13) หากเลือกเผยแพร่ APK 2 รายการแยกกัน คุณจะอัปเดตการเพิ่มใหม่ในคลังแอปในรถเวอร์ชันใหม่ได้ง่ายขึ้นโดยไม่ต้องกังวลว่าจะส่งผลต่อ 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 ที่มีคลังแอปในรถและ MediaBrowserService หรือ MediaLibraryService ควรเปิดใช้ด้วยรหัสเวอร์ชันที่สูงกว่าและ minSdk ที่กำหนดเป้าหมายเป็น Android 14 (34)

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

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

เมื่อสร้าง APK แยกต่างหากสำหรับคลังแอปในรถเวอร์ชันของแอป คุณต้องตั้งค่า 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 ร่วมกันเพื่อสร้างเลย์เอาต์ที่ผสมรายการรูปภาพ และรายการข้อความ

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

รูปที่ 2: 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

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

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

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

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

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

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

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

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

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

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

เพิ่มการดำเนินการ androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK ลงใน intent-filter ของคอมโพเนนต์คลังแอปในรถ (ไม่ว่าจะเป็น 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() ส่งการดำเนินการนี้ไปยัง Intent การสร้าง 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();
    }
}