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

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

הלוגו של Google Cast

בשיעור הזה תלמדו איך לשנות אפליקציה קיימת ל-Android TV כדי לתמוך בהעברה (cast) ובתקשורת מהאפליקציות הקיימות של שולחי Cast.

מה זה Google Cast ו-Cast Connect?

Google Cast מאפשר למשתמשים להעביר (cast) תוכן ממכשיר נייד לטלוויזיה. סשן טיפוסי של Google Cast כולל שני רכיבים – אפליקציה של שולח ואפליקציה של המקבל. אפליקציות של השולחים, כמו אפליקציה לנייד או אתר כמו youtube.com, יכולות להפעיל אפליקציה של מקלט Cast ולשלוט בה. אפליקציות של מקלט Cast הן אפליקציות HTML 5 שפועלות במכשירי Chromecast ו-Android TV.

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

התכונה Cast Connect מתבססת על התשתית הזו, ואפליקציית Android TV משמשת כמקלטת. ספריית Cast Connect מאפשרת לאפליקציית Android TV לקבל הודעות ולשדר סטטוס מדיה כאילו היא אפליקציית cast.

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

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

מה תלמדו

  • איך להוסיף את הספרייה של Cast Connect לאפליקציה לדוגמה של ATV.
  • איך לחבר שולח העברה ולהפעיל את אפליקציית ATV
  • איך להפעיל מדיה באפליקציית ATV דרך אפליקציית שולח Cast.
  • איך לשלוח סטטוס מדיה מאפליקציית ATV לאפליקציות שולח של Cast.

מה הדרישות כדי להצטרף לתוכנית?

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

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

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

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

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

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

רישום מכשירים למפתחים

כדי להפעיל יכולות של Cast Connect לפיתוח אפליקציות, עליך לרשום את המספר הסידורי של ה-Chromecast המובנה של מכשיר ה-Android TV שבו בכוונתך להשתמש ב-Cast Developer Console. ניתן למצוא את המספר הסידורי על ידי מעבר אל הגדרות > העדפות מכשיר > Chromecast Built-In > מספר סידורי ב-Android TV. לתשומת ליבכם, המספר הזה שונה מהמספר הסידורי של המכשיר הפיזי. צריך לקבל אותו בשיטה המתוארת למעלה.

תמונה של מסך Android TV שמוצג בו המסך של Chromecast Built-In, מספר הגרסה והמספר הסידורי

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

התקנת אפליקציית השולח ב-Android

כדי לבדוק בקשות שליחה ממכשיר נייד, סיפקנו אפליקציית שולח פשוטה שנקראת "cast סרטונים" בתור קובץ mobile-sender-0629.apk בהורדת ה-ZIP של קוד המקור. אנחנו נעזר ב-ADB כדי להתקין את ה-APK. אם כבר התקנת גרסה אחרת של סרטון Cast, עליך להסיר את הגרסה הזו מכל הפרופילים במכשיר לפני המשך הפעולה.

  1. מפעילים את האפשרויות למפתחים וניפוי באגים ב-USB בטלפון Android.
  2. מחברים כבל נתונים USB כדי לחבר את טלפון Android למחשב הפיתוח.
  3. מתקינים את mobile-sender-0629.apk בטלפון Android.

תמונה של חלון טרמינל שבו מריצים את פקודת ההתקנה adb כדי להתקין את mobile-sender.apk

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

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

התקנה של אפליקציית Android TV

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

  1. בוחרים באפשרות ייבוא פרויקט במסך הפתיחה או באפשרויות התפריט קובץ > חדש > ייבוא פרויקט....
  2. בוחרים את הספרייה סמל התיקייהapp-done מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.
  3. לוחצים על קובץ > לחצן סנכרון פרויקט Android App Studio עם Gradle סנכרון פרויקט עם קובצי Gradle.
  4. מפעילים את האפשרויות למפתחים וניפוי באגים ב-USB במכשיר Android TV.
  5. ADB מתחבר למכשיר ה-Android TV, המכשיר אמור להופיע ב-Android Studio. תמונה של מכשיר Android TV שמופיע בסרגל הכלים של Android Studio
  6. לוחצים על הלחצן לחצן הרצה ב-Android Studio, משולש ירוק שמפנה ימינההפעלה. לאחר מספר שניות אפליקציית ATV שנקראת Cast Connect Codelab אמורה להופיע.

