|
|
@@ -0,0 +1,268 @@
|
|
|
+package com.adealink.weparty.im.session.adapter
|
|
|
+
|
|
|
+import androidx.recyclerview.widget.RecyclerView
|
|
|
+import com.adealink.weparty.commonui.recycleview.adapter.ExtMultiTypeAdapter
|
|
|
+import com.adealink.weparty.im.session.adapter.data.UnSupportMessageBean
|
|
|
+import com.adealink.weparty.im.session.adapter.viewbinder.ImageMessageViewBinder
|
|
|
+import com.adealink.weparty.im.session.adapter.viewbinder.SoundMessageViewBinder
|
|
|
+import com.adealink.weparty.im.session.adapter.viewbinder.TextMessageViewBinder
|
|
|
+import com.adealink.weparty.im.session.adapter.viewbinder.TipsMessageViewBinder
|
|
|
+import com.adealink.weparty.im.session.adapter.viewbinder.UnSupportMessageViewBinder
|
|
|
+import com.adealink.weparty.im.session.widget.MessageRecyclerView
|
|
|
+import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean
|
|
|
+import com.tencent.qcloud.tuikit.timcommon.component.highlight.HighlightPresenter
|
|
|
+import com.tencent.qcloud.tuikit.timcommon.interfaces.ICommonMessageAdapter
|
|
|
+import com.tencent.qcloud.tuikit.timcommon.interfaces.UserFaceUrlCache
|
|
|
+import com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message.MessageBaseHolder
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.bean.message.ImageMessageBean
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.bean.message.SoundMessageBean
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.bean.message.TextMessageBean
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.bean.message.TipsMessageBean
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.interfaces.IMessageAdapter
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.interfaces.IMessageRecyclerView
|
|
|
+
|
|
|
+class SessionAdapter : ExtMultiTypeAdapter(), IMessageAdapter, ICommonMessageAdapter {
|
|
|
+
|
|
|
+ companion object {
|
|
|
+ private const val ITEM_POSITION_UNKNOWN: Int = -1
|
|
|
+ }
|
|
|
+
|
|
|
+ private var mLoading: Boolean = true
|
|
|
+ private var mRecycleView: MessageRecyclerView? = null
|
|
|
+
|
|
|
+ private val supportMessageTypes = setOf(
|
|
|
+ TextMessageBean::class.java,
|
|
|
+ ImageMessageBean::class.java,
|
|
|
+ SoundMessageBean::class.java,
|
|
|
+ TipsMessageBean::class.java,
|
|
|
+ UnSupportMessageBean::class.java,
|
|
|
+ )
|
|
|
+
|
|
|
+ init {
|
|
|
+ register(TextMessageBean::class.java, TextMessageViewBinder())
|
|
|
+ register(ImageMessageBean::class.java, ImageMessageViewBinder())
|
|
|
+ register(SoundMessageBean::class.java, SoundMessageViewBinder())
|
|
|
+ register(TipsMessageBean::class.java, TipsMessageViewBinder())
|
|
|
+ register(UnSupportMessageBean::class.java, UnSupportMessageViewBinder())
|
|
|
+
|
|
|
+
|
|
|
+// addMessageType(FaceMessageBean::class.java, FaceMessageHolder::class.java)
|
|
|
+// addMessageType(FileMessageBean::class.java, FileMessageHolder::class.java)
|
|
|
+// addMessageType(LocationMessageBean::class.java, LocationMessageHolder::class.java)
|
|
|
+// addMessageType(MergeMessageBean::class.java, MergeMessageHolder::class.java)
|
|
|
+// addMessageType(TextAtMessageBean::class.java, TextMessageHolder::class.java)
|
|
|
+// addMessageType(VideoMessageBean::class.java, VideoMessageHolder::class.java)
|
|
|
+// addMessageType(ReplyMessageBean::class.java, ReplyMessageHolder::class.java)
|
|
|
+// addMessageType(QuoteMessageBean::class.java, QuoteMessageHolder::class.java)
|
|
|
+// addMessageType(CallingMessageBean::class.java, CallingMessageHolder::class.java)
|
|
|
+// addMessageType(CallingTipsMessageBean::class.java, TipsMessageHolder::class.java, true)
|
|
|
+// addMessageType(CustomLinkMessageBean::class.java, CustomLinkMessageHolder::class.java)
|
|
|
+// addMessageType(
|
|
|
+// CustomEvaluationMessageBean::class.java,
|
|
|
+// CustomEvaluationMessageHolder::class.java
|
|
|
+// )
|
|
|
+// addMessageType(CustomOrderMessageBean::class.java, CustomOrderMessageHolder::class.java)
|
|
|
+// addMessageType(MessageTypingBean::class.java, null)
|
|
|
+// addMessageType(EmptyMessageBean::class.java, EmptyMessageHolder::class.java, true)
|
|
|
+// addMessageType(ChatbotMessageBean::class.java, ChatbotMessageHolder::class.java)
|
|
|
+// addMessageType(
|
|
|
+// ChatbotPlaceholderMessageBean::class.java,
|
|
|
+// ChatbotPlaceholderMessageHolder::class.java
|
|
|
+// )
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
|
|
+ super.onAttachedToRecyclerView(recyclerView)
|
|
|
+ mRecycleView = recyclerView as MessageRecyclerView
|
|
|
+ recyclerView.setItemViewCacheSize(5)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
|
|
+ // TODO
|
|
|
+ if (holder is MessageBaseHolder) {
|
|
|
+ holder.setMessageBubbleBackground(null)
|
|
|
+ holder.onRecycled()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun showLoading() {
|
|
|
+ if (mLoading) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mLoading = true
|
|
|
+ notifyItemChanged(0)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun refreshLoadView() {
|
|
|
+ notifyItemChanged(0)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDataSourceChanged(dataSource: List<TUIMessageBean?>?) {
|
|
|
+ val itemList = mutableListOf<TUIMessageBean>()
|
|
|
+ // TODO: 添加一个官方消息
|
|
|
+ dataSource?.forEach { data ->
|
|
|
+ data ?: return@forEach
|
|
|
+ if (supportMessageTypes.contains(data.javaClass)) {
|
|
|
+ itemList.add(data)
|
|
|
+ } else {
|
|
|
+ itemList.add(UnSupportMessageBean(data))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ items = itemList
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onViewNeedRefresh(
|
|
|
+ type: Int,
|
|
|
+ locateMessage: TUIMessageBean?
|
|
|
+ ) {
|
|
|
+ mLoading = false
|
|
|
+ refreshLoadView()
|
|
|
+ if (type == IMessageRecyclerView.DATA_CHANGE_LOCATE_TO_POSITION) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ val position: Int = getMessagePosition(locateMessage)
|
|
|
+ if (position == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mRecycleView?.smoothScrollToPosition(position)
|
|
|
+ locateMessage?.id?.let { id ->
|
|
|
+ HighlightPresenter.startHighlight(id)
|
|
|
+ }
|
|
|
+ } else if (type == IMessageRecyclerView.SCROLL_TO_POSITION) {
|
|
|
+ val position: Int = getMessagePosition(locateMessage)
|
|
|
+ if (position == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mRecycleView?.smoothScrollToPosition(position)
|
|
|
+ locateMessage?.id?.let { id ->
|
|
|
+ HighlightPresenter.startHighlight(id)
|
|
|
+ }
|
|
|
+ mRecycleView?.scrollMessageFinish()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_SCROLL_TO_POSITION) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ val position: Int = getMessagePosition(locateMessage)
|
|
|
+ if (position == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mRecycleView?.scrollToEnd()
|
|
|
+ mRecycleView?.smoothScrollToPosition(position)
|
|
|
+ locateMessage?.id?.let { id ->
|
|
|
+ HighlightPresenter.startHighlight(id)
|
|
|
+ }
|
|
|
+ mRecycleView?.scrollMessageFinish()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE) {
|
|
|
+ val position: Int = getMessagePosition(locateMessage)
|
|
|
+ if (position == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ onItemChanged(position)
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_SCROLL_TO_POSITION_WITHOUT_HIGH_LIGHT) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ val position: Int = getMessagePosition(locateMessage)
|
|
|
+ if (position == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mRecycleView?.smoothScrollToPosition(position)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onViewNeedRefresh(type: Int, value: Int) {
|
|
|
+ mLoading = false
|
|
|
+ if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_REFRESH) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ mRecycleView?.scrollToEnd()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_ADD_BACK) {
|
|
|
+ onItemInsert(items.size + 1, value)
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_NEW_MESSAGE) {
|
|
|
+ onItemInsert(items.size + 1, value)
|
|
|
+ mRecycleView?.onMsgAddBack()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_ADD_FRONT) {
|
|
|
+ // The number of loaded entries is 0, only the animation is updated
|
|
|
+ if (value != 0) {
|
|
|
+ // During the loading process, it is possible that the time interval between the first item before
|
|
|
+ // and the last item newly loaded is not more than 5 minutes, and the time entry needs to be removed,
|
|
|
+ // so the refresh here needs one more entry
|
|
|
+ onItemInsert(0, value)
|
|
|
+ }
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_LOAD) {
|
|
|
+ notifyDataSetChanged()
|
|
|
+ mRecycleView?.scrollToEnd()
|
|
|
+ mRecycleView?.loadMessageFinish()
|
|
|
+ } else if (type == IMessageRecyclerView.DATA_CHANGE_TYPE_DELETE) {
|
|
|
+ if (value == ITEM_POSITION_UNKNOWN) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ onItemRemove(value)
|
|
|
+ }
|
|
|
+ refreshLoadView()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private fun getMessagePosition(message: TUIMessageBean?): Int {
|
|
|
+ message ?: return ITEM_POSITION_UNKNOWN
|
|
|
+ var position = ITEM_POSITION_UNKNOWN
|
|
|
+ if (items.isEmpty()) {
|
|
|
+ return position
|
|
|
+ }
|
|
|
+
|
|
|
+ position = items.indexOfFirst { it == message }
|
|
|
+ if (position == -1) {
|
|
|
+ return ITEM_POSITION_UNKNOWN
|
|
|
+ }
|
|
|
+ return position
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getItem(position: Int): TUIMessageBean? {
|
|
|
+ return items.getOrNull(position) as? TUIMessageBean
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getFirstMessageBean(): TUIMessageBean? {
|
|
|
+ return items.firstOrNull() as? TUIMessageBean
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getLastMessageBean(): TUIMessageBean? {
|
|
|
+ return items.lastOrNull() as? TUIMessageBean
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onItemRefresh(messageBean: TUIMessageBean?) {
|
|
|
+ onViewNeedRefresh(IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE, messageBean)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun onItemChanged(position: Int) {
|
|
|
+ var start = position - 1
|
|
|
+ var end = position + 1
|
|
|
+ if (start < 0) {
|
|
|
+ start = 0
|
|
|
+ }
|
|
|
+ if (end > getItemCount()) {
|
|
|
+ end = position
|
|
|
+ }
|
|
|
+ val count = end - start
|
|
|
+ notifyItemRangeChanged(start, count)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun onItemInsert(start: Int, count: Int) {
|
|
|
+ notifyItemRangeInserted(start, count)
|
|
|
+ val startTemp = start - 2
|
|
|
+ val endTemp = start + count + 2
|
|
|
+ notifyItemRangeChanged(startTemp, endTemp - startTemp)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun onItemRemove(position: Int) {
|
|
|
+ val start = position - 1
|
|
|
+ val end = position + 1
|
|
|
+ if (start >= 0) {
|
|
|
+ notifyItemChanged(start)
|
|
|
+ }
|
|
|
+ if (end <= getItemCount()) {
|
|
|
+ notifyItemChanged(end)
|
|
|
+ }
|
|
|
+ notifyItemRemoved(position)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getUserFaceUrlCache(): UserFaceUrlCache? {
|
|
|
+ // TODO:
|
|
|
+ return null
|
|
|
+ }
|
|
|
+}
|