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

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

הלוגו של Google Cast

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

מה זה Google Cast ו-Cast Connect?

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

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

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

מה אנחנו הולכים ליצור?

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

מה תלמדו

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

מה נדרש

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

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

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

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

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

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

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

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

תמונה של מסך Android TV שבו מוצגים המסך 'Google Cast', מספר הגרסה והמספר הסידורי

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

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

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

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

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

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

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

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

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

  1. בוחרים באפשרות Import Project (ייבוא פרויקט) במסך הפתיחה או באפשרויות התפריט File > New > Import Project…‎ (קובץ > חדש > ייבוא פרויקט…).
  2. בוחרים את הספרייה סמל תיקייהapp-done מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.
  3. לוחצים על File (קובץ) > הלחצן Sync Project with Gradle (סנכרון הפרויקט עם Gradle) ב-Android App Studio Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי Gradle).
  4. מפעילים את האפשרויות למפתחים ואת ניפוי הבאגים ב-USB במכשיר Android TV.
  5. ADB connect עם מכשיר Android TV, המכשיר אמור להופיע ב-Android Studio. תמונה שבה מכשיר Android TV מופיע בסרגל הכלים של Android Studio
  6. לוחצים על הלחצן לחצן ההפעלה של Android Studio, משולש ירוק שמצביע ימינהRun (הפעלה). האפליקציה ל-ATV בשם Cast Connect Codelab אמורה להופיע אחרי כמה שניות.

Let's play Cast Connect with ATV app

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

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

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

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

  1. בוחרים באפשרות Import Project (ייבוא פרויקט) במסך הפתיחה או באפשרויות התפריט File > New > Import Project…‎ (קובץ > חדש > ייבוא פרויקט…).
  2. בוחרים את הספרייה סמל תיקייהapp-start מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.
  3. לוחצים על File (קובץ) > לחצן הסנכרון של הפרויקט עם Gradle ב-Android Studio Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי 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. כדי לעשות זאת, אפליקציית ה-ATV צריכה להיות מעודכנת כך שתשתמש במרחב השמות AndroidX.

כדי לתמוך ב-Cast Connect באפליקציה ל-Android TV, צריך ליצור אירועים ולתמוך בהם מסשן מדיה. הספרייה 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) שמרכז את כל האינטראקציות עם Cast. צריך להטמיע את הממשק ReceiverOptionsProvider כדי לספק את CastReceiverOptions כשCastReceiverContext מופעל.

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

package com.google.sample.cast.castconnect

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

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

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

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

כדי להתחבר לאפליקציית ATV מהמכשיר המשדר את ההעברה, בוחרים פעילות שרוצים להפעיל. בקודלאב הזה נפעיל את 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 הוא מאפיין של היחידה היחידה (singleton) 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)
    ...
}

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

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

6. טעינת מדיה

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7. תמיכה בפקודות של Cast Control

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

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

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

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

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

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

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

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

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

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

תיעוד סטטוס מדיה לפני שליחה

בדומה ל-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.