视频优化
This commit is contained in:
parent
13ee0de952
commit
6f34b5b990
|
|
@ -65,6 +65,8 @@ class ShushuManager private constructor() {
|
||||||
config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.DEBUG)
|
config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.DEBUG)
|
||||||
// config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.NORMAL)
|
// config.setMode(/*if (BuildConfig.DEBUG) TDConfig.ModeEnum.DEBUG else*/ TDConfig.ModeEnum.NORMAL)
|
||||||
mShushuSdk = ThinkingAnalyticsSDK.sharedInstance(config)
|
mShushuSdk = ThinkingAnalyticsSDK.sharedInstance(config)
|
||||||
|
|
||||||
|
StatisticLogger.d("数数DeviceID: ${TDAnalytics.getDeviceId()}")
|
||||||
configAutoTrack()
|
configAutoTrack()
|
||||||
sdkReady = true
|
sdkReady = true
|
||||||
flushPendingEventsAsync()
|
flushPendingEventsAsync()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
|
||||||
|
|
||||||
override fun ViewBinding.initViews() {
|
override fun ViewBinding.initViews() {
|
||||||
viewPager2.setPageTransformer { _, _ -> }
|
viewPager2.setPageTransformer { _, _ -> }
|
||||||
viewPager2.offscreenPageLimit = 1
|
viewPager2.offscreenPageLimit = 3
|
||||||
viewPager2.adapter = mViewPagerAdapter
|
viewPager2.adapter = mViewPagerAdapter
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -236,13 +236,39 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
|
||||||
if (fragment != null) {
|
if (fragment != null) {
|
||||||
setHomeTabStyle(fragment)
|
setHomeTabStyle(fragment)
|
||||||
val curFragment: HomeItemFragment = fragment as HomeItemFragment
|
val curFragment: HomeItemFragment = fragment as HomeItemFragment
|
||||||
curFragment.loadVideo()
|
curFragment.playVideo()
|
||||||
|
|
||||||
handleEventOneVideoSwiped()
|
handleEventOneVideoSwiped()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preload next N videos
|
||||||
|
val preloadCount = 3
|
||||||
|
for (i in 1..preloadCount) {
|
||||||
|
val nextPos = position + i
|
||||||
|
if (nextPos < mViewPagerAdapter.itemCount) {
|
||||||
|
val nextFragment = mViewPagerAdapter.getFragmentByIndex(nextPos)
|
||||||
|
if (nextFragment != null && nextFragment is HomeItemFragment) {
|
||||||
|
nextFragment.preloadVideo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release players for previous fragments to save memory
|
||||||
|
// We keep 'preloadCount' fragments after current, and maybe 0 before?
|
||||||
|
// But offscreenPageLimit keeps them in memory. We should release their heavy resources.
|
||||||
|
val offscreenLimit = 3
|
||||||
|
for (i in 1..offscreenLimit) {
|
||||||
|
val prevPos = position - i
|
||||||
|
if (prevPos >= 0) {
|
||||||
|
val prevFragment = mViewPagerAdapter.getFragmentByIndex(prevPos)
|
||||||
|
if (prevFragment != null && prevFragment is HomeItemFragment) {
|
||||||
|
prevFragment.releasePlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load more
|
// load more
|
||||||
if (mViewPagerAdapter.itemCount > 0 && position == mViewPagerAdapter.itemCount - 2) {
|
if (mViewPagerAdapter.itemCount > 0 && position >= mViewPagerAdapter.itemCount - 3) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
mViewModel.loadVideoList()
|
mViewModel.loadVideoList()
|
||||||
}
|
}
|
||||||
|
|
@ -262,7 +288,6 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
|
|
||||||
viewPager2.offscreenPageLimit = 3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ViewBinding.initObservers() {
|
override fun ViewBinding.initObservers() {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.core.view.isVisible
|
||||||
import com.ama.core.architecture.appBase.AppViewsEmptyViewModelFragment
|
import com.ama.core.architecture.appBase.AppViewsEmptyViewModelFragment
|
||||||
import com.ama.core.architecture.util.AndroidUtil
|
import com.ama.core.architecture.util.AndroidUtil
|
||||||
import com.ama.core.architecture.util.setOnClickBatch
|
import com.ama.core.architecture.util.setOnClickBatch
|
||||||
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import com.gamedog.statisticreporter.StatisticUtil
|
import com.gamedog.statisticreporter.StatisticUtil
|
||||||
import com.viddin.videos.free.R
|
import com.viddin.videos.free.R
|
||||||
import com.gamedog.vididin.beans.YoutubeVideo
|
import com.gamedog.vididin.beans.YoutubeVideo
|
||||||
|
|
@ -27,6 +28,7 @@ import com.gamedog.vididin.youtubestatistic.TickerTimer
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerCallback
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.loadOrCueVideo
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.loadOrCueVideo
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
|
||||||
import com.viddin.videos.free.databinding.VididinappFeatureHomeItemLayoutBinding as ViewBinding
|
import com.viddin.videos.free.databinding.VididinappFeatureHomeItemLayoutBinding as ViewBinding
|
||||||
|
|
@ -43,6 +45,8 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
private var mCurPlayedSecond: Float = 0F
|
private var mCurPlayedSecond: Float = 0F
|
||||||
private var mTotalDuration: Float = 0F
|
private var mTotalDuration: Float = 0F
|
||||||
private val mTickerTimer = TickerTimer()
|
private val mTickerTimer = TickerTimer()
|
||||||
|
private var mPendingPlay: Boolean = false
|
||||||
|
private var mIsPreloading: Boolean = false
|
||||||
|
|
||||||
private val mThumbHandler = Handler(Looper.getMainLooper())
|
private val mThumbHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
|
@ -100,27 +104,67 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun playVideo() {
|
fun playVideo() {
|
||||||
if (mPlayer != null && mVideoData != null && !mVideoData?.id.isNullOrEmpty()) {
|
mIsPreloading = false
|
||||||
mPlayer?.loadOrCueVideo(
|
binding?.playerContainer?.isVisible = true
|
||||||
lifecycle,
|
if (mPlayerView == null) {
|
||||||
mVideoData!!.id,
|
initPlayerView(true)
|
||||||
0f
|
} else {
|
||||||
)
|
if (mPlayer != null) {
|
||||||
|
mPlayer?.unMute()
|
||||||
|
mPlayer?.play()
|
||||||
|
} else {
|
||||||
|
mPendingPlay = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*mPlayerView?.isVisible?.let {
|
fun preloadVideo() {
|
||||||
if (!it) {
|
if (mPlayerView == null) {
|
||||||
mPlayer?.pause()
|
mIsPreloading = true
|
||||||
|
binding?.playerContainer?.isVisible = true
|
||||||
|
initPlayerView(false)
|
||||||
|
} else {
|
||||||
|
if (mPlayer != null && mIsPreloading) {
|
||||||
|
// Already preloading or preloaded
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun releasePlayer() {
|
||||||
|
LogUtils.d("[视频Item] releasePlayer ID: ${getVideoId()}")
|
||||||
|
mIsPreloading = false
|
||||||
|
mPendingPlay = false
|
||||||
|
|
||||||
|
if (mPlayerView != null) {
|
||||||
|
// Try to save last frame as mask if possible and not already saved
|
||||||
|
if (mMaskBitmap == null && binding?.playerContainer?.isVisible == true) {
|
||||||
|
// This is a best-effort sync capture or just skip to avoid async complexity during release
|
||||||
|
// For now, we just release to free memory immediately.
|
||||||
|
}
|
||||||
|
|
||||||
|
mPlayerView!!.release()
|
||||||
|
binding?.playerContainer?.removeView(mPlayerView)
|
||||||
|
mPlayerView = null
|
||||||
|
mPlayer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.playerContainer?.isVisible = false
|
||||||
|
binding?.ivMask?.isVisible = true
|
||||||
|
if (mMaskBitmap != null) {
|
||||||
|
binding?.ivMask?.setImageBitmap(mMaskBitmap)
|
||||||
|
}
|
||||||
|
showPlayIconAnim()
|
||||||
|
mTickerTimer.pause()
|
||||||
|
}
|
||||||
|
|
||||||
fun getVideoId(): String? {
|
fun getVideoId(): String? {
|
||||||
return mVideoData?.id
|
return mVideoData?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadVideo() {
|
private fun initPlayerView(autoPlay: Boolean) {
|
||||||
|
mPendingPlay = autoPlay
|
||||||
|
LogUtils.d("[视频Item] initPlayerView ID: ${getVideoId()} autoPlay: $autoPlay")
|
||||||
if (null == mPlayerView) {
|
if (null == mPlayerView) {
|
||||||
mPlayerView = YouTubePlayerView(requireContext())
|
mPlayerView = YouTubePlayerView(requireContext())
|
||||||
val layoutParam = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
val layoutParam = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||||
|
|
@ -130,16 +174,20 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
mPlayerView?.enableAutomaticInitialization = true
|
mPlayerView?.enableAutomaticInitialization = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPlayerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
|
||||||
val playerView = mPlayerView
|
|
||||||
playerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
|
|
||||||
override fun onReady(@NonNull youTubePlayer: YouTubePlayer) {
|
override fun onReady(@NonNull youTubePlayer: YouTubePlayer) {
|
||||||
mPlayer = youTubePlayer
|
mPlayer = youTubePlayer
|
||||||
|
val playerUiController = MyPlayerControlView(mPlayerView!!, youTubePlayer)
|
||||||
|
mPlayerView!!.setCustomPlayerUi(playerUiController.rootView)
|
||||||
|
|
||||||
val playerUiController = MyPlayerControlView(playerView, youTubePlayer)
|
if (mPendingPlay) {
|
||||||
playerView.setCustomPlayerUi(playerUiController.rootView)
|
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
||||||
|
} else if (mIsPreloading) {
|
||||||
playVideo()
|
mPlayer?.mute()
|
||||||
|
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
||||||
|
} else {
|
||||||
|
mPlayer?.cueVideo(mVideoData!!.id, 0f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
|
override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
|
||||||
|
|
@ -160,10 +208,20 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
) {
|
) {
|
||||||
when (state) {
|
when (state) {
|
||||||
PlayerConstants.PlayerState.PLAYING -> {
|
PlayerConstants.PlayerState.PLAYING -> {
|
||||||
|
if (mIsPreloading) {
|
||||||
|
mPlayer?.pause()
|
||||||
|
} else {
|
||||||
togglePlayingState(true)
|
togglePlayingState(true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PlayerConstants.PlayerState.PAUSED -> {
|
PlayerConstants.PlayerState.PAUSED -> {
|
||||||
|
if (mIsPreloading) {
|
||||||
|
// Do nothing or ensure mask is visible?
|
||||||
|
// togglePlayingState(false) sets mask visible.
|
||||||
togglePlayingState(false)
|
togglePlayingState(false)
|
||||||
|
} else {
|
||||||
|
togglePlayingState(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PlayerConstants.PlayerState.UNKNOWN -> {
|
PlayerConstants.PlayerState.UNKNOWN -> {
|
||||||
togglePlayingState(false)
|
togglePlayingState(false)
|
||||||
|
|
@ -173,7 +231,9 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
}
|
}
|
||||||
PlayerConstants.PlayerState.ENDED -> {
|
PlayerConstants.PlayerState.ENDED -> {
|
||||||
togglePlayingState(false)
|
togglePlayingState(false)
|
||||||
playVideo()
|
if (!mIsPreloading) {
|
||||||
|
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PlayerConstants.PlayerState.BUFFERING -> {
|
PlayerConstants.PlayerState.BUFFERING -> {
|
||||||
//binding?.circlePb?.isVisible = true
|
//binding?.circlePb?.isVisible = true
|
||||||
|
|
@ -217,7 +277,8 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding?.ivMask?.isVisible = !mIsPlaying
|
binding?.ivMask?.isVisible = !mIsPlaying
|
||||||
binding?.playerContainer?.isVisible = mIsPlaying
|
// Ensure playerContainer is visible when playing OR preloading (masked)
|
||||||
|
binding?.playerContainer?.isVisible = mIsPlaying || mIsPreloading
|
||||||
|
|
||||||
if (mIsPlaying) {
|
if (mIsPlaying) {
|
||||||
hidePlayIconAnim()
|
hidePlayIconAnim()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue