Migrar la app emisora de CCL al framework de aplicaciones de Cast (CAF)

El siguiente procedimiento te permite convertir tu app emisora de Android de Cast SDK v2 con CCL a CAF. Toda la funcionalidad de CCL se implementó en CAF, por lo que, una vez que migres, ya no necesitarás usar CCL.

El SDK de CastF de CastF usa CastContext para administrar GoogleAPIClient en tu nombre. CastContext administra los ciclos de vida, los errores y las devoluciones de llamada por ti, lo que simplifica en gran medida el desarrollo de una app de Cast.

Introducción

  • Debido a que la Biblioteca complementaria de Cast influyó en el diseño del remitente de CAF, la migración de la CCL al remitente de CAF involucra asignaciones principalmente de uno a uno de las clases y sus métodos.
  • El remitente de CAF sigue estando distribuido como parte de los Servicios de Google Play mediante el SDK Manager de Android.
  • Los paquetes nuevos (com.google.android.gms.cast.framework.*) que se agregaron al remitente del CAF, con una funcionalidad similar a la CCL, asumen la responsabilidad de cumplir con la lista de tareas de diseño de Google Cast.
  • El emisor de CAF proporciona widgets que satisfacen los requisitos de UX de Cast. Estos widgets son similares a los proporcionados por CCL.
  • El emisor de CAF proporciona devoluciones de llamada asíncronas que son similares a la CCL para realizar un seguimiento de los estados y obtener datos. A diferencia de la CCL, el remitente de CAF no proporciona ninguna implementación no-op de los diversos métodos de interfaz.

En las siguientes secciones, nos enfocaremos principalmente en las aplicaciones centradas en video basadas en VideoCastManager de CCL, pero en muchos casos, los mismos conceptos se aplican a DataCastManager.

Dependencias

CCL y CAF tienen las mismas dependencias en la biblioteca de compatibilidad de AppCompat, en la biblioteca de compatibilidad de MediaRouter v7 y en los Servicios de Google Play. Sin embargo, la diferencia es que CAF depende del nuevo framework de Cast que está disponible en los Servicios de Google Play 9.2.0 o versiones posteriores.

En tu archivo build.gradle, quita las dependencias de com.google.android.gms:play-services-cast y com.google.android.libraries.cast.companionlibrary:ccl y, luego, agrega el nuevo framework de 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'
}

También puedes quitar los metadatos del servicio de Google Play:

<meta‐data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>

Todos los servicios, las actividades y los recursos que forman parte de CAF se combinan automáticamente con el manifiesto y los recursos de tu app.

La versión mínima del SDK de Android que admite CAF es 9 (Gingerbread). La versión mínima del SDK de Android de la CCL es 10.

CCL proporciona un método de conveniencia, BaseCastManager.checkGooglePlayServices(activity), para verificar que haya una versión compatible de los Servicios de Google Play disponible en el dispositivo. CAF no lo proporciona como parte del SDK de Cast. Sigue el procedimiento para garantizar que los dispositivos tengan el APK de servicios de Google Play a fin de asegurarte de que se instale el APK de servicios de Google Play correcto en el dispositivo de un usuario, ya que es posible que las actualizaciones no lleguen a todos los usuarios de inmediato.

Debes usar una variante de Theme.AppCompat para el tema de la aplicación.

Inicialización

Para la CCL, se requería la llamada a VideoCastManager.initialize() en el método onCreate() de la instancia de aplicaciones. Esta lógica se debe quitar del código de clase de la aplicación.

En CAF, también se requiere un paso de inicialización explícito para el framework de Cast. Esto implica inicializar el singleton CastContext, mediante un OptionsProvider apropiado para especificar el ID de la aplicación receptora y cualquier otra opción global. El CastContext cumple una función similar a la VideoCastManager de CCL, ya que proporciona un singleton con el que los clientes interactúan. OptionsProvider es similar a CastConfiguration de CCL para que puedas configurar las funciones del framework de Cast.

Si tu CastConfiguration.Builder actual de CCL tiene el siguiente aspecto:

