Publicado por Don Turner – Defensor de desarrolladores – Android Media
Controles multimedia en Android 11
Parte del fundamento de los controles multimedia es que los usuarios a menudo tienen múltiples aplicaciones multimedia (reproductor de música, podcast, reproductor de video, etc.) y cambian regularmente entre ellas. ; otro. Los controles multimedia muestran hasta cinco sesiones multimedia actuales y recientes en un carrusel, lo que permite al usuario desplazarse por ellas.
En Android 10 y versiones anteriores, las notificaciones multimedia para varias aplicaciones pueden ocupar la mayor parte del área de notificación. Todos esos botones de control también pueden resultar confusos. Mover los controles a un espacio dedicado significa que hay más espacio para otras notificaciones y proporciona una experiencia de usuario más consistente para controlar aplicaciones multimedia.
Aquí está la comparación:
Notificaciones multimedia de Android 10 (izquierda) Controles multimedia de Android 11 (derecha)
Visualización de los controles multimedia de su aplicación
Ahora, las realmente buenas noticias. Siempre que esté utilizando MediaStyle
con un token MediaSession
válido (ambos disponibles en Lollipop API 21), los controles multimedia aparecerán automáticamente para su aplicación, ¡sin trabajo adicional para usted!
En caso de que no esté utilizando un MediaStyle
y MediaSession
aquí hay un breve resumen en el código:
// Crea una sesión multimedia. NotificationCompat.MediaStyle // PlayerService es su servicio o empresa responsable de la reproducción multimedia. val mediaSession = MediaSessionCompat (esto, "PlayerService") // Cree un objeto MediaStyle y proporcione el token de sesión de medios. val mediaStyle = Notification.MediaStyle (). setMediaSession (mediaSession.sessionToken) // Crea una notificación con el estilo de tu objeto MediaStyle. // Esto vincula su sesión multimedia a los controles multimedia. // No olvides incluir un pequeño icono. val notification = Notification.Builder (esto @ PlayerService, CHANNEL_ID) .setStyle (mediaStyle) .setSmallIcon (R.drawable.ic_app_logo) .construir() // Especifique las acciones que los usuarios pueden realizar, como pausar y saltar a la siguiente pista. val pauseAction: Notification.Action = Notification.Action.Builder ( pauseIcon, "Pause", pauseIntent ).construir() notification.addAction (pauseAction)
El icono pequeño y el nombre de la aplicación se muestran en la parte superior izquierda de los controles multimedia. Las acciones se muestran en la parte inferior central.
Los medios controlan la interfaz de usuario y los campos de notificación correspondientes
Los campos restantes de la interfaz de usuario, como el título de la pista y la posición de reproducción, se obtienen de los metadatos de la sesión estado de los medios y la reproducción.
Así es como se asignan los campos de metadatos en la interfaz de usuario.
mediaSession.setMetadata ( MediaMetadataCompat.Builder () // Título. .putString (MediaMetadata.METADATA_KEY_TITLE, currentTrack.title) // Artista. // También podría ser el nombre del canal o la serie de televisión. .putString (MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist) // Portada del álbum. // También podría ser una captura de pantalla o una imagen principal para el contenido de video // El esquema de URI debe ser "contenido", "archivo" o "android.resource". .putString ( MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri) ) // Duración. // Si no se establece la duración, por ejemplo, para transmisiones en vivo, el progreso // el indicador no se mostrará en la barra de búsqueda. .putLong (MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration) // 4 .construir() )
Esta captura de pantalla muestra cómo se muestran estos campos de metadatos en los controles de medios.
Los medios verifican la interfaz de usuario y los campos de metadatos correspondientes
La barra de búsqueda se actualiza utilizando el estado de reproducción de la sesión multimedia de una manera similar:
mediaSession.setPlaybackState ( PlaybackStateCompat.Builder () .setState ( PlaybackStateCompat.STATE_PLAYING, // Posición de reproducción. // Se usa para actualizar el tiempo transcurrido y la barra de progreso. mediaPlayer.currentPosition.toLong (), // Velocidad de reproducción. // Determine qué tan rápido cambia el tiempo transcurrido. velocidad de reproducción ) // isSeekable. // La adición de la acción SEEK_TO indica que la búsqueda es compatible // y hacer que el indicador de posición de la barra de búsqueda se pueda arrastrar. Si no es // la búsqueda proporcionada se desactivará pero aún se mostrará el progreso. .setActions (PlaybackStateCompat.ACTION_SEEK_TO) .construir() )
Esta pantalla muestra cómo se muestran estos campos de estado de reproducción en los controles multimedia.
Interfaz de usuario de controles multimedia y los campos de estado de reproducción correspondientes
¡Los controles multimedia ahora deberían aparecer y funcionar perfectamente!
¿Alguna vez has querido seguir escuchando un podcast, un episodio de televisión o un set de DJ pero no recuerdas dónde lo dejaste o la aplicación que lo estaba reproduciendo? La cobertura de los medios resuelve este problema.
Hay dos etapas para la reanudación de los medios: descubrimiento de aplicaciones multimedia recientes y reanudación de la reproducción .
Descubrimiento de aplicaciones multimedia recientes
Después del lanzamiento, Android buscará aplicaciones multimedia recientes y les preguntará qué contenido se reprodujo más recientemente. Luego, creará controles de medios para ese contenido.
Para ser detectable, la aplicación debe proporcionar un MediaBrowserService
que normalmente utiliza la biblioteca MediaBrowserServiceCompat
de Android Jetpack.
Al iniciarse, Android llamará al método MediaBrowserServiceCompat onGetRoot
por lo que es imperativo que regrese rápidamente. Por lo general, devuelve la raíz de su árbol de medios desde este método, pero el sistema también especifica la sugerencia EXTRA_RECENT
.
Debe considerar EXTRA_RECENT
como un caso especial y, en su lugar, devolver la raíz de un árbol de medios que contiene el elemento multimedia reproducido más recientemente como primer elemento.
El sistema llamará al método onLoadChildren
para obtener este árbol multimedia, que es una lista de objetos MediaItem
.
A continuación, se muestra un diagrama que muestra cómo interactúan el sistema y una aplicación multimedia para recuperar el elemento reproducido más recientemente.
Cómo recupera el sistema el elemento reproducido más recientemente de MediaBrowserService
Para los primeros medios reproducibles en esta lista, el sistema creará controles de medios estáticos con un solo botón de reproducción.
Controles de medios estáticos
No se ha creado ninguna sesión multimedia en este momento. Esto es para ahorrar recursos: el sistema no sabe si el usuario realmente quiere reanudar la reproducción de ese contenido.
Reanudar la reproducción
Si el usuario toca el botón de reproducción, el sistema hará otra llamada a onGetRoot
con la sugerencia EXTRA_RECENT
. De esta manera puede preparar el contenido reproducido anteriormente como antes, en caso de que algo haya cambiado desde que se crearon los controles estáticos.
Android se conectará a la sesión multimedia y le enviará un comando de reproducción. Debe ignorar la devolución de llamada de la sesión de medios onPlay
para comenzar a reproducir los medios y crear la notificación MediaStyle
.
Después de publicar la notificación, los controles de medios estáticos se intercambiarán con los controles de medios creados por la notificación.
Figura 7: Diagrama que muestra la interacción entre la interfaz de usuario del sistema y la aplicación multimedia al reanudar la reproducción
Además de poder reanudar sesiones multimedia, Android 11 también te permite transferir fácilmente la reproducción de un dispositivo a otro. Esto se conoce como "transferencia continua de medios" y se realiza mediante el selector de salida . El selector de salida se muestra en la esquina superior derecha de la notificación de medios que aparece a continuación.
Conmutador de salida (esquina superior derecha)
El conmutador de salida muestra el nombre y el icono de la ruta de medios actual; y cuando lo toque, verá una lista de rutas de medios compatibles con esta aplicación.
Rutas de medios disponibles para la aplicación actual
De forma predeterminada, solo se muestran las rutas de medios locales. Si su aplicación es compatible con otras rutas de medios, como el uso a distancia, debe informar al sistema.
Para hacer esto, agregue la biblioteca jetpack MediaRouter a su aplicación.
adicciones { implementación "androidx.mediarouter: mediarouter: 1.2.0-alpha02" " }
La transferencia de medios sin interrupciones es compatible con 1.2.0-alpha02.
Luego, agrega la clase MediaTransferReceiver
a tu manifiesto de Android.
Ahora en tu aplicación, obtienes el singleton MediaRouter
: este es un objeto que mantiene el estado de todas las rutas de medios disponibles actualmente.
router = MediaRouter.getInstance (this)
Cree un MediaRouteSelector
y especifique las categorías de ruta admitidas por su aplicación. Las categorías definidas aquí determinan las rutas que se muestran en el selector de salida.
Aquí especificaremos solo la categoría de "reproducción remota" utilizada para dispositivos de transmisión.
routeSelector = MediaRouteSelector.Builder () // Agrega categorías de control que interesan a esta aplicación multimedia. .addControlCategory (MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build ()
Si desea admitir la transferencia de dispositivos remotos a locales, debe habilitarla explícitamente usando setTransferToLocalEnabled
:
router.routerParams = MediaRouterParams.Builder (). SetTransferToLocalEnabled (true) .build ()
Ahora podemos usar nuestro selector al agregar un enrutador de medios callback .
router.addCallback (routeSelector, MediaRouterCallback (), MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
También proporcionamos un objeto de devolución de llamada para que podamos ser notificados de los cambios en las rutas de medios.
Aquí está la clase de recuperación:
clase privada MediaRouterCallback: MediaRouter.Callback () { anular la diversión en RouteSelected ( enrutador: MediaRouter, ruta: MediaRouter.RouteInfo, motivo: Int ) { if (motivo == MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { Timber.d ("No seleccionado porque la ruta ha cambiado, sigue jugando") } más si (motivo == MediaRouter.UNSELECT_REASON_STOPPED) { Timber.d ("No seleccionado porque el camino fue interrumpido, deja de jugar") } } }
El método que estamos anulando es onRouteSelected
que se llamará cada vez que se seleccione una nueva ruta multimedia.
Cuando esto sucede, debemos considerar por qué por qué se seleccionó .
Si se desconectaba la ruta existente (por ejemplo, se apagaban los auriculares bluetooth) deberíamos pausar o detener la reproducción.
Si la ruta se ha cambiado activamente, por ejemplo, al cambiar de un teléfono a un dispositivo de transmisión, debe continuar reproduciendo el archivo multimedia desde la posición de reproducción anterior; esta es la parte "sin interrupciones" de la "transferencia multimedia sin interrupciones ":)
Para comenzar con los controles multimedia y las funciones relacionadas en Android 11, echa un vistazo a la documentación oficial. También asegúrese de consultar UAMP, que contiene una implementación de referencia para muchas de las características mencionadas en este artículo.
¡Buena suerte y recuerda jugar bien!