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

1. ภาพรวม

โลโก้ Google Cast

Codelab นี้จะสอนวิธีแก้ไขแอปวิดีโอ Android ที่มีอยู่เพื่อแคสต์เนื้อหาในอุปกรณ์ที่พร้อมใช้งาน Google Cast

Google Cast คืออะไร

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

Google Cast SDK ช่วยให้คุณขยายแอปเพื่อควบคุมทีวีหรือระบบเสียงได้ Cast SDK ช่วยให้คุณเพิ่มคอมโพเนนต์ UI ที่จำเป็นตามรายการตรวจสอบการออกแบบ Google Cast ได้

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

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

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

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

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

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

  • Android SDK ล่าสุด
  • Android Studio เวอร์ชัน 3.2 ขึ้นไป
  • อุปกรณ์เคลื่อนที่ 1 เครื่องที่ใช้ Android 4.1 ขึ้นไป (Jelly Bean) (API ระดับ 16)
  • สายข้อมูล USB เพื่อเชื่อมต่ออุปกรณ์เคลื่อนที่กับคอมพิวเตอร์ที่ใช้พัฒนา
  • อุปกรณ์ Google Cast เช่น Chromecast หรือ Android TV ที่กำหนดค่าให้เข้าถึงอินเทอร์เน็ตได้
  • ทีวีหรือจอภาพที่มีอินพุต HDMI
  • คุณต้องมี Chromecast พร้อม Google TV เพื่อทดสอบการผสานรวม Cast Connect แต่จะใช้หรือไม่ก็ได้สำหรับ Codelab ที่เหลือ หากไม่มี ให้ข้ามขั้นตอนเพิ่มการรองรับ Cast Connect ที่อยู่ท้ายบทแนะนำนี้

ประสบการณ์

  • คุณจะต้องมีความรู้ด้านการพัฒนา Kotlin และ Android มาก่อน
  • นอกจากนี้ คุณยังต้องมีความรู้พื้นฐานเกี่ยวกับการดูทีวีด้วย :)

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านอย่างเดียว อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์ในการสร้างแอป Android เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์การดูทีวีเท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

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

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

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

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

ไอคอนวงเวียน

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

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

เลือกนำเข้าโปรเจ็กต์ในหน้าจอต้อนรับ หรือตัวเลือกเมนูไฟล์ > ใหม่ > นำเข้าโปรเจ็กต์...

เลือกไดเรกทอรี ไอคอนโฟลเดอร์app-done จากโฟลเดอร์โค้ดตัวอย่าง แล้วคลิกตกลง

คลิก File > ปุ่ม "ซิงค์โปรเจ็กต์กับ Gradle" ใน Android Studio Sync Project with Gradle Files

เปิดใช้การแก้ไขข้อบกพร่องผ่าน USB ในอุปกรณ์ Android - ใน Android 4.2 ขึ้นไป หน้าจอตัวเลือกสำหรับนักพัฒนาแอปจะซ่อนอยู่โดยค่าเริ่มต้น หากต้องการให้ตัวเลือกนี้ปรากฏ ให้ไปที่การตั้งค่า > เกี่ยวกับโทรศัพท์ แล้วแตะหมายเลขบิลด์ 7 ครั้ง กลับไปที่หน้าจอก่อนหน้า ไปที่ระบบ > ขั้นสูง แล้วแตะตัวเลือกสำหรับนักพัฒนาแอปที่ด้านล่าง จากนั้นแตะการแก้ไขข้อบกพร่อง USB เพื่อเปิด

เสียบอุปกรณ์ Android แล้วคลิกปุ่มปุ่มเรียกใช้ของ Android Studio ซึ่งเป็นสามเหลี่ยมสีเขียวชี้ไปทางขวาเรียกใช้ใน Android Studio คุณควรเห็นแอปวิดีโอชื่อ Cast Videos ปรากฏขึ้นหลังจากผ่านไป 2-3 วินาที

คลิกปุ่มแคสต์ในแอปวิดีโอ แล้วเลือกอุปกรณ์ Google Cast

เลือกวิดีโอแล้วคลิกปุ่มเล่น

วิดีโอจะเริ่มเล่นในอุปกรณ์ Google Cast

ตัวควบคุมแบบขยายจะปรากฏขึ้น คุณใช้ปุ่มเล่น/หยุดชั่วคราวเพื่อควบคุมการเล่นได้

กลับไปที่รายการวิดีโอ

ตอนนี้คุณจะเห็นมินิคอนโทรลเลอร์ที่ด้านล่างของหน้าจอ ภาพโทรศัพท์ Android ที่เรียกใช้แอป "แคสต์วิดีโอ" โดยมีมินิคอนโทรลเลอร์ 3 ตัวปรากฏที่ด้านล่างของหน้าจอ

