Intervalos de anúncio
O SDK do remetente do Android oferece suporte a intervalos de anúncios e anúncios complementares em um determinado stream de mídia.
Consulte o Visão geral das pausas para anúncios do Web Receiver para mais informações sobre como elas funcionam.
Embora as pausas possam ser especificadas no transmissor e no receptor, é recomendável que elas sejam especificadas no Receptor da Web e no Receptor do Android TV para manter o comportamento consistente em todas as plataformas.
No Android, especifique intervalos de anúncios 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 de remetente pode estender
MediaIntentReceiver
para processar ações personalizadas ou substituir o comportamento. Se você implementou sua
própria MediaIntentReceiver
, adicione-a ao manifesto e defina o
nome dela no CastMediaOptions
. Este exemplo fornece ações personalizadas que
substituem a reprodução de mídia remota, pressionando 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, ele precisa
criar um canal personalizado. O remetente pode usar o canal personalizado para enviar mensagens
de string ao destinatário. 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
interface
Cast.MessageReceivedCallback
:
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 de envio for conectado ao app de recebimento, o canal personalizado poderá
ser criado usando o 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); }
Depois que o canal personalizado é criado, o remetente pode usar o método
sendMessage
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 imagem para widgets de UX
Vários componentes do framework (ou seja, a caixa de diálogo de transmissão, o minicontrolador
e o UIMediaController, se configurados) 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 de envio pode ter uma
origem alternativa para os URLs.
A classe
ImagePicker
define uma forma de selecionar uma imagem adequada da lista de imagens
em um MediaMetadata
, com base no uso da imagem, por exemplo, miniatura
de notificação ou plano de fundo de tela cheia. A implementação padrão de ImagePicker
sempre escolhe a primeira imagem ou retorna null se nenhuma imagem estiver disponível no
MediaMetadata
. O app pode criar uma subclasse de ImagePicker
e substituir o método
onPickImage(MediaMetadata, ImageHints)
para fornecer uma implementação alternativa e, em seguida, selecionar essa subclasse
com o método
setImagePicker
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
Como 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
cria um objeto
Session
e tenta iniciá-lo ou retomá-lo. Quando uma rota é desmarcada,
SessionManager
encerra a sessão atual.
Portanto, para garantir que o SessionManager
gerencie os ciclos de vida da sessão corretamente, é necessário verificar se:
- Na caixa de diálogo do seletor de rota,
chame
MediaRouter.selectRoute(MediaRouter.RouteInfo)
quando um usuário selecionar um dispositivo. - Na caixa de diálogo do controlador de rota (no estado
conectado ou
no estado de
transmissão),
chame
MediaRouter.unselect(int)
quando o usuário parar de transmitir.
Dependendo de como você cria as caixas de diálogo do Google Cast, talvez seja necessário realizar outras ações:
- Se você criar caixas de diálogo de transmissão usando
MediaRouteChooserDialog
eMediaRouteControllerDialog
, elas vão atualizar a seleção de rota emMediaRouter
automaticamente, então nada precisa ser feito. - Se você configurar o botão de transmissão usando
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
ouCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
, as caixas de diálogo serão criadas usandoMediaRouteChooserDialog
eMediaRouteControllerDialog
. Portanto, nada precisa ser feito. - Em outros casos, você vai criar caixas de diálogo personalizadas do Google Cast. Portanto, siga
as instruções acima para atualizar o estado da seleção de rotas em
MediaRouter
.
Estado de zero dispositivos
Se você criar caixas de diálogo personalizadas do Google Cast, o
MediaRouteChooserDialog
personalizado vai processar corretamente o caso em que nenhum dispositivo é
encontrado. A caixa de diálogo precisa ter indicadores que deixem claro para os usuários quando o
app ainda estiver tentando encontrar dispositivos e quando a tentativa de descoberta não estiver
mais ativa.
Se você estiver usando o MediaRouteChooserDialog
padrão, o estado de zero dispositivos
já será processado.
Próximas etapas
Isso conclui os recursos que você pode adicionar ao seu app de envio do Android. Agora você pode criar um app de envio para outra plataforma (iOS ou Web) ou criar um app de recebimento da Web.