Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • circles/circles-android
1 result
Show changes
Commits on Source (7)
Showing
with 493 additions and 114 deletions
...@@ -5,9 +5,16 @@ import androidx.media3.exoplayer.ExoPlayer ...@@ -5,9 +5,16 @@ import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.core.model.Post import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostContentType import org.futo.circles.core.model.PostContentType
import org.futo.circles.core.model.TimelineLoadingItem import org.futo.circles.core.model.TimelineLoadingItem
import org.futo.circles.feature.direct.timeline.list.holder.DmMyImageMessageViewHolder
import org.futo.circles.feature.direct.timeline.list.holder.DmMyTextMessageViewHolder
import org.futo.circles.feature.direct.timeline.list.holder.DmMyVideoMessageViewHolder
import org.futo.circles.feature.direct.timeline.list.holder.DmOtherImageMessageViewHolder
import org.futo.circles.feature.direct.timeline.list.holder.DmOtherTextMessageViewHolder
import org.futo.circles.feature.direct.timeline.list.holder.DmOtherVideoMessageViewHolder
import org.futo.circles.feature.timeline.base.BaseTimelineAdapter
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.timeline.list.PostOptionsListener
import org.futo.circles.feature.timeline.list.holder.OtherEventPostViewHolder import org.futo.circles.feature.timeline.list.holder.OtherEventPostViewHolder
import org.futo.circles.feature.timeline.list.holder.PostListItemViewHolder
import org.futo.circles.feature.timeline.list.holder.TimelineLoadingViewHolder import org.futo.circles.feature.timeline.list.holder.TimelineLoadingViewHolder
private enum class DmTimelineViewType { private enum class DmTimelineViewType {
...@@ -19,8 +26,8 @@ private enum class DmTimelineViewType { ...@@ -19,8 +26,8 @@ private enum class DmTimelineViewType {
class DMTimelineAdapter( class DMTimelineAdapter(
private val postOptionsListener: PostOptionsListener, private val postOptionsListener: PostOptionsListener,
videoPlayer: ExoPlayer private val videoPlayer: ExoPlayer
) : BaseTimelineAdapter<PostListItemViewHolder>(videoPlayer) { ) : BaseTimelineAdapter() {
override fun getItemViewType(position: Int): Int = when (val item = getItem(position)) { override fun getItemViewType(position: Int): Int = when (val item = getItem(position)) {
...@@ -44,29 +51,24 @@ class DMTimelineAdapter( ...@@ -44,29 +51,24 @@ class DMTimelineAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostListItemViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimelineListItemViewHolder {
return when (DmTimelineViewType.entries[viewType]) { return when (DmTimelineViewType.entries[viewType]) {
DmTimelineViewType.MY_TEXT -> DmMyTextMessageViewHolder(parent)
DmTimelineViewType.OTHER_TEXT -> DmOtherTextMessageViewHolder(parent)
DmTimelineViewType.MY_IMAGE -> DmMyImageMessageViewHolder(parent)
DmTimelineViewType.OTHER_IMAGE -> DmOtherImageMessageViewHolder(parent)
DmTimelineViewType.MY_VIDEO -> DmMyVideoMessageViewHolder(
parent,
videoPlayer,
this
)
// TimelineViewType.TEXT -> TextPostViewHolder( DmTimelineViewType.OTHER_VIDEO -> DmOtherVideoMessageViewHolder(
// parent, postOptionsListener, isThread parent,
// ) videoPlayer,
// this
// TimelineViewType.IMAGE -> ImagePostViewHolder(parent, postOptionsListener, isThread) )
//
// TimelineViewType.VIDEO -> VideoPostViewHolder(
// parent,
// postOptionsListener,
// isThread,
// videoPlayer,
// this
// )
DmTimelineViewType.MY_TEXT -> TODO()
DmTimelineViewType.OTHER_TEXT -> TODO()
DmTimelineViewType.MY_IMAGE -> TODO()
DmTimelineViewType.OTHER_IMAGE -> TODO()
DmTimelineViewType.MY_VIDEO -> TODO()
DmTimelineViewType.OTHER_VIDEO -> TODO()
DmTimelineViewType.OTHER -> OtherEventPostViewHolder(parent, postOptionsListener) DmTimelineViewType.OTHER -> OtherEventPostViewHolder(parent, postOptionsListener)
DmTimelineViewType.LOADING -> TimelineLoadingViewHolder(parent) DmTimelineViewType.LOADING -> TimelineLoadingViewHolder(parent)
} }
......
package org.futo.circles.feature.direct.timeline.list
class DMTimelineViewHolder {
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.databinding.ListItemMyImageDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.holder.ImageMediaViewHolder
import java.util.Date
class DmMyImageMessageViewHolder(parent: ViewGroup) : TimelineListItemViewHolder(
inflate(parent, ListItemMyImageDmBinding::inflate)
), ImageMediaViewHolder {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyImageDmBinding
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? MediaContent ?: return
bindMediaCover(content, binding.ivMediaContent)
binding.tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import android.widget.TextView
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.core.model.TextContent
import org.futo.circles.databinding.ListItemMyTextDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import java.util.Date
class DmMyTextMessageViewHolder(parent: ViewGroup) : TimelineListItemViewHolder(
inflate(parent, ListItemMyTextDmBinding::inflate)
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyTextDmBinding
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? TextContent ?: return
binding.tvMessage.setText(content.messageSpanned, TextView.BufferType.SPANNABLE)
binding.tvTime.text = DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.databinding.LayoutPostVideoPlaybackBinding
import org.futo.circles.databinding.ListItemMyVideoDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder
import java.util.Date
class DmMyVideoMessageViewHolder(
parent: ViewGroup,
override val videoPlayer: ExoPlayer,
override val videoPlaybackListener: OnVideoPlayBackStateListener
) : TimelineListItemViewHolder(inflate(parent, ListItemMyVideoDmBinding::inflate)),
VideoPlaybackViewHolder {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyVideoDmBinding
override val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
get() = binding.videoPlaybackLayout
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? MediaContent ?: return
with(binding) {
bindMediaCover(content, videoPlaybackLayout.ivMediaContent)
bindVideoView(content)
videoPlaybackLayout.tvDuration.text = content.mediaFileData.duration
tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.databinding.ListItemOtherImageDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.holder.ImageMediaViewHolder
import java.util.Date
class DmOtherImageMessageViewHolder(parent: ViewGroup) : TimelineListItemViewHolder(
inflate(parent, ListItemOtherImageDmBinding::inflate)
), ImageMediaViewHolder {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherImageDmBinding
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? MediaContent ?: return
bindMediaCover(content, binding.ivMediaContent)
binding.tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import android.widget.TextView
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.core.model.TextContent
import org.futo.circles.databinding.ListItemOtherTextDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import java.util.Date
class DmOtherTextMessageViewHolder(parent: ViewGroup) : TimelineListItemViewHolder(
inflate(parent, ListItemOtherTextDmBinding::inflate)
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherTextDmBinding
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? TextContent ?: return
binding.tvMessage.setText(content.messageSpanned, TextView.BufferType.SPANNABLE)
binding.tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.text.format.DateFormat
import android.view.ViewGroup
import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.databinding.LayoutPostVideoPlaybackBinding
import org.futo.circles.databinding.ListItemOtherVideoDmBinding
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder
import java.util.Date
class DmOtherVideoMessageViewHolder(
parent: ViewGroup,
override val videoPlayer: ExoPlayer,
override val videoPlaybackListener: OnVideoPlayBackStateListener
) : TimelineListItemViewHolder(inflate(parent, ListItemOtherVideoDmBinding::inflate)),
VideoPlaybackViewHolder {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherVideoDmBinding
override val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
get() = binding.videoPlaybackLayout
override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return
val content = post.content as? MediaContent ?: return
with(binding) {
bindMediaCover(content, videoPlaybackLayout.ivMediaContent)
bindVideoView(content)
videoPlaybackLayout.tvDuration.text = content.mediaFileData.duration
tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(post.postInfo.getLastModifiedTimestamp()))
}
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list package org.futo.circles.feature.timeline.base
import androidx.media3.exoplayer.ExoPlayer
import androidx.recyclerview.widget.RecyclerView
import org.futo.circles.core.base.list.BaseRvAdapter import org.futo.circles.core.base.list.BaseRvAdapter
import org.futo.circles.core.model.Post import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem import org.futo.circles.core.model.PostListItem
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
import org.futo.circles.feature.timeline.list.holder.PostViewHolder import org.futo.circles.feature.timeline.list.holder.PostViewHolder
import org.futo.circles.feature.timeline.list.holder.VideoPostViewHolder import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder
import org.futo.circles.model.PostItemPayload import org.futo.circles.model.PostItemPayload
abstract class BaseTimelineAdapter<VH : RecyclerView.ViewHolder>( abstract class BaseTimelineAdapter : BaseRvAdapter<PostListItem, TimelineListItemViewHolder>(
private val videoPlayer: ExoPlayer
) : BaseRvAdapter<PostListItem, VH>(
PayloadIdEntityCallback { old, new -> PayloadIdEntityCallback { old, new ->
if (new is Post && old is Post) if (new is Post && old is Post)
PostItemPayload( PostItemPayload(
...@@ -24,16 +20,16 @@ abstract class BaseTimelineAdapter<VH : RecyclerView.ViewHolder>( ...@@ -24,16 +20,16 @@ abstract class BaseTimelineAdapter<VH : RecyclerView.ViewHolder>(
else null else null
}), OnVideoPlayBackStateListener { }), OnVideoPlayBackStateListener {
private var currentPlayingVideoHolder: VideoPostViewHolder? = null private var currentPlayingVideoHolder: VideoPlaybackViewHolder? = null
override fun getItemId(position: Int): Long = getItem(position).id.hashCode().toLong() override fun getItemId(position: Int): Long = getItem(position).id.hashCode().toLong()
override fun onBindViewHolder(holder: VH, position: Int) { override fun onBindViewHolder(holder: TimelineListItemViewHolder, position: Int) {
holder.bind(getItem(position)) holder.bind(getItem(position))
} }
override fun onBindViewHolder( override fun onBindViewHolder(
holder: VH, holder: TimelineListItemViewHolder,
position: Int, position: Int,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
...@@ -53,12 +49,12 @@ abstract class BaseTimelineAdapter<VH : RecyclerView.ViewHolder>( ...@@ -53,12 +49,12 @@ abstract class BaseTimelineAdapter<VH : RecyclerView.ViewHolder>(
} }
} }
override fun onViewDetachedFromWindow(holder: VH) { override fun onViewDetachedFromWindow(holder: TimelineListItemViewHolder) {
super.onViewDetachedFromWindow(holder) super.onViewDetachedFromWindow(holder)
if (holder == currentPlayingVideoHolder) stopVideoPlayback() if (holder == currentPlayingVideoHolder) stopVideoPlayback()
} }
override fun onVideoPlaybackStateChanged(holder: VideoPostViewHolder, isPlaying: Boolean) { override fun onVideoPlaybackStateChanged(holder: VideoPlaybackViewHolder, isPlaying: Boolean) {
currentPlayingVideoHolder = if (isPlaying) { currentPlayingVideoHolder = if (isPlaying) {
stopVideoPlayback(false) stopVideoPlayback(false)
holder holder
......
package org.futo.circles.feature.timeline.list.holder package org.futo.circles.feature.timeline.base
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.futo.circles.core.model.PostListItem import org.futo.circles.core.model.PostListItem
abstract class PostListItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { abstract class TimelineListItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
abstract fun bind(item: PostListItem) abstract fun bind(item: PostListItem)
......
package org.futo.circles.feature.timeline.list package org.futo.circles.feature.timeline.list
import org.futo.circles.feature.timeline.list.holder.VideoPostViewHolder import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder
interface OnVideoPlayBackStateListener { interface OnVideoPlayBackStateListener {
fun onVideoPlaybackStateChanged(holder: VideoPostViewHolder, isPlaying: Boolean) fun onVideoPlaybackStateChanged(holder: VideoPlaybackViewHolder, isPlaying: Boolean)
} }
\ No newline at end of file
...@@ -5,11 +5,11 @@ import androidx.media3.exoplayer.ExoPlayer ...@@ -5,11 +5,11 @@ import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.core.model.Post import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostContentType import org.futo.circles.core.model.PostContentType
import org.futo.circles.core.model.TimelineLoadingItem import org.futo.circles.core.model.TimelineLoadingItem
import org.futo.circles.feature.direct.timeline.list.BaseTimelineAdapter import org.futo.circles.feature.timeline.base.BaseTimelineAdapter
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.holder.ImagePostViewHolder import org.futo.circles.feature.timeline.list.holder.ImagePostViewHolder
import org.futo.circles.feature.timeline.list.holder.OtherEventPostViewHolder import org.futo.circles.feature.timeline.list.holder.OtherEventPostViewHolder
import org.futo.circles.feature.timeline.list.holder.PollPostViewHolder import org.futo.circles.feature.timeline.list.holder.PollPostViewHolder
import org.futo.circles.feature.timeline.list.holder.PostListItemViewHolder
import org.futo.circles.feature.timeline.list.holder.TextPostViewHolder import org.futo.circles.feature.timeline.list.holder.TextPostViewHolder
import org.futo.circles.feature.timeline.list.holder.TimelineLoadingViewHolder import org.futo.circles.feature.timeline.list.holder.TimelineLoadingViewHolder
import org.futo.circles.feature.timeline.list.holder.VideoPostViewHolder import org.futo.circles.feature.timeline.list.holder.VideoPostViewHolder
...@@ -20,7 +20,7 @@ class TimelineAdapter( ...@@ -20,7 +20,7 @@ class TimelineAdapter(
private val postOptionsListener: PostOptionsListener, private val postOptionsListener: PostOptionsListener,
private val isThread: Boolean, private val isThread: Boolean,
private val videoPlayer: ExoPlayer private val videoPlayer: ExoPlayer
) : BaseTimelineAdapter<PostListItemViewHolder>(videoPlayer) { ) : BaseTimelineAdapter() {
override fun getItemViewType(position: Int): Int = when (val item = getItem(position)) { override fun getItemViewType(position: Int): Int = when (val item = getItem(position)) {
...@@ -35,7 +35,7 @@ class TimelineAdapter( ...@@ -35,7 +35,7 @@ class TimelineAdapter(
is TimelineLoadingItem -> TimelineViewType.LOADING.ordinal is TimelineLoadingItem -> TimelineViewType.LOADING.ordinal
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostListItemViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimelineListItemViewHolder {
return when (TimelineViewType.entries[viewType]) { return when (TimelineViewType.entries[viewType]) {
TimelineViewType.TEXT -> TextPostViewHolder( TimelineViewType.TEXT -> TextPostViewHolder(
......
...@@ -7,7 +7,7 @@ import org.futo.circles.core.extensions.loadEncryptedThumbOrFullIntoWithAspect ...@@ -7,7 +7,7 @@ import org.futo.circles.core.extensions.loadEncryptedThumbOrFullIntoWithAspect
import org.futo.circles.core.extensions.setIsVisible import org.futo.circles.core.extensions.setIsVisible
import org.futo.circles.core.model.MediaContent import org.futo.circles.core.model.MediaContent
interface MediaViewHolder { interface ImageMediaViewHolder {
fun bindMediaCaption(content: MediaContent, textView: TextView) { fun bindMediaCaption(content: MediaContent, textView: TextView) {
textView.apply { textView.apply {
......
...@@ -16,7 +16,7 @@ class ImagePostViewHolder( ...@@ -16,7 +16,7 @@ class ImagePostViewHolder(
postOptionsListener: PostOptionsListener, postOptionsListener: PostOptionsListener,
isThread: Boolean isThread: Boolean
) : PostViewHolder(inflate(parent, ViewImagePostBinding::inflate), postOptionsListener, isThread), ) : PostViewHolder(inflate(parent, ViewImagePostBinding::inflate), postOptionsListener, isThread),
MediaViewHolder { ImageMediaViewHolder {
private companion object : ViewBindingHolder private companion object : ViewBindingHolder
......
...@@ -19,6 +19,7 @@ import org.futo.circles.core.model.PostContent ...@@ -19,6 +19,7 @@ import org.futo.circles.core.model.PostContent
import org.futo.circles.core.model.PostListItem import org.futo.circles.core.model.PostListItem
import org.futo.circles.core.model.TextContent import org.futo.circles.core.model.TextContent
import org.futo.circles.feature.timeline.InternalLinkMovementMethod import org.futo.circles.feature.timeline.InternalLinkMovementMethod
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.feature.timeline.list.OnLinkClickedListener import org.futo.circles.feature.timeline.list.OnLinkClickedListener
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.timeline.list.PostOptionsListener
import org.futo.circles.model.PostItemPayload import org.futo.circles.model.PostItemPayload
...@@ -34,7 +35,7 @@ abstract class PostViewHolder( ...@@ -34,7 +35,7 @@ abstract class PostViewHolder(
view: View, view: View,
protected val optionsListener: PostOptionsListener, protected val optionsListener: PostOptionsListener,
private val isThread: Boolean private val isThread: Boolean
) : PostListItemViewHolder(view) { ) : TimelineListItemViewHolder(view) {
abstract val postLayout: ViewGroup? abstract val postLayout: ViewGroup?
abstract val postFooter: PostFooterView? abstract val postFooter: PostFooterView?
......
...@@ -5,10 +5,11 @@ import org.futo.circles.core.base.list.ViewBindingHolder ...@@ -5,10 +5,11 @@ import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.databinding.ListItemTimelineLoadingBinding import org.futo.circles.core.databinding.ListItemTimelineLoadingBinding
import org.futo.circles.core.model.PostListItem import org.futo.circles.core.model.PostListItem
import org.futo.circles.core.model.TimelineLoadingItem import org.futo.circles.core.model.TimelineLoadingItem
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
class TimelineLoadingViewHolder( class TimelineLoadingViewHolder(
parent: ViewGroup, parent: ViewGroup,
) : PostListItemViewHolder(inflate(parent, ListItemTimelineLoadingBinding::inflate)) { ) : TimelineListItemViewHolder(inflate(parent, ListItemTimelineLoadingBinding::inflate)) {
private companion object : ViewBindingHolder private companion object : ViewBindingHolder
......
package org.futo.circles.feature.timeline.list.holder
import android.net.Uri
import androidx.core.view.updateLayoutParams
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.R
import org.futo.circles.core.extensions.gone
import org.futo.circles.core.extensions.invisible
import org.futo.circles.core.extensions.visible
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.ResLoadingData
import org.futo.circles.databinding.LayoutPostVideoPlaybackBinding
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
interface VideoPlaybackViewHolder : ImageMediaViewHolder {
val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
val videoPlayer: ExoPlayer
val videoPlaybackListener: OnVideoPlayBackStateListener
fun bindVideoView(content: MediaContent) {
with(videoPlaybackLayout.videoView) {
player = null
post {
val size = content.calculateThumbnailSize(width)
updateLayoutParams {
width = size.width
height = size.height
}
}
}
}
fun playVideo(uri: Uri?) {
uri?.let {
with(videoPlaybackLayout) {
tvDuration.gone()
videoView.visible()
ivVideoIndicator.gone()
ivMediaContent.gone()
}
videoPlaybackListener.onVideoPlaybackStateChanged(this, true)
videoPlaybackLayout.videoView.player = videoPlayer
videoPlayer.setMediaItem(MediaItem.fromUri(it))
videoPlayer.prepare()
videoPlayer.play()
} ?: run {
videoPlaybackLayout.vLoadingView.apply {
visible()
setProgress(ResLoadingData(R.string.downloading))
}
}
}
fun stopVideo(shouldNotify: Boolean = true) {
if (shouldNotify) videoPlaybackListener.onVideoPlaybackStateChanged(this, false)
videoPlayer.stop()
with(videoPlaybackLayout) {
videoView.player = null
tvDuration.visible()
ivVideoIndicator.visible()
ivMediaContent.visible()
videoView.invisible()
}
}
}
\ No newline at end of file
package org.futo.circles.feature.timeline.list.holder package org.futo.circles.feature.timeline.list.holder
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.updateLayoutParams
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import org.futo.circles.R
import org.futo.circles.core.base.list.ViewBindingHolder import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.extensions.gone
import org.futo.circles.core.extensions.invisible
import org.futo.circles.core.extensions.visible
import org.futo.circles.core.model.ResLoadingData
import org.futo.circles.core.model.MediaContent import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.Post import org.futo.circles.core.model.Post
import org.futo.circles.databinding.LayoutPostVideoPlaybackBinding
import org.futo.circles.databinding.ViewVideoPostBinding import org.futo.circles.databinding.ViewVideoPostBinding
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.timeline.list.PostOptionsListener
...@@ -24,10 +18,10 @@ class VideoPostViewHolder( ...@@ -24,10 +18,10 @@ class VideoPostViewHolder(
parent: ViewGroup, parent: ViewGroup,
postOptionsListener: PostOptionsListener, postOptionsListener: PostOptionsListener,
isThread: Boolean, isThread: Boolean,
private val videoPlayer: ExoPlayer, override val videoPlayer: ExoPlayer,
private val videoPlaybackListener: OnVideoPlayBackStateListener override val videoPlaybackListener: OnVideoPlayBackStateListener
) : PostViewHolder(inflate(parent, ViewVideoPostBinding::inflate), postOptionsListener, isThread), ) : PostViewHolder(inflate(parent, ViewVideoPostBinding::inflate), postOptionsListener, isThread),
MediaViewHolder { VideoPlaybackViewHolder {
private companion object : ViewBindingHolder private companion object : ViewBindingHolder
...@@ -42,77 +36,36 @@ class VideoPostViewHolder( ...@@ -42,77 +36,36 @@ class VideoPostViewHolder(
get() = binding.vPostStatus get() = binding.vPostStatus
override val readMoreTextView: ReadMoreTextView override val readMoreTextView: ReadMoreTextView
get() = binding.tvTextContent get() = binding.tvTextContent
override val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
get() = binding.videoPlaybackLayout
init { init {
setListeners() setListeners()
binding.ivFullScreen.setOnClickListener { binding.videoPlaybackLayout.ivFullScreen.setOnClickListener {
post?.let { optionsListener.onShowPreview(it.postInfo.roomId, it.id) } post?.let { optionsListener.onShowPreview(it.postInfo.roomId, it.id) }
} }
binding.ivMediaContent.apply {
setOnClickListener { playVideo() } binding.videoPlaybackLayout.ivMediaContent.apply {
setOnClickListener {
val uri = (post?.content as? MediaContent)?.mediaFileData?.videoUri
playVideo(uri)
}
setOnLongClickListener { setOnLongClickListener {
postHeader.showMenu() postHeader.showMenu()
true true
} }
} }
binding.videoView.setOnClickListener { stopVideo() } binding.videoPlaybackLayout.videoView.setOnClickListener { stopVideo() }
} }
override fun bindHolderSpecific(post: Post) { override fun bindHolderSpecific(post: Post) {
with(binding) { with(binding) {
val content = (post.content as? MediaContent) ?: return val content = (post.content as? MediaContent) ?: return
bindMediaCaption(content, tvTextContent) bindMediaCaption(content, tvTextContent)
bindMediaCover(content, ivMediaContent) bindMediaCover(content, videoPlaybackLayout.ivMediaContent)
bindVideoView(content) bindVideoView(content)
tvDuration.text = content.mediaFileData.duration videoPlaybackLayout.tvDuration.text = content.mediaFileData.duration
}
}
private fun bindVideoView(content: MediaContent) {
with(binding) {
videoView.player = null
videoView.post {
val size = content.calculateThumbnailSize(videoView.width)
videoView.updateLayoutParams {
width = size.width
height = size.height
}
}
}
}
private fun playVideo() {
val uri = (post?.content as? MediaContent)?.mediaFileData?.videoUri
uri?.let {
with(binding) {
tvDuration.gone()
videoView.visible()
ivVideoIndicator.gone()
ivMediaContent.gone()
}
videoPlaybackListener.onVideoPlaybackStateChanged(this, true)
binding.videoView.player = videoPlayer
videoPlayer.setMediaItem(MediaItem.fromUri(it))
videoPlayer.prepare()
videoPlayer.play()
} ?: run {
binding.vLoadingView.apply {
visible()
setProgress(ResLoadingData(R.string.downloading))
}
}
}
fun stopVideo(shouldNotify: Boolean = true) {
if (shouldNotify) videoPlaybackListener.onVideoPlaybackStateChanged(this, false)
videoPlayer.stop()
with(binding) {
videoView.player = null
tvDuration.visible()
ivVideoIndicator.visible()
ivMediaContent.visible()
videoView.invisible()
} }
} }
} }
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<androidx.media3.ui.PlayerView
android:id="@+id/videoView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/media_post_background"
android:foreground="?attr/selectableItemBackground"
android:scaleType="fitCenter"
android:soundEffectsEnabled="true"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:resize_mode="fit"
app:shutter_background_color="@android:color/transparent"
app:surface_type="texture_view"
app:use_controller="false"
tools:minHeight="400dp"
tools:visibility="visible" />
<ImageView
android:id="@+id/ivMediaContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@color/media_post_background"
android:foreground="?attr/selectableItemBackground"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/ivVideoIndicator"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:background="@color/white"
android:src="@drawable/ic_play_round"
app:layout_constraintBottom_toBottomOf="@id/videoView"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toEndOf="@id/videoView"
app:layout_constraintStart_toStartOf="@id/videoView"
app:layout_constraintTop_toTopOf="@id/videoView"
app:layout_constraintWidth_percent="0.15"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.CornerSize50Percent"
app:tint="@color/blue" />
<TextView
android:id="@+id/tvDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="4dp"
android:shadowColor="@color/black"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="2"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@+id/videoView"
app:layout_constraintStart_toStartOf="@id/videoView"
tools:text="00:00" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/ivFullScreen"
android:layout_width="45dp"
android:layout_height="45dp"
android:background="@color/background_dim"
android:clickable="true"
android:focusable="true"
android:foreground="?selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/ic_fullscreen"
app:contentPadding="2dp"
app:layout_constraintBottom_toBottomOf="@+id/videoView"
app:layout_constraintEnd_toEndOf="@id/videoView"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.CornerSize50Percent"
app:tint="@color/white" />
<org.futo.circles.core.view.LoadingView
android:id="@+id/vLoadingView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="16dp"
android:background="@color/white"
android:padding="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/videoView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/videoView"
tools:visibility="visible" />
</merge>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/ivBackground"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/chip_selected_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime"
app:layout_constraintEnd_toEndOf="@id/ivMediaContent"
app:layout_constraintStart_toStartOf="@id/ivMediaContent"
app:layout_constraintTop_toTopOf="@id/ivMediaContent"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Message.My" />
<ImageView
android:id="@+id/ivMediaContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/media_post_background"
android:foreground="?attr/selectableItemBackground"
android:padding="8dp"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ivMediaContent"
tools:text="sadasdasdasdasdasdasdasdasdasd" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file