Intervalos de anúncio
O SDK do Android Sender fornece suporte para intervalos de anúncio e anúncios complementares em uma em um determinado fluxo de mídia.
Consulte a Visão geral dos intervalos de anúncios do receptor da Web para mais informações sobre como funcionam os intervalos de anúncio.
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ú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 modificar o comportamento delas. Se você tiver implementado
MediaIntentReceiver
, é necessário adicioná-lo ao manifesto e definir o
no CastMediaOptions
. Este exemplo fornece ações personalizadas que
substituir ativar ou desativar 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 receptor, o app precisa
criar um canal personalizado. O remetente pode usar o canal personalizado para enviar a string
para o destinatário. Cada canal personalizado é definido por um único
namespace e precisa começar com o prefixo urn:x-cast:
, por exemplo,
urn:x-cast:com.example.custom
. É possível ter várias métricas personalizadas
canais, 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 o
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); } }
Quando o app remetente estiver conectado ao app receptor, o canal personalizado poderá
ser criado usando o
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 critério personalizado for criado, o remetente poderá usar o campo
sendMessage
para enviar mensagens de string para o 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 Reprodução automática e APIs de 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 para a arte da imagem são normalmente
incluído no MediaMetadata
da mídia, mas o app remetente pode ter
uma fonte alternativa para os URLs.
O
ImagePicker
define um meio para selecionar uma imagem apropriada na lista de imagens
em uma MediaMetadata
, com base no uso da imagem, por exemplo, uma notificação
miniatura ou plano de fundo em tela cheia. A implementação padrão de ImagePicker
sempre escolhe a primeira imagem ou retorna nulo se nenhuma imagem estiver disponível na
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.
Como 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
as mudanças de estado de seleção de rota
MediaRouter
do Android para iniciar, retomar e encerrar sessões. Quando um trajeto é
selecionada, SessionManager
criará uma
Session
e tenta iniciá-lo ou retomá-lo. Quando uma rota não é selecionada,
SessionManager
vai encerrar a sessão atual.
Portanto, para garantir que o SessionManager
gerencie os ciclos de vida da sessão corretamente, você
precisa se certificar de que:
- 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 roteador (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, pode ser necessário realizar outras ações concluído:
- Se você criar caixas de diálogo "Transmitir" usando
MediaRouteChooserDialog
eMediaRouteControllerDialog
, essas caixas de diálogo vão atualizar a seleção de trajeto noMediaRouter
automaticamente, então nada precisa ser feito. - Se você configurar o botão Transmitir usando
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
ouCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
, os diálogos são criado usandoMediaRouteChooserDialog
eMediaRouteControllerDialog
, então também não precisa fazer nada. - Para outros casos, você criará caixas de diálogo do Google Cast personalizadas, portanto, precisará
siga as instruções acima para atualizar o estado de seleção de trajeto em
MediaRouter
:
Estado de nenhum dispositivo
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 deve ter indicadores que deixem claro para os usuários quando os
ainda está tentando encontrar dispositivos e, quando a tentativa de descoberta
mais ativos.
Se você estiver usando o MediaRouteChooserDialog
padrão, o estado zero dispositivo
já é processado.
Próximas etapas
Esta é a conclusão dos recursos que você pode adicionar ao seu app Android Sender. Agora você pode criar um app remetente para outra plataforma (iOS ou Web) ou criar um app receptor da Web.