Intervalos de anúncio
O SDK do remetente do Android oferece suporte a intervalos de anúncio e anúncios complementares em um fluxo de mídia.
Consulte a visão geral dos intervalos de anúncio do Web Receiver para mais informações sobre como eles funcionam.
Embora os intervalos possam ser especificados no remetente e no receptor, recomendamos que eles sejam especificados no Web Receiver e Android TV Receiver para manter um comportamento consistente em todas as plataformas.
No Android, especifique intervalos de anúncio em um comando de carregamento usando
AdBreakClipInfo
e 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);
Adicionar ações personalizadas
Um app remetente pode estender
MediaIntentReceiver
para processar ações personalizadas ou substituir o comportamento dele. Se você implementou seu próprio MediaIntentReceiver, adicione-o ao manifesto e defina o nome dele em CastMediaOptions. Este exemplo fornece ações personalizadas que substituem a reprodução de mídia remota, pressionam o botão de mídia e outros tipos de ações.
// 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) { } }
Adicionar um canal personalizado
Para que o app remetente se comunique com o app receptor, é necessário criar um canal personalizado. O remetente pode usar o canal personalizado para enviar mensagens de string ao receptor. Cada canal personalizado é definido por um namespace exclusivo e precisa começar com o prefixo urn:x-cast:, por exemplo, urn:x-cast:com.example.custom. É possível ter vários canais personalizados, cada um com um namespace exclusivo. O app receptor também pode
enviar e receber mensagens
usando o mesmo namespace.
O canal personalizado é implementado com a
Cast.MessageReceivedCallback
interface:
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); } }
Depois que o app remetente estiver conectado ao app receptor, o canal personalizado pode
ser criado usando o
setMessageReceivedCallbacks
método:
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); }
Depois que o canal personalizado for criado, o remetente poderá usar o
sendMessage
método para enviar mensagens de string ao receptor por esse 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); } } }
Suporte à reprodução automática
Consulte a seção APIs de reprodução automática e enfileiramento.
Substituir a seleção de imagens para widgets de UX
Vários componentes da estrutura (ou seja, a caixa de diálogo do Google Cast, o minicontrolador e o UIMediaController, se configurado) vão mostrar a arte da mídia transmitida no momento. Os URLs da arte da imagem geralmente são incluídos no MediaMetadata da mídia, mas o app remetente pode ter uma fonte alternativa para os URLs.
A classe
ImagePicker
define um meio de selecionar uma imagem apropriada na lista de imagens
em um MediaMetadata, com base no uso da imagem, por exemplo, miniatura de notificação
ou plano de fundo em tela cheia. A implementação ImagePicker padrão sempre escolhe a primeira imagem ou retorna nulo se nenhuma imagem estiver disponível no MediaMetadata. Seu app pode criar uma subclasse ImagePicker e substituir o
onPickImage(MediaMetadata, ImageHints)
método para fornecer uma implementação alternativa e, em seguida, selecionar essa subclasse
com o
setImagePicker
método de CastMediaOptions.Builder.
ImageHints
fornece dicas para um ImagePicker sobre o tipo e o tamanho de uma imagem a ser selecionada para exibição na interface.
Personalizar caixas de diálogo do Google Cast
Gerenciar o ciclo de vida da sessão
SessionManager
é o local central para gerenciar o ciclo de vida da sessão. SessionManager detecta
mudanças no estado de seleção de rota do Android
MediaRouter
para iniciar, retomar e encerrar sessões. Quando uma rota é
selecionada, SessionManager criará um
Session
objeto e tentará iniciá-lo ou retomá-lo. Quando uma rota é desmarcada, SessionManager encerra a sessão atual.
Portanto, para garantir que SessionManager gerencie os ciclos de vida da sessão corretamente, faça o seguinte:
- Na caixa de diálogo do seletor de rotas,
chame
MediaRouter.selectRoute(MediaRouter.RouteInfo)quando um usuário selecionar um dispositivo. - Na caixa de diálogo do controlador de rotas (no estado conectado ou
de transmissão),
chame
MediaRouter.unselect(int)quando o usuário interromper a transmissão.
Dependendo de como você cria as caixas de diálogo do Google Cast, outras ações podem ser necessárias:
- Se você criar caixas de diálogo do Google Cast usando
MediaRouteChooserDialogeMediaRouteControllerDialog, elas vão atualizar a seleção de rotas noMediaRouterautomaticamente. Portanto, não é necessário fazer nada. - Se você configurar o botão Transmitir usando
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)ouCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton), as caixas de diálogo serão criadas usandoMediaRouteChooserDialogeMediaRouteControllerDialog. Portanto, também não é necessário fazer nada. - Em outros casos, você vai criar caixas de diálogo personalizadas do Google Cast. Portanto, siga as instruções acima para atualizar o estado de seleção de rotas no
MediaRouter.
Estado de zero dispositivos
Se você criar caixas de diálogo personalizadas do Google Cast, seu MediaRouteChooserDialog personalizado vai processar corretamente o caso de zero dispositivos encontrados. A caixa de diálogo precisa ter indicadores que deixem claro para os usuários quando o app ainda está tentando encontrar dispositivos e quando a tentativa de descoberta não está mais ativa.
Se você estiver usando o MediaRouteChooserDialog padrão, o estado de zero dispositivos já será processado.
Próximas etapas
Concluímos os recursos que podem ser adicionados ao app remetente do Android. Agora você pode criar um app remetente para outra plataforma (iOS ou Web) ou criar um app Web Receiver.