เปิดใช้แอป Cast ของ Android TV

1. ภาพรวม

โลโก้ Google Cast

Codelab นี้จะสอนวิธีแก้ไขแอป Android TV ที่มีอยู่เพื่อรองรับการแคสต์และการสื่อสารจากแอปผู้ส่งผู้ส่งที่มีอยู่

Google Cast และ Cast Connect คืออะไร

Google Cast ช่วยให้ผู้ใช้แคสต์เนื้อหาจากอุปกรณ์เคลื่อนที่ไปยัง TV ได้ เซสชัน Google Cast ทั่วไปประกอบด้วยคอมโพเนนต์ 2 อย่างคือผู้ส่งและแอปพลิเคชันตัวรับ แอปพลิเคชันผู้ส่ง เช่น แอปบนอุปกรณ์เคลื่อนที่หรือเว็บไซต์ เช่น YouTube.com เริ่มต้นและควบคุมการเล่นแอปพลิเคชันเครื่องรับ Cast แอปพลิเคชันรับการแคสต์คือแอป HTML 5 ที่ทํางานในอุปกรณ์ Chromecast และ Android TV

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

Cast Connect ต่อยอดมาจากโครงสร้างพื้นฐานนี้ และแอป Android TV ของคุณจะทําหน้าที่เป็นตัวรับ ไลบรารี Cast Connect ช่วยให้แอป Android TV รับข้อความและสถานะสื่อออกอากาศเสมือนว่าเป็นแอปพลิเคชันตัวรับการแคสต์

สิ่งที่เรากําลังจะสร้าง

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

สิ่งที่คุณจะได้เรียนรู้

  • วิธีเพิ่มไลบรารี Cast Connect ในแอป ATV ตัวอย่าง
  • วิธีเชื่อมต่อเครื่องส่งและเปิดแอป ATV
  • วิธีเริ่มการเล่นสื่อในแอป ATV จากแอป "ผู้ส่ง" ของ Cast
  • วิธีส่งสถานะสื่อจากแอป ATV ไปยังแอปผู้ส่ง Cast

สิ่งที่ต้องมี

2. รับโค้ดตัวอย่าง

คุณสามารถดาวน์โหลดตัวอย่างโค้ดทั้งหมดลงในคอมพิวเตอร์ของคุณได้...

และคลายไฟล์ ZIP ที่ดาวน์โหลด

3. เรียกใช้แอปตัวอย่าง

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

รูปภาพขนาดย่อของวิดีโอชุดหนึ่ง (ซึ่งมีการไฮไลต์อยู่) ซ้อนทับตัวอย่างแบบเต็มหน้าจอของวิดีโอ คําว่า "Cast Connect" จะปรากฏขึ้นที่ด้านขวาบน

ลงทะเบียนอุปกรณ์ของนักพัฒนาซอฟต์แวร์

หากต้องการเปิดใช้ความสามารถของ Cast Connect ในการพัฒนาแอปพลิเคชัน คุณต้องลงทะเบียนหมายเลขซีเรียลของ Chromecast ในตัวของอุปกรณ์ Android TV ที่คุณจะใช้ในแผงควบคุมสําหรับนักพัฒนาซอฟต์แวร์ Cast คุณดูหมายเลขซีเรียลได้โดยไปที่การตั้งค่า > ค่ากําหนดอุปกรณ์ > Chromecast Built-In > หมายเลขซีเรียลบน Android TV โปรดทราบว่าหมายเลขนี้แตกต่างจากหมายเลขซีเรียลของอุปกรณ์จริง และต้องมาจากวิธีการที่แจ้งไว้ข้างต้น

รูปภาพหน้าจอ Android TV แสดงหน้าจอ "Chromecast ในตัว" หมายเลขเวอร์ชัน และหมายเลขซีเรียล

หากไม่ลงทะเบียน Cast Connect จะทํางานเฉพาะแอปที่ติดตั้งจาก Google Play สโตร์ด้วยสาเหตุด้านความปลอดภัย หลังจากเริ่มขั้นตอนการลงทะเบียนไป 15 นาทีแล้ว ให้รีสตาร์ทอุปกรณ์

ติดตั้งแอปผู้ส่ง Android

เพื่อทดสอบการส่งคําขอจากอุปกรณ์เคลื่อนที่ เราได้ให้แอปพลิเคชันผู้ส่งแบบง่ายที่เรียกว่า "ส่งวิดีโอ" เป็นไฟล์ mobile-sender-0629.apk ในการดาวน์โหลดไฟล์ ZIP ของซอร์สโค้ด เราจะใช้ ADB ในการติดตั้ง APK หากคุณได้ติดตั้งวิดีโอแคสต์เวอร์ชันอื่นไว้แล้ว โปรดถอนการติดตั้งเวอร์ชันดังกล่าวจากโปรไฟล์ทั้งหมดที่อยู่ในอุปกรณ์ก่อนที่จะดําเนินการต่อ

  1. เปิดใช้ตัวเลือกสําหรับนักพัฒนาแอปและการแก้ปัญหา USB ในโทรศัพท์ Android
  2. เสียบสายข้อมูล USB เพื่อเชื่อมต่อโทรศัพท์ Android กับคอมพิวเตอร์เพื่อการพัฒนาซอฟต์แวร์
  3. ติดตั้ง mobile-sender-0629.apk ลงในโทรศัพท์ Android ของคุณ

รูปภาพของหน้าต่างเทอร์มินัลที่เรียกใช้คําสั่งติดตั้ง adb เพื่อติดตั้ง mobile-sender.apk

  1. หาแอปผู้ส่งแคสต์วิดีโอในโทรศัพท์ Android ได้ ไอคอนแอปผู้ส่งวิดีโอแคสต์

รูปภาพของแอปผู้ส่งวิดีโอแคสต์แสดงบนหน้าจอโทรศัพท์ Android

ติดตั้งแอป Android TV

วิธีการต่อไปนี้อธิบายวิธีเปิดและเรียกใช้แอปตัวอย่างที่สมบูรณ์ใน Android Studio

  1. เลือกนําเข้าโครงการบนหน้าจอต้อนรับหรือตัวเลือกเมนูไฟล์ > ใหม่ > นําเข้าโครงการ...
  2. เลือกไดเรกทอรี ไอคอนโฟลเดอร์app-done จากโฟลเดอร์โค้ดตัวอย่างแล้วคลิก OK
  3. คลิก File > โครงการซิงค์ของ Android App Studio พร้อมปุ่ม Gradle Sync Project with Gradle Files
  4. เปิดใช้ตัวเลือกสําหรับนักพัฒนาแอปและการแก้ปัญหา USB ในอุปกรณ์ Android TV
  5. ADB จะเชื่อมต่อกับอุปกรณ์ Android TV อุปกรณ์ควรแสดงใน Android Studio รูปภาพที่แสดงอุปกรณ์ Android TV ที่ปรากฏบนแถบเครื่องมือ Android Studio
  6. คลิกปุ่ม ปุ่ม Android Studio Run รูปสามเหลี่ยมสีเขียวชี้ไปทางขวาเรียกใช้ คุณจะเห็นแอป ATV ชื่อ Cast Connect Codelab ปรากฏขึ้นหลังจากผ่านไป 2-3 วินาที

เล่น Cast Connect ด้วยแอป ATV

  1. ไปที่หน้าจอหลักของ Android TV
  2. เปิดแอปส่งวิดีโอ Cast จากโทรศัพท์ Android คลิกปุ่ม "แคสต์" ไอคอนปุ่ม "แคสต์" และเลือกอุปกรณ์ ATV ของคุณ
  3. แอป Cast Connect Codelab ATV จะเปิดตัวใน ATV และปุ่ม "แคสต์" ในผู้ส่งจะระบุว่ามีการเชื่อมต่อ ไอคอนปุ่ม "แคสต์" ที่มีการสลับสี
  4. เลือกวิดีโอจากแอป ATV แล้ววิดีโอจะเริ่มเล่นใน ATV
  5. ในโทรศัพท์มือถือ ตัวควบคุมขนาดเล็กจะปรากฏที่ด้านล่างของแอปผู้ส่ง คุณใช้ปุ่มเล่น/หยุดชั่วคราวเพื่อควบคุมการเล่นได้
  6. เลือกวิดีโอจากโทรศัพท์มือถือแล้วเล่น วิดีโอจะเริ่มเล่นใน ATV และตัวควบคุมที่ขยายจะแสดงบนผู้ส่งบนอุปกรณ์เคลื่อนที่
  7. ล็อกโทรศัพท์และเมื่อคุณปลดล็อก คุณจะเห็นการแจ้งเตือนในหน้าจอล็อกเพื่อควบคุมการเล่นสื่อหรือหยุดแคสต์

รูปภาพส่วนหนึ่งของหน้าจอโทรศัพท์ Android ที่มีมินิเพลเยอร์กําลังเล่นวิดีโอ

4. เตรียมโปรเจ็กต์เริ่มต้น

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

  1. เลือกนําเข้าโครงการบนหน้าจอต้อนรับหรือตัวเลือกเมนูไฟล์ > ใหม่ > นําเข้าโครงการ...
  2. เลือกไดเรกทอรี ไอคอนโฟลเดอร์app-start จากโฟลเดอร์โค้ดตัวอย่างแล้วคลิก OK
  3. คลิก File > โครงการซิงค์ของ Android Studio พร้อมปุ่ม Gradle Sync Project with Gradle Files
  4. เลือกอุปกรณ์ ATV แล้วคลิกปุ่ม ปุ่ม Run ของ Android Studio รูปสามเหลี่ยมสีเขียวชี้ไปทางขวาเรียกใช้ เพื่อเรียกใช้แอปและสํารวจ UI แถบเครื่องมือ Android Studio แสดงอุปกรณ์ Android TV ที่เลือกไว้

รูปภาพขนาดย่อของวิดีโอชุดหนึ่ง (ซึ่งมีการไฮไลต์อยู่) ซ้อนทับตัวอย่างแบบเต็มหน้าจอของวิดีโอ คําว่า "Cast Connect" จะปรากฏขึ้นที่ด้านขวาบน

การออกแบบแอป

แอปจะแสดงรายการวิดีโอสําหรับผู้ใช้ที่จะเรียกดู ผู้ใช้สามารถเลือกวิดีโอเพื่อเล่นบน Android TV ได้ แอปนี้ประกอบด้วยกิจกรรมหลัก 2 อย่าง ได้แก่ MainActivity และ PlaybackActivity

MainActivity

กิจกรรมนี้มีส่วนย่อย (MainFragment) รายการวิดีโอและข้อมูลเมตาที่เกี่ยวข้องจะได้รับการกําหนดค่าใน MovieList คลาสและมีการเรียก setupMovies() เพื่อสร้างรายการออบเจ็กต์ Movie รายการ

ออบเจ็กต์ Movie แสดงถึงเอนทิตีวิดีโอที่มีชื่อ คําอธิบาย รูปภาพ ภาพขนาดย่อ และ URL ของวิดีโอ ออบเจ็กต์ Movie แต่ละรายการจะผูกกับ CardPresenter เพื่อแสดงภาพขนาดย่อของวิดีโอที่มีชื่อและสตูดิโอ และส่งไปยัง ArrayObjectAdapter

เมื่อเลือกรายการแล้ว ระบบจะส่งออบเจ็กต์ Movie ที่เกี่ยวข้องไปยัง PlaybackActivity

กิจกรรมการเล่น

