mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
fixed black screen and save player state after detach in video-custom
This commit is contained in:
+6
@@ -29,6 +29,11 @@ class VideoCustomViewController(
|
||||
viewModel.configureVideo()
|
||||
}
|
||||
|
||||
internal fun unbind(viewModel: VideoViewModel) {
|
||||
viewModels.remove(viewModel)
|
||||
viewModel.release()
|
||||
}
|
||||
|
||||
internal fun release(view: VideoView) {
|
||||
val viewModel = viewModels.find { it == view.viewModel }
|
||||
if (viewModel != null) {
|
||||
@@ -94,6 +99,7 @@ class VideoCustomViewController(
|
||||
config,
|
||||
cache,
|
||||
actionNotifier,
|
||||
this@VideoCustomViewController,
|
||||
context
|
||||
).also { viewModels.add(it) }
|
||||
}
|
||||
|
||||
+60
-10
@@ -1,8 +1,11 @@
|
||||
package com.yandex.div.video.custom
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.Bundle
|
||||
import android.view.SurfaceView
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.FrameLayout
|
||||
@@ -11,6 +14,9 @@ import androidx.core.view.isVisible
|
||||
import com.google.android.exoplayer2.ExoPlaybackException
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AUTO_TRANSITION
|
||||
import com.google.android.exoplayer2.Player.STATE_ENDED
|
||||
import com.google.android.exoplayer2.Player.STATE_READY
|
||||
import com.google.android.exoplayer2.SeekParameters
|
||||
import com.google.android.exoplayer2.ui.PlayerView
|
||||
import com.yandex.div.internal.KAssert
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -20,6 +26,7 @@ import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
|
||||
internal class VideoView(
|
||||
context: Context,
|
||||
@@ -51,9 +58,36 @@ internal class VideoView(
|
||||
setBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
|
||||
private val playerActivityCallback = object : Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityStarted(activity: Activity) = Unit
|
||||
override fun onActivityDestroyed(activity: Activity) =
|
||||
(context.applicationContext as Application).unregisterActivityLifecycleCallbacks(this)
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
|
||||
override fun onActivityStopped(activity: Activity) = Unit
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
viewModel?.freezePlayback()
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
//This code used to avoid black player after return from background
|
||||
val player = viewModel?.player ?: return
|
||||
val state = player.playbackState
|
||||
|
||||
viewModel?.unfreezePlayback()
|
||||
if (state == STATE_ENDED) {
|
||||
// play video, because we moved not on the last frame.
|
||||
player.setSeekParameters(SeekParameters.DEFAULT)
|
||||
player.seekTo(max(player.currentPosition - 1, 0L))
|
||||
player.setSeekParameters(SeekParameters.EXACT)
|
||||
viewModel?.player?.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
addView(stubImageView)
|
||||
addView(playerView)
|
||||
}
|
||||
|
||||
private val playerListener = object : Player.Listener {
|
||||
@@ -78,13 +112,23 @@ internal class VideoView(
|
||||
assertBoundToViewModel()
|
||||
viewModel?.onPlaybackError(error)
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(state: Int) {
|
||||
super.onPlaybackStateChanged(state)
|
||||
when (state) {
|
||||
STATE_READY -> if (playerView.parent == null) {
|
||||
stubImageView.setImageBitmap(null)
|
||||
stubImageView.isVisible = false
|
||||
addView(playerView)
|
||||
}
|
||||
else -> return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun bindToViewModel(model: VideoViewModel) {
|
||||
viewModel?.let { pauseObservingViewModel(it) }
|
||||
stubImageView.setImageBitmap(null)
|
||||
stubImageView.isVisible = false
|
||||
|
||||
viewModel?.let { stopObservingViewModel(it) }
|
||||
viewModel = model
|
||||
|
||||
if (isAttachedToWindow) {
|
||||
@@ -114,10 +158,10 @@ internal class VideoView(
|
||||
stubViewObservationJob = observeStubViewState(model)
|
||||
}
|
||||
|
||||
private fun pauseObservingViewModel(model: VideoViewModel) {
|
||||
playerView.player = null
|
||||
private fun stopObservingViewModel(model: VideoViewModel) {
|
||||
model.player.removeListener(playerListener)
|
||||
model.freezePlayback()
|
||||
playerView.player = null
|
||||
model.onDetached()
|
||||
coroutineScope.coroutineContext.cancelChildren()
|
||||
stubViewObservationJob = null
|
||||
}
|
||||
@@ -126,17 +170,23 @@ internal class VideoView(
|
||||
super.onAttachedToWindow()
|
||||
assertBoundToViewModel()
|
||||
viewModel?.let { resumeObservingViewModel(it) }
|
||||
(context.applicationContext as Application)
|
||||
.registerActivityLifecycleCallbacks(playerActivityCallback)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
viewModel?.let { pauseObservingViewModel(it) }
|
||||
viewModel?.let { stopObservingViewModel(it) }
|
||||
(context.applicationContext as Application)
|
||||
.unregisterActivityLifecycleCallbacks(playerActivityCallback)
|
||||
}
|
||||
|
||||
fun release() {
|
||||
viewModel?.let { pauseObservingViewModel(it) }
|
||||
viewModel?.let { stopObservingViewModel(it) }
|
||||
coroutineScope.cancel()
|
||||
playerView.player = null
|
||||
viewModel = null
|
||||
(context.applicationContext as Application)
|
||||
.unregisterActivityLifecycleCallbacks(playerActivityCallback)
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -26,6 +26,10 @@ internal interface VideoViewModel {
|
||||
|
||||
fun onVideoRepeat()
|
||||
|
||||
fun onDetached()
|
||||
|
||||
fun release()
|
||||
|
||||
fun freezePlayback()
|
||||
|
||||
fun unfreezePlayback()
|
||||
@@ -35,6 +39,7 @@ internal class MutableVideoViewModel(
|
||||
private val videoConfig: VideoConfig,
|
||||
private val cache: VideoCache,
|
||||
private val actionNotifier: VideoCustomActionNotifier,
|
||||
private val viewController: VideoCustomViewController,
|
||||
context: Context,
|
||||
) : VideoViewModel {
|
||||
private val viewModelScope = CoroutineScope(Dispatchers.Default)
|
||||
@@ -118,7 +123,12 @@ internal class MutableVideoViewModel(
|
||||
savedIsPlaying = null
|
||||
}
|
||||
|
||||
fun release() {
|
||||
override fun onDetached() {
|
||||
player.stop()
|
||||
viewController.unbind(this)
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
videoConfig.id?.let { id ->
|
||||
if (!isVideoShown) {
|
||||
actionNotifier.notifyPlaybackFinished(id, player.currentPosition)
|
||||
|
||||
Reference in New Issue
Block a user