เปิดใช้แอป Android TV ได้

1. ภาพรวม

โลโก้ Google Cast

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

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

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

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

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

เราจะสร้างอะไร

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

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

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

สิ่งที่คุณต้องมี

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

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

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

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

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

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

ลงทะเบียนอุปกรณ์สำหรับนักพัฒนาแอป

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

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

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

ติดตั้งแอปตัวส่งสำหรับ Android

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

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

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

  1. คุณจะเห็นแอปตัวส่ง Cast Videos ในโทรศัพท์ Android ไอคอนแอปส่งวิดีโอแคสต์

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

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

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

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

มาเล่น Cast Connect ด้วยแอป ATV กัน

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

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

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

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

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

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

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

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

กิจกรรมหลัก

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

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

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

PlaybackActivity

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

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

Cast Connect ใช้บริการ Google Play เวอร์ชันใหม่ที่กำหนดให้แอป ATV ของคุณต้องได้รับการอัปเดตเพื่อใช้เนมสเปซ AndroidX

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

5. การกำหนดค่าการรองรับการแคสต์

แท็กเริ่มการทำงาน

อัปเดตไฟล์ 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 เป็นออบเจ็กต์แบบ Singleton ที่ใช้ประสานงานการโต้ตอบ Cast ทั้งหมด คุณต้องใช้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 ของแอปเมื่อเริ่มเซสชัน Cast ในไฟล์ AndroidManifest.xml ให้เพิ่มตัวกรอง Intent การเปิดตัวใน 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>

วงจรบริบทของตัวรับสัญญาณ Cast

คุณควรเริ่ม 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 Singleton ซึ่งจะจัดการสถานะสื่อ จัดการความตั้งใจในการโหลด แปลงข้อความเนมสเปซสื่อจากผู้ส่งเป็นคำสั่งสื่อ และส่งสถานะสื่อกลับไปยังผู้ส่ง

เมื่อสร้าง 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)
    ...
}

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

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

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

ระบบจะส่งคำสั่งโหลดผ่าน Intent ที่มีชื่อแพ็กเกจที่คุณกำหนดไว้ใน Developer Console คุณต้องเพิ่มตัวกรอง Intent ที่กำหนดไว้ล่วงหน้าต่อไปนี้ในแอป Android TV เพื่อระบุกิจกรรมเป้าหมายที่จะรับ Intent นี้ ในไฟล์ AndroidManifest.xml ให้เพิ่มตัวกรอง Intent การโหลดไปยัง 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 ที่มีคำขอโหลดแล้ว เราจึงต้องจัดการ Intent นี้

แอปจะเรียกใช้เมธอดส่วนตัวที่ชื่อ processIntent เมื่อกิจกรรมเริ่มต้น เมธอดนี้มีตรรกะสำหรับการประมวลผล Intent ขาเข้า หากต้องการจัดการคำขอโหลด เราจะแก้ไขเมธอดนี้และส่ง Intent เพื่อให้ MediaManagerอินสแตนซ์ของ onNewIntent เมธอดประมวลผลต่อไป หาก MediaManager ตรวจพบว่าเจตนาเป็นคำขอโหลด ระบบจะดึงออบเจ็กต์ MediaLoadRequestData จากเจตนาและเรียกใช้ 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())
        }
    }
}

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

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

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

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

เพิ่ม 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 จะรับสถานะสื่อพื้นฐานจากเซสชันสื่อ หากต้องการรองรับฟีเจอร์ขั้นสูง แอป Android TV สามารถระบุและลบล้างพร็อพเพอร์ตี้สถานะเพิ่มเติมผ่าน MediaStatusModifier ได้ MediaStatusModifier จะทำงานบน MediaSession ที่คุณตั้งค่าไว้ใน CastReceiverContext เสมอ

เช่น หากต้องการระบุ setMediaCommandSupported เมื่อทริกเกอร์ Callback 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
        })
    }
}

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

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

ตัวอย่างเช่น คุณสามารถตั้งค่าข้อมูลที่กำหนดเองใน 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