กิจกรรมนี้จะมี Fragment (PlaybackVideoFragment) ซึ่งโฮสต์ VideoView ด้วย ExoPlayer, การควบคุมสื่อบางอย่าง และพื้นที่ข้อความเพื่อแสดงรายละเอียดของวิดีโอที่เลือก และอนุญาตให้ผู้ใช้เล่นวิดีโอบน Android TV ผู้ใช้สามารถใช้รีโมตคอนโทรลเพื่อเล่น/หยุดชั่วคราว หรือเรียกดูการเล่นวิดีโอ

ข้อกําหนดเบื้องต้นของ Cast Connect

Cast Connect ใช้บริการ Google Play เวอร์ชันใหม่ที่จําเป็นต้องอัปเดตแอป ATV เพื่อใช้เนมสเปซ AndroidX

หากต้องการรองรับ Cast Connect ในแอป Android TV คุณต้องสร้างและสนับสนุนเหตุการณ์จากเซสชันสื่อ ไลบรารี Cast Connect จะสร้างสถานะสื่อตามสถานะของเซสชันสื่อ ไลบรารี Cast Connect ยังใช้เซสชันสื่อเพื่อส่งสัญญาณเมื่อได้รับข้อความจากผู้ส่งบางราย เช่น หยุดชั่วคราว

5. การกําหนดค่าการสนับสนุน Cast

การอ้างอิง

อัปเดตไฟล์ build.gradle ของแอปเพื่อใส่ทรัพยากร Dependency ของไลบรารีที่จําเป็น ดังนี้

dependencies {
    ....

    // Cast Connect libraries
    implementation 'com.google.android.gms:play-services-cast-tv:20.0.0'
    implementation 'com.google.android.gms:play-services-cast:21.1.0'
}

ซิงค์โปรเจ็กต์เพื่อยืนยันว่าบิวด์ไม่มีข้อผิดพลาด

การเริ่มต้น

CastReceiverContext เป็นออบเจ็กต์เดี่ยวเพื่อประสานงานการโต้ตอบแคสต์ทั้งหมด คุณต้องใช้อินเทอร์เฟซ ReceiverOptionsProvider เพื่อระบุ CastReceiverOptions เมื่อเริ่มต้น CastReceiverContext

สร้างไฟล์ CastReceiverOptionsProvider.kt และเพิ่มคลาสต่อไปนี้ในโครงการ

package com.google.sample.cast.castconnect

import android.content.Context
import com.google.android.gms.cast.tv.ReceiverOptionsProvider
import com.google.android.gms.cast.tv.CastReceiverOptions

class CastReceiverOptionsProvider : ReceiverOptionsProvider {
    override fun getOptions(context: Context): CastReceiverOptions {
        return CastReceiverOptions.Builder(context)
                .setStatusText("Cast Connect Codelab")
                .build()
    }
}

จากนั้นระบุผู้ให้บริการตัวเลือกผู้รับภายในแท็ก <application> ของไฟล์ AndroidManifest.xml ของแอป

<application>
  ...
  <meta-data
    android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.castconnect.CastReceiverOptionsProvider" />
</application>

หากต้องการเชื่อมต่อกับแอป ATV จากผู้ส่ง Cast ให้เลือกกิจกรรมที่ต้องการเปิดตัว ใน Codelab นี้ เราจะเปิดตัวแอป MainActivity เมื่อมีการเริ่มต้นเซสชันแคสต์ ในไฟล์ AndroidManifest.xml ให้เพิ่มตัวกรองจุดประสงค์การเปิดตัวใน MainActivity

<activity android:name=".MainActivity">
  ...
  <intent-filter>
    <action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

วงจรบริบทของตัวรับการแคสต์

คุณควรเริ่มต้น CastReceiverContext เมื่อมีการเปิดแอปและหยุด CastReceiverContext เมื่อมีการย้ายแอปไปที่พื้นหลัง เราขอแนะนําให้คุณใช้ LifecycleObserver จากไลบรารี androidx.lifecycle ในการจัดการการโทร CastReceiverContext.start() และ CastReceiverContext.stop()

