kopia lustrzana https://github.com/ryukoposting/Signal-Android
Improve smoothness of segmented progress bar and respect video duration.
rodzic
63412b0153
commit
4e57432dbb
|
@ -29,14 +29,14 @@ package org.thoughtcrime.securesms.components.segmentedprogressbar
|
||||||
*/
|
*/
|
||||||
class Segment(val animationDurationMillis: Long) {
|
class Segment(val animationDurationMillis: Long) {
|
||||||
|
|
||||||
private var animationProgress: Int = 0
|
var animationProgressPercentage: Float = 0f
|
||||||
|
|
||||||
var animationState: AnimationState = AnimationState.IDLE
|
var animationState: AnimationState = AnimationState.IDLE
|
||||||
set(value) {
|
set(value) {
|
||||||
animationProgress = when (value) {
|
animationProgressPercentage = when (value) {
|
||||||
AnimationState.ANIMATED -> 100
|
AnimationState.ANIMATED -> 1f
|
||||||
AnimationState.IDLE -> 0
|
AnimationState.IDLE -> 0f
|
||||||
else -> animationProgress
|
else -> animationProgressPercentage
|
||||||
}
|
}
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,4 @@ class Segment(val animationDurationMillis: Long) {
|
||||||
ANIMATING,
|
ANIMATING,
|
||||||
IDLE
|
IDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
val progressPercentage: Float
|
|
||||||
get() = animationProgress.toFloat() / 100
|
|
||||||
|
|
||||||
fun progress() = animationProgress++
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.thoughtcrime.securesms.components.segmentedprogressbar
|
||||||
|
|
||||||
|
data class SegmentState(
|
||||||
|
val position: Long,
|
||||||
|
val duration: Long
|
||||||
|
)
|
|
@ -28,13 +28,12 @@ import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Path
|
import android.graphics.Path
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Tiago Ornelas on 18/04/2020.
|
* Created by Tiago Ornelas on 18/04/2020.
|
||||||
|
@ -42,7 +41,14 @@ import org.thoughtcrime.securesms.R
|
||||||
* @see Segment
|
* @see Segment
|
||||||
* And the progress of each segment is animated based on a set speed
|
* And the progress of each segment is animated based on a set speed
|
||||||
*/
|
*/
|
||||||
class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, View.OnTouchListener {
|
class SegmentedProgressBar : View, ViewPager.OnPageChangeListener, View.OnTouchListener {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* It is common now for devices to run at 60FPS
|
||||||
|
*/
|
||||||
|
val MILLIS_PER_FRAME = TimeUnit.MILLISECONDS.toMillis(17)
|
||||||
|
}
|
||||||
|
|
||||||
private val path = Path()
|
private val path = Path()
|
||||||
private val corners = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
|
private val corners = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
|
||||||
|
@ -57,7 +63,10 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping of segment index -> duration in millis
|
* Mapping of segment index -> duration in millis. Negative durations
|
||||||
|
* ARE valid but they'll result in a call to SegmentedProgressBarListener#onRequestSegmentProgressPercentage
|
||||||
|
* which should return the current % position for the currently playing item. This helps
|
||||||
|
* to avoid synchronizing the seek bar to playback.
|
||||||
*/
|
*/
|
||||||
var segmentDurations: Map<Int, Long> = mapOf()
|
var segmentDurations: Map<Int, Long> = mapOf()
|
||||||
set(value) {
|
set(value) {
|
||||||
|
@ -93,8 +102,6 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
private val selectedSegmentIndex: Int
|
private val selectedSegmentIndex: Int
|
||||||
get() = segments.indexOf(this.selectedSegment)
|
get() = segments.indexOf(this.selectedSegment)
|
||||||
|
|
||||||
private val animationHandler = Handler(Looper.getMainLooper())
|
|
||||||
|
|
||||||
// Drawing
|
// Drawing
|
||||||
val strokeApplicable: Boolean
|
val strokeApplicable: Boolean
|
||||||
get() = segmentStrokeWidth * 4 <= measuredHeight
|
get() = segmentStrokeWidth * 4 <= measuredHeight
|
||||||
|
@ -121,6 +128,8 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
*/
|
*/
|
||||||
var listener: SegmentedProgressBarListener? = null
|
var listener: SegmentedProgressBarListener? = null
|
||||||
|
|
||||||
|
private var lastFrameTimeMillis: Long = 0L
|
||||||
|
|
||||||
constructor(context: Context) : super(context)
|
constructor(context: Context) : super(context)
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
@ -225,6 +234,8 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFrame(System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -233,17 +244,20 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
fun start() {
|
fun start() {
|
||||||
pause()
|
pause()
|
||||||
val segment = selectedSegment
|
val segment = selectedSegment
|
||||||
if (segment == null)
|
if (segment == null) {
|
||||||
next()
|
next()
|
||||||
else
|
} else {
|
||||||
animationHandler.postDelayed(this, segment.animationDurationMillis / 100)
|
isPaused = false
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the animation process
|
* Pauses the animation process
|
||||||
*/
|
*/
|
||||||
fun pause() {
|
fun pause() {
|
||||||
animationHandler.removeCallbacks(this)
|
isPaused = true
|
||||||
|
lastFrameTimeMillis = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,15 +341,24 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
if (nextSegment != null) {
|
if (nextSegment != null) {
|
||||||
pause()
|
pause()
|
||||||
nextSegment.animationState = Segment.AnimationState.ANIMATING
|
nextSegment.animationState = Segment.AnimationState.ANIMATING
|
||||||
animationHandler.postDelayed(this, nextSegment.animationDurationMillis / 100)
|
isPaused = false
|
||||||
|
invalidate()
|
||||||
this.listener?.onPage(oldSegmentIndex, this.selectedSegmentIndex)
|
this.listener?.onPage(oldSegmentIndex, this.selectedSegmentIndex)
|
||||||
viewPager?.currentItem = this.selectedSegmentIndex
|
viewPager?.currentItem = this.selectedSegmentIndex
|
||||||
} else {
|
} else {
|
||||||
animationHandler.removeCallbacks(this)
|
pause()
|
||||||
this.listener?.onFinished()
|
this.listener?.onFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSegmentProgressPercentage(segment: Segment, timeSinceLastFrameMillis: Long): Float {
|
||||||
|
return if (segment.animationDurationMillis > 0) {
|
||||||
|
segment.animationProgressPercentage + timeSinceLastFrameMillis.toFloat() / segment.animationDurationMillis
|
||||||
|
} else {
|
||||||
|
listener?.onRequestSegmentProgressPercentage() ?: 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initSegments() {
|
private fun initSegments() {
|
||||||
this.segments.clear()
|
this.segments.clear()
|
||||||
segments.addAll(
|
segments.addAll(
|
||||||
|
@ -348,12 +371,30 @@ class SegmentedProgressBar : View, Runnable, ViewPager.OnPageChangeListener, Vie
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun run() {
|
private var isPaused = true
|
||||||
if (this.selectedSegment?.progress() ?: 0 >= 100) {
|
|
||||||
|
private fun onFrame(frameTimeMillis: Long) {
|
||||||
|
if (isPaused) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastFrameTimeMillis = this.lastFrameTimeMillis
|
||||||
|
|
||||||
|
this.lastFrameTimeMillis = frameTimeMillis
|
||||||
|
|
||||||
|
val selectedSegment = this.selectedSegment
|
||||||
|
if (selectedSegment == null) {
|
||||||
loadSegment(offset = 1, userAction = false)
|
loadSegment(offset = 1, userAction = false)
|
||||||
|
} else if (lastFrameTimeMillis > 0L) {
|
||||||
|
val segmentProgressPercentage = getSegmentProgressPercentage(selectedSegment, frameTimeMillis - lastFrameTimeMillis)
|
||||||
|
selectedSegment.animationProgressPercentage = segmentProgressPercentage
|
||||||
|
if (selectedSegment.animationProgressPercentage >= 1f) {
|
||||||
|
loadSegment(offset = 1, userAction = false)
|
||||||
|
} else {
|
||||||
|
this.invalidate()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.invalidate()
|
this.invalidate()
|
||||||
animationHandler.postDelayed(this, this.selectedSegment?.animationDurationMillis?.let { it / 100 } ?: (timePerSegmentMs / 100))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,4 +37,6 @@ interface SegmentedProgressBarListener {
|
||||||
* Notifies when last segment finished animating
|
* Notifies when last segment finished animating
|
||||||
*/
|
*/
|
||||||
fun onFinished()
|
fun onFinished()
|
||||||
|
|
||||||
|
fun onRequestSegmentProgressPercentage(): Float?
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ fun SegmentedProgressBar.getDrawingComponents(
|
||||||
RectF(
|
RectF(
|
||||||
startBound + stroke,
|
startBound + stroke,
|
||||||
height - stroke,
|
height - stroke,
|
||||||
startBound + segment.progressPercentage * segmentWidth,
|
startBound + segment.animationProgressPercentage * segmentWidth,
|
||||||
stroke
|
stroke
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.mediapreview
|
package org.thoughtcrime.securesms.mediapreview
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.rxjava3.core.Observable
|
|
||||||
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
|
||||||
import org.thoughtcrime.securesms.video.VideoPlayer
|
import org.thoughtcrime.securesms.video.VideoPlayer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,12 +11,15 @@ class VideoControlsDelegate {
|
||||||
|
|
||||||
private val playWhenReady: MutableMap<Uri, Boolean> = mutableMapOf()
|
private val playWhenReady: MutableMap<Uri, Boolean> = mutableMapOf()
|
||||||
private val playerSubject = BehaviorSubject.create<Player>()
|
private val playerSubject = BehaviorSubject.create<Player>()
|
||||||
private val playerReadySignal = PublishSubject.create<Unit>()
|
|
||||||
val playerUpdates: Observable<PlayerUpdate> = playerReadySignal
|
fun getPlayerState(uri: Uri): PlayerState? {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
val player = playerSubject.value
|
||||||
.flatMap { playerSubject }
|
return if (player?.uri == uri && player.videoPlayer != null) {
|
||||||
.filter { it.videoPlayer != null }
|
PlayerState(uri, player.videoPlayer.playbackPosition, player.videoPlayer.duration)
|
||||||
.map { PlayerUpdate(it.uri, it.videoPlayer?.duration!!) }
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun pause() = playerSubject.value?.videoPlayer?.pause()
|
fun pause() = playerSubject.value?.videoPlayer?.pause()
|
||||||
|
|
||||||
|
@ -41,14 +41,6 @@ class VideoControlsDelegate {
|
||||||
fun attachPlayer(uri: Uri, videoPlayer: VideoPlayer?) {
|
fun attachPlayer(uri: Uri, videoPlayer: VideoPlayer?) {
|
||||||
playerSubject.onNext(Player(uri, videoPlayer))
|
playerSubject.onNext(Player(uri, videoPlayer))
|
||||||
|
|
||||||
if ((videoPlayer?.duration ?: -1L) > 0L) {
|
|
||||||
playerReadySignal.onNext(Unit)
|
|
||||||
} else {
|
|
||||||
videoPlayer?.setPlayerStateCallbacks {
|
|
||||||
playerReadySignal.onNext(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playWhenReady[uri] == true) {
|
if (playWhenReady[uri] == true) {
|
||||||
playWhenReady[uri] = false
|
playWhenReady[uri] = false
|
||||||
videoPlayer?.play()
|
videoPlayer?.play()
|
||||||
|
@ -64,8 +56,9 @@ class VideoControlsDelegate {
|
||||||
val videoPlayer: VideoPlayer? = null
|
val videoPlayer: VideoPlayer? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PlayerUpdate(
|
data class PlayerState(
|
||||||
val mediaUri: Uri,
|
val mediaUri: Uri,
|
||||||
|
val position: Long,
|
||||||
val duration: Long
|
val duration: Long
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.AvatarUtil
|
||||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
import org.thoughtcrime.securesms.util.fragments.requireListener
|
import org.thoughtcrime.securesms.util.fragments.requireListener
|
||||||
import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout
|
import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout
|
||||||
import org.thoughtcrime.securesms.util.visible
|
import org.thoughtcrime.securesms.util.visible
|
||||||
|
@ -173,6 +174,25 @@ class StoryViewerPageFragment : Fragment(R.layout.stories_viewer_fragment_page),
|
||||||
override fun onFinished() {
|
override fun onFinished() {
|
||||||
viewModel.goToNextPost()
|
viewModel.goToNextPost()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRequestSegmentProgressPercentage(): Float? {
|
||||||
|
val attachmentUri = if (viewModel.hasPost() && MediaUtil.isVideo(viewModel.getPost().attachment)) {
|
||||||
|
viewModel.getPost().attachment.uri
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (attachmentUri != null) {
|
||||||
|
val playerState = videoControlsDelegate.getPlayerState(attachmentUri)
|
||||||
|
if (playerState != null) {
|
||||||
|
playerState.position.toFloat() / playerState.duration
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedViewModel.isScrolling.observe(viewLifecycleOwner) { isScrolling ->
|
sharedViewModel.isScrolling.observe(viewLifecycleOwner) { isScrolling ->
|
||||||
|
@ -208,7 +228,7 @@ class StoryViewerPageFragment : Fragment(R.layout.stories_viewer_fragment_page),
|
||||||
|
|
||||||
val durations: Map<Int, Long> = state.posts
|
val durations: Map<Int, Long> = state.posts
|
||||||
.mapIndexed { index, storyPost ->
|
.mapIndexed { index, storyPost ->
|
||||||
index to (storyPost.attachment.uri?.let { state.durations[it] } ?: TimeUnit.SECONDS.toMillis(5))
|
index to if (MediaUtil.isVideo(storyPost.attachment)) -1L else TimeUnit.SECONDS.toMillis(5)
|
||||||
}
|
}
|
||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
|
@ -246,18 +266,17 @@ class StoryViewerPageFragment : Fragment(R.layout.stories_viewer_fragment_page),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleDisposable += videoControlsDelegate.playerUpdates.subscribe { update ->
|
|
||||||
if (update.duration > 0L) {
|
|
||||||
viewModel.setDuration(update.mediaUri, update.duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adjustConstraintsForScreenDimensions(viewsAndReplies, cardWrapper, card)
|
adjustConstraintsForScreenDimensions(viewsAndReplies, cardWrapper, card)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.setIsFragmentResumed(true)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
pauseProgress()
|
viewModel.setIsFragmentResumed(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinishForwardAction() = Unit
|
override fun onFinishForwardAction() = Unit
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.stories.viewer.page
|
package org.thoughtcrime.securesms.stories.viewer.page
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
|
|
||||||
data class StoryViewerPageState(
|
data class StoryViewerPageState(
|
||||||
val posts: List<StoryPost> = emptyList(),
|
val posts: List<StoryPost> = emptyList(),
|
||||||
val durations: Map<Uri, Long> = emptyMap(),
|
|
||||||
val selectedPostIndex: Int = 0,
|
val selectedPostIndex: Int = 0,
|
||||||
val replyState: ReplyState = ReplyState.NONE
|
val replyState: ReplyState = ReplyState.NONE
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.thoughtcrime.securesms.stories.viewer.page
|
package org.thoughtcrime.securesms.stories.viewer.page
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
@ -42,12 +41,6 @@ class StoryViewerPageViewModel(
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDuration(uri: Uri, duration: Long) {
|
|
||||||
store.update {
|
|
||||||
it.copy(durations = it.durations + (uri to duration))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
disposables += repository.getStoryPostsFor(recipientId).subscribe { posts ->
|
disposables += repository.getStoryPostsFor(recipientId).subscribe { posts ->
|
||||||
|
@ -117,6 +110,10 @@ class StoryViewerPageViewModel(
|
||||||
storyViewerDialogSubject.onNext(Optional.of(StoryViewerDialog.GroupDirectReply(recipientId, storyId)))
|
storyViewerDialogSubject.onNext(Optional.of(StoryViewerDialog.GroupDirectReply(recipientId, storyId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setIsFragmentResumed(isFragmentResumed: Boolean) {
|
||||||
|
storyViewerPlaybackStore.update { it.copy(isFragmentResumed = isFragmentResumed) }
|
||||||
|
}
|
||||||
|
|
||||||
fun setIsUserScrollingParent(isUserScrollingParent: Boolean) {
|
fun setIsUserScrollingParent(isUserScrollingParent: Boolean) {
|
||||||
storyViewerPlaybackStore.update { it.copy(isUserScrollingParent = isUserScrollingParent) }
|
storyViewerPlaybackStore.update { it.copy(isUserScrollingParent = isUserScrollingParent) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ data class StoryViewerPlaybackState(
|
||||||
val isDisplayingCaptionOverlay: Boolean = false,
|
val isDisplayingCaptionOverlay: Boolean = false,
|
||||||
val isUserScrollingParent: Boolean = false,
|
val isUserScrollingParent: Boolean = false,
|
||||||
val isSelectedPage: Boolean = false,
|
val isSelectedPage: Boolean = false,
|
||||||
val isDisplayingSlate: Boolean = false
|
val isDisplayingSlate: Boolean = false,
|
||||||
|
val isFragmentResumed: Boolean = false
|
||||||
) {
|
) {
|
||||||
val isPaused: Boolean = !areSegmentsInitialized ||
|
val isPaused: Boolean = !areSegmentsInitialized ||
|
||||||
isUserTouching ||
|
isUserTouching ||
|
||||||
|
@ -24,5 +25,6 @@ data class StoryViewerPlaybackState(
|
||||||
isDisplayingCaptionOverlay ||
|
isDisplayingCaptionOverlay ||
|
||||||
isUserScrollingParent ||
|
isUserScrollingParent ||
|
||||||
!isSelectedPage ||
|
!isSelectedPage ||
|
||||||
isDisplayingSlate
|
isDisplayingSlate ||
|
||||||
|
!isFragmentResumed
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue