Add Advanced Features To Your Android App

Ad Breaks

The Android Sender SDK provides support for Ad Breaks and companion ads within a given media stream.

See the Web Receiver Ad Breaks Overview for more information on how Ad Breaks work.

While breaks can be specified on both the sender and receiver, it is recommended for them to be specified on the Web Receiver and Android TV Receiver to maintain consistent behavior across platforms.

On Android, specify ad breaks in a load command using AdBreakClipInfo and AdBreakInfo :

Kotlin
 val 
  
 breakClip1 
 : 
  
 AdBreakClipInfo 
  
 = 
  
 AdBreakClipInfo 
 . 
 Builder 
 ( 
 "bc0" 
 ) 
  
 . 
 setTitle 
 ( 
 "Clip title" 
 ) 
  
 . 
 setPosterUrl 
 ( 
 "https://www.some.url" 
 ) 
  
 . 
 setDuration 
 ( 
 60000 
 ) 
  
 . 
 setWhenSkippableInMs 
 ( 
 5000 
 ) 
  
 // Set this field so that the ad is skippable 
  
 . 
 build 
 () 
 val 
  
 breakClip2 
 : 
  
 AdBreakClipInfo 
  
 = 
  
  
 val 
  
 breakClip3 
 : 
  
 AdBreakClipInfo 
  
 = 
  
  
 val 
  
 break1 
 : 
  
 AdBreakClipInfo 
  
 = 
  
 AdBreakInfo 
 . 
 Builder 
 ( 
 /* playbackPositionInMs= */ 
  
 10000 
 ) 
  
 . 
 setId 
 ( 
 "b0" 
 ) 
  
 . 
 setBreakClipIds 
 ({ 
 "bc0" 
 , 
 "bc1" 
 , 
 "bc2" 
 }) 
  
  
  
 . 
 build 
 () 
 val 
  
 mediaInfo 
 : 
  
 MediaInfo 
  
 = 
  
 MediaInfo 
 . 
 Builder 
 () 
  
  
  
 . 
 setAdBreaks 
 ({ 
 break1 
 }) 
  
 . 
 setAdBreakClips 
 ({ 
 breakClip1 
 , 
  
 breakClip2 
 , 
  
 breakClip3 
 }) 
  
 . 
 build 
 () 
 val 
  
 mediaLoadRequestData 
 : 
  
 MediaLoadRequestData 
  
 = 
  
 MediaInfo 
 . 
 Builder 
 () 
  
  
  
 . 
 setMediaInfo 
 ( 
 mediaInfo 
 ) 
  
 . 
 build 
 () 
 remoteMediaClient 
 . 
 load 
 ( 
 mediaLoadRequestData 
 ) 
Java
 AdBreakClipInfo 
  
 breakClip1 
  
 = 
  
 new 
  
 AdBreakClipInfo 
 . 
 Builder 
 ( 
 "bc0" 
 ) 
  
 . 
 setTitle 
 ( 
 "Clip title" 
 ) 
  
 . 
 setPosterUrl 
 ( 
 "https://www.some.url" 
 ) 
  
 . 
 setDuration 
 ( 
 60000 
 ) 
  
 . 
 setWhenSkippableInMs 
 ( 
 5000 
 ) 
  
 // Set this field so that the ad is skippable 
  
 . 
 build 
 (); 
 AdBreakClipInfo 
  
 breakClip2 
  
 = 
  
  
 AdBreakClipInfo 
  
 breakClip3 
  
 = 
  
  
 AdBreakInfo 
  
 break1 
  
 = 
  
 new 
  
 AdBreakInfo 
 . 
 Builder 
 ( 
 /* playbackPositionInMs= */ 
  
 10000 
 ) 
  
 . 
 setId 
 ( 
 "b0" 
 ) 
  
 . 
 setBreakClipIds 
 ({ 
 "bc0" 
 , 
 "bc1" 
 , 
 "bc2" 
 }) 
  
  
  
 . 
 build 
 (); 
 MediaInfo 
  
 mediaInfo 
  
 = 
  
 new 
  
 MediaInfo 
 . 
 Builder 
 () 
  
  
  
 . 
 setAdBreaks 
 ({ 
 break1 
 }) 
  
 . 
 setAdBreakClips 
 ({ 
 breakClip1 
 , 
  
 breakClip2 
 , 
  
 breakClip3 
 }) 
  
 . 
 build 
 (); 
 MediaLoadRequestData 
  
 mediaLoadRequestData 
  
 = 
  
 new 
  
 MediaInfo 
 . 
 Builder 
 () 
  
  
  
 . 
 setMediaInfo 
 ( 
 mediaInfo 
 ) 
  
 . 
 build 
 (); 
 remoteMediaClient 
 . 
 load 
 ( 
 mediaLoadRequestData 
 ); 

