Intervalos de anúncio
O SDK do Android Sender oferece suporte a intervalos de anúncio e anúncios complementares em um determinado stream de mídia.
Consulte a Visão geral de intervalos de anúncios do receptor da Web para mais informações sobre como os intervalos de anúncio funcionam.
Embora os intervalos possam ser especificados no remetente e no receptor, é recomendável que eles sejam especificados no Receptor da Web e no Receptor do Android TV 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. Se você tiver implementado sua
própria MediaIntentReceiver
, será necessário adicioná-la ao manifesto e definir o
nome dela no CastMediaOptions
. Este exemplo fornece ações personalizadas que
substituem a alternância de 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, seu app 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); } }
Quando o app remetente estiver conectado ao app receptor, 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 destinatário 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); } } }
Compatibilidade com reprodução automática
Consulte a seção APIs de reprodução automática e de enfileiramento.
Modificar a seleção de imagens para widgets de UX
Vários componentes do framework (ou seja, a caixa de diálogo "Transmitir", o mini
controlador e o UIMediaController, se configurado) vão mostrar a arte
da mídia transmitida no momento. Os URLs da arte da imagem normalmente 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 para selecionar uma imagem apropriada da lista de imagens
em uma 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 ImagePicker
sempre escolhe a primeira imagem ou retorna nulo se nenhuma imagem estiver disponível na
MediaMetadata
. Seu app pode subclassificar ImagePicker
e substituir o método
onPickImage(MediaMetadata, ImageHints)
para oferecer 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. O SessionManager
detecta
as mudanças do estado de seleção de rota
MediaRouter
do Android 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 não é selecionada, o SessionManager
encerra a sessão atual.
Portanto, para garantir que SessionManager
gerencie os ciclos de vida da sessão corretamente, você
precisa garantir 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 controlador de rota (no estado conectado ou no estado 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 precisar ser realizadas:
- Se você criar caixas de diálogo do Cast usando
MediaRouteChooserDialog
eMediaRouteControllerDialog
, essas caixas vão atualizar a seleção de rota emMediaRouter
automaticamente. Portanto, nada precisa ser feito. - 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 usandoMediaRouteChooserDialog
eMediaRouteControllerDialog
. Portanto, também não será necessário fazer nada. - Para outros casos, você criará caixas de diálogo do Cast personalizadas. Portanto, siga as instruções acima para atualizar o estado de seleção de rota em
MediaRouter
.
Estado de nenhum dispositivo
Se você criar caixas de diálogo Transmitir personalizadas, seu
MediaRouteChooserDialog
personalizado vai processar corretamente o caso de nenhum dispositivo ser
encontrado. 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 zero de dispositivos
já será processado.
Próximas etapas
Isso conclui os recursos que você pode adicionar ao app Android Sender. Agora, é possível criar um app remetente para outra plataforma (iOS ou Web) ou criar um app receptor da Web.