คลิกปุ่มหยุดชั่วคราวในมินิคอนโทรลเลอร์เพื่อหยุดวิดีโอชั่วคราวบนเครื่องรับ คลิกปุ่มเล่นในมินิคอนโทรลเลอร์เพื่อเล่นวิดีโอต่อ

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

ล็อกโทรศัพท์และเมื่อปลดล็อกแล้ว คุณควรเห็นการแจ้งเตือนบนหน้าจอล็อกเพื่อควบคุมการเล่นสื่อหรือหยุดการแคสต์

กลับไปที่แอปวิดีโอแล้วคลิกปุ่มแคสต์เพื่อหยุดการแคสต์ในอุปกรณ์ Google Cast

คำถามที่พบบ่อย

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

ภาพโทรศัพท์ Android ที่เรียกใช้แอป "แคสต์วิดีโอ"

เราต้องเพิ่มการรองรับ Google Cast ลงในแอปเริ่มต้นที่คุณดาวน์โหลด คำศัพท์บางคำของ Google Cast ที่เราจะใช้ใน Codelab นี้มีดังนี้

  • แอปผู้ส่งทำงานบนอุปกรณ์เคลื่อนที่หรือแล็ปท็อป
  • แอปตัวรับจะทำงานบนอุปกรณ์ Google Cast

ตอนนี้คุณพร้อมที่จะสร้างต่อจากโปรเจ็กต์เริ่มต้นโดยใช้ Android Studio แล้ว โดยทำดังนี้

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

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

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

แอปประกอบด้วยกิจกรรมหลัก 2 อย่าง ได้แก่ VideoBrowserActivity และ LocalPlayerActivity หากต้องการผสานรวมฟังก์ชัน Google Cast Activities จะต้องรับค่าจาก AppCompatActivity หรือ FragmentActivity ซึ่งเป็นองค์ประกอบหลัก ข้อจำกัดนี้มีอยู่เนื่องจากเราจะต้องเพิ่ม MediaRouteButton (ระบุไว้ในไลบรารีการรองรับ MediaRouter) เป็น MediaRouteActionProvider และจะใช้งานได้ก็ต่อเมื่อกิจกรรมสืบทอดมาจากคลาสที่กล่าวถึงข้างต้น ไลบรารีการรองรับ MediaRouter ขึ้นอยู่กับไลบรารีการรองรับ AppCompat ซึ่งมีคลาสที่จำเป็น

VideoBrowserActivity

กิจกรรมนี้มีFragment (VideoBrowserFragment) ซึ่งมีArrayAdapter (VideoListAdapter) เป็นข้อมูลสำรอง รายการวิดีโอและข้อมูลเมตาที่เกี่ยวข้องจะโฮสต์อยู่ในเซิร์ฟเวอร์ระยะไกลเป็นไฟล์ JSON AsyncTaskLoader (VideoItemLoader) จะดึงข้อมูล JSON นี้และประมวลผลเพื่อสร้างรายการออบเจ็กต์ MediaItem

ออบเจ็กต์ MediaItem จะจำลองวิดีโอและข้อมูลเมตาที่เกี่ยวข้อง เช่น ชื่อ คำอธิบาย URL ของสตรีม URL ของรูปภาพสนับสนุน และแทร็กข้อความที่เกี่ยวข้อง (สำหรับคำบรรยายแทนเสียง) หากมี ออบเจ็กต์ MediaItem จะส่งผ่านระหว่างกิจกรรมต่างๆ ดังนั้น MediaItem จึงมีเมธอดยูทิลิตีเพื่อแปลงเป็น Bundle และในทางกลับกัน

เมื่อโปรแกรมโหลดสร้างรายการ MediaItems แล้ว โปรแกรมจะส่งรายการดังกล่าวไปยัง VideoListAdapter ซึ่งจะแสดงรายการ MediaItems ใน VideoBrowserFragment ผู้ใช้จะเห็นรายการภาพปกวิดีโอพร้อมคำอธิบายสั้นๆ ของวิดีโอแต่ละรายการ เมื่อเลือกรายการ ระบบจะแปลง MediaItem ที่เกี่ยวข้องเป็น Bundle และส่งไปยัง LocalPlayerActivity

LocalPlayerActivity

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

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

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

เนื่องจากเราใช้ AppCompatActivity เราจึงต้องใช้ไลบรารีการสนับสนุน AppCompat เราใช้ไลบรารี Volley ในการจัดการรายการวิดีโอและรับรูปภาพสำหรับรายการแบบไม่พร้อมกัน