רוצה להפעיל את Cast Connect באפליקציית ATV?

  1. עוברים למסך הבית של Android TV.
  2. בטלפון Android, פותחים את אפליקציית השולח של העברת סרטונים. לוחצים על לחצן הפעלת Cast סמל של לחצן הפעלת Cast ובוחרים את מכשיר ה-ATV.
  3. אפליקציית ה-ATV של Cast Connect Codelab תופעל ב-ATV, ולחצן הפעלת Cast בשולח יציין שהוא מחובר סמל של לחצן העברה עם צבעים הפוכים.
  4. בוחרים סרטון מאפליקציית ATV והסרטון יתחיל לפעול ב-ATV.
  5. בטלפון הנייד, מיני שלט רחוק גלוי עכשיו בחלק התחתון של אפליקציית השולח. אפשר להשתמש בלחצן 'הפעלה/השהיה' כדי לשלוט בהפעלה.
  6. בוחרים סרטון מהטלפון הנייד ומפעילים אותו. הסרטון יתחיל לפעול ב-ATV והבקר המורחב יוצג בנייד.
  7. נועלים את הטלפון וכשמבטלים את הנעילה, אמורה להופיע התראה במסך הנעילה כדי לשלוט בהפעלה של מדיה או להפסיק את ההעברה.

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

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

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

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

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

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

האפליקציה מספקת רשימה של סרטונים שהמשתמש יכול לעיין בהם. המשתמשים יכולים לבחור סרטון להפעלה ב-Android TV. האפליקציה מורכבת משתי פעילויות עיקריות: MainActivity ו-PlaybackActivity.

MainActivity

הפעילות הזו מכילה מקטע (MainFragment). רשימת הסרטונים והמטא-נתונים המשויכים אליהם מוגדרים במחלקה MovieList והשיטה setupMovies() מופעלת כדי ליצור רשימה של Movie אובייקטים.

אובייקט Movie מייצג ישות וידאו עם כותרת, תיאור, תמונות ממוזערות של תמונה וכתובת URL של סרטון. כל אובייקט Movie מקושר ל-CardPresenter כדי להציג את התמונה הממוזערת של הסרטון עם השם והאולפן, והוא מועבר אל ArrayObjectAdapter.

כשבוחרים פריט, אובייקט Movie המתאים מועבר אל PlaybackActivity.

PlaybackActivity

פעילות זו מכילה מקטע (PlaybackVideoFragment) שמארח VideoView עם ExoPlayer, פקדי מדיה מסוימים ואזור טקסט להצגת התיאור של הסרטון שנבחר, ומאפשר למשתמש להפעיל את הסרטון ב-Android TV. המשתמש יכול להשתמש בשלט הרחוק כדי להפעיל/להשהות סרטונים או כדי לחפש את ההפעלה של סרטונים.

דרישות מוקדמות ל-Cast Connect

ב-Cast Connect נעשה שימוש בגרסאות חדשות של Google Play Services, שכדי להשתמש בהן במרחב השמות AndroidX, צריך לעדכן את אפליקציית ה-ATV.

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

5. הגדרת התמיכה ב-Cast

יחסי תלות

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

dependencies {
    ....

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

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

אתחול

CastReceiverContext הוא אובייקט singleton לתיאום כל האינטראקציות של ההעברה. עליך ליישם את הממשק ReceiverOptionsProvider כדי לספק את CastReceiverOptions לאחר אתחול של CastReceiverContext.

יוצרים קובץ CastReceiverOptionsProvider.kt ומוסיפים את המחלקה הבאה לפרויקט:

package com.google.sample.cast.castconnect

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

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

לאחר מכן צריך לציין את הספק של אפשרויות המקלט בתוך התג <application> בקובץ AndroidManifest.xml של האפליקציה:

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

כדי להתחבר לאפליקציית ATV משולח ההעברה, בוחרים פעילות שרוצים להפעיל. ב-Codelab הזה, נפעיל את MainActivity של האפליקציה כשתתחיל העברה (cast). בקובץ AndroidManifest.xml, יש להוסיף את מסנן ה-Intent להפעלה בתוך MainActivity.

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

מחזור החיים של הקשר מקלט העברה (cast)

צריך להפעיל את CastReceiverContext כשהאפליקציה מופעלת ולהפסיק את CastReceiverContext כשהאפליקציה מועברת לרקע. מומלץ להשתמש ב-LifecycleObserver מספריית androidx.lifecycle כדי לנהל את השיחות אל CastReceiverContext.start() ואל CastReceiverContext.stop()

פותחים את הקובץ MyApplication.kt, מפעילים את ההקשר להעברה (cast) על ידי קריאה ל-initInstance() בשיטת onCreate של האפליקציה. במחלקה AppLifeCycleObserver start(), CastReceiverContext כשממשיכים את האפליקציה ו-stop() כשהאפליקציה מושהית:

package com.google.sample.cast.castconnect

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

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

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

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

חיבור MediaSession ל-MediaManager

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

כשיוצרים MediaSession, צריך גם לספק את האסימון הנוכחי של MediaSession ל-MediaManager כדי לדעת לאן לשלוח את הפקודות ולאחזר את מצב ההפעלה של המדיה. בקובץ PlaybackVideoFragment.kt, צריך לוודא ש-MediaSession אותחל לפני שמגדירים את האסימון ל-MediaManager.

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

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

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

        }
    }
}

כשמשחררים את MediaSession בגלל הפעלה לא פעילה, צריך להגדיר אסימון null ב-MediaManager:

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

שנפעיל את האפליקציה לדוגמה?

לוחצים על הלחצן לחצן &#39;הפעלה&#39; ב-Android Studio, משולש ירוק שמצביע ימינההפעלה כדי לפרוס את האפליקציה במכשיר ה-ATV, לסגור אותה ולחזור למסך הבית של ATV. מהשולח, לוחצים על לחצן הפעלת Cast סמל של לחצן הפעלת Cast ובוחרים את מכשיר ה-ATV שלכם. תוכלו לראות שאפליקציית ATV מופעלת במכשיר ה-ATV ומצב לחצן הפעלת Cast מחובר.

6. המדיה בטעינה

פקודת הטעינה נשלחת באמצעות Intent עם שם החבילה שהגדרתם ב-Play Console. צריך להוסיף לאפליקציה ל-Android TV את מסנן Intent שהוגדר מראש, כדי לציין את פעילות היעד שמקבלת את ה-Intent הזה. בקובץ AndroidManifest.xml, יש להוסיף את מסנן Intent הטעינה אל PlayerActivity:

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

טיפול בבקשות טעינה ב-Android TV

עכשיו שהפעילות מוגדרת לקבל את ה-Intent הזה שמכיל בקשת טעינה, נצטרך לטפל בה.

האפליקציה מבצעת קריאה לשיטה פרטית שנקראת processIntent כשהפעילות מתחילה. השיטה הזו כוללת את הלוגיקה לעיבוד אובייקטים נכנסים. כדי לטפל בבקשת טעינה, נשנה את השיטה הזו ונשלח את הכוונה להמשך עיבוד על ידי קריאה לשיטת onNewIntent של המכונה של MediaManager. אם MediaManager מזהה שהכוונה היא בקשת טעינה, הוא מחלץ את האובייקט MediaLoadRequestData מה-Intent ומפעיל את MediaLoadCommandCallback.onLoad(). יש לשנות את השיטה processIntent בקובץ PlaybackVideoFragment.kt כדי לטפל ב-Intent שמכיל את בקשת הטעינה:

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

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

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

לאחר מכן נרחיב את המחלקה המופשטת MediaLoadCommandCallback, שתחליף את השיטה onLoad() שנקראה על ידי MediaManager. השיטה הזו מקבלת את הנתונים של בקשת הטעינה וממירה אותם לאובייקט Movie. לאחר ההמרה, הנגן המקומי יפעיל את הסרט. בשלב הבא MediaManager מתעדכן בMediaLoadRequest ומשדר את MediaStatus לשולחים המחוברים. יוצרים מחלקה פרטית מקוננת בשם MyMediaLoadCommandCallback בקובץ PlaybackVideoFragment.kt:

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

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

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

עכשיו, אחרי שהוגדר קריאה חוזרת, אנחנו צריכים לרשום אותו ב-MediaManager. צריך לרשום את הקריאה החוזרת לפני הקריאה ל-MediaManager.onNewIntent(). צריך להוסיף setMediaLoadCommandCallback כשהנגן מאותחל:

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

שנפעיל את האפליקציה לדוגמה?

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

7. תמיכה בפקודות בקרת העברה

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

מוסיפים MyMediaCommandCallback למכונה של MediaManager באמצעות setMediaCommandCallback כשהנגן מאותחל:

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

יוצרים מחלקה MyMediaCommandCallback כדי לשנות את השיטות, למשל onQueueUpdate(), כדי לתמוך בפקודות של בקרת ההעברה:

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

8. עבודה עם סטטוס מדיה

שינוי סטטוס המדיה

אפליקציית Cast Connect מקבלת את סטטוס המדיה הבסיסי מסשן המדיה. כדי לתמוך בתכונות מתקדמות, האפליקציה ל-Android TV יכולה להגדיר מאפייני סטטוס נוספים ולעקוף אותם באמצעות MediaStatusModifier. MediaStatusModifier יפעל תמיד בMediaSession שהגדרת ב-CastReceiverContext.

לדוגמה, כדי לציין setMediaCommandSupported כשמופעלת קריאה חוזרת (callback) של onLoad:

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

יירוט MediaStatus לפני השליחה

בדומה ל-MessageInterceptor של ה-SDK של המקבל באינטרנט, אפשר לציין MediaStatusWriter ב-MediaManager כדי לבצע שינויים נוספים ב-MediaStatus לפני שהוא משודר לשולחים המחוברים.

לדוגמה, אפשר להגדיר נתונים מותאמים אישית בMediaStatus לפני השליחה לשולחים לנייד:

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

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

9. מזל טוב

עכשיו אתה יודע איך להפעיל העברה (cast) לאפליקציה ל-Android TV באמצעות ספריית Cast Connect.

לפרטים נוספים, כדאי לעיין במדריך למפתחים: /cast/docs/android_tv_receiver.