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)
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>
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