Tp 33781 video player consistency (#7088)

TP-33781 | video player consistent on page refresh, etc
This commit is contained in:
Himanshu Tanwar
2023-06-30 14:56:31 +05:30
committed by GitHub Enterprise
parent 1e66244258
commit d15d5c91a8
9 changed files with 174 additions and 53 deletions

View File

@@ -10,6 +10,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.navi.base.model.CtaData
@@ -24,18 +26,22 @@ import com.navi.insurance.common.models.ExoPlayerFullScreenFragmentData
import com.navi.insurance.common.util.ActionHandler
import com.navi.insurance.common.util.NavigationHandler.Companion.EXO_PLAYER_FULL_SCREEN_FRAGMENT
import com.navi.insurance.databinding.FragmentExoplayerFullScreenBinding
import com.navi.insurance.health.viewmodel.InsuranceContainerActivityVM
import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator
import com.navi.insurance.util.CONTENT_DATA_JSON_STRING
import com.navi.insurance.util.Constants
import com.navi.insurance.util.log
import com.navi.naviwidgets.extensions.addOnMultipleClicksHandler
import com.navi.naviwidgets.utils.RESUME_PLAYER
import com.navi.naviwidgets.utils.START_POSITION
import com.navi.naviwidgets.utils.TEXT_TRUE
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class ExoPlayerFullScreenFragment : GiBaseFragment(), ActionHandler.ActionOwner {
private val containerActivityVM by activityViewModels<InsuranceContainerActivityVM>()
private var binding: FragmentExoplayerFullScreenBinding? = null
private var releasePlayer = true
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -51,6 +57,17 @@ class ExoPlayerFullScreenFragment : GiBaseFragment(), ActionHandler.ActionOwner
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launchWhenStarted {
containerActivityVM.backPressedFlow.collect { backPressed ->
if(backPressed && arguments?.getString(RESUME_PLAYER) == TEXT_TRUE) {
releasePlayer = false
}
}
}
}
private fun initUI(data: ExoPlayerFullScreenFragmentData, binding: FragmentExoplayerFullScreenBinding?) {
binding?.crossButton?.addOnMultipleClicksHandler {
data.backCta?.let {
@@ -61,7 +78,7 @@ class ExoPlayerFullScreenFragment : GiBaseFragment(), ActionHandler.ActionOwner
}
data.playerData?.let { videoData ->
binding?.exoPlayerView?.update(fullScreenPlayerData = videoData, lifeCycleOwner = this, startPosition = data.playerData.startPosition ?: arguments?.getString(
START_POSITION)?.toLongWithSafe(), ctaHandler = ::handleCta, videoKey = videoData.videoKey)
START_POSITION)?.toLongWithSafe(), ctaHandler = ::handleCta, videoKey = videoData.videoKey, resumePlayer = arguments?.getString(RESUME_PLAYER) == TEXT_TRUE)
}
}
@@ -111,6 +128,23 @@ class ExoPlayerFullScreenFragment : GiBaseFragment(), ActionHandler.ActionOwner
return false
}
override fun onPause() {
binding?.exoPlayerView?.pauseVideo()
super.onPause()
}
override fun onResume() {
binding?.exoPlayerView?.resumeVideo()
super.onResume()
}
override fun onDestroy() {
if(releasePlayer) {
binding?.exoPlayerView?.releasePlayer()
}
super.onDestroy()
}
override fun getViewModel(): GiBaseVM? = null
override fun getRetryAction(errorMessage: ErrorMessage?): (() -> Unit)? = null

View File

