Como agregar el modo Picture-in-Picture (PIP) a tu reproductor en Android O

android O

En este blog les enseñaré como pueden agregar soporte para el modo PIP a sus apps de Android reproductoras de video, basándome en la app muestra de TV Playback de Google.

La app muestra la reconstruí en Kotlin y convertí todo a su versión “support”, siendo las “Activity” ahora unas “FragmentActivity”, los “VideoFragment” ahora unos “VideoSupportFragment”, etcétera, dado que se evitan mejor los posibles nulos y a mí parecer el código queda más limpio y entendible.

El reproductor funciona como el ejemplo, tiene su “PlaybackTransportControlGlue” y su “MediaPlayerAdapter”, de los cuales solo tenemos que modificar el “PlaybackTransportControlGlue” que es el encargado de mostrar los botones interactivos, para más información visita la página https://developer.android.com/training/tv/playback/transport-controls.

Primero creamos un “PlaybackTransportControlGlue”, el mío lo llame “mTransportControlGlue”, y para ello sobreescribimos el constructor predeterminado.

val mPIPAction = PlaybackControlsRow.PictureInPictureAction(context)mTransportControlGlue = object : PlaybackTransportControlGlue<MediaPlayerAdapter>(context, playerAdapter) {
override fun onCreatePrimaryActions(primaryActionsAdapter: ArrayObjectAdapter?) {}    override fun onCreateSecondaryActions(secondaryActionsAdapter: ArrayObjectAdapter?) {
super.onCreateSecondaryActions(secondaryActionsAdapter)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) secondaryActionsAdapter?.add(mPIPAction)}override fun onActionClicked(action: Action?) {}override fun next() {}override fun previous() {}override fun setControlsRow(controlsRow: PlaybackControlsRow?) {}
}

Nótese la declaración “if”, esta es necesaria para que el modo PIP no esté disponible para dispositivos sin Android O dado que esta funcionalidad solo está disponible de esta versión en adelante.

Pero aún falta definir qué hacer cuando el botón PIP se presione, para ello editamos en el mismo constructor el “onActionClicked”, quedando de la siguiente forma:

when (action) {
mPIPAction -> {
// Handle PIP
hideControlsOverlay(true)
mListener?.enterPIP()
}

}

La función “hideControlsOverlay(true)” es nativa de “PlaybackSupportFragment” y se usa para ocultar el controlador o la barra de acciones, es necesaria para que al pasar a modo PIP no se encuentren y estorben la reproducción, tal y como lo recomienda Google en las buenas prácticas del PIP.

Nótese la línea “mListener?.enterPIP()”, esta función la use para comunicarme con la actividad, dado que es más limpio entrar a modo PIP desde la actividad misma, lo cual se logra con la interfaz “OnFragmentInteractionListener” la cual definí en el mismo fragmento.

private var mListener: OnFragmentInteractionListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)if (context is OnFragmentInteractionListener){
mListener = context
}
}interface OnFragmentInteractionListener {
fun enterPIP()
}La cual para que funcione debe implementarse en la actividad padre.
class PlaybackActivity : FragmentActivity(), PlaybackVideoFragment.OnFragmentInteractionListener {
…@RequiresApi(Build.VERSION_CODES.O)
override fun enterPIP() {
if (!isInPictureInPictureMode) {
enterPictureInPictureMode(PictureInPictureParams.Builder().build())
}
}
}

La cual para que funcione debe implementarse en la actividad padre.

class PlaybackActivity : FragmentActivity(), PlaybackVideoFragment.OnFragmentInteractionListener {
…@RequiresApi(Build.VERSION_CODES.O)
override fun enterPIP() {
if (!isInPictureInPictureMode) {
enterPictureInPictureMode(PictureInPictureParams.Builder().build())
}
}
}

Esto es suficiente para entrar al modo PIP, solo se requieren dos funciones. La primera para saber si ya se encuentra en modo PIP que es lo que está dentro del PIP “isInPictureInPictureMode” cuyo equivalente en JAVA es “isInPictureInPictureMode()”; solo intentamos entrar a PIP si no estamos en modo PIP, siempre consideren el hecho que el usuario pueda presionar el botón rápidamente. La segunda es para entrar al modo PIP, es una simple función que acepta “PictureInPictureParams” los cuales solo se pueden crear a partir de su Builder el cual permite agregar acciones, definir el “aspect ratio” y definir el source bounds hint.

Pero muchos prefieren entrar al modo PIP automáticamente al salirse de la app ya sea por cambiarse a otra o entrar a Home, para ello es necesario sobreescribir el fragmento.

override fun onPause() {
super.onPause()
//mTransportControlGlue.pause()
hideControlsOverlay(true)
mListener?.enterPIP()
}

El código muestra pausa la reproducción al pausarse la actividad para ello comentamos esa línea de código y agregamos lo mismo que hicimos en “onActionClicked” cuando la acción es “mPIPAction”, tal como se muestra arriba.

 

App reproduciendo en modo PIP

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *