androidkotlinandroid-bottomsheetdialogepoxy

MediaController for VideoView inside BottomSheetDialog is hidden behind the BottomSheetDialog


I have a project where I programmatically add custom map markers to a Maps activity. When a user clicks on a marker, a BottomSheetDialog comes up from the bottom of the screen, and contains information about the custom map marker. One of the key pieces of information is a video that will play. The problem is that the MediaController for the VideoView doesnt hover above the video, but is behind the BottomSheetDialog. It is in the correct place on the screen, but behind both the BottomSheetDialog and the VideoView. I am using EpoxyRecyclerView to add the VideoView, MediaController and various TextView to the BottomSheetDialog.

Function that creates the BottomSheetDialog:

override fun onMarkerClick(marker: Marker): Boolean {
    val modalSheetView = layoutInflater.inflate(R.layout.activity_page_details, null)
    val bottomSheetDialog = BottomSheetDialog(this)
    bottomSheetDialog.setContentView(modalSheetView)

    pageController.page = marker.tag as Page

    val epoxyRecyclerView = bottomSheetDialog.findViewById<EpoxyRecyclerView>(R.id.pageDetailsRecyclerView)
    epoxyRecyclerView?.setControllerAndBuildModels(pageController)

    bottomSheetDialog.show()

    bottomSheetDialog.setOnDismissListener { it.dismiss() }

    // Return false to indicate that we have not consumed the event and that we wish
    // for the default behavior to occur (which is for the camera to move such that the
    // marker is centered and for the marker's info window to open, if it has one).
    return false
}

Epoxy function to add the VideoView and MediaController to the BottomSheetDialog:

data class PageVideoEpoxyModel(
    val videoUrl: String
):ViewBindingKotlinModel<ActivityPageVideoBinding>(R.layout.activity_page_video){

    override fun ActivityPageVideoBinding.bind() {
        videoView.setVideoURI(Uri.parse(videoUrl))
        videoView.setOnPreparedListener {

            val mediaController = MediaController(videoView.context)

            mediaController.setMediaPlayer(videoView)
            mediaController.isEnabled = true

            videoView.setMediaController(mediaController)

            mediaController.setAnchorView(videoView)
            mediaController.requestFocus()

            videoView.start()
            mediaController.show(0)
        }
    }
}

Layout file for VideoView:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content">

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="wrap_content"
        android:layout_height="210dp"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:clickable="true"/>

</androidx.constraintlayout.widget.ConstraintLayout>

EpoxyRecycler layout file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapsActivity"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<com.airbnb.epoxy.EpoxyRecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/pageDetailsRecyclerView"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    />

</androidx.constraintlayout.widget.ConstraintLayout>

Solution

  • The below version still works OK, but I completely solved the issue and removed all problems by using ExoPlayer, a third party library. I removed my basic VideoView and replaced it with a ExoPlayerVideoView, and the app now works with zero issues.

    I managed to fix this by adding a FrameLayout to the layout file for my VideoView:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content">
    
    <VideoView
        android:id="@+id/videoView"
        android:layout_width="wrap_content"
        android:layout_height="210dp"
        android:layout_marginTop="16dp"
        android:clickable="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
    <FrameLayout
        android:id="@+id/frameView"
        android:layout_width="@id/videoView"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:layout_constraintStart_toStartOf="@id/videoView"
        app:layout_constraintEnd_toEndOf="@id/videoView"
        app:layout_constraintBottom_toBottomOf="@id/videoView"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    And then removing the MediaController from its parent view and adding it to the FrameView. The code in the method that builds the VideoView inside the EpoxyController:

    data class PageVideoEpoxyModel(
        val videoUrl: String,
    ):ViewBindingKotlinModel<ActivityPageVideoBinding>(R.layout.activity_page_video){
    
        override fun ActivityPageVideoBinding.bind() {
            videoView.setVideoURI(Uri.parse(videoUrl))
            videoView.setOnPreparedListener {
    
                val mediaController = MediaController(videoView.context)
                mediaController.requestFocus()
    
                val parent = mediaController.parent as ViewGroup
                    parent.removeView(mediaController)
    
                frameView.addView(mediaController)
    
                mediaController.setMediaPlayer(videoView)
                mediaController.isEnabled = true
    
                videoView.setMediaController(mediaController)
    
                mediaController.setAnchorView(videoView)
    
                videoView.start()
                mediaController.show(0)
            }
        }
    }