הפעלת העברה (cast) של אפליקציה ל-Android

1. סקירה כללית

הלוגו של Google Cast

בקודלאב הזה תלמדו איך לשנות אפליקציית וידאו קיימת ל-Android כדי להעביר תוכן למכשיר שתומך ב-Google Cast.

מה זה Google Cast?

Google Cast מאפשר למשתמשים להעביר תוכן מהנייד לטלוויזיה. לאחר מכן, המשתמשים יכולים להשתמש בנייד שלהם כשלט רחוק להפעלת מדיה בטלוויזיה.

Google Cast SDK מאפשר לכם להרחיב את האפליקציה כדי לשלוט בטלוויזיה או במערכת סאונד. ה-SDK של Cast מאפשר לך להוסיף את רכיבי ממשק המשתמש הנחוצים על סמך רשימת המשימות לעיצוב של Google Cast.

רשימת המשימות לעיצוב של Google Cast מסופקת כדי להפוך את חוויית המשתמש של Cast לפשוטה וצפויה בכל הפלטפורמות הנתמכות.

מה אנחנו הולכים לבנות?

בסיום הקודלאב הזה, תהיה לכם אפליקציית וידאו ל-Android שתוכלו להעביר (cast) באמצעותה סרטונים למכשיר שתומך ב-Google Cast.

מה תלמדו

  • איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
  • איך מוסיפים את לחצן ההעברה כדי לבחור מכשיר Google Cast.
  • איך מתחברים למכשיר Cast ומפעילים מקלט מדיה.
  • איך מעבירים סרטון.
  • איך מוסיפים לוח בקרה של Cast Mini לאפליקציה.
  • איך לתמוך בהתראות מדיה ובפקדים במסך הנעילה.
  • איך להוסיף בקר מורחב.
  • איך להציג שכבת-על של מבוא.
  • איך מתאימים אישית ווידג'טים של Cast.
  • איך משלבים את Cast Connect

מה נדרש

  • Android SDK בגרסה העדכנית ביותר.
  • Android Studio מגרסה 3.2 ואילך
  • מכשיר נייד אחד עם Android 4.1 ואילך (Jelly Bean, API ברמה 16).
  • כבל USB להעברת נתונים לחיבור המכשיר הנייד למחשב הפיתוח.
  • מכשיר Google Cast, כמו Chromecast או Android TV, עם הגדרת גישה לאינטרנט.
  • טלוויזיה או צג עם יציאת HDMI.
  • כדי לבדוק את השילוב של Cast Connect, נדרש מכשיר Chromecast with Google TV, אבל הוא לא נדרש בשאר השלבים של Codelab. אם אין לכם מכשיר כזה, אתם יכולים לדלג על השלב הוספת תמיכה ב-Cast Connect לקראת סוף המדריך הזה.

ניסיון

  • נדרש ידע קודם בפיתוח ב-Kotlin וב-Android.
  • בנוסף, נדרשת לך ניסיון קודם בצפייה בטלוויזיה :)

איך תוכלו להשתמש במדריך הזה?

לקרוא את המאמר לקרוא את המאמר ולבצע את התרגילים

מה מידת שביעות הרצון שלך מהניסיון שלך בפיתוח אפליקציות ל-Android?

מתחילים בינוניים מומחים

איזה דירוג מגיע לדעתך לחוויית הצפייה בטלוויזיה?

מתחילים בינוניים מומחים

2. קבלת קוד לדוגמה

אתם יכולים להוריד את כל הקוד לדוגמה למחשב...

ופורקים את קובץ ה-ZIP שהורדתם.

3. הרצת האפליקציה לדוגמה

סמל של מצפן

קודם כול נראה איך נראית האפליקציה לדוגמה שהושלמה. האפליקציה היא נגן וידאו בסיסי. המשתמש יכול לבחור סרטון מרשימה ואז להפעיל את הסרטון באופן מקומי במכשיר או להפעיל Cast שלו למכשיר Google Cast.