เปิดไฟล์ MyApplication.kt เริ่มต้นบริบทการแคสต์ด้วยการเรียก initInstance() ในเมธอด onCreate ของแอปพลิเคชัน ใน AppLifeCycleObserver คลาส start() CastReceiverContext เมื่อแอปพลิเคชันกลับมาทํางานอีกครั้ง และ stop() เมื่อแอปพลิเคชันหยุดชั่วคราว:

package com.google.sample.cast.castconnect

import com.google.android.gms.cast.tv.CastReceiverContext
...

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        CastReceiverContext.initInstance(this)
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
    }

    class AppLifecycleObserver : DefaultLifecycleObserver {
        override fun onResume(owner: LifecycleOwner) {
            Log.d(LOG_TAG, "onResume")
            CastReceiverContext.getInstance().start()
        }

        override fun onPause(owner: LifecycleOwner) {
            Log.d(LOG_TAG, "onPause")
            CastReceiverContext.getInstance().stop()
        }
    }
}

การเชื่อมต่อ MediaSession กับ MediaManager

MediaManager เป็นพร็อพเพอร์ตี้ของเดี่ยว CastReceiverContext ซึ่งจะจัดการสถานะสื่อ จัดการจุดประสงค์ในการโหลด แปลข้อความเนมสเปซของสื่อจากผู้ส่งเป็นคําสั่งสื่อ และส่งสถานะของสื่อกลับไปยังผู้ส่ง

เมื่อสร้าง MediaSession คุณต้องระบุโทเค็น MediaSession ปัจจุบันแก่ MediaManager ด้วย เพื่อให้ระบบทราบว่าจะส่งคําสั่งไปที่ใดและเรียกดูสถานะการเล่นสื่อ ในไฟล์ PlaybackVideoFragment.kt โปรดตรวจสอบว่าได้เริ่มต้น MediaSession แล้วก่อนตั้งค่าโทเค็นเป็น MediaManager

import com.google.android.gms.cast.tv.CastReceiverContext
import com.google.android.gms.cast.tv.media.MediaManager
...

class PlaybackVideoFragment : VideoSupportFragment() {
    private var castReceiverContext: CastReceiverContext? = null
    ...

    private fun initializePlayer() {
        if (mPlayer == null) {
            ...
            mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
            ...
            castReceiverContext = CastReceiverContext.getInstance()
            if (castReceiverContext != null) {
                val mediaManager: MediaManager = castReceiverContext!!.getMediaManager()
                mediaManager.setSessionCompatToken(mMediaSession!!.getSessionToken())
            }

        }
    }
}

เมื่อคุณปล่อย MediaSession เนื่องจากการเล่นที่ไม่ทํางาน คุณควรตั้งค่าโทเค็น Null บน MediaManager:

private fun releasePlayer() {
    mMediaSession?.release()
    castReceiverContext?.mediaManager?.setSessionCompatToken(null)
    ...
}

มาเรียกใช้แอปตัวอย่างกัน

คลิกปุ่มปุ่ม Run ของ Android Studio รูปสามเหลี่ยมสีเขียวชี้ไปทางขวาเรียกใช้เพื่อทําให้แอปในอุปกรณ์ ATV ใช้งานได้ ปิดแอปและกลับไปที่หน้าจอหลักของ ATV จากผู้ส่งของคุณ ให้คลิกปุ่ม "แคสต์" ไอคอนปุ่ม &quot;แคสต์&quot; และเลือกอุปกรณ์ ATV ของคุณ คุณจะเห็นแอป ATV เปิดขึ้นบนอุปกรณ์ ATV และมีการเชื่อมต่อปุ่ม "แคสต์"

6. กำลังโหลดสื่อ

ระบบจะส่งคําสั่งโหลดผ่าน Intent ที่มีชื่อแพ็กเกจที่คุณกําหนดไว้ใน Developer Console คุณต้องเพิ่มตัวกรอง Intent ที่กําหนดไว้ล่วงหน้าต่อไปนี้ในแอป Android TV เพื่อระบุกิจกรรมเป้าหมายที่จะได้รับ Intent นี้ ในไฟล์ AndroidManifest.xml ให้เพิ่มตัวกรองจุดประสงค์การโหลดลงใน PlayerActivity