คำถามที่พบบ่อย

5. การเพิ่มปุ่มแคสต์

ภาพส่วนบนของโทรศัพท์ Android ที่มีแอปแคสต์วิดีโอทำงานอยู่ โดยปุ่มแคสต์จะปรากฏที่มุมขวาบนของหน้าจอ

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

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

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

dependencies {
    implementation 'androidx.appcompat:appcompat:1.5.0'
    implementation 'androidx.mediarouter:mediarouter:1.3.1'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    implementation 'com.google.android.gms:play-services-cast-framework:21.1.0'
    implementation 'com.android.volley:volley:1.2.1'
    implementation "androidx.core:core-ktx:1.8.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

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

การเริ่มต้น

เฟรมเวิร์ก Cast มีออบเจ็กต์ Singleton ส่วนกลาง CastContext ซึ่งประสานงานการโต้ตอบ Cast ทั้งหมด

คุณต้องใช้OptionsProvider อินเทอร์เฟซเพื่อจัดหา CastOptions ที่จำเป็นในการเริ่มต้น CastContext Singleton ตัวเลือกที่สำคัญที่สุดคือรหัสแอปพลิเคชันตัวรับ ซึ่งใช้เพื่อกรองผลการค้นหาอุปกรณ์ Cast และเปิดแอปพลิเคชันตัวรับเมื่อเริ่มเซสชัน Cast

เมื่อพัฒนาแอปที่พร้อมใช้งาน Cast ของคุณเอง คุณต้องลงทะเบียนเป็นนักพัฒนาแอป Cast แล้วขอรหัสแอปพลิเคชันสำหรับแอปของคุณ สำหรับ Codelab นี้ เราจะใช้รหัสแอปตัวอย่าง

เพิ่มไฟล์ CastOptionsProvider.kt ใหม่ต่อไปนี้ลงในแพ็กเกจ com.google.sample.cast.refplayer ของโปรเจ็กต์

package com.google.sample.cast.refplayer

import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider

class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}

ตอนนี้ให้ประกาศ OptionsProvider ภายในแท็ก "application" ของไฟล์ AndroidManifest.xml ของแอป

<meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />

เริ่มต้น CastContext อย่างช้าๆ ในเมธอด VideoBrowserActivity onCreate ดังนี้

import com.google.android.gms.cast.framework.CastContext

private var mCastContext: CastContext? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.video_browser)
    setupActionBar()

    mCastContext = CastContext.getSharedInstance(this)
}

เพิ่มตรรกะการเริ่มต้นเดียวกันลงใน LocalPlayerActivity

ปุ่ม "แคสต์"

ตอนนี้เมื่อเริ่มต้น CastContext แล้ว เราต้องเพิ่มปุ่มแคสต์เพื่อให้ผู้ใช้เลือกอุปกรณ์แคสต์ได้ MediaRouteButton ใช้ปุ่มแคสต์จากไลบรารีการสนับสนุน MediaRouter เช่นเดียวกับไอคอนการกระทําอื่นๆ ที่คุณเพิ่มลงในกิจกรรมได้ (โดยใช้ ActionBar หรือ Toolbar) คุณต้องเพิ่มรายการเมนูที่เกี่ยวข้องลงในเมนูก่อน

แก้ไขไฟล์ res/menu/browse.xml และเพิ่มรายการ MediaRouteActionProvider ในเมนูก่อนรายการการตั้งค่า

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

ลบล้างเมธอด onCreateOptionsMenu() ของ VideoBrowserActivity โดยใช้ CastButtonFactory เพื่อเชื่อมต่อ MediaRouteButton กับเฟรมเวิร์ก Cast ดังนี้

import com.google.android.gms.cast.framework.CastButtonFactory

private var mediaRouteMenuItem: MenuItem? = null

override fun onCreateOptionsMenu(menu: Menu): Boolean {
     super.onCreateOptionsMenu(menu)
     menuInflater.inflate(R.menu.browse, menu)
     mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu,
                R.id.media_route_menu_item)
     return true
}

ลบล้าง onCreateOptionsMenu ใน LocalPlayerActivity ในลักษณะเดียวกัน

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

เรายังไม่ได้เชื่อมต่อการรองรับการเล่นสื่อ คุณจึงยังเล่นวิดีโอบนอุปกรณ์ Cast ไม่ได้ คลิกปุ่มแคสต์เพื่อยกเลิกการเชื่อมต่อ

6. การแคสต์เนื้อหาวิดีโอ

ภาพโทรศัพท์ Android ที่เรียกใช้แอป &quot;แคสต์วิดีโอ&quot;

เราจะขยายแอปตัวอย่างให้เล่นวิดีโอจากระยะไกลบนอุปกรณ์ Cast ได้ด้วย โดยเราต้องฟังเหตุการณ์ต่างๆ ที่เฟรมเวิร์ก Cast สร้างขึ้น

การแคสต์สื่อ

ในระดับสูง หากต้องการเล่นสื่อบนอุปกรณ์แคสต์ คุณต้องทำสิ่งต่อไปนี้

  1. สร้างออบเจ็กต์ MediaInfo ที่จำลองรายการสื่อ
  2. เชื่อมต่อกับอุปกรณ์แคสต์และเปิดแอปพลิเคชันตัวรับสัญญาณ
  3. โหลดMediaInfoออบเจ็กต์ลงในตัวรับและเล่นเนื้อหา
  4. ติดตามสถานะสื่อ
  5. ส่งคำสั่งการเล่นไปยังตัวรับสัญญาณตามการโต้ตอบของผู้ใช้

เราได้ทำขั้นตอนที่ 2 ในส่วนก่อนหน้าแล้ว ขั้นตอนที่ 3 ทำได้ง่ายๆ ด้วยเฟรมเวิร์ก Cast ขั้นตอนที่ 1 คือการแมปออบเจ็กต์หนึ่งกับอีกออบเจ็กต์หนึ่ง MediaInfo คือสิ่งที่เฟรมเวิร์ก Cast เข้าใจ และ MediaItem คือการห่อหุ้มของแอปสำหรับรายการสื่อ เราสามารถแมป MediaItem กับ MediaInfo ได้อย่างง่ายดาย

แอปตัวอย่าง LocalPlayerActivity แยกความแตกต่างระหว่างการเล่นในเครื่องกับการเล่นจากระยะไกลโดยใช้การแจงนับนี้อยู่แล้ว

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

enum class PlaybackState {
    PLAYING, PAUSED, BUFFERING, IDLE
}

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

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

การจัดการเซสชันการแคสต์

สำหรับเฟรมเวิร์ก Cast เซสชัน Cast จะรวมขั้นตอนการเชื่อมต่อกับอุปกรณ์ การเปิด (หรือเข้าร่วม) การเชื่อมต่อกับแอปพลิเคชันตัวรับ และการเริ่มต้นช่องควบคุมสื่อหากเหมาะสม ช่องควบคุมสื่อคือวิธีที่เฟรมเวิร์ก Cast ใช้ส่งและรับข้อความจากเครื่องเล่นสื่อตัวรับ

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

มาเพิ่ม SessionManagerListener ลงใน LocalPlayerActivity กัน

import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...

private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...

private fun setupCastListener() {
    mSessionManagerListener = object : SessionManagerListener<CastSession> {
        override fun onSessionEnded(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
            onApplicationConnected(session)
        }

        override fun onSessionResumeFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarted(session: CastSession, sessionId: String) {
            onApplicationConnected(session)
        }

        override fun onSessionStartFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarting(session: CastSession) {}
        override fun onSessionEnding(session: CastSession) {}
        override fun onSessionResuming(session: CastSession, sessionId: String) {}
        override fun onSessionSuspended(session: CastSession, reason: Int) {}
        private fun onApplicationConnected(castSession: CastSession) {
            mCastSession = castSession
            if (null != mSelectedMedia) {
                if (mPlaybackState == PlaybackState.PLAYING) {
                    mVideoView!!.pause()
                    loadRemoteMedia(mSeekbar!!.progress, true)
                    return
                } else {
                    mPlaybackState = PlaybackState.IDLE
                    updatePlaybackLocation(PlaybackLocation.REMOTE)
                }
            }
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
        }

        private fun onApplicationDisconnected() {
            updatePlaybackLocation(PlaybackLocation.LOCAL)
            mPlaybackState = PlaybackState.IDLE
            mLocation = PlaybackLocation.LOCAL
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
       }
   }
}

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

คุณเข้าถึงเซสชันที่ใช้งานอยู่ในปัจจุบันได้ในชื่อ SessionManager.getCurrentSession() ระบบจะสร้างและปิดเซสชันโดยอัตโนมัติเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้กับกล่องโต้ตอบการแคสต์

เราต้องลงทะเบียนเครื่องมือฟังเซสชันและเริ่มต้นตัวแปรบางอย่างที่เราจะใช้ในกิจกรรม เปลี่ยนวิธีการ LocalPlayerActivity onCreate เป็น

import com.google.android.gms.cast.framework.CastContext
...