אחרי שתורידו את הקוד, תוכלו להיעזר בהוראות הבאות כדי לפתוח ולהריץ את אפליקציית הדוגמה המושלמת ב-Android Studio:

בוחרים באפשרות Import Project (ייבוא פרויקט) במסך הפתיחה או באפשרויות התפריט File > New > Import Project…‎ (קובץ > חדש > ייבוא פרויקט…).

בוחרים את הספרייה סמל של תיקייהapp-done מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.

לוחצים על File (קובץ) > לחצן 'סנכרון הפרויקט עם Gradle' ב-Android Studio Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי Gradle).

מפעילים ניפוי באגים ב-USB במכשיר Android – ב-Android מגרסה 4.2 ואילך, מסך האפשרויות למפתחים מוסתר כברירת מחדל. כדי להציג אותו, עוברים אל הגדרות > מידע על הטלפון ומקישים על מספר Build שבע פעמים. חוזרים למסך הקודם, עוברים אל מערכת > מתקדם ומקישים על אפשרויות למפתחים בתחתית המסך. לאחר מכן, מקישים על ניפוי באגים ב-USB כדי להפעיל אותה.

מחברים את מכשיר Android ולוחצים על הלחצן לחצן 'הרצה' ב-Android Studio, משולש ירוק שמצביע ימינהRun ב-Android Studio. אחרי כמה שניות אמורה להופיע אפליקציית הווידאו העברת סרטונים.

לוחצים על הלחצן להפעלת Cast באפליקציית הווידאו ובוחרים את מכשיר Google Cast.

בוחרים סרטון ולוחצים על לחצן ההפעלה.

הסרטון יתחיל לפעול במכשיר Google Cast.

הבקר המורחב יוצג. אפשר להשתמש בלחצן ההפעלה/ההשהיה כדי לשלוט בהפעלה.

חוזרים לרשימת הסרטונים.

אמצעי בקרה מיניאטורי יופיע עכשיו בתחתית המסך. איור של טלפון Android שבו פועלת אפליקציית Cast Videos, עם שלט מיני שמופיע בחלק התחתון של המסך

לוחצים על לחצן ההשהיה בשלט המיני כדי להשהות את הסרטון במכשיר הקולט. לוחצים על לחצן ההפעלה בנגן המיני כדי להמשיך את הפעלת הסרטון.

לוחצים על הלחצן הראשי בנייד. מושכים למטה את ההתראות ועכשיו אמורה להופיע התראה לגבי סשן ההעברה (cast).

נועלים את הטלפון. כשמבטלים את הנעילה, אמורה להופיע התראה במסך הנעילה כדי לשלוט בהפעלת המדיה או להפסיק את ההעברה (cast).

חוזרים לאפליקציית הווידאו ולוחצים על לחצן ההעברה כדי להפסיק את ההעברה במכשיר Google Cast.

שאלות נפוצות

4. הכנת פרויקט ההתחלה

איור של טלפון Android שבו פועלת אפליקציית 'העברת סרטונים'

אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציה שהורדת. ריכזנו כאן כמה מונחים של Google Cast שבהם נשתמש במהלך הקודלאב:

  • אפליקציית שליחת הודעות שפועלת במכשיר נייד או במחשב נייד,
  • אפליקציית מקלט פועלת במכשיר Google Cast.

עכשיו אתם מוכנים לפתח את הפרויקט באמצעות Android Studio:

  1. בוחרים את הספרייה סמל התיקייהapp-start מתוך הורדת הקוד לדוגמה (בוחרים באפשרות ייבוא פרויקט במסך הפתיחה או באפשרות בתפריט קובץ > חדש > ייבוא פרויקט...).
  2. לוחצים על הלחצן לחצן 'סנכרון הפרויקט עם Gradle' ב-Android Studio Sync Project with Gradle Files.
  3. לוחצים על הלחצן לחצן ההפעלה של Android Studio, משולש ירוק שמצביע ימינהRun כדי להריץ את האפליקציה ולעיין בממשק המשתמש.

