Migrar o app Android remetente do SDK do Cast v2 para o framework do aplicativo de transmissão (CAF, na sigla em inglês)

O procedimento a seguir permite converter seu app de envio do Android do SDK do Google Cast v2 para o CAF Sender, que é baseado no singleton CastContext.

O SDK do remetente do CAF do Cast usa o CastContext para gerenciar o GoogleAPIClient em seu nome. O CastContext gerencia ciclos de vida, erros e callbacks para você, o que simplifica muito o desenvolvimento de um app do Cast.

Introdução

  • O CAF Sender ainda é distribuído como parte do Google Play Services usando o gerenciador do SDK do Android.
  • Foram adicionados novos pacotes que assumem a responsabilidade de obedecer à lista de verificação de design do Google Cast (com.google.android.gms.cast.framework.*).
  • O CAF Sender fornece widgets que obedecem aos requisitos de UX do Cast. O v2 não oferecia nenhum componente de interface e exigia que você implementasse esses widgets.
  • O uso do GoogleApiClient não é mais necessário para usar a API Cast.
  • O closed captioning no CAF Sender é semelhante ao da v2.

Dependências

A V2 e a CAF têm as mesmas dependências nas bibliotecas de suporte e no Google Play Services (9.2.0 ou mais recente), conforme descrito no Guia de recursos da Biblioteca de suporte.

A versão mínima do SDK do Android com suporte do CAF é a 9 (Gingerbread).

Inicialização

No CAF, uma etapa de inicialização explícita é necessária para o framework do Cast. Isso envolve inicializar o singleton CastContext, usando um OptionsProvider adequado para especificar o ID do aplicativo do receptor da Web e outras opções globais.

public class CastOptionsProvider implements OptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build();
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Declare o OptionsProvider na tag "application" do arquivo AndroidManifest.xml do app:

<application>
...
    <meta-data
        android:name=
            "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />
</application>

Inicialize lentamente o CastContext no método onCreate de cada atividade:

private CastContext mCastContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.video_browser);
    setupActionBar();

    mCastContext = CastContext.getSharedInstance(this);
}

Essas etapas não eram necessárias na v2.

Descoberta de dispositivos

No CAF, o processo de descoberta é iniciado e interrompido automaticamente pelo framework quando o app passa para o primeiro plano e vai para o segundo plano, respectivamente. MediaRouteSelector e MediaRouter.Callback não devem ser usados.

Botão "Transmitir" e caixa de diálogo "Transmitir"

Assim como na v2, esses componentes são fornecidos pela biblioteca de suporte MediaRouter.

O botão "Transmitir" ainda é implementado pelo MediaRouteButton e pode ser adicionado à atividade (usando um ActionBar ou um Toolbar), como um item de menu.

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

Substitua o método onCreateOptionMenu() de cada atividade usando CastButtonFactory para conectar o MediaRouteButton ao framework do Google Cast:

private MenuItem mediaRouteMenuItem;

public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    mediaRouteMenuItem =
        CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                                menu,
                                                R.id.media_route_menu_item);
    return true;
}

Quando alguém toca no botão, a caixa de diálogo de transmissão é apresentada automaticamente.

Controle do dispositivo

No CAF, o controle do dispositivo é processado principalmente pelo framework. O aplicativo remetente não precisa processar (e não deve tentar processar) a conexão com o dispositivo e iniciar o aplicativo Web Receiver usando GoogleApiClient. A interação entre o remetente e o receptor da Web agora é representada como uma "sessão". A classe SessionManager gerencia o ciclo de vida da sessão e inicia e encerra sessões automaticamente em resposta aos gestos do usuário: uma sessão é iniciada quando o usuário seleciona um dispositivo do Google Cast na caixa de diálogo do Google Cast e é encerrada quando o usuário toca no botão "Parar transmissão" na caixa de diálogo do Google Cast ou quando o próprio app de envio é encerrado. O aplicativo do remetente pode ser notificado sobre eventos do ciclo de vida da sessão registrando um SessionManagerListener com o SessionManager. Os callbacks SessionManagerListener definem métodos de callback para todos os eventos de ciclo de vida da sessão.

A classe CastSession representa uma sessão com um dispositivo Google Cast. A classe tem métodos para controlar o volume e os estados de silenciamento do dispositivo, o que era feito anteriormente na v2 usando métodos em Cast.CastApi.

Na v2, os callbacks Cast.Listener forneciam notificações sobre mudanças no estado do dispositivo, incluindo volume, estado de mudo, status de espera e assim por diante.

No CAF, as notificações de mudança de estado de volume/silenciamento ainda são enviadas por métodos de callback no Cast.Listener. Esses listeners são registrados com CastSession. Todas as notificações restantes do estado do dispositivo são enviadas por callback CastStateListener. Esses listeners são registrados com o CastSession. Não se esqueça de cancelar o registro de listeners quando os fragmentos, atividades ou apps associados forem transferidos para o segundo plano.

Lógica de reativação

Assim como na v2, o CAF tenta restabelecer as conexões de rede que foram perdidas devido à perda temporária do sinal do Wi-Fi ou a outros erros de rede. Isso agora é feito no nível da sessão. Uma sessão pode entrar em um estado "suspenso" quando a conexão é perdida e voltará para um estado "conectado" quando a conectividade for restaurada. O framework se encarrega de se reconectar ao aplicativo do receptor da Web e reconectar todos os canais do Google Cast como parte desse processo.

Além disso, o CAF também adiciona a retomada automática de sessões, que é ativada por padrão (e pode ser desativada usando CastOptions. Se o aplicativo de envio for enviado para o segundo plano ou for encerrado (por deslizar para longe ou devido a uma falha) enquanto uma sessão do Google Cast estiver em andamento, o framework vai tentar retomar essa sessão quando o aplicativo de envio voltar para o primeiro plano ou for reiniciado. Isso é processado automaticamente pelo SessionManager, que emitirá os callbacks apropriados em qualquer instância SessionManagerListener registrada.

Registro de canais personalizados

Na v2, os canais personalizados (implementados usando Cast.MessageReceivedCallback) são registrados com o Cast.CastApi. No CAF, os canais personalizados são registrados com a instância CastSession. O registro pode ser feito no método de callback SessionManagerListener.onSessionStarted. Para aplicativos de mídia, não é mais necessário registrar explicitamente o canal de controle de mídia usando Cast.CastApi.setMessageReceivedCallbacks. Consulte a próxima seção para mais detalhes.

Controle de mídia

A classe v2 RemoteMediaPlayer foi descontinuada e não deve ser usada. No CAF, ele é substituído pela nova classe RemoteMediaClient, que oferece uma funcionalidade equivalente em uma API mais conveniente. Não é necessário inicializar ou registrar esse objeto explicitamente. O framework instanciará o objeto automaticamente e registrará o canal de mídia subjacente no início da sessão se o aplicativo do receptor da Web conectado for compatível com o namespace de mídia.

O RemoteMediaClient pode ser acessado como o método getRemoteMediaClient do objeto CastSession.

Na v2, todas as solicitações de mídia emitidas no RemoteMediaPlayer retornavam um RemoteMediaPlayer.MediaChannelResult por um callback PendingResult.

No CAF, todas as solicitações de mídia emitidas no RemoteMediaClient retornam um RemoteMediaClient.MediaChannelResult por meio de um callback PendingResult que pode ser usado para acompanhar o progresso e o resultado final da solicitação.

O RemoteMediaPlayer da v2 enviava notificações sobre mudanças no estado do reprodutor de mídia no Web Receiver pelo RemoteMediaPlayer.OnStatusUpdatedListener.

No CAF, o RemoteMediaClient fornece callbacks equivalentes pela interface RemoteMediaClient.Listener. Qualquer número de listeners pode ser registrado com o RemoteMediaClient, o que permite que vários componentes do remetente compartilhem a única instância de RemoteMediaClient associada à sessão.

Na v2, o aplicativo remetente precisava assumir a responsabilidade de manter a interface do usuário sincronizada com o estado do player de mídia no receptor da Web.

No CAF, a classe UIMediaController assume a maior parte dessa responsabilidade.

Sobreposição introdutória

A V2 não oferece uma interface de sobreposição introdutória.

O CAF oferece uma visualização personalizada IntroductoryOverlay para destacar o botão "Transmitir" quando ele for mostrado aos usuários pela primeira vez.

Minicontrole

Na v2, é necessário implementar um minicontrole do zero no app remetente.

No CAF, o SDK oferece uma visualização personalizada, MiniControllerFragment, que pode ser adicionada ao arquivo de layout do app das atividades em que você quer mostrar o minicontrole.

Notificação e tela de bloqueio

Na v2, os controles de notificação e tela de bloqueio não são fornecidos pelo SDK. Para esse SDK, é necessário criar esses recursos no app de envio usando as APIs do framework do Android.

No CAF, o SDK oferece um NotificationsOptions.Builder para ajudar a criar controles de mídia para a notificação e a tela de bloqueio no app remetente. Os controles de notificação e da tela de bloqueio podem ser ativados com o CastOptions ao inicializar o CastContext.

public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(VideoBrowserActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

Controle expandido

Na v2, é necessário implementar um controle expandido do zero no app remetente.

O CAF fornece uma classe auxiliar UIMediaController que facilita a criação do próprio controlador expandido.

O CAF adiciona um widget de controlador expandido pré-criado ExpandedControllerActivity que você pode adicionar ao app. Não é mais necessário implementar um controlador expandido personalizado usando UIMediaController.

Seleção de áudio

Na v2, é necessário usar MediaSessionCompat para gerenciar o foco de áudio.

No CAF, o foco de áudio é gerenciado automaticamente.

Registros de depuração

No CAF, não há opções de registro.

Apps de exemplo

Temos tutoriais de codelab e apps de exemplo que usam o CAF.