private var mCastContext: CastContext? = null
...

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    mCastContext = CastContext.getSharedInstance(this)
    mCastSession = mCastContext!!.sessionManager.currentCastSession
    setupCastListener()
    ...
    loadViews()
    ...
    val bundle = intent.extras
    if (bundle != null) {
        ....
        if (shouldStartPlayback) {
              ....

        } else {
            if (mCastSession != null && mCastSession!!.isConnected()) {
                updatePlaybackLocation(PlaybackLocation.REMOTE)
            } else {
                updatePlaybackLocation(PlaybackLocation.LOCAL)
            }
            mPlaybackState = PlaybackState.IDLE
            updatePlayButton(mPlaybackState)
        }
    }
    ...
}

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

ใน Cast SDK RemoteMediaClient มีชุด API ที่สะดวกสำหรับการจัดการการเล่นสื่อจากระยะไกลบนตัวรับ สำหรับ CastSession ที่รองรับการเล่นสื่อ SDK จะสร้างอินสแตนซ์ของ RemoteMediaClient โดยอัตโนมัติ โดยสามารถเข้าถึงได้โดยการเรียกใช้เมธอด getRemoteMediaClient() ในอินสแตนซ์ CastSession เพิ่มวิธีการต่อไปนี้ลงใน LocalPlayerActivity เพื่อโหลดวิดีโอที่เลือกอยู่ในปัจจุบันบนตัวรับ

import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData

private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    if (mCastSession == null) {
        return
    }
    val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
    remoteMediaClient.load( MediaLoadRequestData.Builder()
                .setMediaInfo(buildMediaInfo())
                .setAutoplay(autoPlay)
                .setCurrentTime(position.toLong()).build())
}

private fun buildMediaInfo(): MediaInfo? {
    val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
    mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
    mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
    return mSelectedMedia!!.url?.let {
        MediaInfo.Builder(it)
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("videos/mp4")
            .setMetadata(movieMetadata)
            .setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
            .build()
    }
}

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

private fun play(position: Int) {
    startControllersTimer()
    when (mLocation) {
        PlaybackLocation.LOCAL -> {
            mVideoView!!.seekTo(position)
            mVideoView!!.start()
        }
        PlaybackLocation.REMOTE -> {
            mPlaybackState = PlaybackState.BUFFERING
            updatePlayButton(mPlaybackState)
            //seek to a new position within the current media item's new position 
            //which is in milliseconds from the beginning of the stream
            mCastSession!!.remoteMediaClient?.seek(position.toLong())
        }
        else -> {}
    }
    restartTrickplayTimer()
}
private fun togglePlayback() {
    ...
    PlaybackState.IDLE -> when (mLocation) {
        ...
        PlaybackLocation.REMOTE -> {
            if (mCastSession != null && mCastSession!!.isConnected) {
                loadRemoteMedia(mSeekbar!!.progress, true)
            }
        }
        else -> {}
    }
    ...
}
override fun onPause() {
    ...
    mCastContext!!.sessionManager.removeSessionManagerListener(
                mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
    Log.d(TAG, "onResume() was called")
    mCastContext!!.sessionManager.addSessionManagerListener(
            mSessionManagerListener!!, CastSession::class.java)
    if (mCastSession != null && mCastSession!!.isConnected) {
        updatePlaybackLocation(PlaybackLocation.REMOTE)
    } else {
        updatePlaybackLocation(PlaybackLocation.LOCAL)
    }
    super.onResume()
}

สำหรับupdatePlayButton ให้เปลี่ยนค่าของตัวแปร isConnected ดังนี้

private fun updatePlayButton(state: PlaybackState?) {
    ...
    val isConnected = (mCastSession != null
                && (mCastSession!!.isConnected || mCastSession!!.isConnecting))
    ...
}

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

7. มินิคอนโทรลเลอร์

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

ภาพส่วนล่างของโทรศัพท์ Android ที่แสดงมินิเพลเยอร์ในแอปวิดีโอที่แคสต์

Cast SDK มีมุมมองที่กำหนดเอง MiniControllerFragment ซึ่งเพิ่มลงในไฟล์เลย์เอาต์ของแอปในกิจกรรมที่คุณต้องการแสดงมินิคอนโทรลเลอร์ได้

เพิ่มคำจำกัดความของ Fragment ต่อไปนี้ที่ด้านล่างของทั้ง res/layout/player_activity.xml และ res/layout/video_browser.xml

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>

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

8. การแจ้งเตือนและหน้าจอล็อก

รายการตรวจสอบการออกแบบ Google Cast กำหนดให้แอปผู้ส่งต้องใช้ตัวควบคุมสื่อจากการแจ้งเตือนและหน้าจอล็อก

ภาพโทรศัพท์ Android ที่แสดงการควบคุมสื่อในพื้นที่การแจ้งเตือน

Cast SDK มี MediaNotificationService เพื่อช่วยให้แอปผู้ส่งสร้างตัวควบคุมสื่อสำหรับการแจ้งเตือนและหน้าจอล็อก Gradle จะผสานบริการเข้ากับไฟล์ Manifest ของแอปโดยอัตโนมัติ

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

คุณเปิดใช้การควบคุมการแจ้งเตือนและหน้าจอล็อกได้ด้วย CastOptions เมื่อเริ่มต้น CastContext ตัวควบคุมสื่อสำหรับการแจ้งเตือนและหน้าจอล็อกจะเปิดอยู่โดยค่าเริ่มต้น ระบบจะเปิดฟีเจอร์หน้าจอล็อกตราบใดที่เปิดการแจ้งเตือนไว้

แก้ไข CastOptionsProvider และเปลี่ยนการติดตั้งใช้งาน getCastOptions ให้ตรงกับโค้ดนี้

import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.NotificationOptions

override fun getCastOptions(context: Context): CastOptions {
   val notificationOptions = NotificationOptions.Builder()
            .setTargetActivityClassName(VideoBrowserActivity::class.java.name)
            .build()
    val mediaOptions = CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .build()
   return CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .setCastMediaOptions(mediaOptions)
                .build()
}

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

ภาพโทรศัพท์ Android ที่แสดงตัวควบคุมสื่อบนหน้าจอล็อก

9. การซ้อนทับช่วงแนะนำ

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

ภาพแสดงการซ้อนทับ Cast เบื้องต้นรอบปุ่ม Cast ในแอปวิดีโอ Cast บน Android

Cast SDK มีมุมมองที่กำหนดเอง IntroductoryOverlay ซึ่งใช้เพื่อไฮไลต์ปุ่มแคสต์เมื่อแสดงต่อผู้ใช้เป็นครั้งแรกได้ เพิ่มโค้ดต่อไปนี้ลงใน VideoBrowserActivity

import com.google.android.gms.cast.framework.IntroductoryOverlay
import android.os.Looper

private var mIntroductoryOverlay: IntroductoryOverlay? = null

private fun showIntroductoryOverlay() {
    mIntroductoryOverlay?.remove()
    if (mediaRouteMenuItem?.isVisible == true) {
       Looper.myLooper().run {
           mIntroductoryOverlay = com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(
                    this@VideoBrowserActivity, mediaRouteMenuItem!!)
                   .setTitleText("Introducing Cast")
                   .setSingleTime()
                   .setOnOverlayDismissedListener(
                           object : IntroductoryOverlay.OnOverlayDismissedListener {
                               override fun onOverlayDismissed() {
                                   mIntroductoryOverlay = null
                               }
                          })
                   .build()
          mIntroductoryOverlay!!.show()
        }
    }
}

ตอนนี้ให้เพิ่ม CastStateListener แล้วเรียกใช้เมธอด showIntroductoryOverlay เมื่ออุปกรณ์ Cast พร้อมใช้งานโดยแก้ไขเมธอด onCreate และแทนที่เมธอด onResume และ onPause ให้ตรงกับรายการต่อไปนี้

import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener

private var mCastStateListener: CastStateListener? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.video_browser)
    setupActionBar()
    mCastStateListener = object : CastStateListener {
            override fun onCastStateChanged(newState: Int) {
                if (newState != CastState.NO_DEVICES_AVAILABLE) {
                    showIntroductoryOverlay()
                }
            }
        }
    mCastContext = CastContext.getSharedInstance(this)
}

override fun onResume() {
    super.onResume()
    mCastContext?.addCastStateListener(mCastStateListener!!)
}

override fun onPause() {
    super.onPause()
    mCastContext?.removeCastStateListener(mCastStateListener!!)
}

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

10. ตัวควบคุมที่ขยายแล้ว

รายการตรวจสอบการออกแบบ Google Cast กำหนดให้แอปตัวส่งต้องมีตัวควบคุมแบบขยายสำหรับสื่อที่กำลังแคสต์ ตัวควบคุมแบบขยายคือตัวควบคุมขนาดเล็กเวอร์ชันเต็มหน้าจอ

ภาพวิดีโอที่เล่นบนโทรศัพท์ Android โดยมีตัวควบคุมที่ขยายแล้ววางซ้อนอยู่

Cast SDK มีวิดเจ็ตสำหรับตัวควบคุมแบบขยายที่เรียกว่า ExpandedControllerActivity นี่คือคลาสแบบนามธรรมที่คุณต้องสร้างคลาสย่อยเพื่อเพิ่มปุ่มแคสต์

ก่อนอื่น ให้สร้างไฟล์ทรัพยากรเมนูใหม่ชื่อ expanded_controller.xml สำหรับตัวควบคุมแบบขยายเพื่อแสดงปุ่มแคสต์

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
            android:id="@+id/media_route_menu_item"
            android:title="@string/media_route_menu_title"
            app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

สร้างแพ็กเกจใหม่ expandedcontrols ในแพ็กเกจ com.google.sample.cast.refplayer จากนั้นสร้างไฟล์ใหม่ชื่อ ExpandedControlsActivity.kt ในแพ็กเกจ com.google.sample.cast.refplayer.expandedcontrols

package com.google.sample.cast.refplayer.expandedcontrols

import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory

class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}

ตอนนี้ให้ประกาศ ExpandedControlsActivity ใน AndroidManifest.xml ภายในแท็ก application เหนือ OPTIONS_PROVIDER_CLASS_NAME ดังนี้

<application>
    ...
    <activity
        android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
        </intent-filter>
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
    </activity>
    ...
</application>

แก้ไข CastOptionsProvider และเปลี่ยน NotificationOptions กับ CastMediaOptions เพื่อตั้งค่ากิจกรรมเป้าหมายเป็น ExpandedControlsActivity โดยทำดังนี้

import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity

override fun getCastOptions(context: Context): CastOptions {
    val notificationOptions = NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
            .build()
    val mediaOptions = CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
            .build()
    return CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build()
}

อัปเดตเมธอด LocalPlayerActivity loadRemoteMedia เพื่อแสดง ExpandedControlsActivity เมื่อโหลดสื่อจากระยะไกลแล้ว

import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity

private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    if (mCastSession == null) {
        return
    }
    val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
    remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
        override fun onStatusUpdated() {
            val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
            startActivity(intent)
            remoteMediaClient.unregisterCallback(this)
        }
    })
    remoteMediaClient.load(MediaLoadRequestData.Builder()
                .setMediaInfo(buildMediaInfo())
                .setAutoplay(autoPlay)
                .setCurrentTime(position.toLong()).build())
}

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

11. เพิ่มการรองรับ Cast Connect

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

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

หมายเหตุ: หากต้องการใช้ Cast Connect play-services-cast-framework ต้องเป็น 19.0.0 ขึ้นไป

LaunchOptions

หากต้องการเปิดตัวแอปพลิเคชัน Android TV หรือที่เรียกว่า Android Receiver เราต้องตั้งค่าแฟล็ก setAndroidReceiverCompatible เป็น true ในออบเจ็กต์ LaunchOptions LaunchOptions ออบเจ็กต์นี้กำหนดวิธีเปิดตัวตัวรับและส่งไปยัง CastOptions ที่ส่งคืนโดยคลาส CastOptionsProvider การตั้งค่าสถานะที่กล่าวถึงข้างต้นเป็น false จะเปิดตัวเครื่องรับเว็บสำหรับรหัสแอปที่กำหนดไว้ใน Cast Developer Console

ในไฟล์ CastOptionsProvider.kt ให้เพิ่มโค้ดต่อไปนี้ลงในเมธอด getCastOptions

import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
            .setAndroidReceiverCompatible(true)
            .build()
return new CastOptions.Builder()
        .setLaunchOptions(launchOptions)
        ...
        .build()

ตั้งค่าข้อมูลเข้าสู่ระบบการเปิดตัว

ในฝั่งผู้ส่ง คุณสามารถระบุ CredentialsData เพื่อแสดงถึงผู้ที่เข้าร่วมเซสชันได้ credentials คือสตริงที่ผู้ใช้กำหนดได้ ตราบใดที่แอป ATV เข้าใจสตริงนั้น ระบบจะส่ง CredentialsData ไปยังแอป Android TV ของคุณในระหว่างการเปิดตัวหรือเวลาเข้าร่วมเท่านั้น หากคุณตั้งค่าอีกครั้งขณะที่เชื่อมต่ออยู่ ระบบจะไม่ส่งค่าดังกล่าวไปยังแอป Android TV

คุณต้องกำหนด CredentialsData และส่งไปยังออบเจ็กต์ LaunchOptions เพื่อตั้งค่าข้อมูลเข้าสู่ระบบการเปิดตัว เพิ่มโค้ดต่อไปนี้ลงในเมธอด getCastOptions ในไฟล์ CastOptionsProvider.kt

import com.google.android.gms.cast.CredentialsData
...

val credentialsData = CredentialsData.Builder()
        .setCredentials("{\"userId\": \"abc\"}")
        .build()
val launchOptions = LaunchOptions.Builder()
       ...
       .setCredentialsData(credentialsData)
       .build()

ตั้งค่าข้อมูลเข้าสู่ระบบใน LoadRequest

ในกรณีที่แอป Web Receiver และแอป Android TV จัดการ credentials แตกต่างกัน คุณอาจต้องกำหนด credentials แยกกันสำหรับแต่ละแอป หากต้องการจัดการเรื่องนี้ ให้เพิ่มโค้ดต่อไปนี้ในไฟล์ LocalPlayerActivity.kt ภายใต้ฟังก์ชัน loadRemoteMedia

remoteMediaClient.load(MediaLoadRequestData.Builder()
       ...
       .setCredentials("user-credentials")
       .setAtvCredentials("atv-user-credentials")
       .build())

ตอนนี้ SDK จะจัดการข้อมูลเข้าสู่ระบบที่จะใช้สำหรับเซสชันปัจจุบันโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับแอปตัวรับที่ผู้ส่งแคสต์

การทดสอบ Cast Connect

ขั้นตอนในการติดตั้ง Android TV APK บน Chromecast พร้อม Google TV

  1. ค้นหาที่อยู่ IP ของอุปกรณ์ Android TV โดยปกติแล้ว คุณจะดูได้ที่การตั้งค่า > เครือข่ายและอินเทอร์เน็ต > (ชื่อเครือข่ายที่อุปกรณ์เชื่อมต่ออยู่) ทางด้านขวาจะแสดงรายละเอียดและ IP ของอุปกรณ์ในเครือข่าย
  2. ใช้ที่อยู่ IP ของอุปกรณ์เพื่อเชื่อมต่อผ่าน ADB โดยใช้เทอร์มินัล
$ adb connect <device_ip_address>:5555
  1. จากหน้าต่างเทอร์มินัล ให้ไปที่โฟลเดอร์ระดับบนสุดสำหรับตัวอย่าง Codelab ที่คุณดาวน์โหลดไว้ตอนต้นของ Codelab นี้ เช่น
$ cd Desktop/android_codelab_src
  1. ติดตั้งไฟล์ .apk ในโฟลเดอร์นี้ลงใน Android TV โดยเรียกใช้คำสั่งต่อไปนี้
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. ตอนนี้คุณควรจะเห็นแอปชื่อ Cast Videos ในเมนูแอปของคุณบนอุปกรณ์ Android TV
  2. กลับไปที่โปรเจ็กต์ Android Studio แล้วคลิกปุ่มเรียกใช้เพื่อติดตั้งและเรียกใช้แอปผู้ส่งในอุปกรณ์เคลื่อนที่จริง ที่มุมบนขวา ให้คลิกไอคอนแคสต์ แล้วเลือกอุปกรณ์ Android TV จากตัวเลือกที่มี ตอนนี้คุณควรเห็นแอป Android TV เปิดขึ้นในอุปกรณ์ Android TV และการเล่นวิดีโอจะช่วยให้คุณควบคุมการเล่นวิดีโอโดยใช้รีโมต Android TV ได้

12. ปรับแต่งวิดเจ็ต Cast

คุณปรับแต่งวิดเจ็ต Cast ได้โดยการตั้งค่าสี จัดรูปแบบปุ่ม ข้อความ และลักษณะภาพปก รวมถึงเลือกประเภทปุ่มที่จะแสดง

อัปเดต res/values/styles_castvideo.xml

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
    <item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
    <item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
    <item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
    <item name="castExpandedControllerToolbarStyle">
        @style/ThemeOverlay.AppCompat.ActionBar
    </item>
    ...
</style>

ประกาศธีมที่กำหนดเองต่อไปนี้

<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
    <item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
    <item name="mediaRouteButtonTint">#EEFF41</item>
</style>

<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
    <item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
    <item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
    <item name="android:textColor">#FFFFFF</item>
</style>

<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
    <item name="castShowImageThumbnail">true</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
    <item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
    <item name="castBackground">@color/accent</item>
    <item name="castProgressBarColor">@color/orange</item>
</style>

<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castButtonColor">#FFFFFF</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>

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

ตอนนี้คุณทราบวิธีเปิดใช้การแคสต์ในแอปวิดีโอโดยใช้วิดเจ็ต Cast SDK บน Android แล้ว

ดูรายละเอียดเพิ่มเติมได้ที่คู่มือนักพัฒนาซอฟต์แวร์ Android Sender