עיצוב אפליקציות

האפליקציה מאחזרת רשימה של סרטונים משרת אינטרנט מרוחק ומספקת רשימה למשתמש לגלישה. המשתמשים יכולים לבחור סרטון כדי לראות את הפרטים שלו או להפעיל אותו באופן מקומי במכשיר הנייד.

האפליקציה מורכבת משתי פעילויות עיקריות: VideoBrowserActivity ו-LocalPlayerActivity. כדי לשלב את הפונקציונליות של Google Cast, הפעילויות צריכות לקבל בירושה מה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. הוספת הלחצן להפעלת Cast

איור של החלק העליון של טלפון Android שבו פועלת אפליקציית Cast Video. לחצן ההעברה מופיע בפינה השמאלית העליונה של המסך.

באפליקציה שתומכת ב-Cast, הלחצן להפעלת Cast מוצג בכל אחת מהפעילויות שלה. לחיצה על הלחצן להפעלת Cast תציג רשימה של מכשירי Cast שהמשתמש יכול לבחור. אם המשתמש הפעיל תוכן באופן מקומי במכשיר השולח, בחירה במכשיר Cast מפעילה או ממשיכה את ההפעלה במכשיר ה-Cast הזה. בכל שלב במהלך סשן העברה, המשתמש יכול ללחוץ על לחצן ההעברה ולהפסיק את העברת האפליקציה למכשיר ההעברה. המשתמש צריך להיות מסוגל להתחבר למכשיר ההעברה או להתנתק ממנו בכל פעילות באפליקציה, כפי שמתואר ברשימת המשימות לעיצוב של Google Cast.

יחסי תלות

מעדכנים את הקובץ build.gradle של האפליקציה כך שיכלול את יחסי התלות הנדרשים בספריות:

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"
}

צריך לסנכרן את הפרויקט כדי לאשר את פיתוח גרסת ה-build של הפרויקט ללא שגיאות.

אתחול

למסגרת Cast יש אובייקט singleton גלובלי, CastContext, שמרכז את כל האינטראקציות עם Cast.

עליכם להטמיע את הממשק OptionsProvider כדי לספק את ה-CastOptions הדרוש לאתחול הסינגלטון CastContext. האפשרות החשובה ביותר היא מזהה האפליקציה של הנמען, שמשמשים לסינון תוצאות החיפוש של מכשירי 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.

לחצן הפעלת Cast

עכשיו, אחרי שה-CastContext הותחל, אנחנו צריכים להוסיף את לחצן ההעברה כדי לאפשר למשתמש לבחור מכשיר להעברה. הלחצן להפעלת Cast מוטמע על ידי 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 ל-framework של 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, משולש ירוק שמצביע ימינהRun כדי להריץ את האפליקציה בנייד. לחצן Cast אמור להופיע בסרגל הפעולות של האפליקציה. כשלוחצים עליו, מוצגת רשימה של מכשירי Cast ברשת המקומית. זיהוי המכשירים מנוהל באופן אוטומטי על ידי CastContext. עליך לבחור את מכשיר ה-Cast שלך ואפליקציית המקלט לדוגמה תיטען במכשיר ה-Cast. ניתן לנווט בין פעילות הגלישה לבין הפעילות המקומית בנגן, ומצב הלחצן להפעלת Cast נשמר בסנכרון.

עדיין לא הוספנו תמיכה בהפעלת מדיה, ולכן אי אפשר להפעיל סרטונים במכשיר ההעברה (cast). לוחצים על הלחצן להפעלת Cast כדי להתנתק.

6. העברה (cast) של תוכן סרטון

איור של טלפון Android שבו פועלת אפליקציית &#39;העברת סרטונים&#39;

נרחיב את האפליקציה לדוגמה כדי להפעיל סרטונים מרחוק במכשיר Cast. כדי לעשות זאת, עלינו להאזין לאירועים השונים שנוצרו על ידי Cast frame

העברת מדיה

באופן כללי, כדי להפעיל מדיה במכשיר Cast, צריך לבצע את הפעולות הבאות:

  1. יוצרים אובייקט MediaInfo שמתאר פריט מדיה.
  2. מתחברים למכשיר ההעברה (cast) ופותחים את אפליקציית המקלט.
  3. טוענים את האובייקט MediaInfo למכשיר המקבל ומפעילים את התוכן.
  4. מעקב אחר סטטוס המדיה.
  5. שליחת פקודות הפעלה למכשיר הקולט על סמך אינטראקציות של משתמשים.

כבר ביצענו את שלב 2 בקטע הקודם. קל לבצע את שלב 3 באמצעות מסגרת Cast. שלב 1 הוא למעשה מיפוי של אובייקט אחד לאובייקט אחר. MediaInfo הוא אובייקט שמערכת Cast מבינה, ו-MediaItem הוא האנקפסולציה של פריט המדיה באפליקציה שלנו. אנחנו יכולים למפות בקלות MediaItem ל-MediaInfo.

באפליקציית הדוגמה LocalPlayerActivity כבר יש הבחנה בין הפעלה מקומית לבין הפעלה מרחוק באמצעות המאפיין enum הזה:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

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

ב-Codelab הזה לא חשוב שתבינו בדיוק איך פועלת כל הלוגיקה של הנגן לדוגמה. חשוב להבין שיהיה צורך לשנות את נגן המדיה של האפליקציה כדי שיהיה מודע לשני מיקומי ההפעלה באופן דומה.

בשלב הזה הנגן המקומי תמיד נמצא במצב ההפעלה המקומי כי הוא עדיין לא יודע על המצבים של ההעברה (cast). אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי מצבים שמתרחשים ב-Cast framework. לדוגמה, אם אנחנו מתחילים להפעיל Cast, צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהפקדים. באופן דומה, אם נפסיק את ההעברה (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(). סשנים נוצרים ומסתיימים באופן אוטומטי בתגובה לאינטראקציות של המשתמשים עם תיבות הדו-שיח של Cast.

אנחנו צריכים לרשום את מאזין הסשנים שלנו ולאתחל כמה משתנים שבהם נשתמש בפעילות. משנים את השיטה 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. אפשר לגשת ל-method 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()
    }
}

עכשיו אפשר לעדכן את השיטות השונות הקיימות כדי להשתמש בלוגיקת הסשן של Cast שתומכת בהפעלה מרחוק:

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, משולש ירוק שמצביע ימינההפעלה כדי להפעיל את האפליקציה בנייד. מחברים את מכשיר Cast ומתחילים להפעיל סרטון. הסרטון אמור להופיע במכשיר המקבל.

7. בקר מיני

לפי רשימת המשימות לעיצוב של Cast, כל אפליקציית Cast צריכה לספק שליטה מינימליסטית שמופיעה כשהמשתמש עוזב את דף התוכן הנוכחי. בנגן המיני יש גישה מיידית ותזכורת חזותית לסשן ההעברה הנוכחי.

איור של החלק התחתון של טלפון Android שבו מוצג נגן הווידאו המיני באפליקציית Cast Videos

‏Cast SDK מספק תצוגה מותאמת אישית, MiniControllerFragment, שאפשר להוסיף לקובץ הפריסה של האפליקציה של הפעילויות שבהן רוצים להציג את השליטה המינימלית.

מוסיפים את הגדרת הקטע הבא לתחתית הקובץ 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.

MediaNotificationService יפעל ברקע כשהשולח מעביר תוכן, ויציג התראה עם תמונה ממוזערת של התמונה ומטא-נתונים לגבי הפריט הנוכחי שניתן להפעיל Cast, לחצן הפעלה/השהיה ולחצן עצירה.

אפשר להפעיל את הפקדים של ההתראות ומסך הנעילה באמצעות 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, משולש ירוק שמצביע ימינהRun כדי להריץ את האפליקציה בנייד. מעבירים סרטון ומנווטים מחוץ לאפליקציה לדוגמה. אמורה להופיע התראה על הסרטון שפועל כרגע במכשיר המקבל. נועלים את המכשיר הנייד, ומסך הנעילה אמור להציג עכשיו את הפקדים להפעלת המדיה במכשיר ההעברה.

איור של טלפון Android שמוצגים בו פקדי מדיה במסך הנעילה

9. שכבת-על של מבצע היכרות

כדי להשתמש ברשימת המשימות לעיצוב של Google Cast, יש צורך באפליקציית שולח שתציג את לחצן הפעלת Cast למשתמשים קיימים, כדי להודיע להם שאפליקציית השולח תומכת עכשיו בהעברה וגם עוזרת למשתמשים חדשים ב-Google Cast.

איור שבו מוצגת שכבת-העל המקדימה של Cast סביב הלחצן להפעלת Cast באפליקציית Cast Videos ל-Android

ערכת ה-SDK של Cast מספקת תצוגה מותאמת אישית, IntroductoryOverlay, שאפשר להשתמש בה כדי להדגיש את לחצן ההעברה (cast) כשהוא מוצג למשתמשים בפעם הראשונה. מוסיפים ל-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 וקוראים ל-method 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, משולש ירוק שמצביע ימינהRun (הפעלה) כדי להפעיל את האפליקציה במכשיר הנייד. אמורה להופיע שכבת-על עם הסבר על האפליקציה (אם שכבת-העל לא מוצגת, צריך לנקות את נתוני האפליקציה).

10. שלט רחוק מורחב

לפי רשימת המשימות לעיצוב של Google Cast, אפליקציית השליחה צריכה לספק אמצעי בקרה מורחב למדיה שמעבירים. השליטה המורחבת היא גרסה במסך מלא של השליטה המינימלית.

איור של סרטון שמתנגן בטלפון Android עם שכבת-על של הבקר המורחב

‏Cast SDK מספק ווידג'ט לבקר המורחב שנקרא ExpandedControllerActivity. זהו כיתת מופשטת שצריך ליצור ממנה מחלקה משנית כדי להוסיף לחצן להפעלת Cast.

קודם כול, יוצרים קובץ משאבים חדש של תפריט, שנקרא expanded_controller.xml, עבור השליטה המורחבת כדי לספק את לחצן ההעברה (cast):

<?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())
}

לוחצים על הלחצן לחצן &#39;הרצה&#39; ב-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.

מוסיפים את הטקסט הבא לשיטה getCastOptions בקובץ CastOptionsProvider.kt:

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

אם האפליקציה של מקלט האינטרנט והאפליקציה ל-Android TV מטפלות ב-credentials באופן שונה, יכול להיות שתצטרכו להגדיר credentials נפרד לכל אחת מהן. כדי לטפל בבעיה הזו, מוסיפים את הקוד הבא לקובץ LocalPlayerActivity.kt, בפונקציה loadRemoteMedia:

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

בהתאם לאפליקציית המקבל שאליה השולח מבצע העברה (cast), ה-SDK יטפל עכשיו באופן אוטומטי בפרטי הכניסה שבהם יש להשתמש בסשן הנוכחי.

בדיקת Cast Connect

השלבים להתקנת קובץ ה-APK של Android TV ב-Chromecast with 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. עכשיו אמורה להופיע אפליקציה בשם העברת סרטונים בתפריט האפליקציות שלך במכשיר 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) באפליקציית וידאו באמצעות ווידג'טים של Cast SDK ב-Android.

פרטים נוספים זמינים במדריך למפתחים של Android Sender.