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 477 additions and 314 deletions
...@@ -15,8 +15,6 @@ import org.futo.circles.feature.direct.timeline.list.holder.DmOtherVideoMessageV ...@@ -15,8 +15,6 @@ import org.futo.circles.feature.direct.timeline.list.holder.DmOtherVideoMessageV
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.feature.timeline.base.BaseTimelineAdapter import org.futo.circles.feature.timeline.base.BaseTimelineAdapter
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
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.TimelineLoadingViewHolder import org.futo.circles.feature.timeline.list.holder.TimelineLoadingViewHolder
private enum class DmTimelineViewType { private enum class DmTimelineViewType {
...@@ -55,23 +53,29 @@ class DMTimelineAdapter( ...@@ -55,23 +53,29 @@ class DMTimelineAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimelineListItemViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimelineListItemViewHolder {
return when (DmTimelineViewType.entries[viewType]) { return when (DmTimelineViewType.entries[viewType]) {
DmTimelineViewType.MY_TEXT -> DmMyTextMessageViewHolder(parent) DmTimelineViewType.MY_TEXT -> DmMyTextMessageViewHolder(parent, dmOptionsListener)
DmTimelineViewType.OTHER_TEXT -> DmOtherTextMessageViewHolder(parent) DmTimelineViewType.OTHER_TEXT -> DmOtherTextMessageViewHolder(parent, dmOptionsListener)
DmTimelineViewType.MY_IMAGE -> DmMyImageMessageViewHolder(parent) DmTimelineViewType.MY_IMAGE -> DmMyImageMessageViewHolder(parent, dmOptionsListener)
DmTimelineViewType.OTHER_IMAGE -> DmOtherImageMessageViewHolder(parent) DmTimelineViewType.OTHER_IMAGE -> DmOtherImageMessageViewHolder(
parent,
dmOptionsListener
)
DmTimelineViewType.MY_VIDEO -> DmMyVideoMessageViewHolder( DmTimelineViewType.MY_VIDEO -> DmMyVideoMessageViewHolder(
parent, parent,
dmOptionsListener,
videoPlayer, videoPlayer,
this this
) )
DmTimelineViewType.OTHER_VIDEO -> DmOtherVideoMessageViewHolder( DmTimelineViewType.OTHER_VIDEO -> DmOtherVideoMessageViewHolder(
parent, parent,
dmOptionsListener,
videoPlayer, videoPlayer,
this this
) )
DmTimelineViewType.OTHER -> DmNotMessageEventViewHolder(parent) DmTimelineViewType.OTHER -> DmNotMessageEventViewHolder(parent, dmOptionsListener)
DmTimelineViewType.LOADING -> TimelineLoadingViewHolder(parent) DmTimelineViewType.LOADING -> TimelineLoadingViewHolder(parent)
} }
} }
......
package org.futo.circles.feature.direct.timeline.list.holder
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.google.android.material.imageview.ShapeableImageView
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.databinding.ListItemMyImageDmBinding
import org.futo.circles.databinding.ListItemOtherImageDmBinding
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.feature.timeline.list.holder.ImageMediaViewHolder
import org.futo.circles.view.DmFooterView
abstract class DmImageMessageViewHolder(
view: View,
dmOptionsListener: DmOptionsListener
) : DmViewHolder(view, dmOptionsListener), ImageMediaViewHolder {
abstract val ivMediaContent: ImageView?
override fun bindHolderSpecific(post: Post) {
bindImage(post)
}
protected fun initListeners() {
setListeners()
ivMediaContent?.apply {
setOnClickListener {
post?.let { dmOptionsListener.onShowPreview(it.id) }
}
setOnLongClickListener {
post?.let { dmOptionsListener.onShowMenuClicked(it.id) }
true
}
}
}
private fun bindImage(post: Post) {
val content = post.content as? MediaContent ?: return
ivMediaContent?.let { bindMediaCover(content, it) }
}
}
class DmMyImageMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener
) : DmImageMessageViewHolder(
inflate(parent, ListItemMyImageDmBinding::inflate), dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyImageDmBinding
override val ivMediaContent: ImageView
get() = binding.ivMediaContent
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
init {
initListeners()
}
}
class DmOtherImageMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener
) : DmImageMessageViewHolder(
inflate(parent, ListItemOtherImageDmBinding::inflate), dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherImageDmBinding
override val ivMediaContent: ImageView
get() = binding.ivMediaContent
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
init {
initListeners()
}
}
\ 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
...@@ -7,11 +7,15 @@ import org.futo.circles.core.model.OtherEventContent ...@@ -7,11 +7,15 @@ import org.futo.circles.core.model.OtherEventContent
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.databinding.ListItemDmNotMessageEventBinding import org.futo.circles.databinding.ListItemDmNotMessageEventBinding
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import java.util.Date import java.util.Date
class DmNotMessageEventViewHolder(parent: ViewGroup) : TimelineListItemViewHolder( class DmNotMessageEventViewHolder(
parent: ViewGroup,
private val dmOptionsListener: DmOptionsListener
) : TimelineListItemViewHolder(
inflate(parent, ListItemDmNotMessageEventBinding::inflate) inflate(parent, ListItemDmNotMessageEventBinding::inflate)
) { ) {
...@@ -19,9 +23,18 @@ class DmNotMessageEventViewHolder(parent: ViewGroup) : TimelineListItemViewHolde ...@@ -19,9 +23,18 @@ class DmNotMessageEventViewHolder(parent: ViewGroup) : TimelineListItemViewHolde
private val binding = baseBinding as ListItemDmNotMessageEventBinding private val binding = baseBinding as ListItemDmNotMessageEventBinding
private var post: Post? = null
init {
itemView.setOnClickListener {
post?.let { dmOptionsListener.onShowMenuClicked(it.id) }
}
}
override fun bind(item: PostListItem) { override fun bind(item: PostListItem) {
val post = (item as? Post) ?: return val post = (item as? Post) ?: return
this.post = post
val content = (post.content as? OtherEventContent) ?: return val content = (post.content as? OtherEventContent) ?: return
binding.tvMessage.text = content.eventType binding.tvMessage.text = content.eventType
binding.tvTime.text = binding.tvTime.text =
......
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.holder
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.imageview.ShapeableImageView
import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.TextContent
import org.futo.circles.databinding.ListItemMyTextDmBinding
import org.futo.circles.databinding.ListItemOtherTextDmBinding
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.view.DmFooterView
abstract class DmTextMessageViewHolder(
view: View,
dmOptionsListener: DmOptionsListener
) : DmViewHolder(view, dmOptionsListener) {
abstract val tvMessage: TextView?
override fun bindHolderSpecific(post: Post) {
bindTextMessage(post)
}
private fun bindTextMessage(post: Post) {
val content = post.content as? TextContent ?: return
tvMessage?.setText(content.messageSpanned, TextView.BufferType.SPANNABLE)
}
}
class DmMyTextMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener
) : DmTextMessageViewHolder(
inflate(parent, ListItemMyTextDmBinding::inflate), dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyTextDmBinding
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
override val tvMessage: TextView
get() = binding.tvMessage
init {
setListeners()
handleLinkClick(binding.tvMessage)
}
}
class DmOtherTextMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener
) : DmTextMessageViewHolder(
inflate(parent, ListItemOtherTextDmBinding::inflate), dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherTextDmBinding
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
override val tvMessage: TextView
get() = binding.tvMessage
init {
setListeners()
handleLinkClick(binding.tvMessage)
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.view.View
import android.view.ViewGroup
import androidx.media3.exoplayer.ExoPlayer
import com.google.android.material.imageview.ShapeableImageView
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.databinding.LayoutPostVideoPlaybackBinding
import org.futo.circles.databinding.ListItemMyVideoDmBinding
import org.futo.circles.databinding.ListItemOtherVideoDmBinding
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.feature.timeline.list.OnVideoPlayBackStateListener
import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder
import org.futo.circles.view.DmFooterView
abstract class DmVideoMessageViewHolder(
view: View,
dmOptionsListener: DmOptionsListener
) : DmViewHolder(view, dmOptionsListener), VideoPlaybackViewHolder {
override fun bindHolderSpecific(post: Post) {
bindVideo(post)
}
protected fun initListeners() {
setListeners()
videoPlaybackLayout.ivFullScreen.setOnClickListener {
post?.let { dmOptionsListener.onShowPreview(it.id) }
}
videoPlaybackLayout.ivMediaContent.apply {
setOnClickListener {
val uri = (post?.content as? MediaContent)?.mediaFileData?.videoUri
playVideo(uri)
}
setOnLongClickListener {
post?.let { dmOptionsListener.onShowMenuClicked(it.id) }
true
}
}
videoPlaybackLayout.videoView.setOnClickListener { stopVideo() }
}
private fun bindVideo(post: Post) {
val content = post.content as? MediaContent ?: return
bindMediaCover(content, videoPlaybackLayout.ivMediaContent)
bindVideoView(content)
videoPlaybackLayout.tvDuration.text = content.mediaFileData.duration
}
}
class DmMyVideoMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener,
override val videoPlayer: ExoPlayer,
override val videoPlaybackListener: OnVideoPlayBackStateListener
) : DmVideoMessageViewHolder(
inflate(parent, ListItemMyVideoDmBinding::inflate),
dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemMyVideoDmBinding
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
override val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
get() = binding.videoPlaybackLayout
init {
initListeners()
}
}
class DmOtherVideoMessageViewHolder(
parent: ViewGroup,
dmOptionsListener: DmOptionsListener,
override val videoPlayer: ExoPlayer,
override val videoPlaybackListener: OnVideoPlayBackStateListener
) : DmVideoMessageViewHolder(
inflate(parent, ListItemOtherVideoDmBinding::inflate),
dmOptionsListener
) {
private companion object : ViewBindingHolder
private val binding = baseBinding as ListItemOtherVideoDmBinding
override val dmBackground: ShapeableImageView
get() = binding.ivBackground
override val dmFooter: DmFooterView
get() = binding.dmFooter
override val videoPlaybackLayout: LayoutPostVideoPlaybackBinding
get() = binding.videoPlaybackLayout
init {
initListeners()
}
}
\ No newline at end of file
package org.futo.circles.feature.direct.timeline.list.holder
import android.annotation.SuppressLint
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import com.google.android.material.imageview.ShapeableImageView
import org.futo.circles.core.base.list.context
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
import org.futo.circles.model.PostItemPayload
import org.futo.circles.view.DmFooterView
@SuppressLint("ClickableViewAccessibility")
abstract class DmViewHolder(
view: View,
protected val dmOptionsListener: DmOptionsListener
) : TimelineListItemViewHolder(view) {
abstract val dmBackground: ShapeableImageView?
abstract val dmFooter: DmFooterView?
abstract fun bindHolderSpecific(post: Post)
protected var post: Post? = null
private val gestureDetector =
GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
post?.let {
dmOptionsListener.onShowEmoji(it.id) { emoji ->
dmFooter?.addEmojiFromPickerLocalUpdate(emoji)
}
}
return true
}
override fun onLongPress(e: MotionEvent) {
post?.let {
dmOptionsListener.onShowMenuClicked(it.id)
}
}
override fun onDoubleTapEvent(e: MotionEvent) = true
override fun onDown(e: MotionEvent) = true
}).apply {
setIsLongpressEnabled(true)
}
override fun bind(item: PostListItem) {
(item as? Post)?.let { bindPost(item) }
}
protected fun setListeners() {
dmBackground?.setOnTouchListener { _, event ->
gestureDetector.onTouchEvent(event)
false
}
dmFooter?.setListener(dmOptionsListener)
}
private fun bindPost(post: Post) {
this.post = post
dmFooter?.setData(post)
bindHolderSpecific(post)
}
fun bindPayload(payload: PostItemPayload) {
dmFooter?.bindPayload(payload.reactions)
}
}
package org.futo.circles.feature.timeline.base package org.futo.circles.feature.timeline.base
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.futo.circles.R
import org.futo.circles.core.model.PostListItem import org.futo.circles.core.model.PostListItem
import org.futo.circles.feature.timeline.InternalLinkMovementMethod
import org.futo.circles.feature.timeline.list.OnLinkClickedListener
import org.matrix.android.sdk.api.extensions.tryOrNull
abstract class TimelineListItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { abstract class TimelineListItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
abstract fun bind(item: PostListItem) abstract fun bind(item: PostListItem)
@SuppressLint("ClickableViewAccessibility")
protected fun handleLinkClick(textView: TextView?) {
textView?.apply {
movementMethod = InternalLinkMovementMethod(object : OnLinkClickedListener {
override fun onLinkClicked(url: String) {
showLinkConfirmation(context, url)
}
})
setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) v.requestFocus()
false
}
}
}
private fun showLinkConfirmation(context: Context, url: String) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.do_you_want_to_open_this_url)
.setMessage(url)
.setPositiveButton(android.R.string.ok) { dialogInterface, _ ->
tryOrNull {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
dialogInterface.dismiss()
}
.setNegativeButton(android.R.string.cancel) { dialogInterface, _ ->
dialogInterface.dismiss()
}
.show()
}
} }
\ No newline at end of file
package org.futo.circles.feature.timeline.list.holder package org.futo.circles.feature.timeline.list.holder
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.GestureDetector import android.view.GestureDetector
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.futo.circles.R import org.futo.circles.R
import org.futo.circles.core.base.list.context import org.futo.circles.core.base.list.context
import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.core.feature.markdown.MarkdownParser
...@@ -18,16 +14,13 @@ import org.futo.circles.core.model.Post ...@@ -18,16 +14,13 @@ import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostContent 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.base.TimelineListItemViewHolder import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder
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
import org.futo.circles.view.PostFooterView import org.futo.circles.view.PostFooterView
import org.futo.circles.view.PostHeaderView import org.futo.circles.view.PostHeaderView
import org.futo.circles.view.PostStatusView import org.futo.circles.view.PostStatusView
import org.futo.circles.view.ReadMoreTextView import org.futo.circles.view.ReadMoreTextView
import org.matrix.android.sdk.api.extensions.tryOrNull
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
...@@ -84,7 +77,7 @@ abstract class PostViewHolder( ...@@ -84,7 +77,7 @@ abstract class PostViewHolder(
} }
postFooter?.setListener(optionsListener) postFooter?.setListener(optionsListener)
postHeader.setListener(optionsListener) postHeader.setListener(optionsListener)
handleLinkClick() readMoreTextView?.let { handleLinkClick(it) }
readMoreTextView?.apply { readMoreTextView?.apply {
setNotCollapsableClickAction { openReplies() } setNotCollapsableClickAction { openReplies() }
setOnLongClickListener { setOnLongClickListener {
...@@ -125,38 +118,6 @@ abstract class PostViewHolder( ...@@ -125,38 +118,6 @@ abstract class PostViewHolder(
else postLayout?.background = null else postLayout?.background = null
} }
@SuppressLint("ClickableViewAccessibility")
private fun handleLinkClick() {
readMoreTextView?.apply {
movementMethod = InternalLinkMovementMethod(object : OnLinkClickedListener {
override fun onLinkClicked(url: String) {
showLinkConfirmation(context, url)
}
})
setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) v.requestFocus()
false
}
}
}
private fun showLinkConfirmation(context: Context, url: String) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.do_you_want_to_open_this_url)
.setMessage(url)
.setPositiveButton(android.R.string.ok) { dialogInterface, _ ->
tryOrNull {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
dialogInterface.dismiss()
}
.setNegativeButton(android.R.string.cancel) { dialogInterface, _ ->
dialogInterface.dismiss()
}
.show()
}
private fun openReplies() { private fun openReplies() {
post?.let { optionsListener.onReply(it.postInfo.roomId, it.id) } post?.let { optionsListener.onReply(it.postInfo.roomId, it.id) }
} }
......
package org.futo.circles.view package org.futo.circles.view
import android.content.Context import android.content.Context
import android.text.format.DateFormat
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import org.futo.circles.core.extensions.setIsVisible
import org.futo.circles.core.model.Post import org.futo.circles.core.model.Post
import org.futo.circles.core.model.ReactionsData import org.futo.circles.core.model.ReactionsData
import org.futo.circles.databinding.ViewDmFooterBinding import org.futo.circles.databinding.ViewDmFooterBinding
import org.futo.circles.feature.timeline.list.PostOptionsListener import org.futo.circles.feature.direct.timeline.listeners.DmOptionsListener
import java.util.Date
class DmFooterView( class DmFooterView(
...@@ -18,20 +21,24 @@ class DmFooterView( ...@@ -18,20 +21,24 @@ class DmFooterView(
private val binding = private val binding =
ViewDmFooterBinding.inflate(LayoutInflater.from(context), this) ViewDmFooterBinding.inflate(LayoutInflater.from(context), this)
private var optionsListener: PostOptionsListener? = null private var optionsListener: DmOptionsListener? = null
private var post: Post? = null private var post: Post? = null
fun setData(data: Post, isThread: Boolean) { fun setListener(optionsListener: DmOptionsListener) {
this.optionsListener = optionsListener
}
fun setData(data: Post) {
post = data post = data
binding.tvTime.text =
DateFormat.format("MMM dd, h:mm a", Date(data.postInfo.getLastModifiedTimestamp()))
binding.tvEditedLabel.setIsVisible(data.postInfo.isEdited)
bindReactionsList(data.reactionsData) bindReactionsList(data.reactionsData)
} }
fun bindPayload(reactions: List<ReactionsData>, isEdited: Boolean, time: Long) { fun bindPayload(reactions: List<ReactionsData>) {
post = post?.copy( post = post?.copy(reactionsData = reactions)
postInfo = post!!.postInfo.copy(isEdited = isEdited, timestamp = time),
reactionsData = reactions
)
bindReactionsList(reactions) bindReactionsList(reactions)
} }
...@@ -44,7 +51,6 @@ class DmFooterView( ...@@ -44,7 +51,6 @@ class DmFooterView(
override fun onReactionChipClicked(emoji: String, isAddedByMe: Boolean) { override fun onReactionChipClicked(emoji: String, isAddedByMe: Boolean) {
post?.let { post?.let {
optionsListener?.onEmojiChipClicked( optionsListener?.onEmojiChipClicked(
it.postInfo.roomId,
it.id, it.id,
emoji, emoji,
isAddedByMe isAddedByMe
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="36dp" android:layout_marginStart="36dp"
...@@ -13,7 +12,7 @@ ...@@ -13,7 +12,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@color/chip_selected_color" android:background="@color/chip_selected_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime" app:layout_constraintBottom_toBottomOf="@id/dmFooter"
app:layout_constraintEnd_toEndOf="@id/ivMediaContent" app:layout_constraintEnd_toEndOf="@id/ivMediaContent"
app:layout_constraintStart_toStartOf="@id/ivMediaContent" app:layout_constraintStart_toStartOf="@id/ivMediaContent"
app:layout_constraintTop_toTopOf="@id/ivMediaContent" app:layout_constraintTop_toTopOf="@id/ivMediaContent"
...@@ -31,16 +30,15 @@ ...@@ -31,16 +30,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvTime" <org.futo.circles.view.DmFooterView
android:layout_width="0dp" android:id="@+id/dmFooter"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:paddingHorizontal="12dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ivMediaContent" app:layout_constraintTop_toBottomOf="@id/ivMediaContent" />
tools:text="sadasdasdasdasdasdasdasdasdasd" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@color/chip_selected_color" android:background="@color/chip_selected_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime" app:layout_constraintBottom_toBottomOf="@id/dmFooter"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/messageBarrier" app:layout_constraintStart_toStartOf="@id/messageBarrier"
app:layout_constraintTop_toTopOf="@id/tvMessage" app:layout_constraintTop_toTopOf="@id/tvMessage"
...@@ -34,27 +34,24 @@ ...@@ -34,27 +34,24 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Some long message" /> tools:text="Some long message" />
<TextView
android:id="@+id/tvTime" <org.futo.circles.view.DmFooterView
android:id="@+id/dmFooter"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="end"
android:lines="1"
android:paddingHorizontal="12dp" android:paddingHorizontal="12dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvMessage" app:layout_constraintTop_toBottomOf="@id/tvMessage" />
tools:text="Jul 17, 6:00 PM" />
<androidx.constraintlayout.widget.Barrier <androidx.constraintlayout.widget.Barrier
android:id="@+id/messageBarrier" android:id="@+id/messageBarrier"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="start" app:barrierDirection="start"
app:constraint_referenced_ids="tvMessage, tvTime" app:constraint_referenced_ids="tvMessage, dmFooter"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="36dp" android:layout_marginStart="36dp"
...@@ -13,7 +12,7 @@ ...@@ -13,7 +12,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@color/chip_selected_color" android:background="@color/chip_selected_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime" app:layout_constraintBottom_toBottomOf="@id/dmFooter"
app:layout_constraintEnd_toEndOf="@id/videoPlaybackLayout" app:layout_constraintEnd_toEndOf="@id/videoPlaybackLayout"
app:layout_constraintStart_toStartOf="@id/videoPlaybackLayout" app:layout_constraintStart_toStartOf="@id/videoPlaybackLayout"
app:layout_constraintTop_toTopOf="@id/videoPlaybackLayout" app:layout_constraintTop_toTopOf="@id/videoPlaybackLayout"
...@@ -30,16 +29,14 @@ ...@@ -30,16 +29,14 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <org.futo.circles.view.DmFooterView
android:id="@+id/tvTime" android:id="@+id/dmFooter"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:paddingHorizontal="12dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/videoPlaybackLayout" app:layout_constraintTop_toBottomOf="@id/videoPlaybackLayout" />
tools:text="sadasdasdasdasdasdasdasdasdasd" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
...@@ -13,7 +12,7 @@ ...@@ -13,7 +12,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@color/post_card_background_color" android:background="@color/post_card_background_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime" app:layout_constraintBottom_toBottomOf="@id/dmFooter"
app:layout_constraintEnd_toEndOf="@id/ivMediaContent" app:layout_constraintEnd_toEndOf="@id/ivMediaContent"
app:layout_constraintStart_toStartOf="@id/ivMediaContent" app:layout_constraintStart_toStartOf="@id/ivMediaContent"
app:layout_constraintTop_toTopOf="@id/ivMediaContent" app:layout_constraintTop_toTopOf="@id/ivMediaContent"
...@@ -30,16 +29,15 @@ ...@@ -30,16 +29,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvTime" <org.futo.circles.view.DmFooterView
android:layout_width="0dp" android:id="@+id/dmFooter"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:paddingHorizontal="12dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ivMediaContent" app:layout_constraintTop_toBottomOf="@id/ivMediaContent" />
tools:text="sadasdasdasdasdasdasdasdasdasd" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@color/post_card_background_color" android:background="@color/post_card_background_color"
app:layout_constraintBottom_toBottomOf="@id/tvTime" app:layout_constraintBottom_toBottomOf="@id/dmFooter"
app:layout_constraintEnd_toEndOf="@id/messageBarrier" app:layout_constraintEnd_toEndOf="@id/messageBarrier"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tvMessage" app:layout_constraintTop_toTopOf="@id/tvMessage"
...@@ -31,29 +31,25 @@ ...@@ -31,29 +31,25 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Some long message" /> tools:text="Some long message" />
<TextView
android:id="@+id/tvTime" <org.futo.circles.view.DmFooterView
android:id="@+id/dmFooter"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="end"
android:lines="1"
android:paddingHorizontal="12dp" android:paddingHorizontal="12dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1" app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvMessage" app:layout_constraintTop_toBottomOf="@id/tvMessage" />
tools:text="Jul 17, 6:00 PM" />
<androidx.constraintlayout.widget.Barrier <androidx.constraintlayout.widget.Barrier
android:id="@+id/messageBarrier" android:id="@+id/messageBarrier"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="end" app:barrierDirection="end"
app:constraint_referenced_ids="tvMessage, tvTime" app:constraint_referenced_ids="tvMessage, dmFooter"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
......