Adicionar recursos avançados ao app Android

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 os intervalos possam ser especificados tanto para quem envia quanto para quem recebe, recomendamos que eles sejam especificado no Receptor da Web e Receptor do Android TV para manter a consistência do comportamento entre as plataformas.

No Android, especifique intervalos de anúncio em um comando de carregamento usando: AdBreakClipInfo e AdBreakInfo:

Kotlin
.
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)
Java
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" />
Kotlin
.
// 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) {
    }
}
Java
// 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:

Kotlin
.
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")
    }
}
Java
.
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 :

Kotlin
.
try {
    mCastSession.setMessageReceivedCallbacks(
        mHelloWorldChannel.namespace,
        mHelloWorldChannel)
} catch (e: IOException) {
    Log.e(TAG, "Exception while creating channel", e)
}
Java
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:

Kotlin
.
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)
        }
    }
}
Java
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 Reprodução automática e APIs de enfileiramento.

Modificar a seleção de imagens para widgets de UX

Vários componentes do framework, como a caixa de diálogo "Transmitir", os e o UIMediaController, se configurado, vai exibir a arte para a 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.

A 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. Seu app pode criar uma subclasse para ImagePicker e substituir a onPickImage(MediaMetadata, ImageHints) para fornecer uma implementação alternativa e, em seguida, selecionar a subclasse com o setImagePicker de CastMediaOptions.Builder. ImageHints fornece dicas para um ImagePicker sobre o tipo e o tamanho de uma imagem a ser selecionados 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. Você ouviu SessionManager vezes para Android MediaRouter o estado de seleção de rota muda 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:

Dependendo de como você cria as caixas de diálogo do Google Cast, pode ser necessário realizar outras ações concluído:

Estado de nenhum dispositivo

Se você criar caixas de diálogo Transmitir personalizadas, MediaRouteChooserDialog precisa processar corretamente o caso de nenhum dispositivo encontradas. 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.