Add custom actions

A sender app can extend MediaIntentReceiver to handle custom actions or override its behavior. If you have implemented your own MediaIntentReceiver , you need to add it to the manifest, and also set its name in the CastMediaOptions . This example provides custom actions that override toggle remote media playback, pressing the media button and other types of actions.

  // In AndroidManifest.xml 
< receiver 
  
 android 
 : 
 name 
 = 
 "com.example.MyMediaIntentReceiver" 
  
 / 
> 
Kotlin
 // In your OptionsProvider 
 var 
  
 mediaOptions 
  
 = 
  
 CastMediaOptions 
 . 
 Builder 
 () 
  
 . 
 setMediaIntentReceiverClassName 
 ( 
 MyMediaIntentReceiver 
 :: 
 class 
 . 
 java 
 . 
 name 
 ) 
  
 . 
 build 
 () 
 // Implementation of MyMediaIntentReceiver 
 internal 
  
 class 
  
 MyMediaIntentReceiver 
  
 : 
  
 MediaIntentReceiver 
 () 
  
 { 
  
 override 
  
 fun 
  
 onReceiveActionTogglePlayback 
 ( 
 currentSession 
 : 
  
 Session 
 ) 
  
 { 
  
 } 
  
 override 
  
 fun 
  
 onReceiveActionMediaButton 
 ( 
 currentSession 
 : 
  
 Session 
 , 
  
 intent 
 : 
  
 Intent 
 ) 
  
 { 
  
 } 
  
 override 
  
 fun 
  
 onReceiveOtherAction 
 ( 
 context 
 : 
  
 Context?, 
  
 action 
 : 
  
 String 
 , 
  
 intent 
 : 
  
 Intent 
 ) 
  
 { 
  
 } 
 } 
Java
 // In your OptionsProvider 
 CastMediaOptions 
  
 mediaOptions 
  
 = 
  
 new 
  
 CastMediaOptions 
 . 
 Builder 
 () 
  
 . 
 setMediaIntentReceiverClassName 
 ( 
 MyMediaIntentReceiver 
 . 
 class 
 . 
 getName 
 ()) 
  
 . 
 build 
 (); 
 // Implementation of MyMediaIntentReceiver 
 class 
 MyMediaIntentReceiver 
  
 extends 
  
 MediaIntentReceiver 
  
 { 
  
 @Override 
  
 protected 
  
 void 
  
 onReceiveActionTogglePlayback 
 ( 
 Session 
  
 currentSession 
 ) 
  
 { 
  
 } 
  
 @Override 
  
 protected 
  
 void 
  
 onReceiveActionMediaButton 
 ( 
 Session 
  
 currentSession 
 , 
  
 Intent 
  
 intent 
 ) 
  
 { 
  
 } 
  
 @Override 
  
 protected 
  
 void 
  
 onReceiveOtherAction 
 ( 
 Context 
  
 context 
 , 
  
 String 
  
 action 
 , 
  
 Intent 
  
 intent 
 ) 
  
 { 
  
 } 
 } 

Add a custom channel

For the sender app to communicate with the receiver app, your app needs to create a custom channel. The sender can use the custom channel to send string messages to the receiver. Each custom channel is defined by a unique namespace and must start with the prefix urn:x-cast: , for example, urn:x-cast:com.example.custom . It is possible to have multiple custom channels, each with a unique namespace. The receiver app can also send and receive messages using the same namespace.

The custom channel is implemented with the Cast.MessageReceivedCallback interface:

Kotlin
 class 
  
 HelloWorldChannel 
  
 : 
  
 MessageReceivedCallback 
  
 { 
  
 val 
  
 namespace 
 : 
  
 String 
  
 get 
 () 
  
 = 
  
 "urn:x-cast:com.example.custom" 
  
 override 
  
 fun 
  
 onMessageReceived 
 ( 
 castDevice 
 : 
  
 CastDevice 
 , 
  
 namespace 
 : 
  
 String 
 , 
  
 message 
 : 
  
 String 
 ) 
  
 { 
  
 Log 
 . 
 d 
 ( 
 TAG 
 , 
  
 "onMessageReceived: 
 $ 
 message 
 " 
 ) 
  
 } 
 } 
Java
 class 
 HelloWorldChannel 
  
 implements 
  
 Cast 
 . 
 MessageReceivedCallback 
  
 { 
  
 public 
  
 String 
  
 getNamespace 
 () 
  
 { 
  
 return 
  
 "urn:x-cast:com.example.custom" 
 ; 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onMessageReceived 
 ( 
 CastDevice 
  
 castDevice 
 , 
  
 String 
  
 namespace 
 , 
  
 String 
  
 message 
 ) 
  
 { 
  
 Log 
 . 
 d 
 ( 
 TAG 
 , 
  
 "onMessageReceived: " 
  
 + 
  
 message 
 ); 
  
 } 
 } 

Once the sender app is connected to the receiver app, the custom channel can be created using the setMessageReceivedCallbacks method:

Kotlin
 try 
  
 { 
  
 mCastSession 
 . 
 setMessageReceivedCallbacks 
 ( 
  
 mHelloWorldChannel 
 . 
 namespace 
 , 
  
 mHelloWorldChannel 
 ) 
 } 
  
 catch 
  
 ( 
 e 
 : 
  
 IOException 
 ) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Exception while creating channel" 
 , 
  
 e 
 ) 
 } 
Java
 try 
  
 { 
  
 mCastSession 
 . 
 setMessageReceivedCallbacks 
 ( 
  
 mHelloWorldChannel 
 . 
 getNamespace 
 (), 
  
 mHelloWorldChannel 
 ); 
 } 
  
 catch 
  
 ( 
 IOException 
  
 e 
 ) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Exception while creating channel" 
 , 
  
 e 
 ); 
 } 

Once the custom channel is created, the sender can use the sendMessage method to send string messages to the receiver over that channel:

Kotlin
 private 
  
 fun 
  
 sendMessage 
 ( 
 message 
 : 
  
 String 
 ) 
  
 { 
  
 if 
  
 ( 
 mHelloWorldChannel 
  
 != 
  
 null 
 ) 
  
 { 
  
 try 
  
 { 
  
 mCastSession 
 . 
 sendMessage 
 ( 
 mHelloWorldChannel 
 . 
 namespace 
 , 
  
 message 
 ) 
  
 . 
 setResultCallback 
  
 { 
  
 status 
  
 - 
>  
 if 
  
 ( 
 ! 
 status 
 . 
 isSuccess 
 ) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Sending message failed" 
 ) 
  
 } 
  
 } 
  
 } 
  
 catch 
  
 ( 
 e 
 : 
  
 Exception 
 ) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Exception while sending message" 
 , 
  
 e 
 ) 
  
 } 
  
 } 
 } 
Java
 private 
  
 void 
  
 sendMessage 
 ( 
 String 
  
 message 
 ) 
  
 { 
  
 if 
  
 ( 
 mHelloWorldChannel 
  
 != 
  
 null 
 ) 
  
 { 
  
 try 
  
 { 
  
 mCastSession 
 . 
 sendMessage 
 ( 
 mHelloWorldChannel 
 . 
 getNamespace 
 (), 
  
 message 
 ) 
  
 . 
 setResultCallback 
 ( 
  
 status 
  
 - 
>  
 { 
  
 if 
  
 ( 
 ! 
 status 
 . 
 isSuccess 
 ()) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Sending message failed" 
 ); 
  
 } 
  
 }); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 Log 
 . 
 e 
 ( 
 TAG 
 , 
  
 "Exception while sending message" 
 , 
  
 e 
 ); 
  
 } 
  
 } 
 } 

Supporting autoplay

See the section Autoplay & Queueing APIs .

Override image selection for UX widgets

Various components of the framework (namely the Cast dialog, the mini controller, and the UIMediaController, if so configured) will display artwork for the currently casting media. The URLs to the image artwork are typically included in the MediaMetadata for the media, but the sender app may have an alternate source for the URLs.

The ImagePicker class defines a means for selecting an appropriate image from the list of images in a MediaMetadata , based on the use of the image, for example, notification thumbnail or full screen background. The default ImagePicker implementation always chooses the first image, or returns null if no image is available in the MediaMetadata . Your app can subclass ImagePicker and override the onPickImage(MediaMetadata, ImageHints) method to provide an alternate implementation, and then select that subclass with the setImagePicker method of CastMediaOptions.Builder . ImageHints provides hints to an ImagePicker about the type and size of an image to be selected for display in the UI.

Customizing Cast dialogs

Managing Session Lifecycle

SessionManager is the central place for managing session lifecycle. SessionManager listens to Android MediaRouter route selection state changes to start, resume and end sessions. When a route is selected, SessionManager will create a Session object and tries to start or resume it. When a route is unselected, SessionManager will end the current session.

Therefore, to ensure SessionManager manages session lifecycles properly, you must make sure that:

Depending on how you create the Cast dialogs, additional actions may need to be done:

Zero Devices State

If you create custom Cast dialogs, your custom MediaRouteChooserDialog should properly handle the case of zero devices being found. The dialog should have indicators making it clear to your users when your app is still attempting to find devices and when the discovery attempt is no longer active.

If you are using the default MediaRouteChooserDialog , the zero devices state is already handled.

Next steps

This concludes the features that you can add to your Android Sender app. You can now build a sender app for another platform ( iOS or Web ), or build a Web Receiver app .

Create a Mobile Website
View Site in Mobile | Classic
Share by: