בדף הזה מופיעים קטעי קוד ותיאורים של התכונות הזמינות להתאמה אישית של אפליקציית Android TV Receiver.
הגדרת ספריות
כדי להפוך את ממשקי ה-API של Cast Connect לזמינים לאפליקציה ל-Android TV:
-
פותחים את הקובץ
build.gradle
בתוך ספריית המודול של האפליקציה. -
מוודאים ש-
google()
נכלל ב-repositories
שמופיע.repositories { google() }
-
בהתאם לסוג המכשיר היעד של האפליקציה, מוסיפים את הגרסאות העדכניות ביותר של הספריות ליחסי התלות:
-
באפליקציית Android Receiver:
dependencies { implementation 'com.google.android.gms:play-services-cast-tv:21.1.1' implementation 'com.google.android.gms:play-services-cast:22.0.0' }
-
באפליקציית Sender ל-Android:
dependencies { implementation 'com.google.android.gms:play-services-cast:21.1.1' implementation 'com.google.android.gms:play-services-cast-framework:22.0.0' }
-
באפליקציית Android Receiver:
-
שומרים את השינויים ולוחצים על
Sync Project with Gradle Files
בסרגל הכלים.
-
מוודאים שה-
Podfile
מטרגט אתgoogle-cast-sdk
בגרסה 4.8.3 ואילך -
טירגוט ל-iOS מגרסה 14 ואילך. פרטים נוספים זמינים בהערות הגרסה.
platform: ios, '14' def target_pods pod 'google-cast-sdk', '~>4.8.3' end
- נדרש דפדפן Chromium מגרסה M87 ואילך.
-
הוספה של ספריית Web Sender API לפרויקט
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
דרישת AndroidX
בגרסאות חדשות של Google Play Services, האפליקציה צריכה להיות מעודכנת כדי להשתמש במרחב השמות androidx
. פועלים לפי ההוראות למיגרציה ל-AndroidX.
אפליקציה ל-Android TV – תנאים מוקדמים
כדי לתמוך ב-Cast Connect באפליקציה ל-Android TV, צריך ליצור אירועים מסשן מדיה ולתמוך בהם. הנתונים שמתקבלים מסשן המדיה מספקים את המידע הבסיסי – למשל המיקום, מצב ההפעלה וכו' – לגבי סטטוס המדיה. סשן המדיה משמש גם את הספרייה של Cast Connect כדי לסמן מתי היא קיבלה הודעות מסוימות מהשולח, כמו 'השהיה'.
מידע נוסף על סשן מדיה ועל איך מאתחלים סשן מדיה זמין במדריך לעבודה עם סשן מדיה.
מחזור החיים של סשן מדיה
האפליקציה צריכה ליצור סשן מדיה כשההפעלה מתחילה ולשחרר אותו כשאי אפשר יותר לשלוט בו. לדוגמה, אם האפליקציה שלכם היא אפליקציית וידאו, עליכם לסיים את הסשן כשהמשתמש יוצא מפעילות ההפעלה – על ידי בחירה באפשרות 'חזרה' כדי לעיין בתוכן אחר או על ידי העברת האפליקציה למצב רקע. אם האפליקציה שלכם היא אפליקציית מוזיקה, עליכם לסיים את הסשן כשהאפליקציה כבר לא מפעילה מדיה.
עדכון סטטוס הסשן
הנתונים בסשן המדיה צריכים להיות מעודכנים בהתאם לסטטוס של הנגן. לדוגמה, כשהפעלה מושהית, צריך לעדכן את מצב ההפעלה ואת הפעולות הנתמכות. בטבלאות הבאות מפורטות המצבים שאתם אחראים לעדכן.
MediaMetadataCompat
שדה מטא-נתונים | תיאור |
---|---|
METADATA_KEY_TITLE (חובה) | כותרת המדיה. |
METADATA_KEY_DISPLAY_SUBTITLE | כותרת המשנה. |
METADATA_KEY_DISPLAY_ICON_URI | כתובת ה-URL של הסמל. |
METADATA_KEY_DURATION (חובה) | משך המדיה. |
METADATA_KEY_MEDIA_URI | מזהה התוכן. |
METADATA_KEY_ARTIST | האומן או האומנית. |
METADATA_KEY_ALBUM | האלבום. |
PlaybackStateCompat
שיטת הבדיקה הנדרשת | תיאור |
---|---|
setActions() | מגדיר את הפקודות הנתמכות לשימוש במדיה. |
setState() | מגדירים את מצב ההפעלה ואת המיקום הנוכחי. |
MediaSessionCompat
שיטת הבדיקה הנדרשת | תיאור |
---|---|
setRepeatMode() | מגדיר את מצב החזרה. |
setShuffleMode() | הגדרת מצב ההפעלה האקראית. |
setMetadata() | הגדרת מטא-נתונים של מדיה. |
setPlaybackState() | הגדרת מצב ההפעלה. |
private fun updateMediaSession() { val metadata = MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, mMovie.getCardImageUrl()) .build() val playbackState = PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis() ) .build() mediaSession.setMetadata(metadata) mediaSession.setPlaybackState(playbackState) }
private void updateMediaSession() { MediaMetadataCompat metadata = new MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,mMovie.getCardImageUrl()) .build(); PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis()) .build(); mediaSession.setMetadata(metadata); mediaSession.setPlaybackState(playbackState); }
טיפול בבקרת התעבורה
באפליקציה צריך להטמיע קריאה חוזרת (callback) לבקרת התעבורה של סשן המדיה. בטבלה הבאה מפורטות פעולות בקרת התעבורה שהן צריכות לטפל בהן:
MediaSessionCompat.Callback
פעולות | תיאור |
---|---|
onPlay() | המשך |
onPause() | השהיה |
onSeekTo() | דילוג למיקום מסוים |
onStop() | הפסקת המדיה הנוכחית |
class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. ... } override fun onPlay() { // Resume the player and update the play state. ... } override fun onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback( MyMediaSessionCallback() );
public MyMediaSessionCallback extends MediaSessionCompat.Callback { public void onPause() { // Pause the player and update the play state. ... } public void onPlay() { // Resume the player and update the play state. ... } public void onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback(new MyMediaSessionCallback());
הגדרת תמיכה ב-Cast
כשבקשת הפעלה נשלחת על ידי אפליקציית השולח, נוצרת כוונה עם מרחבים משותפים של אפליקציות. האפליקציה שלכם אחראית לטפל בו וליצור מופע של האובייקט CastReceiverContext
כשאפליקציית הטלוויזיה מופעלת. האובייקט CastReceiverContext
נדרש כדי ליצור אינטראקציה עם Cast בזמן שאפליקציית הטלוויזיה פועלת. האובייקט הזה מאפשר לאפליקציה בטלוויזיה לקבל הודעות של מדיה להעברה (cast) מכל שולחים מחוברים.
הגדרת Android TV
הוספת מסנן של כוונה להשקת מודעה
מוסיפים מסנן Intent חדש לפעילות שבה רוצים לטפל בכוונה להפעלה מאפליקציית השולח:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
ציון הספק של אפשרויות הנמען
צריך להטמיע ReceiverOptionsProvider
כדי לספק את CastReceiverOptions
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setStatusText("My App") .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setStatusText("My App") .build(); } }
לאחר מכן מציינים את ספק האפשרויות ב-AndroidManifest
:
<meta-data
android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.example.mysimpleatvapplication.MyReceiverOptionsProvider" />
השדה ReceiverOptionsProvider
משמש כדי לספק את CastReceiverOptions
כשCastReceiverContext
מופעל.
ההקשר של מקלט ההעברה (cast)
מפעילים את CastReceiverContext
כשהאפליקציה נוצרת:
override fun onCreate() { CastReceiverContext.initInstance(this) ... }
@Override public void onCreate() { CastReceiverContext.initInstance(this); ... }
מפעילים את CastReceiverContext
כשהאפליקציה עוברת לחזית:
CastReceiverContext.getInstance().start()
CastReceiverContext.getInstance().start();
באפליקציות וידאו או באפליקציות שלא תומכות בהפעלה ברקע, אחרי שהאפליקציה עוברת לרקע, מקישים על stop()
בCastReceiverContext
:
// Player has stopped. CastReceiverContext.getInstance().stop()
// Player has stopped. CastReceiverContext.getInstance().stop();
בנוסף, אם האפליקציה תומכת בהפעלה ברקע, תוכלו להפעיל את הפקודה stop()
ב-CastReceiverContext
כשהיא תפסיק לפעול ברקע.
מומלץ מאוד להשתמש ב-LifecycleObserver מהספרייה androidx.lifecycle
כדי לנהל את הקריאה ל-CastReceiverContext.start()
ול-CastReceiverContext.stop()
, במיוחד אם לאפליקציה המקורית יש כמה פעילויות. כך אפשר למנוע תנאי מרוץ כשקוראים ל-start()
ול-stop()
מפעילויות שונות.
// Create a LifecycleObserver class. class MyLifecycleObserver : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start() } override fun onStop(owner: LifecycleOwner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop() } } // Add the observer when your application is being created. class MyApplication : Application() { fun onCreate() { super.onCreate() // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */) // Register LifecycleObserver ProcessLifecycleOwner.get().lifecycle.addObserver( MyLifecycleObserver()) } }
// Create a LifecycleObserver class. public class MyLifecycleObserver implements DefaultLifecycleObserver { @Override public void onStart(LifecycleOwner owner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start(); } @Override public void onStop(LifecycleOwner owner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop(); } } // Add the observer when your application is being created. public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */); // Register LifecycleObserver ProcessLifecycleOwner.get().getLifecycle().addObserver( new MyLifecycleObserver()); } }
// In AndroidManifest.xml set MyApplication as the application class
<application
...
android:name=".MyApplication">
קישור MediaSession ל-MediaManager
כשיוצרים MediaSession
, צריך גם לספק את אסימון ה-MediaSession
הנוכחי ל-CastReceiverContext
כדי שהוא ידע לאן לשלוח את הפקודות ולאחזר את מצב ההפעלה של המדיה:
val mediaManager: MediaManager = receiverContext.getMediaManager() mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken())
MediaManager mediaManager = receiverContext.getMediaManager(); mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken());
כשמשחררים את MediaSession
בגלל חוסר פעילות בהפעלה, צריך להגדיר אסימון null ב-MediaManager
:
myPlayer.stop() mediaSession.release() mediaManager.setSessionCompatToken(null)
myPlayer.stop(); mediaSession.release(); mediaManager.setSessionCompatToken(null);
אם האפליקציה תומכת בהפעלת מדיה בזמן שהיא ברקע, במקום להפעיל את CastReceiverContext.stop()
כשהאפליקציה מועברת לרקע, צריך להפעיל אותה רק כשהיא ברקע ולא מפעילה יותר מדיה. לדוגמה:
class MyLifecycleObserver : DefaultLifecycleObserver { ... // App has moved to the background. override fun onPause(owner: LifecycleOwner) { mIsBackground = true myStopCastReceiverContextIfNeeded() } } // Stop playback on the player. private fun myStopPlayback() { myPlayer.stop() myStopCastReceiverContextIfNeeded() } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private fun myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop() } }
public class MyLifecycleObserver implements DefaultLifecycleObserver { ... // App has moved to the background. @Override public void onPause(LifecycleOwner owner) { mIsBackground = true; myStopCastReceiverContextIfNeeded(); } } // Stop playback on the player. private void myStopPlayback() { myPlayer.stop(); myStopCastReceiverContextIfNeeded(); } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private void myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop(); } }
שימוש ב-Exoplayer עם Cast Connect
אם אתם משתמשים ב-Exoplayer
, אתם יכולים להשתמש ב-MediaSessionConnector
כדי לשמור באופן אוטומטי את הסשן ואת כל המידע הקשור, כולל מצב ההפעלה, במקום לעקוב אחרי השינויים באופן ידני.
אפשר להשתמש ב-MediaSessionConnector.MediaButtonEventHandler
כדי לטפל באירועים של MediaButton באמצעות קריאה ל-setMediaButtonEventHandler(MediaButtonEventHandler)
, שבמקרה אחר מטופלים על ידי MediaSessionCompat.Callback
כברירת מחדל.
כדי לשלב את MediaSessionConnector
באפליקציה, מוסיפים את הקוד הבא לכיתה של פעילות הנגן או למקום שבו מנהלים את סשן המדיה:
class PlayerActivity : Activity() { private var mMediaSession: MediaSessionCompat? = null private var mMediaSessionConnector: MediaSessionConnector? = null private var mMediaManager: MediaManager? = null override fun onCreate(savedInstanceState: Bundle?) { ... mMediaSession = MediaSessionCompat(this, LOG_TAG) mMediaSessionConnector = MediaSessionConnector(mMediaSession!!) ... } override fun onStart() { ... mMediaManager = receiverContext.getMediaManager() mMediaManager!!.setSessionCompatToken(currentMediaSession.getSessionToken()) mMediaSessionConnector!!.setPlayer(mExoPlayer) mMediaSessionConnector!!.setMediaMetadataProvider(mMediaMetadataProvider) mMediaSession!!.isActive = true ... } override fun onStop() { ... mMediaSessionConnector!!.setPlayer(null) mMediaSession!!.release() mMediaManager!!.setSessionCompatToken(null) ... } }
public class PlayerActivity extends Activity { private MediaSessionCompat mMediaSession; private MediaSessionConnector mMediaSessionConnector; private MediaManager mMediaManager; @Override protected void onCreate(Bundle savedInstanceState) { ... mMediaSession = new MediaSessionCompat(this, LOG_TAG); mMediaSessionConnector = new MediaSessionConnector(mMediaSession); ... } @Override protected void onStart() { ... mMediaManager = receiverContext.getMediaManager(); mMediaManager.setSessionCompatToken(currentMediaSession.getSessionToken()); mMediaSessionConnector.setPlayer(mExoPlayer); mMediaSessionConnector.setMediaMetadataProvider(mMediaMetadataProvider); mMediaSession.setActive(true); ... } @Override protected void onStop() { ... mMediaSessionConnector.setPlayer(null); mMediaSession.release(); mMediaManager.setSessionCompatToken(null); ... } }
הגדרת אפליקציית השליחה
הפעלת התמיכה ב-Cast Connect
אחרי שתעדכנו את אפליקציית השולח עם תמיכה ב-Cast Connect, תוכלו להצהיר על המוכנות שלה על ידי הגדרת הערך true לסימון androidReceiverCompatible
ב-LaunchOptions
.
נדרשת גרסת play-services-cast-framework
19.0.0
ואילך.
הדגל androidReceiverCompatible
מוגדר ב-LaunchOptions
(שנכלל ב-CastOptions
):
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context?): CastOptions { val launchOptions: LaunchOptions = Builder() .setAndroidReceiverCompatible(true) .build() return CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { LaunchOptions launchOptions = new LaunchOptions.Builder() .setAndroidReceiverCompatible(true) .build(); return new CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build(); } }
נדרשת גרסת google-cast-sdk
v4.4.8
ואילך.
הדגל androidReceiverCompatible
מוגדר ב-GCKLaunchOptions
(שנכלל ב-GCKCastOptions
):
let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID)) ... let launchOptions = GCKLaunchOptions() launchOptions.androidReceiverCompatible = true options.launchOptions = launchOptions GCKCastContext.setSharedInstanceWith(options)
נדרשת גרסת דפדפן Chromium מגרסה M87
ואילך.
const context = cast.framework.CastContext.getInstance(); const castOptions = new cast.framework.CastOptions(); castOptions.receiverApplicationId = kReceiverAppID; castOptions.androidReceiverCompatible = true; context.setOptions(castOptions);
הגדרת מסוף הפיתוח של Cast
הגדרת אפליקציית Android TV
מוסיפים את שם החבילה של אפליקציית Android TV ב-Cast Developer Console כדי לשייך אותה למזהה של אפליקציית Cast.
רישום מכשירים למפתחים
במסוף הפיתוח של Cast, רושמים את המספר הסידורי של מכשיר Android TV שבו תשתמשו לפיתוח.
ללא רישום, Cast Connect יפעל רק באפליקציות שהותקנו מחנות Google Play, מטעמי אבטחה.
למידע נוסף על רישום מכשיר Cast או Android TV לפיתוח Cast, אפשר לעיין בדף הרישום.
טעינה של מדיה
אם כבר הטמעתם תמיכה בקישורי עומק באפליקציה ל-Android TV, צריכה להיות הגדרה דומה במניפסט של Android TV:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
<data android:host="www.example.com"/>
<data android:pathPattern=".*"/>
</intent-filter>
</activity>
טעינה לפי ישות בשולח
בצד השולח, אפשר להעביר את הקישור המעמיק על ידי הגדרת השדה entity
בפרטי המדיה של בקשת הטעינה:
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
נדרשת גרסת דפדפן Chromium מגרסה M87
ואילך.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
פקודת הטעינה נשלחת באמצעות כוונה (intent) עם הקישור המעמיק ושם החבילה שהגדרתם במסוף הפיתוח.
הגדרת פרטי הכניסה ל-ATV בשולח
יכול להיות שאפליקציית Web Receiver ואפליקציית Android TV תומכות בקישור עומק וב-credentials
שונים (לדוגמה, אם אתם מטפלים באימות בצורה שונה בשתי הפלטפורמות). כדי לטפל בבעיה, אפשר לספק entity
ו-credentials
חלופיים ל-Android TV:
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") mediaInfoBuilder.atvEntity = "myscheme://example.com/atv/some-id" ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" mediaLoadRequestDataBuilder.atvCredentials = "atv-user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
נדרשת גרסת דפדפן Chromium מגרסה M87
ואילך.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; mediaInfo.atvEntity = 'myscheme://example.com/atv/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; request.atvCredentials = 'atv-user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
אם אפליקציית Web Receiver מופעלת, היא משתמשת ב-entity
וב-credentials
בבקשת הטעינה. עם זאת, אם האפליקציה ל-Android TV מופעלת, ה-SDK מבטל את ההגדרות של entity
ו-credentials
ומחליף אותן ב-atvEntity
וב-atvCredentials
(אם צוינו).
טעינה באמצעות Content ID או MediaQueueData
אם אתם לא משתמשים ב-entity
או ב-atvEntity
, ואתם משתמשים ב-Content ID או בכתובת ה-URL של התוכן ב'פרטי המדיה', או משתמשים בנתוני הבקשה המפורטים יותר של טעינת המדיה, תצטרכו להוסיף את מסנן הכוונה המוגדר מראש הבא לאפליקציה ל-Android TV:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
בצד השולח, בדומה לטעינה לפי ישות, אפשר ליצור בקשת טעינה עם פרטי התוכן ולקרוא ל-load()
.
val mediaToLoad = MediaInfo.Builder("some-id").build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id").build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(contentId: "some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
נדרשת גרסת דפדפן Chromium מגרסה M87
ואילך.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); ... let request = new chrome.cast.media.LoadRequest(mediaInfo); ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
טיפול בבקשות טעינה
כדי לטפל בבקשות הטעינה האלה בפעילות, צריך לטפל בכוונות בקריאות החזרה (callbacks) של מחזור החיים של הפעילות:
class MyActivity : Activity() { override fun onStart() { super.onStart() val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). override fun onNewIntent(intent: Intent) { val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
public class MyActivity extends Activity { @Override protected void onStart() { super.onStart(); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(getIntent())) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). @Override protected void onNewIntent(Intent intent) { MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
אם MediaManager
מזהה שהכוונה היא כוונה לטעינה, היא מחלצת אובייקט MediaLoadRequestData
מהכוונה ומפעילה את MediaLoadCommandCallback.onLoad()
.
כדי לטפל בבקשת הטעינה, צריך לשנות את השיטה הזו. צריך לרשום את הקריאה החוזרת לפני שמפעילים את MediaManager.onNewIntent()
(מומלץ להשתמש בשיטה onCreate()
של פעילות או אפליקציה).
class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback()) } } class MyMediaLoadCommandCallback : MediaLoadCommandCallback() { override fun onLoad( senderId: String?, loadRequestData: MediaLoadRequestData ): Task{ return Tasks.call { // Resolve the entity into your data structure and load media. val mediaInfo = loadRequestData.getMediaInfo() if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw MediaException( MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build() ) } myFillMediaInfo(MediaInfoWriter(mediaInfo)) myPlayerLoad(mediaInfo.getContentUrl()) // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData) ... castReceiverContext.getMediaManager().broadcastMediaStatus() // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData } } private fun myPlayerLoad(contentURL: String) { myPlayer.load(contentURL) // Update the MediaSession state. val playbackState: PlaybackStateCompat = Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis() ) ... .build() mediaSession.setPlaybackState(playbackState) }
public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaLoadCommandCallback(new MyMediaLoadCommandCallback()); } } public class MyMediaLoadCommandCallback extends MediaLoadCommandCallback { @Override public TaskonLoad(String senderId, MediaLoadRequestData loadRequestData) { return Tasks.call(() -> { // Resolve the entity into your data structure and load media. MediaInfo mediaInfo = loadRequestData.getMediaInfo(); if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw new MediaException( new MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build()); } myFillMediaInfo(new MediaInfoWriter(mediaInfo)); myPlayerLoad(mediaInfo.getContentUrl()); // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData); ... castReceiverContext.getMediaManager().broadcastMediaStatus(); // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData; }); } private void myPlayerLoad(String contentURL) { myPlayer.load(contentURL); // Update the MediaSession state. PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis()) ... .build(); mediaSession.setPlaybackState(playbackState); }
כדי לעבד את כוונת הטעינה, אפשר לנתח את הכוונה למבני הנתונים שהגדרנו (MediaLoadRequestData
לבקשות טעינה).
תמיכה בפקודות מדיה
תמיכה בפקדי הפעלה בסיסיים
פקודות אינטגרציה בסיסיות כוללות את הפקודות שתואמות לסשן המדיה. ההודעות על הפקודות האלה מתקבלות באמצעות קריאות חזרה (callbacks) של סשן המדיה. כדי לתמוך בכך, צריך לרשום קריאה חוזרת (callback) לסשן המדיה (יכול להיות שאתם כבר עושים זאת).
private class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. myPlayer.pause() } override fun onPlay() { // Resume the player and update the play state. myPlayer.play() } override fun onSeekTo(pos: Long) { // Seek and update the play state. myPlayer.seekTo(pos) } ... } mediaSession.setCallback(MyMediaSessionCallback())
private class MyMediaSessionCallback extends MediaSessionCompat.Callback { @Override public void onPause() { // Pause the player and update the play state. myPlayer.pause(); } @Override public void onPlay() { // Resume the player and update the play state. myPlayer.play(); } @Override public void onSeekTo(long pos) { // Seek and update the play state. myPlayer.seekTo(pos); } ... } mediaSession.setCallback(new MyMediaSessionCallback());
תמיכה בפקודות שליטה ב-Cast
יש פקודות העברה (cast) שלא זמינות ב-MediaSession
, כמו skipAd()
או setActiveMediaTracks()
.
בנוסף, צריך להטמיע כאן כמה פקודות של תורים כי התור של Cast לא תואם באופן מלא לתור MediaSession
.
class MyMediaCommandCallback : MediaCommandCallback() { override fun onSkipAd(requestData: RequestData?): Task<Void?> { // Skip your ad ... return Tasks.forResult(null) } } val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
public class MyMediaCommandCallback extends MediaCommandCallback { @Override public TaskonSkipAd(RequestData requestData) { // Skip your ad ... return Tasks.forResult(null); } } MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaCommandCallback(new MyMediaCommandCallback());
ציון הפקודות הנתמכות בנושא מדיה
בדומה למכשיר הקולט להעברה (cast), באפליקציה ל-Android TV צריך לציין אילו פקודות נתמכות, כדי שהשולחים יוכלו להפעיל או להשבית אמצעי בקרה מסוימים בממשק המשתמש. לפקודות שנכללות ב-MediaSession
, צריך לציין את הפקודות ב-PlaybackStateCompat
.
צריך לציין פקודות נוספות ב-MediaStatusModifier
.
// Set media session supported commands val playbackState: PlaybackStateCompat = PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build() mediaSession.setPlaybackState(playbackState) // Set additional commands in MediaStatusModifier val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT)
// Set media session supported commands PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build(); mediaSession.setPlaybackState(playbackState); // Set additional commands in MediaStatusModifier MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT);
הסתרת לחצנים לא נתמכים
אם אפליקציית Android TV תומכת רק בשליטה בסיסית על מדיה, אבל אפליקציית Web Receiver תומכת בשליטה מתקדמת יותר, עליכם לוודא שאפליקציית השולח פועלת בצורה תקינה כשמבצעים העברה (cast) לאפליקציית Android TV. לדוגמה, אם אפליקציית Android TV לא תומכת בשינוי קצב ההפעלה, אבל אפליקציית Web Receiver כן תומכת בכך, עליכם להגדיר את הפעולות הנתמכות בצורה נכונה בכל פלטפורמה ולוודא שאפליקציית השולח מרינדרת את ממשק המשתמש בצורה תקינה.
שינוי של MediaStatus
כדי לתמוך בתכונות מתקדמות כמו טראקים, מודעות, שידורים חיים והוספה לרשימת הצפייה, אפליקציית Android TV צריכה לספק מידע נוסף שלא ניתן לקבל דרך MediaSession
.
כדי לעשות זאת, אנחנו מספקים את הכיתה MediaStatusModifier
. MediaStatusModifier
תמיד יפעל במהירות MediaSession
שהגדרתם ב-CastReceiverContext
.
כדי ליצור ולשדר MediaStatus
:
val mediaManager: MediaManager = castReceiverContext.getMediaManager() val statusModifier: MediaStatusModifier = mediaManager.getMediaStatusModifier() statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData) mediaManager.broadcastMediaStatus()
MediaManager mediaManager = castReceiverContext.getMediaManager(); MediaStatusModifier statusModifier = mediaManager.getMediaStatusModifier(); statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData); mediaManager.broadcastMediaStatus();
ספריית הלקוח שלנו תקבל את הערך הבסיסי MediaStatus
מ-MediaSession
, ואפליקציית Android TV יכולה לציין סטטוס נוסף ולשנות את הסטטוס באמצעות המשתנה MediaStatus
.
אפשר להגדיר חלק מהמצבים והמטא-נתונים גם ב-MediaSession
וגם ב-MediaStatusModifier
. מומלץ מאוד להגדיר אותם רק ב-MediaSession
. עדיין אפשר להשתמש במשנה כדי לשנות את המצבים ב-MediaSession
, אבל לא מומלץ לעשות זאת כי לסטטוס במשנה תמיד יש עדיפות גבוהה יותר מאשר לערכים שסופקו על ידי MediaSession
.
תיעוד נתוני MediaStatus לפני השליחה
בדומה ל-SDK של מקלט האינטרנט, אם רוצים לבצע שינויים אחרונים לפני השליחה, אפשר לציין MediaStatusInterceptor
כדי לעבד את MediaStatus
שרוצים לשלוח. אנחנו מעבירים את הערך של MediaStatusWriter
כדי לבצע פעולות על MediaStatus
לפני שהוא נשלח.
mediaManager.setMediaStatusInterceptor(object : MediaStatusInterceptor { override fun intercept(mediaStatusWriter: MediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(JSONObject("{data: \"my Hello\"}")) } })
mediaManager.setMediaStatusInterceptor(new MediaStatusInterceptor() { @Override public void intercept(MediaStatusWriter mediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(new JSONObject("{data: \"my Hello\"}")); } });
טיפול בפרטי הכניסה של המשתמשים
יכול להיות שאפליקציית Android TV תאפשר רק למשתמשים מסוימים להפעיל את הסשן באפליקציה או להצטרף אליו. לדוגמה, אפשר לאפשר לשולח להתחיל או להצטרף רק אם:
- באפליקציה השולחת צריך להיות חשבון ופרופיל זהים לאלה של אפליקציית ה-ATV.
- האפליקציה השולחת מחוברת לאותו חשבון, אבל לפרופיל שונה מזה של אפליקציית ה-ATV.
אם האפליקציה שלכם יכולה לטפל במספר משתמשים או במשתמשים אנונימיים, תוכלו לאפשר לכל משתמש להצטרף לסשן ה-ATV. אם המשתמש מספק פרטי כניסה, אפליקציית ה-ATV צריכה לטפל בפרטי הכניסה שלו כדי שאפשר יהיה לעקוב בצורה נכונה אחרי ההתקדמות שלו ונתוני משתמשים אחרים.
כשאפליקציית השולח מופעלת או מצטרפת לאפליקציה ב-Android TV, אפליקציית השולח צריכה לספק את פרטי הכניסה שמייצגים את מי שמצטרף לסשן.
לפני ששולח מפעיל את האפליקציה ב-Android TV ומצטרף אליה, אפשר לציין בודק הפעלה כדי לבדוק אם פרטי הכניסה של השולח מותרים. אם לא, ה-SDK של Cast Connect יעבור להפעלת מקלט האינטרנט.
נתוני פרטי הכניסה להפעלת האפליקציה של השולח
בצד השולח, אפשר לציין את השדה CredentialsData
כדי לייצג את מי שמצטרף לסשן.
השדה credentials
הוא מחרוזת שהמשתמש יכול להגדיר, כל עוד אפליקציית ה-ATV יכולה להבין אותה. השדה credentialsType
מגדיר את הפלטפורמה שממנה מגיע הערך CredentialsData
, או יכול להיות ערך מותאם אישית. כברירת מחדל, הוא מוגדר לפלטפורמה שממנה הוא נשלח.
הערך של CredentialsData
מועבר לאפליקציה ל-Android TV רק במהלך ההפעלה או במהלך ההצטרפות. אם תגדירו אותו שוב בזמן החיבור, הוא לא יועבר לאפליקציה ל-Android TV. אם השולח מחליף את הפרופיל בזמן החיבור, תוכלו להישאר בסשן או להתקשר למספר SessionManager.endCurrentCastSession(boolean stopCasting)
אם לדעתכם הפרופיל החדש לא תואם לסשן.
אפשר לאחזר את הערך של CredentialsData
לכל שולח באמצעות getSenders
ב-CastReceiverContext
כדי לקבל את הערך של SenderInfo
, getCastLaunchRequest()
כדי לקבל את הערך של CastLaunchRequest
ואז getCredentialsData()
.
נדרשת גרסת play-services-cast-framework
19.0.0
ואילך.
CastContext.getSharedInstance().setLaunchCredentialsData( CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build() )
CastContext.getSharedInstance().setLaunchCredentialsData( new CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build());
נדרשת גרסת google-cast-sdk
v4.8.3
ואילך.
אפשר להפעיל את הפונקציה הזו בכל שלב אחרי שמגדירים את האפשרויות:
GCKCastContext.setSharedInstanceWith(options)
.
GCKCastContext.sharedInstance().setLaunch( GCKCredentialsData(credentials: "{\"userId\": \"abc\"}")
נדרשת גרסת דפדפן Chromium מגרסה M87
ואילך.
אפשר להפעיל את הפונקציה הזו בכל שלב אחרי שמגדירים את האפשרויות:
cast.framework.CastContext.getInstance().setOptions(options);
.
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}"); cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
הטמעת כלי לבדיקת בקשות להשקת טלוויזיות חכמות
הערך CredentialsData
מועבר לאפליקציה של Android TV כששולח מנסה להפעיל את הפגישה או להצטרף אליה. אפשר להטמיע LaunchRequestChecker
.
כדי לאשר או לדחות את הבקשה הזו.
אם הבקשה נדחית, מקלט האינטרנט נטען במקום להפעיל את אפליקציית ה-ATV באופן מקורי. צריך לדחות בקשה אם ה-ATV לא יכול לטפל במשתמש שמבקש להפעיל או להצטרף. לדוגמה, משתמש אחר מחובר לאפליקציית ATV מאשר המשתמש שמבצע את הבקשה, והאפליקציה לא יכולה לטפל בהחלפת פרטי הכניסה. לחלופין, אין משתמש שמחובר כרגע לאפליקציית ATV.
אם הבקשה תאושר, אפליקציית ATV תופעל. אתם יכולים להתאים אישית את ההתנהגות הזו בהתאם ליכולת של האפליקציה לשלוח בקשות טעינה כשהמשתמש לא מחובר לאפליקציה לטלוויזיה או אם יש אי-התאמה של המשתמש. אפשר להתאים אישית את ההתנהגות הזו באופן מלא ב-LaunchRequestChecker
.
יוצרים מחלקה שמטמיעה את הממשק CastReceiverOptions.LaunchRequestChecker
:
class MyLaunchRequestChecker : LaunchRequestChecker { override fun checkLaunchRequestSupported(launchRequest: CastLaunchRequest): Task{ return Tasks.call { myCheckLaunchRequest( launchRequest ) } } } private fun myCheckLaunchRequest(launchRequest: CastLaunchRequest): Boolean { val credentialsData = launchRequest.getCredentialsData() ?: return false // or true if you allow anonymous users to join. // The request comes from a mobile device, e.g. checking user match. return if (credentialsData.credentialsType == CredentialsData.CREDENTIALS_TYPE_ANDROID) { myCheckMobileCredentialsAllowed(credentialsData.getCredentials()) } else false // Unrecognized credentials type. }
public class MyLaunchRequestChecker implements CastReceiverOptions.LaunchRequestChecker { @Override public TaskcheckLaunchRequestSupported(CastLaunchRequest launchRequest) { return Tasks.call(() -> myCheckLaunchRequest(launchRequest)); } } private boolean myCheckLaunchRequest(CastLaunchRequest launchRequest) { CredentialsData credentialsData = launchRequest.getCredentialsData(); if (credentialsData == null) { return false; // or true if you allow anonymous users to join. } // The request comes from a mobile device, e.g. checking user match. if (credentialsData.getCredentialsType().equals(CredentialsData.CREDENTIALS_TYPE_ANDROID)) { return myCheckMobileCredentialsAllowed(credentialsData.getCredentials()); } // Unrecognized credentials type. return false; }
לאחר מכן מגדירים אותו ב-ReceiverOptionsProvider
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(MyLaunchRequestChecker()) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(new MyLaunchRequestChecker()) .build(); } }
כשתבחרו ב-true
ב-LaunchRequestChecker
, תוכלו להפעיל את אפליקציית ATV ואת אפליקציית מקלט האינטרנט.false
שליחה וקבלה של הודעות בהתאמה אישית
פרוטוקול Cast מאפשר לשלוח הודעות מחרוזות בהתאמה אישית בין השולחים לאפליקציית המקבל. לפני שמפעילים את CastReceiverContext
, צריך לרשום מרחבים משותפים לשמות (ערוצים) כדי לשלוח הודעות.
Android TV – ציון מרחב שמות מותאם אישית
במהלך ההגדרה, צריך לציין את מרחבי השמות הנתמכים ב-CastReceiverOptions
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace") ) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace")) .build(); } }
Android TV – שליחת הודעות
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString)
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString);
Android TV – קבלת הודעות ממרחב שמות מותאם אישית
class MyCustomMessageListener : MessageReceivedListener { override fun onMessageReceived( namespace: String, senderId: String?, message: String ) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());
class MyCustomMessageListener implements CastReceiverContext.MessageReceivedListener { @Override public void onMessageReceived( String namespace, String senderId, String message) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());