O procedimento a seguir permite converter o app de transmissão do Android do SDK do Cast v2 com CCL para CAF. Todas as funcionalidades da CCL foram implementadas no CAF. Portanto, depois da migração, não será mais necessário usar a CCL.
O SDK do remetente do CAF do Google 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 bastante o desenvolvimento de um app Cast.
Introdução
- Como o design do remetente do CAF foi influenciado pela Biblioteca complementar do Cast, a migração do CCL para o remetente do CAF envolve principalmente mapeamentos de classes de um para um e os métodos deles.
- O remetente do CAF ainda é distribuído como parte do Google Play Services usando o Android SDK Manager.
- Os novos pacotes (
com.google.android.gms.cast.framework.*
) que foram adicionados ao remetente do CAF, com funcionalidade semelhante ao CCL, assumem a responsabilidade de obedecer à lista de verificação de design do Google Cast. - O remetente do CAF fornece widgets que obedecem aos requisitos de UX do Cast. Esses widgets são semelhantes aos fornecidos pela CCL.
- O remetente do CAF fornece callbacks assíncronos semelhantes ao CCL, para rastrear estados e receber dados. Ao contrário do CCL, o Remetente do CAF não fornece implementações do ambiente autônomo dos vários métodos de interface.
Nas seções a seguir, vamos nos concentrar principalmente nos aplicativos centradas em vídeo com base no VideoCastManager da CCL, mas em muitos casos os mesmos conceitos também se aplicam ao DataCastManager.
Dependências
O CCL e o CAF têm as mesmas dependências na biblioteca de suporte AppCompat, na Biblioteca de Suporte MediaRouter v7 e no Google Play Services. No entanto, a diferença é que o CAF depende do novo framework do Google Cast que está disponível no Google Play Services 9.2.0 ou mais recente.
No arquivo build.gradle, remova as dependências em
com.google.android.gms:play-services-cast
e
com.google.android.libraries.cast.companionlibrary:ccl
e adicione o novo framework do Cast:
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:mediarouter-v7:23.4.0'
compile 'com.google.android.gms:play-services-cast-framework:9.4.0'
}
Também é possível remover os metadados do Google Play Services:
<meta‐data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
Todos os serviços, atividades e recursos que fazem parte do CAF são automaticamente integrados ao manifesto e aos recursos do app.
A versão mínima do SDK do Android compatível com o CAF é 9 (Gingerbread) e a versão mínima do SDK do CCL para Android é 10.
A CCL fornece um método de conveniência,
BaseCastManager.checkGooglePlayServices(activity)
, para verificar se uma versão
compatível do Google Play Services está disponível no dispositivo. O CAF não
fornece isso como parte do SDK do Cast. Siga o procedimento
Garantir que os dispositivos tenham o APK do Google Play Services
para garantir que o APK do Google Play Services correto esteja instalado no dispositivo
de um usuário, já que as atualizações podem não chegar a todos os usuários imediatamente.
Ainda é necessário usar uma variante de Theme.AppCompat para o tema do app.
Inicialização
Para a CCL, era preciso chamar VideoCastManager.initialize()
no
método onCreate()
da instância do aplicativo. Essa lógica precisa ser
removida do código de classe do aplicativo.
No CAF, também é necessária uma etapa de inicialização explícita para o framework
do Cast. Isso envolve a inicialização do singleton CastContext
, usando um
OptionsProvider
apropriado para especificar o ID do aplicativo receptor e quaisquer
outras opções globais. O CastContext
desempenha um papel semelhante ao
VideoCastManager
da CCL, fornecendo um Singleton com que os clientes interajam.
O OptionsProvider
é semelhante ao CastConfiguration
da CCL para permitir que você
configure os recursos do framework do Cast.
Se o CastConfiguration.Builder
da CCL atual estiver assim:
VideoCastManager.initialize(
getApplicationContext(),
new CastConfiguration.Builder(context.getString(R.string.app_id))
.enableWifiReconnection()
.enableAutoReconnect()
.build());
Em seguida, no CAF, o CastOptionsProvider
a seguir usando o CastOptions.Builder
seria semelhante:
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;
}
}
Confira nosso app de exemplo para ver uma implementação completa do OptionsProvider.
Declare o OptionsProvider no elemento "application" do arquivo AndroidManifest.xml:
<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 Activity
,
e não na instância Application
:
private CastContext mCastContext;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video_browser);
setupActionBar();
mCastContext = CastContext.getSharedInstance(this);
}
Para acessar o Singleton CastContext
, use:
mCastContext = CastContext.getSharedInstance(this);
Descoberta de dispositivos
Os incrementUiCounter
e decrementUiCounter
de VideoCastManager
da CCL precisam
ser removidos dos métodos onResume
e onPause
do Activities
.
No CAF, o processo de descoberta é iniciado e interrompido automaticamente pelo framework quando o app entra em primeiro plano e vai para o segundo plano, respectivamente.
Botão e caixa de diálogo de transmissão
Assim como na CCL, esses componentes são fornecidos pela Biblioteca de Suporte do MediaRouter v7.
O botão Transmitir ainda é implementado pelo MediaRouteButton
e pode ser adicionado
à sua atividade (usando um ActionBar
ou um Toolbar
) como um item
de menu.
A declaração de MediaRouteActionProvider
no XML de menu é a mesma
da CCL:
<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"/>
Semelhante ao CCL, modifique o método onCreateOptionMenu() de cada atividade. No entanto, em vez de usar CastManager.addMediaRouterButton, use o CastButtonFactory do CAF para conectar o MediaRouteButton ao framework do Cast:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.browse, menu);
CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
menu,
R.id.media_route_menu_item);
return true;
}
Controle do dispositivo
Semelhante ao CCL, no CAF, o controle do dispositivo é amplamente tratado pelo framework.
O aplicativo remetente não precisa processar (e não tentar processar)
a conexão com o dispositivo e a inicialização do app receptor usando
GoogleApiClient
.
A interação entre o remetente e o destinatário agora é representada como uma "sessão". A
classe SessionManager
processa o ciclo de vida da sessão e inicia e interrompe
automaticamente as sessões em resposta a gestos do usuário. Uma sessão é iniciada quando o
usuário seleciona um dispositivo de transmissão na caixa de diálogo de transmissão e termina quando ele toca
no botão "Parar transmissão" na caixa de diálogo ou quando o próprio app remetente
é encerrado.
Na CCL, é necessário estender a classe VideoCastConsumerImpl
para rastrear o status da
sessão de transmissão:
private final VideoCastConsumer mCastConsumer = new VideoCastConsumerImpl() {
public void onApplicationConnected(ApplicationMetadata appMetadata,
String sessionId,
boolean wasLaunched) {}
public void onDisconnectionReason(int reason) {}
public void onDisconnected() {}
}
No CAF, 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 do
ciclo de vida da sessão.
Os seguintes métodos SessionManagerListener
são mapeados na interface
VideoCastConsumer
da CCL:
VideoCastConsumer.onApplicationConnected
->SessionManagerListener.onSessionStarted
VideoCastConsumer.onDisconnected
->SessionManagerListener.onSessionEnded
Declare uma classe que implemente a interface SessionManagerListener
e mova
a lógica VideoCastConsumerImpl
para os métodos correspondentes:
private class CastSessionManagerListener implements SessionManagerListener<CastSession> {
public void onSessionEnded(CastSession session, int error) {}
public void onSessionStarted(CastSession session, String sessionId) {}
public void onSessionEnding(CastSession session) {}
...
}
A classe CastSession
representa uma sessão com um dispositivo de transmissão. A classe tem
métodos para controlar o volume do dispositivo e os estados de silenciamento, o que a CCL faz no
BaseCastManager
.
Em vez de usar o CCL VideoCastManager
para adicionar um consumidor:
VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);
Agora, registre seu SessionManagerListener
:
mCastSessionManager =
CastContext.getSharedInstance(this).getSessionManager();
mCastSessionManagerListener = new CastSessionManagerListener();
mCastSessionManager.addSessionManagerListener(mCastSessionManagerListener,
CastSession.class);
Para parar de detectar eventos na CCL, faça o seguinte:
VideoCastManager.getInstance().removeVideoCastConsumer(mCastConsumer);
Agora, use o SessionManager
para parar de detectar eventos de sessão:
mCastSessionManager.removeSessionManagerListener(mCastSessionManagerListener,
CastSession.class);
Para se desconectar explicitamente do dispositivo de transmissão, a CCL usou:
VideoCastManager.disconnectDevice(boolean stopAppOnExit,
boolean clearPersistedConnectionData,
boolean setDefaultRoute)
Para o CAF, use o SessionManager
:
CastContext.getSharedInstance(this).getSessionManager()
.endCurrentSession(true);
Para determinar se o remetente está conectado ao destinatário, o CCL fornece
VideoCastManager.getInstance().isConnected()
, mas no CAF use o
SessionManager
:
public boolean isConnected() {
CastSession castSession = CastContext.getSharedInstance(mAppContext)
.getSessionManager()
.getCurrentCastSession();
return (castSession != null && castSession.isConnected());
}
No CAF, as notificações de mudança de estado de volume/mudo ainda são entregues por métodos
de callback na Cast.Listener
. Esses listeners são registrados em
CastSession
. Todas as outras notificações de estado do dispositivo são entregues por
callbacks CastStateListener
. Esses listeners são registrados com o
CastSession
. Cancele o registro dos listeners quando os fragmentos, as atividades ou os apps associados ficarem em segundo plano.
Lógica de reconexão
O CAF tenta restabelecer conexões de rede perdidas devido a perda temporária de sinal Wi-Fi ou outros erros de rede. Agora, isso é feito no nível da sessão. Uma sessão pode entrar no estado "suspenso" quando a conexão for perdida e passar para o estado "conectado" quando a conectividade for restaurada. O framework cuida da reconexão ao app receptor e da reconexão de todos os canais de transmissão como parte desse processo.
O CAF fornece o próprio serviço de reconexão para que você possa remover
o CCL ReconnectionService
do manifesto:
<service android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>
Você também não precisa das seguintes permissões no manifesto para a lógica de reconexão:
<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE"/>
O serviço de reconexão do CAF é ativado por padrão, mas pode ser desativado usando o
CastOptions
.
Além disso, o CAF também adiciona a retomada automática da sessão, que é ativada por padrão e pode ser desativada por CastOptions
. Se o aplicativo remetente for enviado para o segundo plano ou for encerrado (deslizando para fora ou devido a uma falha) enquanto uma sessão de transmissão estiver em andamento, o framework tentará retomar essa sessão quando o aplicativo remetente voltar para o primeiro plano ou for reiniciado. Isso será processado automaticamente pelo SessionManager
, que
Registro de canal personalizado
O CCL oferece duas maneiras de criar um canal de mensagens personalizado para o destinatário:
CastConfiguration
permite que você especifique vários namespaces, e o CCL criará o canal.DataCastManager
é semelhante ao VideoCastManager, mas focado em casos de uso que não sejam de mídia.
Nenhuma dessas formas de criar um canal personalizado é compatível com o CAF. Você precisa seguir o procedimento Adicionar um canal personalizado para seu app remetente.
Assim como na CCL, para aplicativos de mídia, não é necessário registrar explicitamente o canal de controle de mídia.
Controle de mídia
No CAF, a classe RemoteMediaClient
é equivalente aos métodos de mídia
VideoCastManager
. O RemoteMediaClient.Listener
é equivalente aos
métodos VideoCastConsumer
. Especificamente, os
métodos onRemoteMediaPlayerMetadataUpdated
e onRemoteMediaPlayerStatusUpdated
de VideoCastConsumer
são mapeados para os métodos
onMetadataUpdated
e onStatusUpdated
de RemoteMediaClient.Listener
, respectivamente:
private class CastMediaClientListener implements RemoteMediaClient.Listener {
@Override
public void onMetadataUpdated() {
setMetadataFromRemote();
}
@Override
public void onStatusUpdated() {
updatePlaybackState();
}
@Override
public void onSendingRemoteMediaRequest() {
}
@Override
public void onQueueStatusUpdated() {
}
@Override
public void onPreloadStatusUpdated() {
}
}
Não é necessário inicializar ou registrar explicitamente o objeto RemoteMediaClient
.
O framework vai instanciar automaticamente o objeto e registrar o
canal de mídia subjacente no horário de início da sessão se o app receptor que está sendo
conectado for compatível com o namespace de mídia.
O RemoteMediaClient
pode ser acessado como o método getRemoteMediaClient
do
objeto CastSession
.
CastSession castSession = CastContext.getSharedInstance(mAppContext)
.getSessionManager()
.getCurrentCastSession();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClientListener = new CastMediaClientListener();
Em vez de CCLs:
VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);
Agora use o CAF:
mRemoteMediaClient.addListener(mRemoteMediaClientListener);
Qualquer número de listeners pode ser registrado no RemoteMediaClient
,
que permite que vários componentes de remetente compartilhem a única instância de
RemoteMediaClient
associada à sessão.
O VideoCastManager
da CCL fornece métodos para processar a reprodução de mídia:
VideoCastManager manager = VideoCastManager.getInstance();
if (manager.isRemoteMediaLoaded()) {
manager.pause();
mCurrentPosition = (int) manager.getCurrentMediaPosition();
}
Agora, eles são implementados pelo RemoteMediaClient no CAF:
if (mRemoteMediaClient.hasMediaSession()) {
mRemoteMediaClient.pause();
mCurrentPosition =
(int)mRemoteMediaClient.getApproximateStreamPosition();
}
No CAF, todas as solicitações de mídia emitidas no RemoteMediaClient
retornam um
RemoteMediaClient.MediaChannelResult
usando um callback PendingResult
,
que pode ser usado para rastrear o progresso e o resultado final da solicitação.
Tanto a CCL quanto o CAF usam as classes MediaInfo
e MediaMetadata
para representar
itens de mídia e carregar mídia.
Para carregar mídia em CCL, use o VideoCastManager
:
VideoCastManager.getInstance().loadMedia(media, autoPlay, mCurrentPosition, customData);
No CAF, o RemoteMediaClient
é usado para carregar a mídia:
mRemoteMediaClient.load(media, autoPlay, mCurrentPosition, customData);
Para ver as informações e o status Media
de uma sessão de mídia atual no
receptor, a CCL usa o VideoCastManager
:
MediaInfo mediaInfo = VideoCastManager.getInstance()
.getRemoteMediaInformation();
int status = VideoCastManager.getInstance().getPlaybackStatus();
int idleReason = VideoCastManager.getInstance().getIdleReason();
No CAF, use o RemoteMediaClient
para acessar as mesmas informações:
MediaInfo mediaInfo = mRemoteMediaClient.getMediaInfo();
int status = mRemoteMediaClient.getPlayerState();
int idleReason = mRemoteMediaClient.getIdleReason();
Sobreposição introdutória
Semelhante à CCL, o CAF fornece uma visualização personalizada IntroductoryOverlay
para destacar
o botão Transmitir quando ele é mostrado aos usuários pela primeira vez.
Em vez de usar o método onCastAvailabilityChanged
da VideoCastConsumer
da
CLC para saber quando exibir a sobreposição, declare um CastStateListener
para determinar
quando o botão Transmitir ficará visível quando os dispositivos de transmissão forem descobertos na
rede local pelo MediaRouter
:
private IntroductoryOverlay mIntroductoryOverlay;
private MenuItem mMediaRouteMenuItem;
protected void onCreate(Bundle savedInstanceState) {
...
mCastStateListener = new CastStateListener() {
@Override
public void onCastStateChanged(int newState) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay();
}
}
};
mCastContext = CastContext.getSharedInstance(this);
mCastContext.registerLifecycleCallbacksBeforeIceCreamSandwich(this,
savedInstanceState);
}
protected void onResume() {
mCastContext.addCastStateListener(mCastStateListener);
...
}
protected void onPause() {
mCastContext.removeCastStateListener(mCastStateListener);
...
}
Acompanhe a instância MediaRouteMenuItem
:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.browse, menu);
mMediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(
getApplicationContext(), menu,
R.id.media_route_menu_item);
showIntroductoryOverlay();
return true;
}
Verifique se o MediaRouteButton
está visível para que a sobreposição introdutória
seja exibida:
private void showIntroductoryOverlay() {
if (mIntroductoryOverlay != null) {
mIntroductoryOverlay.remove();
}
if ((mMediaRouteMenuItem != null) && mMediaRouteMenuItem.isVisible()) {
new Handler().post(new Runnable() {
@Override
public void run() {
mIntroductoryOverlay = new IntroductoryOverlay.Builder(
VideoBrowserActivity.this, mMediaRouteMenuItem)
.setTitleText(getString(R.string.introducing_cast))
.setOverlayColor(R.color.primary)
.setSingleTime()
.setOnOverlayDismissedListener(
new IntroductoryOverlay
.OnOverlayDismissedListener() {
@Override
public void onOverlayDismissed() {
mIntroductoryOverlay = null;
}
})
.build();
mIntroductoryOverlay.show();
}
});
}
}
Confira nosso app de exemplo para ver o código de trabalho completo para mostrar a sobreposição introdutória.
Para personalizar o estilo da sobreposição introdutória, siga o procedimento Personalizar sobreposição introdutória.
Minicontrole
Em vez de MiniController
do CCL, use o MiniControllerFragment
do CAF no
arquivo de layout do app das atividades em que você quer mostrar o mini
controlador:
<fragment
android:id="@+id/cast_mini_controller"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:castShowImageThumbnail="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
O CAF não é compatível com a configuração manual aceita pelo MiniController
da CCL
e também com o recurso Autoplay
.
Para personalizar o estilo e os botões do minicontrole, siga o procedimento Personalizar minicontrole.
Notificação e tela de bloqueio
Semelhante ao VideoCastNotificationService
do CCL, o CAF fornece um
MediaNotificationService
para gerenciar a exibição de notificações de mídia
durante a transmissão.
Você precisa remover o seguinte do seu manifesto:
VideoIntentReceiver
VideoCastNotificationService
O CCL oferece suporte a um serviço de notificação personalizado com o
CastConfiguration.Builder
, o que não é suportado pelo CAF.
Considere a seguinte inicialização de CastManager
usando CCL:
VideoCastManager.initialize(
getApplicationContext(),
new CastConfiguration.Builder(
context.getString(R.string.app_id))
.addNotificationAction(
CastConfiguration.NOTIFICATION_ACTION_PLAY_PAUSE,true)
.addNotificationAction(
CastConfiguration.NOTIFICATION_ACTION_DISCONNECT,true)
.build());
Para a configuração equivalente no CAF, o SDK fornece um
NotificationsOptions.Builder
para ajudar você a criar controles de mídia para a
notificação e a tela de bloqueio no app remetente. Os controles da tela de notificação e
bloqueio podem ser ativados com o CastOptions
ao inicializar o
CastContext
.
public CastOptions getCastOptions(Context context) {
NotificationOptions notificationOptions =
new NotificationOptions.Builder()
.setActions(Arrays.asList(
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{0, 1})
.build();
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build();
return new CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build();
}
As notificações e os controles da tela de bloqueio estão sempre ativados no CAF. Além disso, os botões de reproduzir/pausar e interromper a transmissão são fornecidos por padrão. O CAF
vai rastrear automaticamente a visibilidade das atividades para decidir
quando exibir a notificação de mídia, exceto o Gingerbread.
Para o Gingerbread, consulte a observação anterior sobre
o uso de registerLifecycleCallbacksBeforeIceCreamSandwich()
. As chamadas
VideoCastManager
incrementUiCounter
e decrementUiCounter
do CCL
precisam ser removidas.
Para personalizar os botões mostrados nas notificações, siga o procedimento Adicionar controles de mídia à tela de bloqueio e notificação.
Controle expandido
A CCL fornece a VideoCastControllerActivity
e a VideoCastControllerFragment
para exibir um controlador expandido ao transmitir mídia.
Você pode remover a declaração VideoCastControllerActivity
no manifesto.
No CAF, é necessário estender a ExpandedControllerActivity e adicionar o botão "Transmitir".
Para personalizar os estilos e botões exibidos no controle expandido, siga o procedimento Personalizar controle expandido.
Seleção de áudio
Assim como na CCL, a seleção de áudio é gerenciada automaticamente.
Controle do volume
Para o Gingerbread, o dispatchKeyEvent
é obrigatório, assim como o CCL. No ICS e em versões posteriores,
o controle de volume do CCL e do CAF é processado automaticamente.
O CAF permite controlar o volume de transmissão usando o botão de volume rígido no smartphone das atividades do app e também mostra uma barra de volume visual ao transmitir em versões compatíveis. O CAF também processa a mudança de volume com o volume rígido, mesmo que o app não esteja na frente, esteja bloqueado ou mesmo se a tela esteja desativada.
Closed captions
No Android KitKat e em versões mais recentes, as legendas podem ser personalizadas nas configurações de legendas, em "Configurações > Acessibilidade". No entanto, as versões anteriores do Android não têm esse recurso. O CCL lida com isso fornecendo configurações personalizadas para versões anteriores e delegando às configurações do sistema no KitKat e versões mais recentes.
O CAF não oferece configurações personalizadas para alterar as preferências de legenda. Remova
as referências CaptionsPreferenceActivity
do manifesto
e do XML de preferências.
O TracksChooserDialog
do CCL não é mais necessário, já que a mudança das faixas de
legenda fechada é processada pela IU do controlador expandida.
A API de legenda no CAF é semelhante à v2.
Registro de depuração
O CAF não oferece configurações de geração de registros de depuração.
Diversos
Os seguintes recursos do CCL não são compatíveis com o CAF:
- Como conseguir autorização antes da reprodução fornecendo um
MediaAuthService
- Mensagens de IU configuráveis
Apps de exemplo
Confira a diferença para migrar nosso app de exemplo Universal Music Player para Android (uamp) do CCL para o CAF.
Também temos tutoriais do codelab e apps de exemplo que usam o CAF.