Search code examples
androidkotlinexoplayer

Exoplayer video going outisde the BottomsheetDialogFragment on Scrolling ==


Exoplayer video goes outisde the BottomsheetDialogFragment on Scrolling when loading a existing fragment in BottomsheetDialogFragment which has exoplayer tiles in it.

Tried many solution changing style ,behaviour ,nested scroll view etc. but didn't solve the problem.

(https://i.sstatic.net/BJH5a.png)Issue what i am facing

ReelBottomSheetDialogFragment.class

import android.app.Dialog
import android.content.DialogInterface
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowManager
import androidx.databinding.DataBindingUtil
import com.caratlane.android.Fragments.ListingPageFragment
import com.caratlane.android.R
import com.caratlane.android.Utils.Constants
import com.caratlane.android.Utils.Constants.ReelsKey.PDP_ORIGIN_KEY
import com.caratlane.android.Utils.Constants.ReelsKey.PRODUCT_SKU
import com.caratlane.android.Utils.Methods
import com.caratlane.android.activity.CLBaseToolbarActivity
import com.caratlane.android.activity.MainActivity
import com.caratlane.android.activity.ShoppingCartActivity
import com.caratlane.android.databinding.LayoutProductDetailBottomSheetBinding
import com.caratlane.android.mvvm.home.view.HomeFragment
import com.caratlane.android.mvvm.listing.view.ListingFragment
import com.caratlane.android.mvvm.loginFeature.model.apimodel.NavigationSource
import com.caratlane.android.mvvm.loginFeature.view.fragments.LoginLandingFragment
import com.caratlane.android.mvvm.reels.view.ReelsFragment
import com.caratlane.android.mvvm.reviewsratings.view.BaseProductDetailFragment
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment


class ReelBottomSheetDialogFragment : BottomSheetDialogFragment(),
    LoginLandingFragment.LogInCallback{
    var binding: LayoutProductDetailBottomSheetBinding? = null
    var onReelsBottomSheetStatusChange: OnBottomSheetStatusChange? = null
    var onPdpBottomSheetStatusChange: OnBottomSheetStatusChange? = null
    private var logInReelListener: LoginLandingFragment.LogInCallback? = null
    var sku: String? = null
    var reelClickedListner : ReelClickedListener? =null

    override fun getTheme(): Int {
        return R.style.AppBottomSheetDialogTheme
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        (activity as CLBaseToolbarActivity?)?.hideToolBar()
        (activity as CLBaseToolbarActivity?)?.hideBackToolbar()
        return if (arguments?.getBoolean(ReelsFragment.OPEN_PDP, false) == true) {
            sku = arguments?.getString(PRODUCT_SKU)
            addProductDetailFragment()
            //BottomSheetDialog(requireContext(), theme)
            openFullExpandedBottomSheet()
        } else {
            openLoginFragment()
            openFullExpandedBottomSheet()

        }
    }

    private fun openFullExpandedBottomSheet(): BottomSheetDialog {
        val dialog = BottomSheetDialog(requireContext(), theme)
        dialog.setOnShowListener {
            val bottomSheetDialog = it as BottomSheetDialog
            val parentLayout =
                bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
            parentLayout?.let { it ->
                val behaviour = BottomSheetBehavior.from(it)
                //setupFullHeight(it)
                behaviour.state = BottomSheetBehavior.STATE_COLLAPSED
                behaviour.addBottomSheetCallback(object :
                    BottomSheetBehavior.BottomSheetCallback() {
                    override fun onStateChanged(bottomSheet: View, newState: Int) {
                    /*    if (newState == BottomSheetBehavior.STATE_EXPANDED) {
                            // Bottom sheet is expanded, change status bar color
                            //changeStatusBarColor(getResources().getColor(R.color.black))
                            setStatusBarColor(getResources().getColor(R.color.black))
                        } else {
                            // Revert to default status bar color when bottom sheet is closed
                            //changeStatusBarColor(resources.getColor(R.color.white))
                            setStatusBarColor(getResources().getColor(R.color.black))

                        }*/

                    }

                    override fun onSlide(bottomSheet: View, slideOffset: Float) {
                        //updateStatusBarColor(R.color.black)
                        //changeStatusBarColor(R.color.black)

                    }
                })
            }
        }
        return dialog
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(
            LayoutInflater.from(context),
            R.layout.layout_product_detail_bottom_sheet,
            container,
            false
        )
        return binding?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.viewTreeObserver.addOnGlobalLayoutListener(object :
            ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                view.viewTreeObserver.removeOnGlobalLayoutListener(this)
                dialog?.setOnShowListener {
                }
            }
        })
    }

    private fun setupFullHeight(bottomSheet: View) {
        val layoutParams = bottomSheet.layoutParams
        layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
        bottomSheet.layoutParams = layoutParams
    }

    private fun addProductDetailFragment() {
        val productDetailFragment = BaseProductDetailFragment()
        arguments?.putBoolean("isToolbarVisible", false)
        arguments?.putString(PDP_ORIGIN_KEY, arguments?.getString(PDP_ORIGIN_KEY))
        arguments?.putString(
            Constants.ReelsKey.IS_FROM,
            Constants.ReelsKey.REELS_PAGE
        )
        productDetailFragment.arguments = arguments
        productDetailFragment.takeCallback(object : BaseProductDetailFragment.ViewClick{
            override fun onAnyViewClicked() {
                Log.d("bottomsheetdialogfragment","in on click")
                dialog?.dismiss()
            }

            override fun onReelClicked(videoId: Long) {
                Log.d("bottomsheetdialogfragment","reel video id "+videoId.toString())
                reelClickedListner?.onReelClickedFromPdTray(videoId)
            }
        })
        val fragmentManager = childFragmentManager.beginTransaction()
        fragmentManager.replace(R.id.product_detail_container, productDetailFragment, productDetailFragment.tag)
        fragmentManager.commit()
    }

    override fun onResume() {
        super.onResume()
        if (sku != null) {
            onPdpBottomSheetStatusChange?.onPDBottomSheetOpened(sku)
            onReelsBottomSheetStatusChange?.onPDBottomSheetOpened(sku)
        }
    }

    fun setPdpBottomSheetOpenListener(listener: OnBottomSheetStatusChange) {
        onPdpBottomSheetStatusChange = listener
    }

    fun setReelsBottomSheetStatusChangeListener(listener: OnBottomSheetStatusChange){
        onReelsBottomSheetStatusChange = listener
    }

    private fun openLoginFragment() {
        if (!Methods.isActivityAlive(activity)) return
        val loginLandingFragment: LoginLandingFragment = if (activity is MainActivity) {
            (activity as MainActivity).signInSignUpFragment
        } else {
            (activity as ShoppingCartActivity).signInSignUpFragment
        }
        val arguments = Bundle()
        arguments.putSerializable(
            Constants.BUNDLE_EXTRAS.SOURCE_NAV,
            NavigationSource.ReelsPage
        )
        loginLandingFragment.arguments = arguments
        loginLandingFragment.setLoginListener(this)
        childFragmentManager.beginTransaction()
            .add(R.id.product_detail_container, loginLandingFragment, loginLandingFragment.tag)
            .commitAllowingStateLoss()
    }

    fun setLoginListener(listener : LoginLandingFragment.LogInCallback){
        logInReelListener = listener
    }


    override fun onLoginSuccess() {
        logInReelListener?.onLoginSuccess()
        dismiss()
    }

    override fun onLoginBackPressed() {
        dialog?.dismiss()
    }

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)
        if (sku != null) {
            onPdpBottomSheetStatusChange?.onPDBottomSheetClose(sku)
            onReelsBottomSheetStatusChange?.onPDBottomSheetClose(sku)
        }
        //logInReelListener?.onLoginBottomSheetDismiss()
    }

    fun setReelClickedListener(listener  : ReelClickedListener){
        reelClickedListner = listener
    }

    interface OnBottomSheetStatusChange {
        fun onPDBottomSheetOpened(sku: String?)
        fun onPDBottomSheetClose(sku: String?)
    }

    interface ReelClickedListener{
        fun onReelClickedFromPdTray(videoId : Long)
    }
}

styles.xml

  <!--    Rounded corner BottomSheet-->
    <style name="AppBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowSoftInputMode">adjustResize|stateAlwaysVisible</item>
        <item name="bottomSheetStyle">@style/AppModalStyle</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

layout_product_detail_bottom_sheet.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">


    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/cl_product_detail"
        android:layout_height="match_parent"
        android:layout_width="match_parent">

        <androidx.appcompat.widget.LinearLayoutCompat
            android:id="@+id/ll_product_detail_header"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_10"
                android:layout_marginStart="@dimen/dp_10"
                android:layout_marginBottom="@dimen/dp_10"
                android:gravity="center">

                <LinearLayout
                    android:layout_width="@dimen/_32dp"
                    android:layout_height="@dimen/dp_4"
                    android:layout_gravity="center_horizontal"
                    android:background="@drawable/shape_purple_round_corner"
                    android:gravity="center_horizontal"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent">

                </LinearLayout>


            </androidx.constraintlayout.widget.ConstraintLayout>


            <androidx.appcompat.widget.LinearLayoutCompat
                android:id="@+id/product_detail_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

            </androidx.appcompat.widget.LinearLayoutCompat>
        </androidx.appcompat.widget.LinearLayoutCompat>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</layout>

Solution

  • ExoPlayer renders into whatever Surface is provided to it.

    you can work around this issue by setting app:surface_type="texture_view" on the PlayerView instances that you're using. TextureView is treated much more like a "normal" view than SurfaceView is, when it comes to compositing, and so is less prone to this type of issue. There are also some disadvantages.

    Read pro's and con's for TextureView

    https://exoplayer.dev/ui-components.html#choosing-a-surface-type