Pausas publicitarias
El SDK de remitente de Android proporciona compatibilidad con las pausas publicitarias y los anuncios complementarios dentro de una transmisión de contenido multimedia determinada.
Consulta la Descripción general de las pausas publicitarias del receptor web para obtener más información sobre cómo funcionan las pausas publicitarias.
Si bien las pausas se pueden especificar tanto en el remitente como en el receptor, se recomienda que se especifiquen en el receptor web y receptor de Android TV para mantener un comportamiento coherente en todas las plataformas.
En Android, especifica las pausas publicitarias en un comando de carga con
AdBreakClipInfo
y AdBreakInfo:
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)
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);
Agrega acciones personalizadas
Una app de remitente puede extender
MediaIntentReceiver
para controlar acciones personalizadas o anular su comportamiento. Si implementaste tu propio MediaIntentReceiver, debes agregarlo al manifiesto y también establecer su nombre en CastMediaOptions. En este ejemplo, se proporcionan acciones personalizadas que anulan la reproducción de contenido multimedia remoto, presionar el botón de contenido multimedia y otros tipos de acciones.
// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
// 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) { } }
// 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) { } }
Agrega un canal personalizado
Para que la app de remitente se comunique con la app de receptor, tu app debe crear un canal personalizado. El remitente puede usar el canal personalizado para enviar mensajes de cadena al receptor. Cada canal personalizado se define con un espacio de nombres único
y debe comenzar con el prefijo urn:x-cast:, por ejemplo,
urn:x-cast:com.example.custom. Es posible tener varios canales personalizados, cada uno con un espacio de nombres único. La app de receptor también puede
enviar y recibir mensajes
con el mismo espacio de nombres.
El canal personalizado se implementa con la
Cast.MessageReceivedCallback
interfaz:
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") } }
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); } }
Una vez que la app de remitente se conecta a la app de receptor, se puede crear el canal personalizado con el
método:
setMessageReceivedCallbacks
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.namespace, mHelloWorldChannel) } catch (e: IOException) { Log.e(TAG, "Exception while creating channel", e) }
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.getNamespace(), mHelloWorldChannel); } catch (IOException e) { Log.e(TAG, "Exception while creating channel", e); }
Una vez que se crea el canal personalizado, el remitente puede usar el
sendMessage
método para enviar mensajes de cadena al receptor a través de ese canal:
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) } } }
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); } } }
Compatibilidad con la reproducción automática
Consulta la sección APIs de reproducción automática y en cola.
Anula la selección de imágenes para los widgets de UX
Varios componentes del framework (es decir, el diálogo de Cast, el minicontrolador y el UIMediaController, si está configurado) mostrarán material gráfico para el contenido multimedia que se está transmitiendo. Las URLs del material gráfico de la imagen suelen incluirse en el MediaMetadata del contenido multimedia, pero es posible que la app de remitente tenga una fuente alternativa para las URLs.
La
ImagePicker
clase define un medio para seleccionar una imagen adecuada de la lista de imágenes
en un MediaMetadata, según el uso de la imagen, por ejemplo, la miniatura de notificación
o el fondo de pantalla completa. La implementación predeterminada de ImagePicker siempre elige la primera imagen o muestra un valor nulo si no hay ninguna imagen disponible en MediaMetadata. Tu app puede crear una subclase de ImagePicker y anular el
onPickImage(MediaMetadata, ImageHints)
método para proporcionar una implementación alternativa y, luego, seleccionar esa subclase
con el
setImagePicker
método de CastMediaOptions.Builder.
ImageHints
proporciona sugerencias a un ImagePicker sobre el tipo y el tamaño de una imagen que se seleccionará para mostrar en la IU.
Personaliza los diálogos de Cast
Administra el ciclo de vida de la sesión
SessionManager
es el lugar central para administrar el ciclo de vida de la sesión. SessionManager escucha
los cambios de estado de selección de ruta
MediaRouter
para iniciar, reanudar y finalizar sesiones. Cuando se selecciona una ruta, SessionManager crea un objeto
Session
y trata de iniciarlo o reanudarlo. Cuando se anula la selección de una ruta, SessionManager finaliza la sesión actual.
Por lo tanto, para garantizar que SessionManager administre correctamente los ciclos de vida de la sesión, debes asegurarte de lo siguiente:
- En el diálogo del selector de rutas,
llama
MediaRouter.selectRoute(MediaRouter.RouteInfo)cuando un usuario selecciona un dispositivo. - En el diálogo del controlador de rutas (ya sea en estado
conectado o
estado
de transmisión),
llama a
MediaRouter.unselect(int)cuando el usuario detiene la transmisión.
Según cómo crees los diálogos de Cast, es posible que debas realizar acciones adicionales:
- Si creas diálogos de Cast con
MediaRouteChooserDialogyMediaRouteControllerDialog, estos diálogos actualizarán automáticamente la selección de rutas enMediaRouter, por lo que no es necesario hacer nada. - Si configuras el botón para transmitir con
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)oCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton), los diálogos se crearán conMediaRouteChooserDialogyMediaRouteControllerDialog, por lo que tampoco es necesario hacer nada. - En otros casos, crearás diálogos de Cast personalizados, por lo que debes seguir las instrucciones anteriores para actualizar el estado de selección de rutas en
MediaRouter.
Estado de cero dispositivos
Si creas diálogos de Cast personalizados, tu MediaRouteChooserDialog personalizado debe controlar correctamente el caso de que no se encuentren dispositivos. El diálogo debe tener indicadores que les permitan a los usuarios saber cuándo tu app aún intenta encontrar dispositivos y cuándo el intento de descubrimiento ya no está activo.
Si usas el MediaRouteChooserDialog predeterminado, el estado de cero dispositivos ya se controla.
Próximos pasos
Con esto, concluyen las funciones que puedes agregar a tu app de remitente de Android. Ahora puedes compilar una app de remitente para otra plataforma (iOS o Web) o una app de receptor web.