feat:完成预留活动(模版消息)公屏
This commit is contained in:
@@ -5,6 +5,7 @@ import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUS
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_GUARDIAN_PLANET;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_RED_PACKAGE;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_ROOM_ALBUM;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_ROOM_TEMPLATE;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_BOX_ME;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_CONVERT_L1;
|
||||
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_CONVERT_L2;
|
||||
@@ -145,6 +146,7 @@ import com.yizhuan.xchat_android_core.im.custom.bean.RoomReceivedLuckyGiftAttach
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.RoomTipAttachment;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.TarotAttachment;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.TarotMsgBean;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.TemplateMessageAttachment;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.User;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.VipMessageAttachment;
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.WelcomeAttachment;
|
||||
@@ -255,6 +257,7 @@ public class MessageView extends FrameLayout {
|
||||
private OnClick onClick;
|
||||
|
||||
private OnMsgLongClickListener onLongClickListener;
|
||||
private TemplateMessageAdapter templateMessageAdapter;
|
||||
|
||||
public MessageView(Context context) {
|
||||
this(context, null);
|
||||
@@ -435,7 +438,17 @@ public class MessageView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TemplateMessageAdapter getTemplateMessageAdapter() {
|
||||
if (templateMessageAdapter == null) {
|
||||
templateMessageAdapter = new TemplateMessageAdapter(uid -> {
|
||||
if (clickConsumer != null) {
|
||||
Single.just(String.valueOf(uid)).subscribe(clickConsumer);
|
||||
}
|
||||
});
|
||||
}
|
||||
return templateMessageAdapter;
|
||||
}
|
||||
|
||||
public void onCurrentRoomReceiveNewMsg(List<ChatRoomMessage> messages) {
|
||||
@@ -619,6 +632,19 @@ public class MessageView extends FrameLayout {
|
||||
builder.setSpan(imageSpan, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @param drawable -icon url
|
||||
* @return -返回一個spannableStringBuilder
|
||||
*/
|
||||
public SpannableBuilder appendImg(String drawable, Object what) {
|
||||
if (TextUtils.isEmpty(drawable)) return this;
|
||||
int start = builder.length();
|
||||
builder.append("-");
|
||||
CustomImageSpan imageSpan = new CustomImageSpan(new ColorDrawable(Color.TRANSPARENT), textView, drawable);
|
||||
builder.setSpan(imageSpan, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(what, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param drawable -icon url
|
||||
@@ -635,6 +661,16 @@ public class MessageView extends FrameLayout {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpannableBuilder append(String drawable, int width, int height, Object what) {
|
||||
if (TextUtils.isEmpty(drawable)) return this;
|
||||
int start = builder.length();
|
||||
builder.append("-");
|
||||
CustomImageSpan imageSpan = new CustomImageSpan(new ColorDrawable(Color.TRANSPARENT), textView, drawable, width, height);
|
||||
builder.setSpan(imageSpan, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(what, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本和背景分離的情況
|
||||
*/
|
||||
@@ -676,6 +712,16 @@ public class MessageView extends FrameLayout {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpannableBuilder append(String imgUrl, int height, Object what) {
|
||||
if (TextUtils.isEmpty(imgUrl)) return this;
|
||||
int start = builder.length();
|
||||
builder.append("-");
|
||||
builder.setSpan(new CustomAutoWidthImageSpan(new ColorDrawable(Color.TRANSPARENT), textView, imgUrl, height)
|
||||
, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(what, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param drawable -icon
|
||||
* @param width 寬
|
||||
@@ -1036,6 +1082,13 @@ public class MessageView extends FrameLayout {
|
||||
setRoomAlbumMsg(chatRoomMessage, baseViewHolder);
|
||||
} else if (first == CUSTOM_MSG_GUARDIAN_PLANET) {
|
||||
setGuardianPlanetMsg(chatRoomMessage, tvContent);
|
||||
} else if (first == CUSTOM_MSG_ROOM_TEMPLATE) {
|
||||
TemplateMessageAttachment templateMessageAttachment = (TemplateMessageAttachment) chatRoomMessage.getAttachment();
|
||||
if (templateMessageAttachment != null) {
|
||||
getTemplateMessageAdapter().convert(tvContent, templateMessageAttachment.getTemplateMessage());
|
||||
} else {
|
||||
getTemplateMessageAdapter().convert(tvContent, null);
|
||||
}
|
||||
} else {
|
||||
tvContent.setTextColor(Color.WHITE);
|
||||
tvContent.setText(tvContent.getResources().getText(R.string.not_support_message_tip));
|
||||
|
@@ -0,0 +1,133 @@
|
||||
package com.yizhuan.erban.avroom.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.chuhai.utils.UiUtils
|
||||
import com.yizhuan.erban.common.widget.OriginalDrawStatusClickSpan
|
||||
import com.yizhuan.erban.utils.CommonJumpHelper
|
||||
import com.yizhuan.xchat_android_core.home.bean.BannerInfo
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.TemplateMessage
|
||||
import com.yizhuan.xchat_android_core.im.custom.bean.TemplateMessage.TemplateNode
|
||||
|
||||
/**
|
||||
* Created by Max on 2024/2/22 17:20
|
||||
* Desc:模版消息适配器
|
||||
**/
|
||||
class TemplateMessageAdapter(val listener: Listener) {
|
||||
|
||||
fun convert(textView: TextView, attachment: TemplateMessage?) {
|
||||
if (attachment == null) {
|
||||
textView.text = ""
|
||||
return
|
||||
}
|
||||
val nodeList = attachment.getNodeList()
|
||||
val textBuilder = MessageView.SpannableBuilder(textView)
|
||||
nodeList.forEach {
|
||||
if (it is TemplateNode.NormalNode) {
|
||||
val textColor = parseColor(it.textColor)
|
||||
if (textColor != null) {
|
||||
textBuilder.append(it.text, ForegroundColorSpan(textColor))
|
||||
} else {
|
||||
textBuilder.append(it.text)
|
||||
}
|
||||
} else if (it is TemplateNode.SpecialNode) {
|
||||
when (it.content.type) {
|
||||
TemplateMessage.Content.TEXT -> {
|
||||
val text = it.content.text?.getFirstText()
|
||||
if (!text.isNullOrEmpty()) {
|
||||
val textColor = parseColor(it.content.textColor)
|
||||
val clickSpan = createClickSpan(textView.context, it.content)
|
||||
val list = ArrayList<Any>()
|
||||
if (textColor != null) {
|
||||
list.add(ForegroundColorSpan(textColor))
|
||||
}
|
||||
if (clickSpan != null) {
|
||||
list.add(clickSpan)
|
||||
}
|
||||
textBuilder.append(text, *list.toArray())
|
||||
}
|
||||
}
|
||||
|
||||
TemplateMessage.Content.IMAGE -> {
|
||||
val image = it.content.image
|
||||
val width = it.content.width ?: 0
|
||||
val height = it.content.height ?: 0
|
||||
val clickSpan = createClickSpan(textView.context, it.content)
|
||||
if (height > 0 && width == 0) {
|
||||
if (clickSpan != null) {
|
||||
textBuilder.append(
|
||||
image,
|
||||
UiUtils.dip2px(height.toFloat()),
|
||||
clickSpan
|
||||
)
|
||||
} else {
|
||||
textBuilder.append(image, UiUtils.dip2px(height.toFloat()))
|
||||
}
|
||||
} else if (height > 0 && width > 0) {
|
||||
if (clickSpan != null) {
|
||||
textBuilder.append(
|
||||
image,
|
||||
UiUtils.dip2px(width.toFloat()),
|
||||
UiUtils.dip2px(height.toFloat()), clickSpan
|
||||
)
|
||||
} else {
|
||||
textBuilder.append(
|
||||
image,
|
||||
UiUtils.dip2px(width.toFloat()),
|
||||
UiUtils.dip2px(height.toFloat())
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (clickSpan != null) {
|
||||
textBuilder.appendImg(image, clickSpan)
|
||||
} else {
|
||||
textBuilder.appendImg(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
textView.text = textBuilder.build()
|
||||
textView.setOnClickListener(null)
|
||||
textView.movementMethod = LinkMovementMethod()
|
||||
}
|
||||
|
||||
private fun createClickSpan(
|
||||
context: Context,
|
||||
content: TemplateMessage.Content
|
||||
): OriginalDrawStatusClickSpan? {
|
||||
val skipType = content.getSkipType()
|
||||
val skipUri = content.getSkipUri()
|
||||
if (skipType > 0 && !skipUri.isNullOrEmpty()) {
|
||||
return object : OriginalDrawStatusClickSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
if (skipType == BannerInfo.SKIP_TYPE_ROOM_USER_CARD) {
|
||||
listener.onShowUserCard(skipUri)
|
||||
} else {
|
||||
CommonJumpHelper.bannerJump(context, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseColor(color: String?): Int? {
|
||||
try {
|
||||
return Color.parseColor(color)
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onShowUserCard(uid: String)
|
||||
}
|
||||
}
|
@@ -31,6 +31,10 @@ public class BannerInfo implements Parcelable, Serializable, IRouterData {
|
||||
* routerhandler跳转规则
|
||||
*/
|
||||
public final static transient int SKIP_TYPE_ROUTER = 5;
|
||||
/**
|
||||
* 房间用户资料卡
|
||||
*/
|
||||
public final static transient int SKIP_TYPE_ROOM_USER_CARD = 6;
|
||||
|
||||
/*
|
||||
bannerId:1 //id
|
||||
|
@@ -12,7 +12,9 @@ interface IRouterData : Serializable {
|
||||
|
||||
fun getSkipType(): Int
|
||||
|
||||
@Deprecated("SkipType==5时用到该值,后台讲这种已经没用到了")
|
||||
fun getRouterType(): String?
|
||||
|
||||
@Deprecated("SkipType==5时用到该值,后台讲这种已经没用到了")
|
||||
fun getRouterValue(): String?
|
||||
}
|
@@ -1009,6 +1009,14 @@ public final class IMNetEaseManager {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CUSTOM_MSG_ROOM_TEMPLATE:
|
||||
switch (second){
|
||||
case CUSTOM_MSG_ROOM_TEMPLATE_SUB_ROOM:
|
||||
case CUSTOM_MSG_ROOM_TEMPLATE_SUB_ALL_ROOM:
|
||||
addMessages(msg);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CUSTOM_MSG_RADISH:
|
||||
RoomBoxPrizeAttachment boxPrizeAttachment = ((RoomBoxPrizeAttachment) msg.getAttachment());
|
||||
UserInfo userInfo = UserModel.get().getCacheLoginUserInfo();
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package com.yizhuan.xchat_android_core.bean
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
|
||||
/**
|
||||
* Created by Max on 2024/2/22 18:36
|
||||
* Desc:
|
||||
**/
|
||||
class I18N : HashMap<String, String>(), Serializable {
|
||||
/**
|
||||
* 获取优先显示文本
|
||||
*/
|
||||
fun getFirstText(): String? {
|
||||
// 目前应用只支持繁体,后续支持其他语言,这里需要调整
|
||||
val content = get("zh-TW")
|
||||
return if (content.isNullOrEmpty()) {
|
||||
this.values.firstOrNull()
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
@@ -659,6 +659,12 @@ public class CustomAttachParser implements MsgAttachmentParser {
|
||||
attachment = new RoomAlbumAttachment();
|
||||
}
|
||||
break;
|
||||
case CustomAttachment.CUSTOM_MSG_ROOM_TEMPLATE:
|
||||
if (second == CustomAttachment.CUSTOM_MSG_ROOM_TEMPLATE_SUB_ROOM
|
||||
|| second == CustomAttachment.CUSTOM_MSG_ROOM_TEMPLATE_SUB_ALL_ROOM) {
|
||||
attachment = new TemplateMessageAttachment(first, second);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogUtils.e(ResUtil.getString(R.string.custom_bean_customattachparser_01) + first + " second=" + second);
|
||||
break;
|
||||
|
@@ -485,6 +485,10 @@ public class CustomAttachment implements MsgAttachment {
|
||||
public static final int CUSTOM_MSG_ROOM_ALBUM = 101;
|
||||
public static final int CUSTOM_MSG_ROOM_ALBUM_SUB = 1011;
|
||||
|
||||
// 模版消息
|
||||
public static final int CUSTOM_MSG_ROOM_TEMPLATE = 103;
|
||||
public static final int CUSTOM_MSG_ROOM_TEMPLATE_SUB_ROOM = 1031;
|
||||
public static final int CUSTOM_MSG_ROOM_TEMPLATE_SUB_ALL_ROOM = 1032;
|
||||
|
||||
/**
|
||||
* 自定义消息附件的类型,根据该字段区分不同的自定义消息
|
||||
|
@@ -0,0 +1,132 @@
|
||||
package com.yizhuan.xchat_android_core.im.custom.bean
|
||||
|
||||
import com.chuhai.utils.StringUtils
|
||||
import com.yizhuan.xchat_android_core.bean.I18N
|
||||
import com.yizhuan.xchat_android_core.home.bean.IRouterData
|
||||
import java.io.Serializable
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Created by Max on 2024/2/22 18:51
|
||||
* Desc:
|
||||
**/
|
||||
data class TemplateMessage(
|
||||
var template: I18N? = null,
|
||||
var textColor: String? = null,
|
||||
val contents: List<Content>? = null
|
||||
) : Serializable {
|
||||
|
||||
private val nodeList: List<TemplateNode>? = null
|
||||
|
||||
fun getNodeList(): List<TemplateNode> {
|
||||
var nodeList = this.nodeList
|
||||
if (nodeList == null) {
|
||||
nodeList = parseNodes()
|
||||
}
|
||||
return nodeList
|
||||
}
|
||||
|
||||
// 转化为方便渲染的节点列表
|
||||
private fun parseNodes(): List<TemplateNode> {
|
||||
val templateText = template?.getFirstText()
|
||||
val contentList = this.contents
|
||||
val defTextColor = this.textColor
|
||||
if (templateText.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
if (contentList.isNullOrEmpty()) {
|
||||
return listOf(
|
||||
TemplateNode.NormalNode(
|
||||
text = templateText,
|
||||
textColor = defTextColor
|
||||
)
|
||||
)
|
||||
}
|
||||
val list = ArrayList<TemplateNode>()
|
||||
StringUtils.split(
|
||||
content = templateText,
|
||||
pattern = Pattern.compile("\\{.+?\\}"),
|
||||
onNormalNode = {
|
||||
list.add(
|
||||
TemplateNode.NormalNode(
|
||||
text = it,
|
||||
textColor = defTextColor
|
||||
)
|
||||
)
|
||||
},
|
||||
onMatchNode = { text ->
|
||||
val content = contentList.firstOrNull {
|
||||
"{${it.key}}" == text
|
||||
}
|
||||
if (content?.type == Content.TEXT && content.textColor.isNullOrEmpty()) {
|
||||
// 默认文本色
|
||||
content.textColor = textColor
|
||||
}
|
||||
if (content != null) {
|
||||
list.add(
|
||||
TemplateNode.SpecialNode(
|
||||
content = content
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
data class Content(
|
||||
/**
|
||||
* 公共字段
|
||||
*/
|
||||
val key: String? = null,
|
||||
// TEXT,IMAGE
|
||||
val type: String? = null,
|
||||
val skipType: Int? = null,
|
||||
val skipContent: String? = null,
|
||||
|
||||
/**
|
||||
* 文本相关字段(type=TEXT)
|
||||
*/
|
||||
val text: I18N? = null,
|
||||
var textColor: String? = null,
|
||||
|
||||
/**
|
||||
* 图片相关字段(type=IMAGE)
|
||||
*/
|
||||
val image: String? = null,
|
||||
val width: Int? = null,
|
||||
val height: Int? = null
|
||||
) : Serializable, IRouterData {
|
||||
|
||||
override fun getSkipType(): Int {
|
||||
return skipType ?: 0
|
||||
}
|
||||
|
||||
override fun getRouterType(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getRouterValue(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getSkipUri(): String? {
|
||||
return skipContent
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEXT = "TEXT"
|
||||
const val IMAGE = "IMAGE"
|
||||
}
|
||||
}
|
||||
|
||||
interface TemplateNode : Serializable {
|
||||
data class NormalNode(
|
||||
var text: String,
|
||||
var textColor: String? = null
|
||||
) : TemplateNode
|
||||
|
||||
data class SpecialNode(
|
||||
var content: Content
|
||||
) : TemplateNode
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.yizhuan.xchat_android_core.im.custom.bean
|
||||
|
||||
import com.alibaba.fastjson.JSONObject
|
||||
import com.google.gson.Gson
|
||||
|
||||
/**
|
||||
* Created by Max on 2024/2/22 17:28
|
||||
* Desc:
|
||||
**/
|
||||
class TemplateMessageAttachment : CustomAttachment {
|
||||
|
||||
constructor() : super()
|
||||
constructor(first: Int, second: Int) : super(first, second)
|
||||
|
||||
private var templateMessage: TemplateMessage? = null
|
||||
|
||||
fun getTemplateMessage() = templateMessage
|
||||
|
||||
override fun parseData(data: JSONObject?) {
|
||||
super.parseData(data)
|
||||
if (data != null) {
|
||||
try {
|
||||
templateMessage = Gson().fromJson<TemplateMessage>(
|
||||
data.toJSONString(),
|
||||
TemplateMessage::class.java
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package com.chuhai.utils
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Created by Max on 2/10/21 4:56 PM
|
||||
* Desc:字符串工具
|
||||
*/
|
||||
object StringUtils {
|
||||
|
||||
/**
|
||||
* 拆分字符串(根据匹配规则,按顺序拆分出来)
|
||||
* @param pattern 匹配节点的规则模式
|
||||
* @param onNormalNode<节点内容> 普通节点
|
||||
* @param onMatchNode<节点内容> 匹配节点
|
||||
*/
|
||||
fun split(
|
||||
content: String,
|
||||
pattern: Pattern,
|
||||
onNormalNode: (String) -> Unit,
|
||||
onMatchNode: (String) -> Unit,
|
||||
) {
|
||||
try {
|
||||
if (content.isEmpty()) {
|
||||
onNormalNode.invoke(content)
|
||||
return
|
||||
}
|
||||
val matcher = pattern.matcher(content)
|
||||
// 最后一个匹配项的结束位置
|
||||
var lastItemEnd = 0
|
||||
var noMatch = true
|
||||
while (matcher.find()) {
|
||||
noMatch = false
|
||||
// 匹配元素的开启位置
|
||||
val start = matcher.start()
|
||||
// 匹配元素的结束位置
|
||||
val end = matcher.end()
|
||||
// 匹配元素的文本
|
||||
val text = matcher.group()
|
||||
// 匹配元素的对应索引
|
||||
// logD("split() start:$start ,end:$end ,text:$text")
|
||||
if (start > lastItemEnd) {
|
||||
// 普通节点
|
||||
val nodeContent = content.substring(lastItemEnd, start)
|
||||
onNormalNode.invoke(nodeContent)
|
||||
}
|
||||
// 匹配节点显示内容
|
||||
onMatchNode.invoke(text)
|
||||
lastItemEnd = end
|
||||
}
|
||||
if (lastItemEnd > 0 && lastItemEnd < content.length) {
|
||||
// 最后的匹配项不是尾部(追加最后的尾部)
|
||||
val nodeContent = content.substring(lastItemEnd, content.length)
|
||||
onNormalNode.invoke(nodeContent)
|
||||
}
|
||||
if (noMatch) {
|
||||
// 无匹配
|
||||
onNormalNode.invoke(content)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user