VideoCastManager.initialize(
   getApplicationContext(),
   new CastConfiguration.Builder(context.getString(R.string.app_id))
       .enableWifiReconnection()
       .enableAutoReconnect()
       .build());

Entonces, en CAF, el CastOptionsProvider siguiente con el CastOptions.Builder sería similar:

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;
    }
}

Consulta nuestra app de muestra para ver una implementación completa de OptionsProvider.

Declara el objeto OptionsProvider dentro del elemento "application" del archivo 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>

Inicializa de forma diferida el CastContext en el método onCreate de cada Activity (no en la instancia Application):

private CastContext mCastContext;

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

    mCastContext = CastContext.getSharedInstance(this);
}

Para acceder al singleton CastContext, usa lo siguiente:

mCastContext = CastContext.getSharedInstance(this);

Detección de dispositivos

Los VideoCastManager incrementUiCounter y decrementUiCounter de CCL deben quitarse de los métodos onResume y onPause de tus Activities.

En CAF, el framework inicia y detiene automáticamente el proceso de descubrimiento cuando la app pasa a primer plano y pasa a segundo plano, respectivamente.

Botón para transmitir y diálogo de transmisión

Al igual que con la CCL, la biblioteca de compatibilidad de MediaRouter v7 proporciona estos componentes.

El botón para transmitir seguirá implementado por MediaRouteButton y se podrá agregar a tu actividad (mediante ActionBar o Toolbar) como un elemento de menú.

La declaración de MediaRouteActionProvider en el archivo XML del menú es la misma que con 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"/>

De manera similar a la CCL, anula el método onCreateOptionMenu() de cada actividad, pero en lugar de usar CastManager.addMediaRouterButton, usa CastButtonFactory de CAF para conectar el MediaRouteButton al framework de 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;
}

Control de dispositivos

Al igual que las CCL, en CAF, el control del dispositivo se controla en gran medida mediante el framework. La aplicación emisora no necesita controlar (y no debe intentar controlar) la conexión al dispositivo y el inicio de la aplicación receptora mediante GoogleApiClient.

La interacción entre el remitente y el receptor ahora se representa como una "sesión". La clase SessionManager controla el ciclo de vida de la sesión y, luego, inicia y detiene automáticamente las sesiones en respuesta a los gestos del usuario: una sesión se inicia cuando el usuario selecciona un dispositivo de transmisión en el diálogo de Cast y finaliza cuando el usuario presiona el botón "Detener transmisión" en el diálogo de transmisión o cuando finaliza la app emisora.

En CCL, debes extender la clase VideoCastConsumerImpl para realizar un seguimiento del estado de la sesión de transmisión:

private final VideoCastConsumer mCastConsumer = new VideoCastConsumerImpl() {
  public void onApplicationConnected(ApplicationMetadata appMetadata, 
                                     String sessionId,
                                     boolean wasLaunched) {}
  public void onDisconnectionReason(int reason) {}
  public void onDisconnected() {}
}

En CAF, la aplicación emisora puede recibir notificaciones de eventos del ciclo de vida de la sesión mediante el registro de una SessionManagerListener con SessionManager. Las devoluciones de llamada de SessionManagerListener definen los métodos de devolución de llamada para todos los eventos de ciclo de vida de la sesión.

Los siguientes métodos SessionManagerListener se asignan desde la interfaz VideoCastConsumer de CCL:

  • VideoCastConsumer.onApplicationConnected -> SessionManagerListener.onSessionStarted
  • VideoCastConsumer.onDisconnected -> SessionManagerListener.onSessionEnded

Declara una clase que implemente la interfaz SessionManagerListener y mueve la lógica VideoCastConsumerImpl a los métodos correspondientes:

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) {}
  ...
}

La clase CastSession representa una sesión con un dispositivo de transmisión. La clase tiene métodos para controlar los estados de volumen y silencio del dispositivo, lo que hace la CCL en BaseCastManager.

En lugar de usar la CCL VideoCastManager para agregar un consumidor:

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

Ahora, registra tu SessionManagerListener:

mCastSessionManager = 
    CastContext.getSharedInstance(this).getSessionManager();
mCastSessionManagerListener = new CastSessionManagerListener();
mCastSessionManager.addSessionManagerListener(mCastSessionManagerListener,
                  CastSession.class);

Para dejar de escuchar eventos en CCL, haz lo siguiente:

VideoCastManager.getInstance().removeVideoCastConsumer(mCastConsumer);

Ahora, usa SessionManager para dejar de escuchar eventos de sesión:

mCastSessionManager.removeSessionManagerListener(mCastSessionManagerListener,
                    CastSession.class);

Para desconectarte explícitamente del dispositivo de transmisión, CCL usó:

VideoCastManager.disconnectDevice(boolean stopAppOnExit, 
            boolean clearPersistedConnectionData,
            boolean setDefaultRoute)

Para CAF, usa el SessionManager:

CastContext.getSharedInstance(this).getSessionManager()
                                   .endCurrentSession(true);

Para determinar si el remitente está conectado al receptor, CCL proporciona VideoCastManager.getInstance().isConnected(), pero en CAF usa SessionManager:

public boolean isConnected() {
    CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                  .getSessionManager()
                                  .getCurrentCastSession();
    return (castSession != null && castSession.isConnected());
}

En CAF, las notificaciones de cambio de estado o silencio y volumen se entregan a través de métodos de devolución de llamada en Cast.Listener. Estos objetos de escucha se registran con CastSession. Todas las notificaciones de estado restantes del dispositivo se entregan a través de devoluciones de llamada CastStateListener, y estos objetos de escucha se registran con CastSession. Asegúrate de anular el registro de los objetos de escucha cuando los fragmentos, las actividades o las apps asociados estén en segundo plano.

Lógica de reconexión

El CAF intenta restablecer las conexiones de red que se pierden debido a una pérdida temporal de la señal Wi-Fi o a otros errores de red. Ahora, esto se hace a nivel de sesión; una sesión puede entrar en un estado "suspendido" cuando se pierde la conexión y volverá a un estado "conectado" cuando se restablezca la conectividad. El marco de trabajo se encarga de volver a conectarse a la aplicación receptora y a volver a conectar los canales de transmisión como parte de este proceso.

CAF proporciona su propio servicio de reconexión, de modo que puedes quitar ReconnectionService de CCL de tu manifiesto:

<service android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>

Tampoco necesitas los siguientes permisos en tu manifiesto para la lógica de reconexión:

<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE"/>

El servicio de reconexión de CAF está habilitado de forma predeterminada, pero se puede inhabilitar mediante CastOptions.

Además, CAF también agrega la reanudación de sesión automática que está habilitada de forma predeterminada (y se puede desactivar mediante CastOptions). Si la aplicación emisora se envía a segundo plano o se finaliza (al deslizarse hacia fuera o debido a una falla) mientras una sesión de transmisión está en curso, el framework intentará reanudarla cuando la aplicación emisora regrese a primer plano o se reinicie. Esto se controla automáticamente mediante las instancias de SessionManagerListener, lo cual emitirá las instancias apropiadas, lo cual emitirá las instancias apropiadas. SessionManagerListener

Registro de canales personalizados

CCL proporciona dos formas de crear un canal de mensajes personalizado para el receptor:

  • CastConfiguration te permite especificar varios espacios de nombres y CCL creará el canal por ti.
  • DataCastManager es similar a VideoCastManager, pero se enfoca en casos prácticos de contenido multimedia.

CAF no admite ninguna de estas formas de crear un canal personalizado. En su lugar, debes seguir el procedimiento Agrega un canal personalizado para tu app emisora.

Al igual que las CCL, en el caso de las aplicaciones multimedia, no es necesario registrar de manera explícita el canal de control de medios.

Control multimedia

En CAF, la clase RemoteMediaClient es equivalente a los métodos multimedia VideoCastManager. El RemoteMediaClient.Listener es equivalente a los métodos VideoCastConsumer. En particular, los métodos onRemoteMediaPlayerMetadataUpdated y onRemoteMediaPlayerStatusUpdated de VideoCastConsumer se asignan a los métodos onMetadataUpdated y 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() {
    }
}

