列表支持前后视频预加载
This commit is contained in:
parent
45b63e870e
commit
da258f69f6
|
|
@ -241,9 +241,10 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
|
||||||
handleEventOneVideoSwiped()
|
handleEventOneVideoSwiped()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload next N videos
|
// Preload next N videos and keep previous N videos
|
||||||
val preloadCount = 3
|
val preloadCount = 3
|
||||||
for (i in 1..preloadCount) {
|
for (i in 1..preloadCount) {
|
||||||
|
// Next N
|
||||||
val nextPos = position + i
|
val nextPos = position + i
|
||||||
if (nextPos < mViewPagerAdapter.itemCount) {
|
if (nextPos < mViewPagerAdapter.itemCount) {
|
||||||
val nextFragment = mViewPagerAdapter.getFragmentByIndex(nextPos)
|
val nextFragment = mViewPagerAdapter.getFragmentByIndex(nextPos)
|
||||||
|
|
@ -251,22 +252,35 @@ class HomeFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnSwit
|
||||||
nextFragment.preloadVideo()
|
nextFragment.preloadVideo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Prev N (Keep them ready for playback)
|
||||||
// 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
|
val prevPos = position - i
|
||||||
if (prevPos >= 0) {
|
if (prevPos >= 0) {
|
||||||
val prevFragment = mViewPagerAdapter.getFragmentByIndex(prevPos)
|
val prevFragment = mViewPagerAdapter.getFragmentByIndex(prevPos)
|
||||||
if (prevFragment != null && prevFragment is HomeItemFragment) {
|
if (prevFragment != null && prevFragment is HomeItemFragment) {
|
||||||
prevFragment.releasePlayer()
|
prevFragment.preloadVideo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release players outside of the window (position - preloadCount - 1)
|
||||||
|
val releasePosBefore = position - preloadCount - 1
|
||||||
|
if (releasePosBefore >= 0) {
|
||||||
|
val prevFragment = mViewPagerAdapter.getFragmentByIndex(releasePosBefore)
|
||||||
|
if (prevFragment != null && prevFragment is HomeItemFragment) {
|
||||||
|
prevFragment.releasePlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release players outside of the window (position + preloadCount + 1)
|
||||||
|
val releasePosAfter = position + preloadCount + 1
|
||||||
|
if (releasePosAfter < mViewPagerAdapter.itemCount) {
|
||||||
|
val nextFragment = mViewPagerAdapter.getFragmentByIndex(releasePosAfter)
|
||||||
|
if (nextFragment != null && nextFragment is HomeItemFragment) {
|
||||||
|
nextFragment.releasePlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load more
|
// load more
|
||||||
if (mViewPagerAdapter.itemCount > 0 && position >= mViewPagerAdapter.itemCount - 3) {
|
if (mViewPagerAdapter.itemCount > 0 && position >= mViewPagerAdapter.itemCount - 3) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|
|
||||||
|
|
@ -93,17 +93,23 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putFloat("curPlayedSecond", mCurPlayedSecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mCurPlayedSecond = savedInstanceState.getFloat("curPlayedSecond", 0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
mMaskBitmap?.recycle()
|
mMaskBitmap?.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun playVideo() {
|
fun playVideo() {
|
||||||
mIsPreloading = false
|
mIsPreloading = false
|
||||||
binding?.playerContainer?.isVisible = true
|
binding?.playerContainer?.isVisible = true
|
||||||
|
|
@ -174,19 +180,21 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
mPlayerView?.enableAutomaticInitialization = true
|
mPlayerView?.enableAutomaticInitialization = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// val playerUiController = MyPlayerControlView(mPlayerView!!)
|
||||||
|
// mPlayerView!!.setCustomPlayerUi(playerUiController.rootView)
|
||||||
|
mPlayerView!!.removeViews(1, mPlayerView!!.childCount - 1)
|
||||||
|
|
||||||
mPlayerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
|
mPlayerView!!.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
|
||||||
override fun onReady(@NonNull youTubePlayer: YouTubePlayer) {
|
override fun onReady(youTubePlayer: YouTubePlayer) {
|
||||||
mPlayer = youTubePlayer
|
mPlayer = youTubePlayer
|
||||||
val playerUiController = MyPlayerControlView(mPlayerView!!, youTubePlayer)
|
|
||||||
mPlayerView!!.setCustomPlayerUi(playerUiController.rootView)
|
|
||||||
|
|
||||||
if (mPendingPlay) {
|
if (mPendingPlay) {
|
||||||
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
mPlayer?.loadVideo(mVideoData!!.id, mCurPlayedSecond)
|
||||||
} else if (mIsPreloading) {
|
} else if (mIsPreloading) {
|
||||||
mPlayer?.mute()
|
mPlayer?.mute()
|
||||||
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
mPlayer?.loadVideo(mVideoData!!.id, mCurPlayedSecond)
|
||||||
} else {
|
} else {
|
||||||
mPlayer?.cueVideo(mVideoData!!.id, 0f)
|
mPlayer?.cueVideo(mVideoData!!.id, mCurPlayedSecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,6 +240,7 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
PlayerConstants.PlayerState.ENDED -> {
|
PlayerConstants.PlayerState.ENDED -> {
|
||||||
togglePlayingState(false)
|
togglePlayingState(false)
|
||||||
if (!mIsPreloading) {
|
if (!mIsPreloading) {
|
||||||
|
mCurPlayedSecond = 0f
|
||||||
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
mPlayer?.loadVideo(mVideoData!!.id, 0f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,9 +281,9 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
private fun togglePlayingState1(isPlaying: Boolean) {
|
private fun togglePlayingState1(isPlaying: Boolean) {
|
||||||
if (mIsPlaying != isPlaying) {
|
if (mIsPlaying != isPlaying) {
|
||||||
mIsPlaying = isPlaying
|
mIsPlaying = isPlaying
|
||||||
if (mIsPlaying) {
|
// if (mIsPlaying) {
|
||||||
binding?.circlePb?.isVisible = false
|
// binding?.circlePb?.isVisible = false
|
||||||
}
|
// }
|
||||||
|
|
||||||
binding?.ivMask?.isVisible = !mIsPlaying
|
binding?.ivMask?.isVisible = !mIsPlaying
|
||||||
// Ensure playerContainer is visible when playing OR preloading (masked)
|
// Ensure playerContainer is visible when playing OR preloading (masked)
|
||||||
|
|
@ -295,7 +304,7 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
private fun switchState2Play() {
|
private fun switchState2Play() {
|
||||||
mThumbHandler.removeCallbacksAndMessages(null)
|
mThumbHandler.removeCallbacksAndMessages(null)
|
||||||
|
|
||||||
binding?.circlePb?.isVisible = false
|
// binding?.circlePb?.isVisible = false
|
||||||
binding?.ivMask?.isVisible = false
|
binding?.ivMask?.isVisible = false
|
||||||
binding?.playerContainer?.isVisible = true
|
binding?.playerContainer?.isVisible = true
|
||||||
hidePlayIconAnim()
|
hidePlayIconAnim()
|
||||||
|
|
@ -305,7 +314,7 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
private fun switchState2Pause() {
|
private fun switchState2Pause() {
|
||||||
binding?.ivMask?.isVisible = true
|
binding?.ivMask?.isVisible = true
|
||||||
binding?.playerContainer?.isVisible = false
|
binding?.playerContainer?.isVisible = false
|
||||||
showPlayIconAnim()
|
// showPlayIconAnim()
|
||||||
mTickerTimer.pause()
|
mTickerTimer.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,6 +378,7 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
|
|
||||||
|
|
||||||
private fun hidePlayIconAnim() {
|
private fun hidePlayIconAnim() {
|
||||||
|
return
|
||||||
if (!binding?.playIcon!!.isVisible) {
|
if (!binding?.playIcon!!.isVisible) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -404,6 +414,7 @@ class HomeItemFragment : AppViewsEmptyViewModelFragment<ViewBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPlayIconAnim() {
|
private fun showPlayIconAnim() {
|
||||||
|
return
|
||||||
with (binding?.playIcon!!) {
|
with (binding?.playIcon!!) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,14 @@ import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.menu.YouTub
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.utils.FadeViewHelper
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.utils.FadeViewHelper
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.views.YouTubePlayerSeekBar
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.views.YouTubePlayerSeekBar
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.views.YouTubePlayerSeekBarListener
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.customui.views.YouTubePlayerSeekBarListener
|
||||||
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerCallback
|
||||||
import kotlin.jvm.javaClass
|
import kotlin.jvm.javaClass
|
||||||
|
|
||||||
|
|
||||||
class MyPlayerControlView(
|
class MyPlayerControlView(
|
||||||
private val youTubePlayerView: YouTubePlayerView,
|
private val youTubePlayerView: YouTubePlayerView,
|
||||||
private val youTubePlayer: YouTubePlayer
|
|
||||||
) : PlayerUiController {
|
) : PlayerUiController {
|
||||||
|
var youTubePlayer: YouTubePlayer? = null
|
||||||
|
|
||||||
val rootView: View = View.inflate(youTubePlayerView.context, R.layout.layout_player_controller, null)
|
val rootView: View = View.inflate(youTubePlayerView.context, R.layout.layout_player_controller, null)
|
||||||
|
|
||||||
|
|
@ -133,16 +134,25 @@ class MyPlayerControlView(
|
||||||
|
|
||||||
onMenuButtonClickListener = View.OnClickListener { youTubePlayerMenu.show(menuButton) }
|
onMenuButtonClickListener = View.OnClickListener { youTubePlayerMenu.show(menuButton) }
|
||||||
|
|
||||||
initClickListeners()
|
youTubePlayerView.getYouTubePlayerWhenReady(object : YouTubePlayerCallback{
|
||||||
|
override fun onYouTubePlayer(youTubePlayer: YouTubePlayer) {
|
||||||
|
this@MyPlayerControlView.youTubePlayer = youTubePlayer
|
||||||
|
initClickListeners()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initClickListeners() {
|
|
||||||
youTubePlayer.addListener(youtubePlayerSeekBar)
|
|
||||||
youTubePlayer.addListener(fadeControlsContainer)
|
|
||||||
youTubePlayer.addListener(youTubePlayerStateListener)
|
|
||||||
|
|
||||||
youtubePlayerSeekBar.youtubePlayerSeekBarListener = object : YouTubePlayerSeekBarListener {
|
private fun initClickListeners() {
|
||||||
override fun seekTo(time: Float) = youTubePlayer.seekTo(time)
|
youTubePlayer?.addListener(youtubePlayerSeekBar)
|
||||||
|
youTubePlayer?.addListener(fadeControlsContainer)
|
||||||
|
youTubePlayer?.addListener(youTubePlayerStateListener)
|
||||||
|
|
||||||
|
youTubePlayer?.let {
|
||||||
|
youtubePlayerSeekBar.youtubePlayerSeekBarListener = object : YouTubePlayerSeekBarListener {
|
||||||
|
override fun seekTo(time: Float) = it.seekTo(time)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panel.setOnClickListener { fadeControlsContainer.toggleVisibility() }
|
panel.setOnClickListener { fadeControlsContainer.toggleVisibility() }
|
||||||
playPauseButton.setOnClickListener { onPlayButtonPressed() }
|
playPauseButton.setOnClickListener { onPlayButtonPressed() }
|
||||||
|
|
@ -270,9 +280,9 @@ class MyPlayerControlView(
|
||||||
|
|
||||||
private fun onPlayButtonPressed() {
|
private fun onPlayButtonPressed() {
|
||||||
if (isPlaying)
|
if (isPlaying)
|
||||||
youTubePlayer.pause()
|
youTubePlayer?.pause()
|
||||||
else
|
else
|
||||||
youTubePlayer.play()
|
youTubePlayer?.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateState(state: PlayerConstants.PlayerState) {
|
private fun updateState(state: PlayerConstants.PlayerState) {
|
||||||
|
|
|
||||||
|
|
@ -8,44 +8,43 @@
|
||||||
android:id="@+id/player_container"
|
android:id="@+id/player_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/iv_mask"
|
android:id="@+id/iv_mask"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="false"
|
android:clickable="false" />
|
||||||
android:layout_gravity="center"/>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/click_mask_view"
|
android:id="@+id/click_mask_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clickable="true"
|
|
||||||
android:layout_marginBottom="30dp"
|
android:layout_marginBottom="30dp"
|
||||||
/>
|
android:clickable="true" />
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/play_icon"
|
android:id="@+id/play_icon"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
android:src="@mipmap/icon_play"
|
android:src="@mipmap/icon_play"
|
||||||
android:visibility="gone"
|
android:visibility="gone" />
|
||||||
android:layout_gravity="center"/>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/bottom_container"
|
android:id="@+id/bottom_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginVertical="25dp"
|
android:layout_marginVertical="25dp"
|
||||||
android:layout_marginLeft="15dp"
|
android:layout_marginLeft="15dp"
|
||||||
android:layout_marginRight="80dp"
|
android:layout_marginRight="80dp"
|
||||||
android:layout_gravity="bottom">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_video_from"
|
android:id="@+id/tv_video_from"
|
||||||
|
|
@ -53,34 +52,34 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="From"
|
android:text="From"
|
||||||
android:textSize="15sp"
|
android:textColor="@color/white"
|
||||||
android:textColor="@color/white" />
|
android:textSize="15sp" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_video_intro"
|
android:id="@+id/tv_video_intro"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:text="introduce"
|
android:layout_marginEnd="45dp"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:layout_marginEnd="45dp"
|
android:text="introduce"
|
||||||
android:textSize="15sp"
|
android:textColor="@color/white_al80"
|
||||||
android:textColor="@color/white_al80" />
|
android:textSize="15sp" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/iv_intro_expand"
|
android:id="@+id/iv_intro_expand"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="right|bottom"
|
||||||
android:paddingHorizontal="10dp"
|
android:paddingHorizontal="10dp"
|
||||||
android:paddingVertical="5dp"
|
android:paddingVertical="5dp"
|
||||||
android:layout_gravity="right|bottom"
|
|
||||||
android:src="@mipmap/arrow_up"
|
android:src="@mipmap/arrow_up"
|
||||||
android:visibility="gone"
|
android:visibility="gone" />
|
||||||
/>
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
@ -89,18 +88,17 @@
|
||||||
android:id="@+id/progress_bar_player"
|
android:id="@+id/progress_bar_player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="3dp"
|
android:layout_height="3dp"
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:layout_marginHorizontal="15dp"
|
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
/>
|
android:layout_marginHorizontal="15dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/circle_pb"
|
android:id="@+id/circle_pb"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:indeterminateTint="@android:color/white"
|
android:indeterminateTint="@android:color/white"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
android:visibility="gone" />
|
||||||
/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
Loading…
Reference in New Issue