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

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

הלוגו של Google Cast

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

מה זה Google Cast?

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

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

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

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

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

מה תלמדו

  • איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
  • איך מוסיפים את לחצן ההעברה כדי לבחור מכשיר Google Cast.
  • איך מתחברים למכשיר 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 מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.

לוחצים על קובץ > לחצן 'סנכרון הפרויקט עם Gradle' ב-Android Studio סנכרון הפרויקט עם קובצי Gradle

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

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

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

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

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

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

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

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

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

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

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

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

שאלות נפוצות

4. מכינים את הפרויקט לתחילת העבודה

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

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

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

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

  1. בוחרים את הספרייה סמל התיקייהapp-start מההורדה של קוד לדוגמה (בוחרים באפשרות Import Project במסך הפתיחה או באפשרות File > New > Import Project... בתפריט).
  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 יש אובייקט גלובלי מסוג סינגלטון, 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 כדי לאפשר למשתמש לבחור מכשיר Cast. הלחצן להפעלת 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 באופן דומה.

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

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

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

אנחנו צריכים לרשום את ה-Session Listener שלנו ולהפעיל כמה משתנים שבהם נשתמש בפעילות. משנים את השיטה 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))
    ...
}

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

8. התראה ומסך הנעילה

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

איור של טלפון Android שמוצגים בו פקדי מדיה באזור ההתראות

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

ה-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

ה-Cast SDK מספק תצוגה מותאמת אישית, 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 ומפעילים את השיטה 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 Console.

בקובץ CastOptionsProvider.kt, מוסיפים את הפקודה הבאה ל-method 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.

כדי להגדיר את Launch Credentials, צריך להגדיר את 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. עכשיו אמורה להופיע אפליקציה בשם הפעלת Cast של סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
  2. חוזרים לפרויקט ב-Android Studio ולוחצים על הלחצן Run כדי להתקין ולהפעיל את אפליקציית השליחה בנייד הפיזי. בפינה השמאלית העליונה, לוחצים על הסמל להפעלת Cast ובוחרים את מכשיר 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.