No es necesario inicializar o registrar de forma explícita el objeto RemoteMediaClient; el framework creará una instancia del objeto de forma automática y registrará el canal multimedia subyacente en el momento de inicio de la sesión si la aplicación receptora a la que se conecta admite el espacio de nombres multimedia.

Se puede acceder a RemoteMediaClient como el método getRemoteMediaClient del objeto CastSession.

CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                     .getSessionManager()
                                     .getCurrentCastSession();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClientListener = new CastMediaClientListener();

En lugar de los siguientes elementos de las CCL:

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

Ahora usa CAF:

mRemoteMediaClient.addListener(mRemoteMediaClientListener);

Se puede registrar cualquier cantidad de objetos de escucha con el RemoteMediaClient, lo que permite que varios componentes del remitente compartan la única instancia de RemoteMediaClient asociada con la sesión.

VideoCastManager de CCL proporciona métodos para controlar la reproducción de contenido multimedia:

VideoCastManager manager = VideoCastManager.getInstance();
if (manager.isRemoteMediaLoaded()) {
    manager.pause();
    mCurrentPosition = (int) manager.getCurrentMediaPosition();
}

RemoteMediaClient implementa estas herramientas en CAF:

if (mRemoteMediaClient.hasMediaSession()) {
    mRemoteMediaClient.pause();
    mCurrentPosition = 
        (int)mRemoteMediaClient.getApproximateStreamPosition();
}

En CAF, todas las solicitudes de contenido multimedia emitidas en el RemoteMediaClient muestran un RemoteMediaClient.MediaChannelResult a través de una devolución de llamada PendingResult, que se puede usar para realizar un seguimiento del progreso y el resultado final de la solicitud.

Tanto CCL como CAF usan las clases MediaInfo y MediaMetadata para representar elementos multimedia y cargar contenido multimedia.

Para cargar contenido multimedia en CCL, se usa VideoCastManager:

VideoCastManager.getInstance().loadMedia(media, autoPlay, mCurrentPosition, customData);

En CAF, se usa RemoteMediaClient para cargar el contenido multimedia:

mRemoteMediaClient.load(media, autoPlay, mCurrentPosition, customData);

Para obtener la información Media y el estado de una sesión multimedia actual en el receptor, CCL usa el VideoCastManager:

MediaInfo mediaInfo = VideoCastManager.getInstance()
                                      .getRemoteMediaInformation();
int status = VideoCastManager.getInstance().getPlaybackStatus();
int idleReason = VideoCastManager.getInstance().getIdleReason();

En CAF, usa RemoteMediaClient para obtener la misma información:

MediaInfo mediaInfo = mRemoteMediaClient.getMediaInfo();
int status = mRemoteMediaClient.getPlayerState();
int idleReason = mRemoteMediaClient.getIdleReason();

Superposición introductoria

Al igual que la CCL, CAF proporciona una vista personalizada IntroductoryOverlay para destacar el botón para transmitir cuando se muestra por primera vez a los usuarios.

En lugar de usar el método VideoCastConsumer onCastAvailabilityChanged de CCL para saber cuándo mostrar la superposición, declara un CastStateListener a fin de determinar cuándo el botón para transmitir será visible una vez que MediaRouter descubra los dispositivos de transmisión en la red local:

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);
    ...
}

Haz un seguimiento de la instancia 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;
}

Verifica si MediaRouteButton es visible para que se pueda mostrar la superposición introductoria:

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();
            }
        });
    }
}

Consulta nuestra app de muestra para ver el código funcional completo que muestra la superposición introductoria.

Para personalizar el estilo de la superposición introductoria, sigue el procedimiento Personaliza la superposición de introducción.

Minicontrolador

En lugar de MiniController de CCL, usa MiniControllerFragment de CAF en tu archivo de diseño de la app de las actividades en las que quieres mostrar el minicontrolador:

<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" />

CAF no admite la configuración manual compatible con MiniController de CCL y tampoco admite la función Autoplay.

Para personalizar el estilo y los botones del minicontrolador, sigue el procedimiento que se indica en Personaliza el minicontrolador.

Notificaciones y pantalla de bloqueo

Al igual que VideoCastNotificationService de CCL, CAF proporciona un MediaNotificationService para administrar la visualización de notificaciones multimedia cuando se realizan transmisiones.

Debes quitar lo siguiente de tu manifiesto:

  • VideoIntentReceiver
  • VideoCastNotificationService

CCL admite la prestación de un servicio de notificaciones personalizado con el CastConfiguration.Builder; no es compatible con el CAF.

Considera la siguiente inicialización de CastManager mediante 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 la configuración equivalente en CAF, el SDK proporciona un NotificationsOptions.Builder a fin de ayudarte a compilar controles multimedia para la pantalla de bloqueo y notificaciones en la app emisora. Los controles de notificación y pantalla de bloqueo se pueden habilitar con CastOptions cuando se inicializa el 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();
}

Las notificaciones y los controles de pantalla de bloqueo siempre están habilitados en CAF. Además, ten en cuenta que los botones para reproducir, pausar y detener la transmisión se proporcionan de forma predeterminada. CAF rastreará automáticamente la visibilidad de las actividades para decidir cuándo mostrar la notificación multimedia, excepto Gingerbread. (Para Gingerbread, consulta la nota anterior sobre el uso de registerLifecycleCallbacksBeforeIceCreamSandwich(); las llamadas VideoCastManager incrementUiCounter y decrementUiCounter de CCL deben quitarse).

Para personalizar los botones que se muestran en las notificaciones, sigue el procedimiento Agrega controles multimedia a la pantalla de notificación y bloqueo.

Control expandido

CCL proporciona VideoCastControllerActivity y VideoCastControllerFragment para mostrar un control expandido cuando se transmite contenido multimedia.

Puedes quitar la declaración VideoCastControllerActivity del manifiesto.

En CAF, debes extender el ExpandedControllerActivity y agregar el botón para transmitir.

Para personalizar los estilos y los botones que se muestran en el control expandido, sigue el procedimiento Personaliza el controlador expandido.

Enfoque de audio

Al igual que con las CCL, el foco de audio se administra automáticamente.

Control de volumen

Para Gingerbread, dispatchKeyEvent es obligatorio, al igual que con CCL. En ICS y versiones posteriores, tanto el control de volumen de CCL como de CAF se controlan automáticamente.

CAF permite controlar el volumen de transmisión mediante el botón de volumen estricto del teléfono dentro de las actividades de las apps y también muestra una barra de volumen visual cuando se transmite contenido a versiones compatibles. CAF también controla el cambio de volumen mediante el volumen estricto, incluso si la app no está en primer plano, bloqueada o incluso si la pantalla está apagada.

Subtítulos

En Android KitKat y versiones posteriores, los subtítulos se pueden personalizar a través de la Configuración de subtítulos, que se encuentra en Configuración > Accesibilidad. Sin embargo, las versiones anteriores de Android no tienen esta función. CCL controla esto mediante la configuración personalizada para versiones anteriores y la delegación a la configuración del sistema en KitKat y versiones posteriores.

CAF no proporciona una configuración personalizada para cambiar las preferencias de subtítulos. Debes quitar las referencias CaptionsPreferenceActivity en tu manifiesto y tu XML de preferencias.

Ya no se necesita el TracksChooserDialog de CCL, ya que la IU del controlador expandido se ocupa de cambiar las pistas de subtítulos opcionales.

La API de subtítulos opcionales en CAF es similar a la v2.

Registro de depuración

CAF no proporciona la configuración de registro de depuración.

Varios

Las siguientes funciones de CCL no son compatibles con CAF:

  • Obtener una autorización antes de la reproducción mediante un MediaAuthService
  • Mensajes configurables de la IU

Apps de ejemplo

Echa un vistazo a la diferencia para migrar nuestra aplicación de Universal Music Player for Android (uamp) de CCL a CAF.

También tenemos instructivos de codelab y apps de muestra que usan CAF.