<activity android:name="com.google.sample.cast.castconnect.PlaybackActivity"
          android:launchMode="singleTask"
          android:exported="true">
  <intent-filter>
     <action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
     <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

การจัดการคําขอโหลดบน Android TV

เมื่อมีการกําหนดค่ากิจกรรมให้รับ Intent นี้โดยที่มีคําขอการโหลดแล้ว เราจะต้องจัดการ

แอปจะเรียกเมธอดส่วนตัวชื่อ processIntent เมื่อกิจกรรมเริ่มต้นขึ้น วิธีการนี้ประกอบด้วยตรรกะสําหรับการประมวลผล Intent ขาเข้า ในการจัดการคําขอโหลด เราจะแก้ไขวิธีการนี้และส่ง Intent เพื่อดําเนินการเพิ่มเติมโดยเรียกใช้เมธอด onNewIntent ของอินสแตนซ์ MediaManager หาก MediaManager ตรวจพบว่า Intent เป็นคําขอขณะโหลด ระบบจะแยกออบเจ็กต์ MediaLoadRequestData ออกจาก Intent และเรียก MediaLoadCommandCallback.onLoad() แก้ไขเมธอด processIntent ในไฟล์ PlaybackVideoFragment.kt เพื่อจัดการ Intent ที่มีคําขอโหลด:

fun processIntent(intent: Intent?) {
    val mediaManager: MediaManager = CastReceiverContext.getInstance().getMediaManager()
    // Pass intent to Cast SDK
    if (mediaManager.onNewIntent(intent)) {
        return
    }

    // Clears all overrides in the modifier.
    mediaManager.getMediaStatusModifier().clear()

    // If the SDK doesn't recognize the intent, handle the intent with your own logic.
    ...
}

ต่อไป เราจะขยายคลาสนามธรรม MediaLoadCommandCallback ซึ่งจะลบล้างเมธอด onLoad() ที่ MediaManager เรียกใช้ เมธอดนี้จะรับข้อมูลของคําขอโหลดและแปลงเป็นออบเจ็กต์ Movie เมื่อแปลงแล้ว ภาพยนตร์จะเล่นโดยโปรแกรมเล่นในระบบ จากนั้น MediaManager จะอัปเดตด้วย MediaLoadRequest และประกาศ MediaStatus ไปยังผู้ส่งที่เชื่อมต่อ สร้างชั้นเรียนส่วนตัวที่ซ้อนกันชื่อ MyMediaLoadCommandCallback ในไฟล์ PlaybackVideoFragment.kt ดังนี้

import com.google.android.gms.cast.MediaLoadRequestData
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.cast.MediaError
import com.google.android.gms.cast.tv.media.MediaException
import com.google.android.gms.cast.tv.media.MediaCommandCallback
import com.google.android.gms.cast.tv.media.QueueUpdateRequestData
import com.google.android.gms.cast.tv.media.MediaLoadCommandCallback
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import android.widget.Toast
...

private inner class MyMediaLoadCommandCallback :  MediaLoadCommandCallback() {
    override fun onLoad(
        senderId: String?, mediaLoadRequestData: MediaLoadRequestData): Task<MediaLoadRequestData> {
        Toast.makeText(activity, "onLoad()", Toast.LENGTH_SHORT).show()
        return if (mediaLoadRequestData == null) {
            // Throw MediaException to indicate load failure.
            Tasks.forException(MediaException(
                MediaError.Builder()
                    .setDetailedErrorCode(MediaError.DetailedErrorCode.LOAD_FAILED)
                    .setReason(MediaError.ERROR_REASON_INVALID_REQUEST)
                    .build()))
        } else Tasks.call {
            play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
            // Update media metadata and state
            val mediaManager = castReceiverContext!!.mediaManager
            mediaManager.setDataFromLoad(mediaLoadRequestData)
            mediaLoadRequestData
        }
    }
}

private fun convertLoadRequestToMovie(mediaLoadRequestData: MediaLoadRequestData?): Movie? {
    if (mediaLoadRequestData == null) {
        return null
    }
    val mediaInfo: MediaInfo = mediaLoadRequestData.getMediaInfo() ?: return null
    var videoUrl: String = mediaInfo.getContentId()
    if (mediaInfo.getContentUrl() != null) {
        videoUrl = mediaInfo.getContentUrl()
    }
    val metadata: MediaMetadata = mediaInfo.getMetadata()
    val movie = Movie()
    movie.videoUrl = videoUrl
    movie.title = metadata?.getString(MediaMetadata.KEY_TITLE)
    movie.description = metadata?.getString(MediaMetadata.KEY_SUBTITLE)
    if(metadata?.hasImages() == true) {
        movie.cardImageUrl = metadata.images[0].url.toString()
    }
    return movie
}

เมื่อมีการกําหนดการเรียกกลับแล้ว เราต้องลงทะเบียนหมายเลขโทรศัพท์กับ MediaManager ต้องลงทะเบียนโค้ดเรียกกลับก่อนที่จะเรียก MediaManager.onNewIntent() เพิ่ม setMediaLoadCommandCallback เมื่อโปรแกรมเล่นเริ่มต้น:

private fun initializePlayer() {
    if (mPlayer == null) {
        ...
        mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
        ...
        castReceiverContext = CastReceiverContext.getInstance()
        if (castReceiverContext != null) {
            val mediaManager: MediaManager = castReceiverContext.getMediaManager()
            mediaManager.setSessionCompatToken(mMediaSession.getSessionToken())
            mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback())
        }
    }
}

มาเรียกใช้แอปตัวอย่างกัน

คลิกปุ่มปุ่ม Run ของ Android Studio รูปสามเหลี่ยมสีเขียวชี้ไปทางขวาเรียกใช้เพื่อทําให้แอปในอุปกรณ์ ATV ใช้งานได้ จากผู้ส่งของคุณ ให้คลิกปุ่ม "แคสต์" ไอคอนปุ่ม &quot;แคสต์&quot; และเลือกอุปกรณ์ ATV ของคุณ แอป ATV จะเปิดตัวในอุปกรณ์ ATV เลือกวิดีโอบนอุปกรณ์เคลื่อนที่ วิดีโอจะเริ่มเล่นใน ATV ตรวจสอบว่าคุณได้รับการแจ้งเตือนบนโทรศัพท์ที่คุณมีการควบคุมการเล่นหรือไม่ ลองใช้ตัวควบคุม เช่น หยุดชั่วคราว วิดีโอบนอุปกรณ์ ATV ควรหยุดชั่วคราว

7. การสนับสนุนคําสั่งควบคุมการแคสต์

แอปพลิเคชันปัจจุบันรองรับคําสั่งพื้นฐานที่เข้ากันได้กับเซสชันสื่อ เช่น เล่น หยุดชั่วคราว และกรอ แต่คําสั่งตัวควบคุม Cast บางรายการไม่พร้อมใช้งานในเซสชันสื่อ คุณต้องลงทะเบียน MediaCommandCallback เพื่อรองรับคําสั่งควบคุมของ Cast

เพิ่ม MyMediaCommandCallback ไปยังอินสแตนซ์ MediaManager โดยใช้ setMediaCommandCallback เมื่อโปรแกรมเล่นเริ่มต้น:

private fun initializePlayer() {
    ...
    castReceiverContext = CastReceiverContext.getInstance()
    if (castReceiverContext != null) {
        val mediaManager = castReceiverContext!!.mediaManager
        ...
        mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
    }
}

สร้างชั้นเรียน MyMediaCommandCallback เพื่อลบล้างเมธอด เช่น onQueueUpdate() เพื่อรองรับคําสั่งควบคุมของ Cast ดังนี้

private inner class MyMediaCommandCallback : MediaCommandCallback() {
    override fun onQueueUpdate(
        senderId: String?,
        queueUpdateRequestData: QueueUpdateRequestData
    ): Task<Void> {
        Toast.makeText(getActivity(), "onQueueUpdate()", Toast.LENGTH_SHORT).show()
        // Queue Prev / Next
        if (queueUpdateRequestData.getJump() != null) {
            Toast.makeText(
                getActivity(),
                "onQueueUpdate(): Jump = " + queueUpdateRequestData.getJump(),
                Toast.LENGTH_SHORT
            ).show()
        }
        return super.onQueueUpdate(senderId, queueUpdateRequestData)
    }
}

8. การทํางานกับสถานะสื่อ

การแก้ไขสถานะสื่อ

Cast Connect จะได้รับสถานะสื่อพื้นฐานจากเซสชันสื่อ แอป YouTube TV สามารถระบุและลบล้างพร็อพเพอร์ตี้สถานะเพิ่มเติมผ่าน MediaStatusModifier เพื่อรองรับฟีเจอร์ขั้นสูงได้ MediaStatusModifier จะทํางานบน MediaSession ที่คุณตั้งไว้ใน CastReceiverContext เสมอ

ตัวอย่างเช่น หากต้องการระบุ setMediaCommandSupported เมื่อมีการทริกเกอร์การเรียกกลับ onLoad:

import com.google.android.gms.cast.MediaStatus
...
private class MyMediaLoadCommandCallback : MediaLoadCommandCallback() {
    fun onLoad(
        senderId: String?,
        mediaLoadRequestData: MediaLoadRequestData
    ): Task<MediaLoadRequestData> {
        Toast.makeText(getActivity(), "onLoad()", Toast.LENGTH_SHORT).show()
        ...
        return Tasks.call({
            play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
            ...
            // Use MediaStatusModifier to provide additional information for Cast senders.
            mediaManager.getMediaStatusModifier()
                .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT, true)
                .setIsPlayingAd(false)
            mediaManager.broadcastMediaStatus()
            // Return the resolved MediaLoadRequestData to indicate load success.
            mediaLoadRequestData
        })
    }
}

การสกัดกั้น MediaMedia ก่อนส่ง

เช่นเดียวกับ MessageInterceptor ของตัวรับสัญญาณเว็บ คุณสามารถระบุ MediaStatusWriter ใน MediaManager เพื่อดําเนินการแก้ไขเพิ่มเติมกับ MediaStatus ก่อนที่จะเผยแพร่ไปยังผู้ส่งที่เชื่อมต่อ

ตัวอย่างเช่น คุณตั้งค่าข้อมูลที่กําหนดเองได้ในMediaStatus ก่อนที่จะส่งไปยังผู้ส่งในอุปกรณ์เคลื่อนที่

import com.google.android.gms.cast.tv.media.MediaManager.MediaStatusInterceptor
import com.google.android.gms.cast.tv.media.MediaStatusWriter
import org.json.JSONObject
import org.json.JSONException
...

private fun initializePlayer() {
    if (mPlayer == null) {
        ...
        if (castReceiverContext != null) {
            ...
            val mediaManager: MediaManager = castReceiverContext.getMediaManager()
            ...
            // Use MediaStatusInterceptor to process the MediaStatus before sending out.
            mediaManager.setMediaStatusInterceptor(
                MediaStatusInterceptor { mediaStatusWriter: MediaStatusWriter ->
                    try {
                        mediaStatusWriter.setCustomData(JSONObject("{myData: 'CustomData'}"))
                    } catch (e: JSONException) {
                        Log.e(LOG_TAG,e.message,e);
                    }
            })
        }
    }
}        

9. ขอแสดงความยินดี

ตอนนี้คุณทราบวิธีแคสต์เพื่อเปิดใช้แอป Android TV โดยใช้ไลบรารี Cast Connect แล้ว

โปรดดูรายละเอียดเพิ่มเติมในคู่มือนักพัฒนาซอฟต์แวร์ /cast/docs/android_tv_receiver