@@ -153,6 +153,7 @@ class InsuranceContainerActivity : GiBaseActivity(), PaymentStatusBottomSheetLis
override fun onBackPressed() {
viewModel.onBackPressed()
if (backCta != null) {
sendAnalyticsEvent(backCta)
NaviInsuranceDeeplinkNavigator.navigate(

View File

@@ -13,18 +13,27 @@ import com.navi.base.model.CtaData
import com.navi.insurance.common.GiBaseVM
import com.navi.insurance.common.util.ActionHandler
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
@HiltViewModel
class InsuranceContainerActivityVM
@Inject
constructor(actionHandler: ActionHandler) : GiBaseVM(actionHandler){
private var _paymentRetryCta = MutableLiveData<CtaData>()
var paymentFallbackCta: CtaData? = null
private var _paymentRetryCta = MutableLiveData<CtaData>()
val paymentRetryCta: LiveData<CtaData>
get() = _paymentRetryCta
private var hideKeyboardOnFocusChange = false
private val _backPressedFlow = MutableStateFlow<Boolean>(false)
val backPressedFlow: StateFlow<Boolean>
get() = _backPressedFlow.asStateFlow()
fun onBackPressed() {
_backPressedFlow.value = true
}
fun setPaymentRetryCta(ctaData: CtaData){
_paymentRetryCta.postValue(ctaData)

View File

@@ -131,6 +131,7 @@ const val ZERO_MARGIN = "0,0,0,0"
const val TYPE_PROPERTY_SEARCH = "PROPERTY_SEARCH"
const val DEFAULT_PAGE_SIZE = 10
const val START_POSITION = "startPosition"
const val RESUME_PLAYER = "resumePlayer"
const val ENABLE_FOOTER = "enableFooter"
const val MEMBER_NAME_MAP = "memberNameMap"
const val MEMBER_DOB_MAP = "memberDobMap"

View File

@@ -0,0 +1,23 @@
package com.navi.naviwidgets.video
import android.content.Context
object ExoPlayerManager {
private var exoPlayer: NaviExoPlayer? = null
fun initializePlayer(context: Context): NaviExoPlayer? {
if (exoPlayer == null) {
exoPlayer = NaviExoPlayer(context)
}
return exoPlayer
}
fun getPlayer(): NaviExoPlayer? {
return exoPlayer
}
fun releasePlayer() {
exoPlayer?.releasePlayer()
exoPlayer = null
}
}

View File

@@ -28,7 +28,7 @@ import javax.inject.Inject
class NaviExoPlayer
@Inject
constructor(
@ApplicationContext private val context: Context
@ApplicationContext private val context: Context,
) : DefaultLifecycleObserver {
private var latencyStartTime = -1L
@@ -120,6 +120,10 @@ constructor(
}
}
fun isMuted(): Boolean {
return player?.volume == 0f
}
fun unmute(): Boolean {
return player?.let {
it.volume = currentVolume

View File

@@ -19,6 +19,7 @@ import com.navi.naviwidgets.databinding.ExoPlayerFullScreenViewBinding
import com.navi.naviwidgets.extensions.addOnMultipleClicksHandler
import com.navi.naviwidgets.extensions.setTextFieldData
import com.navi.naviwidgets.models.FullScreenPlayerData
import com.navi.naviwidgets.video.ExoPlayerManager
import com.navi.naviwidgets.video.NaviExoPlayer
import com.navi.naviwidgets.video.NaviVideo
import kotlinx.coroutines.CoroutineScope
@@ -27,10 +28,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class ExoPlayerFullScreenView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : ConstraintLayout(context, attrs) {
private val parentContext: Context, attrs: AttributeSet? = null
) : ConstraintLayout(parentContext, attrs) {
private val binding = ExoPlayerFullScreenViewBinding.inflate(
LayoutInflater.from(context), this, true
LayoutInflater.from(parentContext), this, true
)
private var player: NaviExoPlayer? = null
private var isMuted = MutableStateFlow(false)
@@ -44,10 +45,11 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
lifeCycleOwner: LifecycleOwner,
startPosition: Long?,
ctaHandler: (CtaData?) -> Unit,
videoKey: String?
videoKey: String?,
resumePlayer: Boolean
) {
this.videoKey = videoKey
player = NaviExoPlayer(context)
player = ExoPlayerManager.initializePlayer(parentContext.applicationContext)
volumeButton = findViewById(R.id.volumeButton)
actionButton = findViewById(R.id.actionButton)
titleTv = findViewById(R.id.title)
@@ -60,7 +62,7 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
actionButton?.isVisible = false
}
fullScreenPlayerData.videoData?.let {
handleVideo(it, fullScreenPlayerData.muted, startPosition = startPosition, lifeCycleOwner = lifeCycleOwner, autoplay = fullScreenPlayerData.autoplay, ctaHandler)
handleVideo(it, fullScreenPlayerData.muted, startPosition = startPosition, lifeCycleOwner = lifeCycleOwner, autoplay = fullScreenPlayerData.autoplay, ctaHandler, resumePlayer = resumePlayer)
}
volumeButton?.setOnClickListener {
if (isMuted.value) {
@@ -86,8 +88,10 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
isMuted.collect { isMuted ->
if (isMuted) {
volumeButton?.setImageResource(R.drawable.ic_volume_muted)
player?.mute()
} else {
volumeButton?.setImageResource(R.drawable.ic_volume_unmuted)
player?.unmute()
}
}
}
@@ -100,7 +104,8 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
startPosition: Long?,
lifeCycleOwner: LifecycleOwner,
autoplay: Boolean?,
ctaHandler: (CtaData?) -> Unit
ctaHandler: (CtaData?) -> Unit,
resumePlayer: Boolean
) {
player?.setListeners(object : NaviExoPlayer.EventListener {
override fun onInitDone() {
@@ -121,7 +126,14 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
ctaHandler.invoke(CtaData(analyticsEventProperties = analyticsEvent))
}
})
player?.initAndPlayVideo(video = videoData, startPosition = startPosition, playVideo = autoplay == true)
if(resumePlayer && player?.player != null) {
binding.player.player = player?.player
binding.player.controllerHideOnTouch = false
binding.player.showController()
player?.resumeVideo()
} else {
player?.initAndPlayVideo(video = videoData, startPosition = startPosition, playVideo = autoplay == true)
}
isMuted.value = muted == true && player?.mute() == true
binding.player.resizeMode = if (
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIT ||
@@ -135,4 +147,16 @@ class ExoPlayerFullScreenView @JvmOverloads constructor(
AspectRatioFrameLayout.RESIZE_MODE_FIT
}
}
fun releasePlayer() {
player?.releasePlayer()
}
fun resumeVideo() {
player?.resumeVideo()
}
fun pauseVideo() {
player?.pauseVideo()
}
}

View File

@@ -21,6 +21,7 @@ import com.navi.naviwidgets.extensions.setTextFieldData
import com.navi.naviwidgets.interfaces.VideoPlayerWidgetInfo
import com.navi.naviwidgets.models.VideoPlayerWidgetBody
import com.navi.naviwidgets.utils.*
import com.navi.naviwidgets.video.ExoPlayerManager
import com.navi.naviwidgets.video.NaviExoPlayer
import com.navi.naviwidgets.video.NaviVideo
import kotlinx.coroutines.CoroutineScope
@@ -38,10 +39,11 @@ constructor(
) : ConstraintLayout(parentContext, attrs) {
private lateinit var binding: VideoPlayerWidgetLayoutBinding
private var player: NaviExoPlayer? = NaviExoPlayer(context)
private var player: NaviExoPlayer? = ExoPlayerManager.initializePlayer(parentContext.applicationContext)
private var isMuted = MutableStateFlow(false)
private var widgetData: VideoPlayerWidgetInfo? = null
private var videoKey: String? = null
private var fullScreenButtonClicked = false
fun setProperties(
widgetData: VideoPlayerWidgetInfo?,
@@ -53,11 +55,15 @@ constructor(
videoKey = widgetData?.widgetData()?.videoKey
setWidgetLayoutParams(widgetData?.widgetLayoutParams(), binding.rootLayout)
collectMuteState()
if (widgetData?.widgetData()?.showCollapsedState == true && !videoKey.isNullOrEmpty() && PreferenceManager.getBooleanPreference(videoKey!!, false)
if (widgetData?.widgetData()?.showCollapsedState == true && !videoKey.isNullOrEmpty() && PreferenceManager.getBooleanPreference(
videoKey!!,
false
)
) {
binding.collapseCl.isVisible = true
binding.rootLayout.isVisible = false
showCollapsedState(widgetData.widgetData()?.collapsedStateData, widgetCallback)
player?.mute()
} else {
binding.collapseCl.isVisible = false
binding.rootLayout.isVisible = true
@@ -69,7 +75,13 @@ constructor(
?: 2.0)).roundToInt()
videoPlayerWidgetBody.videoData?.let {
handleVideo(it, videoPlayerWidgetBody.muted, videoPlayerWidgetBody.startPosition, videoPlayerWidgetBody.autoplay, widgetCallback)
handleVideo(
it,
videoPlayerWidgetBody.muted,
videoPlayerWidgetBody.startPosition,
videoPlayerWidgetBody.autoplay,
widgetCallback
)
}
}
binding.volumeLayout.setOnClickListener {
@@ -85,9 +97,10 @@ constructor(
binding.fullScreenButton.performClick()
}
binding.fullScreenButton.addOnMultipleClicksHandler {
fullScreenButtonClicked = true
widgetData?.widgetData()?.fullScreenButtonCta?.let {
val lineItem =
LineItem(key = START_POSITION, value = player?.getCurrentPosition()?.toString())
LineItem(key = RESUME_PLAYER, value = TEXT_TRUE)
widgetCallback?.onClick(
it.copy(
parameters = it.parameters?.plus(lineItem) ?: listOf(lineItem)
@@ -119,12 +132,6 @@ constructor(
private fun collectMuteState() {
CoroutineScope(Dispatchers.Main).launch {
launch {
delay(2000)
binding.volumeLayout.isVisible = true
delay(2000)
binding.volumeTv.isVisible = false
}
launch {
isMuted.collect { isMuted ->
if (isMuted) {
@@ -148,10 +155,7 @@ constructor(
) {
player?.setListeners(object : NaviExoPlayer.EventListener {
override fun onInitDone() {
binding.player.player = player?.player
binding.player.setControllerVisibilityListener {
binding.fullScreenButton.isVisible = it != View.VISIBLE
}
setPlayerAndControllerVisibilityListener()
}
override fun onPlaybackStateChanged(state: Int) {
@@ -167,49 +171,69 @@ constructor(
}
})
player?.initAndPlayVideo(video = videoData, startPosition = startPosition, playVideo = autoplay == true)
isMuted.value = muted == true && player?.mute() == true
binding.player.resizeMode = if (
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIT ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FILL ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_ZOOM
) {
videoData.resizeMode
if(player?.player == null) {
player?.initAndPlayVideo(video = videoData, startPosition = startPosition, playVideo = autoplay == true)
CoroutineScope(Dispatchers.Main).launch {
delay(2000)
binding.volumeLayout.isVisible = true
delay(2000)
binding.volumeTv.isVisible = false
}
if(muted == true) {
player?.mute()
} else {
player?.unmute()
}
isMuted.value = muted == true
} else {
AspectRatioFrameLayout.RESIZE_MODE_FIT
player?.resumeVideo()
binding.volumeLayout.isVisible = true
binding.volumeTv.isVisible = false
setPlayerAndControllerVisibilityListener()
isMuted.value = player?.isMuted() == true
}
}
private fun setPlayerAndControllerVisibilityListener() {
binding.player.player = null
binding.player.player = player?.player
isMuted.value = player?.isMuted() == true
binding.fullScreenButton.isVisible = !binding.player.isControllerVisible
binding.player.setControllerVisibilityListener {
binding.fullScreenButton.isVisible = it != View.VISIBLE
}
widgetData?.widgetData()?.videoData?.let { videoData ->
binding.player.resizeMode = if (
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIT ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FILL ||
videoData.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_ZOOM
) {
videoData.resizeMode
} else {
AspectRatioFrameLayout.RESIZE_MODE_FIT
}
}
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
if ((visibility == View.GONE || visibility == View.INVISIBLE) && player?.isVideoPlaying() == true) {
if (!fullScreenButtonClicked && (visibility == View.GONE || visibility == View.INVISIBLE) && player?.isVideoPlaying() == true) {
player?.pauseVideo()
} else if (visibility == View.VISIBLE && widgetData?.widgetData()?.autoplayAfterResume == true && player?.isVideoPlaying() == false) {
player?.playVideo()
} else if (binding.rootLayout.isShown && visibility == View.VISIBLE && widgetData?.widgetData()?.autoplayAfterResume == true && player?.isVideoPlaying() == false) {
player?.resumeVideo()
setPlayerAndControllerVisibilityListener()
fullScreenButtonClicked = false
}
super.onVisibilityChanged(changedView, visibility)
}
override fun onVisibilityAggregated(isVisible: Boolean) {
if (!isVisible && player?.isVideoPlaying() == true) {
if (!fullScreenButtonClicked && !isVisible && player?.isVideoPlaying() == true) {
player?.pauseVideo()
}
super.onVisibilityAggregated(isVisible)
}
override fun onAttachedToWindow() {
if(widgetData?.widgetData()?.autoplayAfterResume == true) {
player?.playVideo()
}
super.onAttachedToWindow()
}
override fun onDetachedFromWindow() {
player?.pauseVideo()
super.onDetachedFromWindow()
}
override fun onViewRemoved(view: View?) {
super.onViewRemoved(view)
if(view is VideoPlayerWidgetLayout) {

View File

@@ -155,6 +155,7 @@
android:paddingBottom="@dimen/dp_14"
android:paddingStart="@dimen/dp_18"
android:gravity="end"
android:visibility="gone"
android:src="@drawable/full_screen_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />